commit cd8d7144d9e32e67d8566fc53dc401a7c4b9264c Author: sftang Date: Tue Mar 8 15:31:53 2022 +0800 第一版v1.0,支持签到功能 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9bb35e3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/logs/* +/db/* +/backups/* +*.pyc diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..10aec7c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,77 @@ +# pull official base image +FROM python:3.9-slim-buster + +# set environment variables +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED 1 +ARG DEBIAN_FRONTEND=noninteractive + +# create the appropriate directories +ENV APP_HOME=/home/data/www +RUN mkdir -p $APP_HOME +WORKDIR $APP_HOME + +RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime +# install dependencies +RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \ + sed -i 's/security.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \ + apt-get update && \ + apt-get install -y apt-utils gnupg libgl1-mesa-glx libglib2.0 + +RUN pip install --upgrade pip -i https://pypi.mirrors.ustc.edu.cn/simple/ + +RUN pip install --no-cache-dir --upgrade pip -i https://pypi.mirrors.ustc.edu.cn/simple/ +#RUN pip install --no-cache-dir --upgrade setuptools -i https://pypi.mirrors.ustc.edu.cn/simple/ + +COPY requirements.txt . + +#将需要的包体全部打包成直接安装的包whl +RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.mirrors.ustc.edu.cn/simple/ + +#安装nginx +#RUN apt install curl gnupg2 ca-certificates lsb-release -y +#RUN curl -o /tmp/nginx_signing.key https://nginx.org/keys/nginx_signing.key && \ +COPY nginx_signing.key /tmp +RUN cat /tmp/nginx_signing.key | apt-key add - + +RUN echo "deb http://nginx.org/packages/debian buster nginx" > /etc/apt/sources.list.d/nginx.list && \ + apt update && \ + #apt install nginx supervisor -y && \ + apt install nginx -y && \ + rm -f /etc/nginx/conf.d/default.conf && \ + ln -sf /dev/stdout /var/log/nginx/access.log && \ + ln -sf /dev/stderr /var/log/nginx/error.log + +#RUN pip install --no-cache-dir supervisor -i https://pypi.mirrors.ustc.edu.cn/simple/ + +COPY conf/nginx.conf /etc/nginx/nginx.conf +COPY conf/vhost.conf /etc/nginx/conf.d/vhost.conf +COPY conf/supervisord.conf /etc/supervisord.conf +COPY start.sh /usr/local/bin/start.sh +RUN chmod +x /usr/local/bin/start.sh + +RUN apt-get purge gnupg -y && \ + apt-get autoremove -y && \ + apt-get clean + +# copy project +COPY . $APP_HOME + +RUN ln -s /home/data/www/db /db && \ + ln -s /home/data/www/logs /logs && \ + ln -s /home/data/www/backups /backups && \ + rm -rf conf && \ + rm -rf docker-compose.yml && \ + rm -rf Dockerfile && \ + rm -rf README.md && \ + rm -rf requirements.txt && \ + rm -rf db/* logs/* backups/* \ + rm -rf .git* + +EXPOSE 80 +#apt-get安装路径 +#CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"] +#pip安装路径 +#CMD ["/usr/local/bin/supervisord", "-c", "/etc/supervisord.conf"] + +CMD ["/usr/local/bin/start.sh"] \ No newline at end of file diff --git a/Dockerfile.alpine b/Dockerfile.alpine new file mode 100644 index 0000000..96336f9 --- /dev/null +++ b/Dockerfile.alpine @@ -0,0 +1,60 @@ +# pull official base image +FROM python:3.9-alpine + +# set environment variables +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED 1 + +# create the appropriate directories +ENV APP_HOME=/home/data/www +RUN mkdir -p $APP_HOME +WORKDIR $APP_HOME + +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories && apk update +RUN apk add --update-cache tzdata && \ + cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && apk del tzdata && \ + rm -rf /var/cache/apk/* + +#apt-get install -y apt-utils gnupg libgl1-mesa-glx libglib2.0 + +RUN apk add --no-cache --virtual .build-deps \ + ca-certificates gcc linux-headers musl-dev \ + libffi-dev jpeg-dev zlib-dev + +RUN pip install --upgrade pip -i https://pypi.mirrors.ustc.edu.cn/simple/ + +RUN pip install --no-cache-dir --upgrade pip -i https://pypi.mirrors.ustc.edu.cn/simple/ +#RUN pip install --no-cache-dir --upgrade setuptools -i https://pypi.mirrors.ustc.edu.cn/simple/ + +COPY requirements.txt . + +#将需要的包体全部打包成直接安装的包whl +RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.mirrors.ustc.edu.cn/simple/ + +COPY start.sh /usr/local/bin/start.sh +RUN chmod +x /usr/local/bin/start.sh + +RUN apk del .build-deps +RUN rm -rf /var/cache/apk/* + +# copy project +COPY . $APP_HOME + +RUN ln -s /home/data/www/db /db && \ + ln -s /home/data/www/logs /logs && \ + ln -s /home/data/www/backups /backups && \ + rm -rf conf && \ + rm -rf docker-compose.yml && \ + rm -rf Dockerfile && \ + rm -rf README.md && \ + rm -rf requirements.txt && \ + rm -rf db/* logs/* backups/* \ + rm -rf .git* + +EXPOSE 80 +#apt-get安装路径 +#CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"] +#pip安装路径 +#CMD ["/usr/local/bin/supervisord", "-c", "/etc/supervisord.conf"] + +CMD ["/usr/local/bin/start.sh"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..8ec82b0 --- /dev/null +++ b/README.md @@ -0,0 +1,98 @@ + +使用Djano编写PT助手,代码完全开放,欢迎有兴趣的小伙伴进行修改发布。禁止用于商业用途。 + +由于涉及ocr本地验证,因此docker镜像相对比较大。 + +# v1.0 说明 +1、支持签到功能 +2、支持配置导出导入 +3、支持签到信息发送IYUU,Telegram,邮箱 + +## 支持站点 +站点 | 签到支持| +--- |--- | +红豆饭(hdfans) | ✔ +壹PT吧(1ptba) | ✔ +铂金学院(ptchina) | ✔ +瓷器(hdchina) | ✔ +芒果(3wmg) | ✔ +碟粉(discfan) | ✔ +杜比(hddolby) | ✔ +阿童木(hdatmos) | ✔ +聆音(soulvoice) | ✔ +铂金家(pthome) | ✔ +高清时间(hdtime) | ✔ +高清地带(hdzone) | ✔ +海棠(htpt) | ✔ +观众(audiences) | ✔ +老师(nicept) | ✔ +家园(hdhome) | ✔ +时间(pttime) | ✔ +柠檬(lemonhd) | ✔ +我堡(ourbits) | ✔ +猫站(pterclub) | ✔ +城市(hdcity) | ✔ +高清视界(hdarea) | ✔ +白兔(hares) | ✔ +A-SOUL(asf) | ✔ +听听歌(totheglory) | ✔ +52PT(52pt) | ✔ +海胆(haidan) | ✔ +皇后(open) | ✔ +天空(hdsky) | ✔ +备胎(beitai) | ✘ +马杀鸡(msg) | ✘ +奥申(oshen) | ✘ +爱薇(avgv) | ✘ +吐鲁番(eastgame) | ✘ +小蚂蚁(hdmayi) | ✘ +朋友(keepfrds) | ✘ +北洋园(tjupt) | ✘ +他吹吹风(et8)) | ✘ +海带(hd) | ✘ +海豹(greatposterwall) | ✘ + + +## 暂时不支持站点 +站点 | +--- | +馒头 | + +## 已知问题站点 +站点 |--- | +--- |--- | +学校(btschool) | ✔ + +## 截图 +1. IYUU +![](https://gcore.jsdelivr.net/gh/xingsu1021/pthelper@master/static/screenshot/iyuu.png) +2. Telegram +![](https://gcore.jsdelivr.net/gh/xingsu1021/pthelper@master/static/screenshot/tg.png) +3. 邮箱 +![](https://gcore.jsdelivr.net/gh/xingsu1021/pthelper@master/static/screenshot/mail.png) +4. 补签 +![](https://gcore.jsdelivr.net/gh/xingsu1021/pthelper@master/static/screenshot/buqian.png) + + +## 感谢 +带带弟弟OCR通用验证码识别:https://github.com/sml2h3/ddddocr +爱语飞飞:https://iyuu.cn + https://github.com/ledccn/IYUUPlus +PTPP:https://github.com/ronggang/PT-Plugin-Plus +PTPP增强版:白大版PTPP@菩提蛋(没找到仓库:() + + +以下忽略,只做个人记录 +------------------------------------------------------------- +# 本地启动测试命令 +python manage.py runserver --settings=pthelper.local_settings + +# 编译镜像 +docker build -t pthelper:v1.0 . + +# py文件编译pyc +python -O -m compileall -b . + +# 上传镜像 +docker commit 4f7d7c5a8b58 xingsu1021/pthelper:latest +docker push xingsu1021/pthelper \ No newline at end of file diff --git a/apps/__init__.py b/apps/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/common/__init__.py b/apps/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/common/admin.py b/apps/common/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/apps/common/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/common/apps.py b/apps/common/apps.py new file mode 100644 index 0000000..01cca2f --- /dev/null +++ b/apps/common/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CommonConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'common' diff --git a/apps/common/exportdata.py b/apps/common/exportdata.py new file mode 100644 index 0000000..c9cd088 --- /dev/null +++ b/apps/common/exportdata.py @@ -0,0 +1,88 @@ +import xlwt +import io +import json +from django.http import HttpResponse + +#导出Excel表格 +def get_excel_stream(file): + # StringIO操作的只能是str,如果要操作二进制数据,就需要使用BytesIO。 + excel_stream = io.BytesIO() + # 这点很重要,传给save函数的不是保存文件名,而是一个BytesIO流(在内存中读写) + file.save(excel_stream) + # getvalue方法用于获得写入后的byte将结果返回给re + res = excel_stream.getvalue() + excel_stream.close() + return res +def export_excel(data, name,header): + # 打开一个Excel工作簿 + file = xlwt.Workbook() + # 新建一个sheet,如果对一个单元格重复操作,会引发异常,所以加上参数cell_overwrite_ok=True + table = file.add_sheet(name, cell_overwrite_ok=True) + if data is None: + return file + # 表示行 + l = 0 + n = len(header) + # 将data中的数据依次写入到table中 + for line in data: + for i in range(n): + table.write(l, i, line[header[i]]) + l += 1 + # 获取将数据保存到流 + res=get_excel_stream(file) + # 返回一个response响应 + response = HttpResponse(content_type='application/vnd.ms-excel') + from urllib import parse + response['Content-Disposition'] = 'attachment;filename=' + parse.quote(excel_name) + '.xls' + # 将文件流写入到response返回 + response.write(res) + return response + +#导出json文件 +def get_stream(item): + # 开始这里我用ByteIO流总是出错,但是后来参考廖雪峰网站用StringIO就没问题 + file = io.StringIO() + data=json.dumps(data) + file.write(data) + res=file.getvalue() + file.close() + return res +def export_json(item): + try: + json_stream=get_stream(item) + response = HttpResponse(content_type='application/json') + from urllib import parse + response['Content-Disposition'] = 'attachment;filename=' + parse.quote(file_name) + '.json' + response.write(json_stream) + return response + except Exception as e: + logging.error(e) + print(e) + +#导出压缩包 +def write_zip(e_data, j_data, export_name): + try: + # 保存到本地文件 + # 返回文件名,注意此时保存的方法和前面导出保存的json、excel文件区别 + j_name = write_json(j_data, export_name[1]) + e_name = write_excel(e_data, export_name[1]) + # 本地文件写入zip,重命名,然后删除本地临时文件 + z_name='export.zip' + z_file = zipfile.ZipFile(z_name, 'w') + z_file.write(j_name) + z_file.write(e_name) + os.remove(j_name) + os.remove(e_name) + z_file.close() + # 再次读取zip文件,将文件流返回,但是此时打开方式要以二进制方式打开 + z_file = open(z_name, 'rb') + data = z_file.read() + z_file.close() + os.remove(z_file.name) + response = HttpResponse(data, content_type='application/zip') + from urllib import parse + response['Content-Disposition'] = 'attachment;filename=' + parse.quote(z_name) + return response + except Exception as e: + logging.error(e) + print(e) \ No newline at end of file diff --git a/apps/common/migrations/0001_initial.py b/apps/common/migrations/0001_initial.py new file mode 100644 index 0000000..7120dfb --- /dev/null +++ b/apps/common/migrations/0001_initial.py @@ -0,0 +1,23 @@ +# Generated by Django 4.0.2 on 2022-03-06 15:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='BaiDuOcr', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('app_id', models.CharField(max_length=50, unique=True, verbose_name='APP_ID')), + ('app_key', models.CharField(max_length=50, unique=True, verbose_name='API_KEY')), + ('secret_key', models.CharField(max_length=50, unique=True, verbose_name='SECRET_KEY')), + ], + ), + ] diff --git a/apps/common/migrations/0002_backupinfo.py b/apps/common/migrations/0002_backupinfo.py new file mode 100644 index 0000000..ffaa2a6 --- /dev/null +++ b/apps/common/migrations/0002_backupinfo.py @@ -0,0 +1,22 @@ +# Generated by Django 4.0.2 on 2022-03-06 17:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('common', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='BackupInfo', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(db_index=True, max_length=50, verbose_name='文件名')), + ('url', models.CharField(max_length=200, verbose_name='备份下载地址')), + ('created_at', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='创建时间')), + ], + ), + ] diff --git a/apps/common/migrations/0003_delete_baiduocr.py b/apps/common/migrations/0003_delete_baiduocr.py new file mode 100644 index 0000000..f61b293 --- /dev/null +++ b/apps/common/migrations/0003_delete_baiduocr.py @@ -0,0 +1,16 @@ +# Generated by Django 4.0.2 on 2022-03-07 19:04 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('common', '0002_backupinfo'), + ] + + operations = [ + migrations.DeleteModel( + name='BaiDuOcr', + ), + ] diff --git a/apps/common/migrations/__init__.py b/apps/common/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/common/models.py b/apps/common/models.py new file mode 100644 index 0000000..2d5c15c --- /dev/null +++ b/apps/common/models.py @@ -0,0 +1,18 @@ +from django.db import models + +# Create your models here. +#class BaiDuOcr(models.Model): + #""" + #百度OCR信息 + #""" + #app_id = models.CharField('APP_ID', max_length=50, unique=True) + #app_key = models.CharField('API_KEY', max_length=50, unique=True) + #secret_key = models.CharField('SECRET_KEY', max_length=50, unique=True) + +class BackupInfo(models.Model): + """ + 备份信息 + """ + name = models.CharField('文件名', max_length=50,db_index = True) + url = models.CharField('备份下载地址', max_length=200,) + created_at = models.DateTimeField('创建时间', auto_now_add=True,db_index = True) \ No newline at end of file diff --git a/apps/common/sites_sign.py b/apps/common/sites_sign.py new file mode 100644 index 0000000..19d92dc --- /dev/null +++ b/apps/common/sites_sign.py @@ -0,0 +1,1161 @@ +# -*- coding:utf-8 -*- +from django.conf import settings +import logging +import os +from bs4 import BeautifulSoup +import requests +import simplejson as json +import re +from .utils import getSiteUrl +import ddddocr + +logger = logging.getLogger('sign') +user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36' + +def hdchina(site_name, site_name_cn, site_url, site_cookie): + """ + 瓷器签到 + """ + headers = { + 'user-agent': user_agent, + 'referer': site_url, + 'cookie': site_cookie + } + sign_url = 'https://hdchina.org/plugin_sign-in.php?cmd=signin' + + logger.info('--------------%s开始签到----------------' % site_name) + + try: + + #请求首页获取csrf + #response = requests.get(site_url, headers=headers, timeout=10) + + #soup = BeautifulSoup(response.text, "lxml") + #csrf = soup.find('meta',{'name':'x-csrf'}) + #csrf = csrf.attrs['content'] + + data = { + #'csrf': csrf + 'csrf': 'e3a2895bb529c567fb2aae3723a97ba9144c1c5c' + } + + response = requests.post(sign_url, headers=headers, data=data) + logger.info(response.text) + if response.status_code == 200: + try: + #正确请求,得到json字符串 + response_msg = json.loads(response.text) + if response_msg['state'] == 'success': + signindays = response_msg['signindays'] + integral = response_msg['integral'] + msg = "%s(%s) 签到成功,连续签到%s,今日签到得%s积分" % (site_name,site_name_cn,str(signindays),str(integral)) + else: + #crsf不正确 + if 'msg' in response_msg: + msg = "%s(%s) 错误:%s" % (site_name,site_name_cn,response_msg['msg']) + return False, msg + else: + msg = "%s(%s) 您今天已经签到过了,请勿重复签到" % (site_name,site_name_cn) + except: + #失败,返回html + msg = "%s(%s) cookie失效" % (site_name,site_name_cn) + return False, msg + + ##cookie失效的抓取(待后期确认页面内容) + #soup = BeautifulSoup(response.text, "lxml") + #error = soup.findAll('h1') + #if len(error) != 0: + #if '未登录' in str(error[0]): + #msg = "%s(%s) cookie失效" % (site_name,site_name_cn) + #return False, msg + + return True, msg + else: + msg = "%s(%s) 请求签到地址失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(response.text) + + return False, msg + + except Exception as e: + msg = "%s(%s) 请求地址失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(str(e)) + + return False, msg + +def hdarea(site_name, site_name_cn, site_url, site_cookie): + """ + 高清视界签到 + """ + headers = { + 'user-agent': user_agent, + 'referer': site_url, + 'cookie': site_cookie + } + sign_url = 'https://www.hdarea.co/sign_in.php' + + logger.info('--------------%s开始签到----------------' % site_name) + + try: + data = { + 'action': 'sign_in' + } + + response = requests.post(sign_url, headers=headers, data=data) + + if response.status_code == 200: + try: + response_msg = response.text + if response_msg.strip() == '': + msg = "%s(%s) cookie失效" % (site_name,site_name_cn) + return False, msg + else: + + if '重复签到' in response_msg: + msg = "%s(%s) 请不要重复签到哦!" % (site_name,site_name_cn) + else: + msg = "%s(%s) 签到成功,%s" % (site_name,site_name_cn,response_msg) + + return True, msg + except: + #失败,返回html + msg = "%s(%s) cookie失效" % (site_name,site_name_cn) + return False, msg + + return True, msg + else: + msg = "%s(%s) 请求签到地址失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(response.text) + + return False, msg + + except Exception as e: + msg = "%s(%s) 请求地址失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(str(e)) + + return False, msg + +def pterclub(site_name, site_name_cn, site_url, site_cookie): + """ + 猫站签到 + """ + headers = { + 'user-agent': user_agent, + 'referer': site_url, + 'cookie': site_cookie + } + #获取网站url,不带/结尾 + sign_url = getSiteUrl(site_url) + '/attendance-ajax.php' + + logger.info('--------------%s开始签到----------------' % site_name) + + try: + + response = requests.get(sign_url, headers=headers) + + if response.status_code == 200: + try: + #正确请求,得到json字符串 + response_msg = json.loads(response.text) + + if response_msg['status'] == '1': + message = response_msg['message'] + msg = "%s(%s) 签到成功,%s" % (site_name,site_name_cn,message.replace('

','').replace('

','')) + + else: + msg = "%s(%s) 您今天已经签到过了,请勿重复签到" % (site_name,site_name_cn) + + except: + #失败,返回html + msg = "%s(%s) cookie失效" % (site_name,site_name_cn) + return False, msg + + return True, msg + else: + msg = "%s(%s) 请求签到地址失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(response.text) + + return False, msg + + except Exception as e: + msg = "%s(%s) 请求地址失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(str(e)) + + return False, msg + +def hdcity(site_name, site_name_cn, site_url, site_cookie): + """ + 城市签到 + """ + + headers = { + 'user-agent': user_agent, + 'referer': site_url, + 'cookie': site_cookie + } + + #获取网站url,不带/结尾 + sign_url = getSiteUrl(site_url) + '/sign' + + logger.info('--------------%s开始签到----------------' % site_name) + + try: + response = requests.get(sign_url, headers=headers, timeout=10) + + if response.status_code == 200: + + #二次请求 + response = requests.get(sign_url, headers=headers, timeout=10) + + html = response.text + + soup = BeautifulSoup(html, "lxml") + + form = soup.findAll('form',{'action':'signinhandler'}) + + if len(form) >= 1: + #cookie失效 + msg = "%s(%s) cookie失效" % (site_name,site_name_cn) + return False, msg + else: + tables = soup.findAll('div', {'class':'container'}) + + check_sign = tables[1].find('span',{'class':'colored'}).get_text() + if '已经签过' in check_sign: + data = tables[1].find('div',{'class':'col-md-12'}).p.get_text() + msg = "%s(%s) %s" % (site_name,site_name_cn, data.replace('

','').replace('

','')) + else: + msg = "%s(%s) 未知错误" % (site_name,site_name_cn) + return False, msg + + return True, msg + else: + msg = "%s(%s) 请求签到地址失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(response.text) + + return False, msg + + except Exception as e: + msg = "%s(%s) 请求失败" % (site_name,site_name_cn) + logger.error(str(e)) + + return False, msg + +def btschool(site_name, site_name_cn, site_url, site_cookie): + """ + 学校签到 + """ + + headers = { + 'user-agent': user_agent, + 'referer': site_url, + 'cookie': site_cookie + } + + #获取网站url,不带/结尾 + sign_url = getSiteUrl(site_url) + '/index.php?action=addbonus' + + logger.info('--------------%s开始签到----------------' % site_name) + + #s = requests.session() + #cookie = cookie_parse(site_cookie) + #s.headers.update(headers) + #s.cookies.update(cookie) + try: + response = requests.get(sign_url, headers=headers, timeout=10) + #response = s.get(sign_url) + #print(response.text) + + if response.status_code == 200: + + html = response.text + + soup = BeautifulSoup(html, "lxml") + font = soup.findAll('font',{'color':'white'}) + + if len(font) != 0: + msg = "%s(%s) %s" % (site_name,site_name_cn,font[0].get_text()) + else: + tables = soup.findAll('table', {'id':'info_block'}) + + if len(tables) != 0: + msg = "%s(%s) 您今天已经签到过了,请勿重复签到" % (site_name,site_name_cn) + else: + msg = "%s(%s) cookie失效" % (site_name,site_name_cn) + return False, msg + + return True, msg + else: + msg = "%s(%s) 请求签到地址失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(response.text) + + return False, msg + + except Exception as e: + msg = "%s(%s) 请求失败" % (site_name,site_name_cn) + logger.error(str(e)) + + return False, msg + +def hares(site_name, site_name_cn, site_url, site_cookie): + """ + 白兔签到 + """ + + headers = { + 'user-agent': user_agent, + 'referer': site_url, + 'cookie': site_cookie + } + + #获取网站url,不带/结尾 + sign_url = getSiteUrl(site_url) + '/attendance.php' + + logger.info('--------------%s开始签到----------------' % site_name) + + try: + response = requests.get(sign_url, headers=headers, timeout=10) + + if response.status_code == 200: + + html = response.text + + soup = BeautifulSoup(html, "lxml") + #script = soup.findAll('script') + #sign_data = script[-1] + #print(sign_data) + pattern = re.compile(r"layer.confirm\('(.*)',") + script = soup.find("script", text=pattern) + + if script != None: + + data = re.search(r"layer.confirm\('(.*),",str(script)).group() + sign_data = data.replace("layer.confirm('",'').replace("。',",'') + + if '签到获得' in sign_data: + #替换掉p标签,tg不支持 + msg = "%s(%s) %s" % (site_name,site_name_cn,sign_data) + + else: + msg = "%s(%s) 您今天已经签到过了,请勿重复签到" % (site_name,site_name_cn) + else: + msg = "%s(%s) cookie失效" % (site_name,site_name_cn) + return False, msg + + return True, msg + else: + msg = "%s(%s) 请求签到地址失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(response.text) + + return False, msg + + except Exception as e: + msg = "%s(%s) 请求失败" % (site_name,site_name_cn) + logger.error(str(e)) + + return False, msg + + +def general(site_name, site_name_cn, site_url, site_cookie): + """ + 常规签到 + """ + + headers = { + 'user-agent': user_agent, + 'referer': site_url, + 'cookie': site_cookie + } + + #获取网站url,不带/结尾 + sign_url = getSiteUrl(site_url) + '/attendance.php' + + logger.info('--------------%s开始签到----------------' % site_name) + + try: + response = requests.get(sign_url, headers=headers, timeout=10) + + if response.status_code == 200: + + html = response.text + + #需要安装pip install lxml + soup = BeautifulSoup(html, "lxml") + + for j in soup.findAll('table', {'class':'main'}): + if '签到成功' in str(j) or '簽到成功' in str(j): + #print(i) + #info = i.findAll('td', {'class':'text'}) + d = j.find('p') + #替换掉p标签,tg不支持 + msg = "%s(%s) %s" % (site_name,site_name_cn,str(d).replace('

','').replace('

','')) + + elif '已经签到' in str(j) or '已經簽到' in str(j): + msg = "%s(%s) 您今天已经签到过了,请勿重复签到" % (site_name,site_name_cn) + + #cookie失效的抓取 + error_table = soup.findAll('table', {'class':'mainouter'}) + + if len(error_table) != 0: + fail_msg = error_table[0].find('p') + + if '错误' in str(fail_msg) or '錯誤' in str(fail_msg): + msg = "%s(%s) cookie失效" % (site_name,site_name_cn) + return False, msg + + return True, msg + else: + msg = "%s(%s) 请求签到地址失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(response.text) + + return False, msg + + except Exception as e: + msg = "%s(%s) 请求失败" % (site_name,site_name_cn) + logger.error(str(e)) + + return False, msg + +def noSign(site_name, site_name_cn, site_url, site_cookie): + """ + 无签到,直接访问 + """ + + headers = { + 'user-agent': user_agent, + 'cookie': site_cookie + } + + #获取网站url,不带/结尾 + sign_url = getSiteUrl(site_url) + '/index.php' + + logger.info('--------------%s开始签到----------------' % site_name) + + try: + response = requests.get(sign_url, headers=headers, timeout=10) + + if response.status_code == 200: + #需要安装pip install lxml + soup = BeautifulSoup(response.text, "lxml") + + tables = soup.findAll('table', {'id':'info_block'}) + + if len(tables) != 0: + + #span获取有2个,一个为用户信息,一个为信箱信息 + info = tables[0].findAll('span', {'class':'medium'}) + user_info = info[0] + + #print(user_info) + #print("-------------") + #用户详情链接 + user_info_link = user_info.a.attrs['href'] + #用户名 + user_name = user_info.a.get_text() + #魔力值 + bonus = user_info.find('font',{'class':'color_bonus'}).nextSibling.nextSibling.nextSibling.get_text().replace(']:','') + #分享率 + ratio = user_info.find('font',{'class':'color_ratio'}).nextSibling.get_text() + #上传量 + uploaded = user_info.find('font',{'class':'color_uploaded'}).nextSibling.get_text() + #下载量 + downloaded = user_info.find('font',{'class':'color_downloaded'}).nextSibling.get_text() + + #print(user_info.find('font',{'class':'color_active'}).get_text(),user_info.find('img',{'class':'arrowup'}).nextSibling.get_text(),user_info.find('img',{'class':'arrowdown'}).nextSibling.get_text()) + #print("-------------") + message_info = info[1] + new_message = message_info.a.nextSibling.get_text() + #print('用户详情链接:%s' % user_info_link) + msg = "%s(%s) 魔力:%s,分享率:%s,新消息:%s" % (site_name,site_name_cn, bonus,ratio,new_message) + + return True, msg + else: + msg = "%s(%s) 登录网站失败,cookie已经失效" % (site_name,site_name_cn) + return False, msg + + else: + msg = "%s(%s) 请求地址失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(msg) + + return False, msg + + except Exception as e: + msg = "%s(%s) 请求失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(str(e)) + + return False, msg + +def keepfrds(site_name, site_name_cn, site_url, site_cookie): + """ + keepfrds 朋友 + """ + + headers = { + 'user-agent': user_agent, + 'cookie': site_cookie + } + + #获取网站url,不带/结尾 + sign_url = getSiteUrl(site_url) + '/index.php' + + logger.info('--------------%s开始签到----------------' % site_name) + + try: + response = requests.get(sign_url, headers=headers, timeout=10) + + if response.status_code == 200: + #需要安装pip install lxml + soup = BeautifulSoup(response.text, "lxml") + + tables = soup.findAll('table', {'id':'info_block'}) + + if len(tables) != 0: + + #获取有3个,0:所有,1:为用户信息,2:为信箱信息 + tds = tables[0].findAll('td') + td1 = tds[1].findAll('span',{'class':'nowrap'}) + + #用户详情链接 + user_info_link = td1[0].a.attrs['href'] + #用户名 + user_name = td1[0].a.get_text() + + #魔力值 + bonus = tds[1].find('font',{'class':'color_bonus'}).nextSibling.nextSibling.nextSibling.get_text().replace(']:','') + #分享率 + ratio = tds[1].find('font',{'class':'color_ratio'}).nextSibling.get_text() + #上传量 + uploaded = tds[1].find('font',{'class':'color_uploaded'}).nextSibling.get_text() + #下载量 + downloaded = tds[1].find('font',{'class':'color_downloaded'}).nextSibling.get_text() + + message_info = tds[2] + new_message = message_info.a.get_text() + if new_message == "": + new_message = 0 + msg = "%s(%s) 魔力:%s,分享率:%s,新消息:%s" % (site_name,site_name_cn, bonus,ratio,new_message) + + return True, msg + else: + msg = "%s(%s) 登录网站失败,cookie已经失效" % (site_name,site_name_cn) + return False, msg + + else: + msg = "%s(%s) 请求地址失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(msg) + + return False, msg + + except Exception as e: + msg = "%s(%s) 请求失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(str(e)) + + return False, msg + +def tjupt(site_name, site_name_cn, site_url, site_cookie): + """ + tjupt 北洋园 + """ + + headers = { + 'user-agent': user_agent, + 'cookie': site_cookie + } + + #获取网站url,不带/结尾 + sign_url = getSiteUrl(site_url) + '/index.php' + + logger.info('--------------%s开始签到----------------' % site_name) + + try: + response = requests.get(sign_url, headers=headers, timeout=10) + + if response.status_code == 200: + #需要安装pip install lxml + soup = BeautifulSoup(response.text, "lxml") + + tables = soup.findAll('table', {'id':'info_block'}) + if len(tables) != 0: + #获取有1个 + tds = tables[0].findAll('td') + td1 = tds[1].findAll('span',{'class':'nowrap'}) + + #用户详情链接 + user_info_link = td1[0].a.attrs['href'] + #用户名 + user_name = td1[0].a.get_text() + + #魔力值 + bonus = tds[1].find('a',{'href':'mybonusapps.php'}).nextSibling.get_text().replace(']:','') + + #上传量 + uploaded = tds[1].find('font',{'class':'color_uploaded'}).nextSibling.get_text() + #HR积分 + ratio = tds[1].find('a',{'href':'/hnr_bonus.php'}).nextSibling.get_text().replace(']:','') + + new_message = tds[2].a.nextSibling.get_text() + + if new_message == "": + new_message = 0 + msg = "%s(%s) 魔力:%s,HR积分:%s,新消息:%s" % (site_name,site_name_cn, bonus,ratio,new_message) + + return True, msg + + else: + msg = "%s(%s) 登录网站失败,cookie已经失效" % (site_name,site_name_cn) + return False, msg + + else: + msg = "%s(%s) 请求地址失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(msg) + + return False, msg + + except Exception as e: + msg = "%s(%s) 请求失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(str(e)) + + return False, msg + +def hd(site_name, site_name_cn, site_url, site_cookie): + """ + 海带,直接访问 + """ + + headers = { + 'user-agent': user_agent, + 'cookie': site_cookie + } + + #获取网站url,不带/结尾 + sign_url = getSiteUrl(site_url) + '/index.php' + + logger.info('--------------%s开始签到----------------' % site_name) + + try: + response = requests.get(sign_url, headers=headers, timeout=10) + + if response.status_code == 200: + #需要安装pip install lxml + soup = BeautifulSoup(response.text, "lxml") + + div = soup.findAll('div', {'class':'site-topbar'}) + + if len(div) != 0: + #上传,下载,分享 + nav = div[0].findAll('div', {'class':'topbar-nav'}) + + navs = nav[0].findAll('a') + #上传量 + uploaded = navs[0].get_text().strip() + #下载量 + downloaded= navs[1].get_text().strip() + #分享率 + ratio = navs[2].get_text().strip() + + #用户信息,信箱 + info = div[0].findAll('div', {'class':'topbar-info'}) + infos = info[0].findAll('a') + #print(infos) + #用户详情链接 + user_info_link = infos[0].attrs['href'] + #魔力值 + bonus = infos[1].get_text().strip() + #邀请数量 + invite = infos[2].get_text().strip() + #消息 + new_message = infos[3].get_text().strip() + + msg = "%s(%s) 魔力:%s,分享率:%s,新消息:%s" % (site_name,site_name_cn, bonus,ratio,new_message) + + return True, msg + else: + msg = "%s(%s) 登录网站失败,cookie已经失效" % (site_name,site_name_cn) + return False, msg + + else: + msg = "%s(%s) 请求地址失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(msg) + + return False, msg + + except Exception as e: + msg = "%s(%s) 请求失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(str(e)) + + return False, msg + +def ttg(site_name, site_name_cn, site_url, site_cookie): + """ + 听听歌,签到 + """ + + headers = { + 'user-agent': user_agent, + 'referer': site_url, + 'cookie': site_cookie + } + + #获取网站url,不带/结尾 + sign_url = getSiteUrl(site_url) + '/signed.php' + + logger.info('--------------%s开始签到----------------' % site_name) + + try: + #首先请求获取token + response = requests.get(site_url, headers=headers, timeout=10) + if response.status_code == 200: + #TTG返回页面乱码,需要处理 + html = response.text.encode(response.encoding).decode('utf-8') + try: + b = re.search(r'signed_timestamp.*\}',html).group() + data = b.replace('}','') + signed_timestamp = data.split(',')[0].split(':')[-1].strip().replace("\"","") + signed_token = data.split(',')[1].split(':')[-1].strip().replace("\"","") + + data = { + 'signed_timestamp':signed_timestamp, + 'signed_token':signed_token + } + + response = requests.post(sign_url, headers=headers, data=data) + response_msg = response.text.encode(response.encoding).decode('utf-8') + if '已签到过' in response_msg: + msg = "%s(%s) 您今天已经签到过了,请勿重复签到" % (site_name,site_name_cn) + else: + msg = "%s(%s) %s" % (site_name,site_name_cn, response_msg) + + except: + msg = "%s(%s) 登录网站失败,cookie已经失效" % (site_name,site_name_cn) + return False, msg + + return True, msg + else: + msg = "%s(%s) 请求地址失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(msg) + + return False, msg + + except Exception as e: + msg = "%s(%s) 请求失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(str(e)) + + return False, msg + +def pt52(site_name, site_name_cn, site_url, site_cookie): + """ + 52pt,签到 + """ + + headers = { + 'user-agent': user_agent, + 'referer': site_url, + 'cookie': site_cookie + } + + #获取网站url,不带/结尾 + sign_url = getSiteUrl(site_url) + '/bakatest.php' + + logger.info('--------------%s开始签到----------------' % site_name) + + try: + #获取问题id + response = requests.get(sign_url, headers=headers, timeout=10) + html = response.text + + soup = BeautifulSoup(html, "lxml") + input = soup.findAll('input', {'name':'questionid'}) + + if len(input) != 0: + #上传,下载,分享 + value = input[0].attrs['value'] + + data = {'wantskip': '不会', + 'choice': [1], + 'questionid': value + } + #请求签到 + response = requests.post(sign_url, headers=headers, data=data, timeout=10) + + html = response.text + soup = BeautifulSoup(html, "lxml") + font = soup.findAll('font', {'color':'white'}) + + if len(font) != 0: + sign_msg = font[0].get_text().strip() + + msg = "%s(%s) %s" % (site_name,site_name_cn,sign_msg ) + else: + msg = "%s(%s) 未知错误" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + return False, msg + + return True, msg + else: + font = soup.findAll('font', {'color':'white'}) + + if len(font) != 0: + msg = "%s(%s) 您今天已经签到过了,请勿重复签到" % (site_name,site_name_cn) + else: + #input和font都没有找到,说明cookie失效 + msg = "%s(%s) 登录网站失败,cookie已经失效" % (site_name,site_name_cn) + return False, msg + + return True, msg + + except Exception as e: + msg = "%s(%s) 请求失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(str(e)) + + return False, msg + +def greatposterwall(site_name, site_name_cn, site_url, site_cookie): + """ + 海豹,直接访问 + """ + + headers = { + 'user-agent': user_agent, + 'cookie': site_cookie + } + + #获取网站url,不带/结尾 + sign_url = getSiteUrl(site_url) + '/index.php' + + logger.info('--------------%s开始签到----------------' % site_name) + + try: + response = requests.get(sign_url, headers=headers, timeout=10) + + if response.status_code == 200: + + soup = BeautifulSoup(response.text, "lxml") + #常规 + ul1 = soup.findAll('ul', {'class':'Header-infoMiddle Header-stat'}) + #积分,用户链接 + ul2 = soup.findAll('ul', {'class':'Header-infoRight Header-quickaction'}) + + + if len(ul1) != 0: + + value = ul1[0].findAll('span',{'class':'Header-statValue'}) + + #上传量 + uploaded = value[0].get_text().strip() + #下载量 + downloaded= value[1].get_text().strip() + #分享率 + ratio = value[2].get_text().strip() + #合格分享率 + required_ratio = value[3].get_text().strip() + #令牌 + fl_token = value[4].get_text().strip() + #积分 + value_bonus = ul2[0].findAll('a',{'class':'Header-quickactionLink tooltip'}) + bonus = value_bonus[1].attrs['title'].replace("积分 ","").replace("(","").replace(")","") + + value_user_info_link = ul2[0].findAll('a',{'class':'Header-moreLink DropdownMenu-item is-profile'}) + #用户链接 + user_info_link = value_user_info_link[0].attrs['href'] + msg = "%s(%s) 积分:%s,分享率:%s,令牌:%s" % (site_name,site_name_cn, bonus,ratio,fl_token) + + return True, msg + + else: + msg = "%s(%s) 登录网站失败,cookie已经失效" % (site_name,site_name_cn) + return False, msg + + else: + msg = "%s(%s) 请求地址失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(msg) + + return False, msg + + except Exception as e: + msg = "%s(%s) 请求失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(str(e)) + + return False, msg + +def opencd(site_name, site_name_cn, site_url, site_cookie): + """ + 皇后,签到 + """ + + + headers = { + 'user-agent': user_agent, + 'referer': site_url, + 'cookie': site_cookie + } + + image_code_name = os.path.join(settings.TMP_LOG_DIR,'opencd_code.png') + + logger.info('--------------%s开始签到----------------' % site_name) + + status = False + ocr = ddddocr.DdddOcr(show_ad=False,old=True) + try: + #验证码签到执行3次验证 + for i in range(3): + #验证成功退出 + if status or i > 2: + break + + response = requests.get("https://open.cd/plugin_sign-in.php", headers=headers, timeout=10) + if response.status_code == 200: + html = response.text + #print(html) + soup = BeautifulSoup(html, "lxml") + value = soup.findAll('input',{'name':'imagehash'}) + if len(value) != 0: + + imagehash = value[0].attrs['value'] + #https://open.cd/image.php?action=regimage&imagehash=672effec852f76345b025ea97f5a4e7e + img_url = "https://open.cd/image.php?action=regimage&imagehash=" + imagehash + logger.info(img_url) + + response = requests.get(img_url, headers=headers, timeout=10) + #获取文件内容 #text(字符串) content(二进制) json(对象) + #image_data = response.content + with open(image_code_name, "wb") as fp: + fp.write(response.content) + + with open(image_code_name, 'rb') as f: + image = f.read() + + data_ocr = ocr.classification(image) + logger.info(data_ocr) + + data = {'imagehash': imagehash, + 'imagestring': data_ocr + } + response = requests.post("https://open.cd/plugin_sign-in.php?cmd=signin", headers=headers, data=data, timeout=10) + logger.info(response.text) + + result = json.loads(response.text) + + if result['state'] == 'success': + signindays = result['signindays'] + integral = result['integral'] + msg = "%s(%s) 连续签到%s,本次获得%s" % (site_name,site_name_cn, signindays,integral) + status = True + return True, msg + else: + msg = "%s(%s) 签到失败:%s" % (site_name,site_name_cn, result['msg']) + if status or i > 2: + return False, msg + + else: + msg = "%s(%s) 登录网站失败,cookie已经失效" % (site_name,site_name_cn) + return False, msg + + else: + msg = "%s(%s) 请求地址失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(msg) + + return False, msg + + except Exception as e: + msg = "%s(%s) 请求失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(str(e)) + + return False, msg + +def hdsky(site_name, site_name_cn, site_url, site_cookie): + """ + 天空,签到 + """ + + headers = { + 'user-agent': user_agent, + 'referer': site_url, + 'cookie': site_cookie + } + + sign_url = 'https://hdsky.me/image_code_ajax.php' + + data = { + 'action': 'new' + } + + image_code_name = os.path.join(settings.TMP_LOG_DIR,'hdsky_code.png') + + logger.info('--------------%s开始签到----------------' % site_name) + status = False + ocr = ddddocr.DdddOcr(show_ad=False,old=True) + try: + #验证码签到执行3次验证 + for i in range(3): + #验证成功退出 + if status or i > 2: + break + + response = requests.post(sign_url, headers=headers, data=data, timeout=10) + if response.status_code == 200: + result = json.loads(response.text) + if result['success']: + code = result['code'] + #拼接验证码图片 + hdsky_image_url = 'https://hdsky.me/image.php?action=regimage&imagehash=' + code + response = requests.get(hdsky_image_url, headers=headers, timeout=10) + with open(image_code_name, "wb") as fp: + fp.write(response.content) + + with open(image_code_name, 'rb') as f: + image = f.read() + + data_ocr = ocr.classification(image) + logger.info(data_ocr) + + data = {'action': 'showup', + 'imagehash': code, + 'imagestring': data_ocr + } + response = requests.post("https://hdsky.me/showup.php", headers=headers, data=data, timeout=10) + try: + result = json.loads(response.text) + if result['success']: + msg = "%s(%s) 连续签到%s" % (site_name,site_name_cn, result['message']) + status = True + return True, msg + else: + if 'invalid_imagehash' == result['message']: + msg = "%s(%s) 您今天已经签到过了,请勿重复签到" % (site_name,site_name_cn) + status = True + return True, msg + else: + msg = "%s(%s) 错误:%s" % (site_name,site_name_cn, result['message']) + if status or i > 2: + return False,msg + except: + msg = "%s(%s) 登录网站失败,cookie已经失效" % (site_name,site_name_cn) + return False, msg + + + else: + msg = "%s(%s) 请求地址失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(msg) + + return False, msg + + except Exception as e: + msg = "%s(%s) 请求失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(str(e)) + + return False, msg + +def haidan(site_name, site_name_cn, site_url, site_cookie): + """ + 海胆,签到,由于海胆请求后返回登录页面,因此先验证数据 + """ + + headers = { + 'user-agent': user_agent, + 'referer': site_url, + 'cookie': site_cookie + } + + sign_url = 'https://www.haidan.video/signin.php' + + url = getSiteUrl(site_url) + '/index.php' + + logger.info('--------------%s开始签到----------------' % site_name) + + try: + + response = requests.get(url, headers=headers, timeout=10) + if response.status_code == 200: + html = response.text + soup = BeautifulSoup(html, "lxml") + #常规 + div = soup.findAll('div', {'class':'userinfo-half'}) + + if len(div) != 0: + + #用户连接 + user_info = div[0].findAll('span',{'class':'nowrap username-center'}) + user_info_link = user_info[0].a.attrs['href'] + + #签到状态 + sign_status = div[0].findAll('input',{'class':'dt_button'}) + sign_status = sign_status[0].attrs['value'] + + #详细信息 + value = div[1].findAll('div',{'class':'user_item'}) + #积分 + bonus = value[0].span.get_text().strip() + + #魔力值 + magic_num = value[1].span.get_text().strip() + + #分享率 + ratio = value[2].font.nextSibling.get_text().strip() + + #上传量 + uploaded = value[3].font.nextSibling.get_text().strip() + + #下载量 + downloaded= value[4].font.nextSibling.get_text().strip() + + #收件箱 + message = value[6].a.nextSibling.get_text().strip() + + if '已经' not in sign_status: + #开始签到 + response = requests.get(sign_url, headers=headers, timeout=10) + + if response.status_code == 200: + + msg = "%s(%s) 签到成功" % (site_name,site_name_cn) + return True, msg + else: + msg = "%s(%s) 签到失败" % (site_name,site_name_cn) + return False, msg + else: + msg = "%s(%s) 您今天已经签到过了,请勿重复签到" % (site_name,site_name_cn) + return True, msg + + else: + msg = "%s(%s) 登录网站失败,cookie已经失效" % (site_name,site_name_cn) + return False, msg + + else: + msg = "%s(%s) 请求地址失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(msg) + + return False, msg + + except Exception as e: + msg = "%s(%s) 请求失败" % (site_name,site_name_cn) + logger.error('--------------%s----------------' % site_name) + logger.error(str(e)) + + return False, msg \ No newline at end of file diff --git a/apps/common/templates/common/backup_load.html b/apps/common/templates/common/backup_load.html new file mode 100644 index 0000000..89083b4 --- /dev/null +++ b/apps/common/templates/common/backup_load.html @@ -0,0 +1,75 @@ + + +
+
+
+ +
{% csrf_token %} + + +
+
+ + + +
+
+
+ +
+
+ +
+
说明
+
+
备份恢复数据 +
+
+
+
+ + + \ No newline at end of file diff --git a/apps/common/templates/common/baidu_ocr.html b/apps/common/templates/common/baidu_ocr.html new file mode 100644 index 0000000..7a5a248 --- /dev/null +++ b/apps/common/templates/common/baidu_ocr.html @@ -0,0 +1,134 @@ + + +
+
+
+ +
{% csrf_token %} + + +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ + + +
+
+ +
+
+ + +
+
+
+ +
+
+ +
+
说明
+
+
申请百度云的免费图片和文字识别,用于验证码识别,天空、皇后必须 +
+
+
+
+ + + \ No newline at end of file diff --git a/apps/common/tests.py b/apps/common/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/common/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/common/urls/__init__.py b/apps/common/urls/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/apps/common/urls/__init__.py @@ -0,0 +1 @@ + diff --git a/apps/common/urls/views_urls.py b/apps/common/urls/views_urls.py new file mode 100644 index 0000000..d746771 --- /dev/null +++ b/apps/common/urls/views_urls.py @@ -0,0 +1,19 @@ +# coding:utf-8 +from django.urls import path +from .. import views, views_request + +app_name = 'common' + +urlpatterns = [ + # Resource asset path + + #百度ocr + #path('baiduocr', views.BaiDuOcrView.as_view()), + #备份恢复 + path('backupload', views.BackupLoadView.as_view()), + path('backup', views_request.backup), + #补签 + path('signagain', views_request.signAgain), + +] + diff --git a/apps/common/utils.py b/apps/common/utils.py new file mode 100644 index 0000000..8b33e93 --- /dev/null +++ b/apps/common/utils.py @@ -0,0 +1,670 @@ +# -*- coding: utf-8 -*- +# +from django.core.files.storage import FileSystemStorage +from django.conf import settings +from django.utils.encoding import filepath_to_uri +from collections import OrderedDict +import os +import sys +import logging +import datetime +import time +import hashlib +from email.utils import formatdate +import calendar +import threading +import uuid +import requests +import simplejson as json +import ftplib +import socket +from urllib.parse import urlparse +import pytz +from collections import OrderedDict +import smtplib +from email.mime.text import MIMEText +from email.header import Header +import telegram +from urllib import parse +#from PIL import Image +#from io import BytesIO + +logger = logging.getLogger('django') + +def download_file(url, fname): + """ + 下载文件 + """ + + download_path = os.path.join(settings.BASE_DIR, 'downloads') + + if not os.path.exists(download_path): + os.makedirs(download_path) + + download_file = os.path.join(download_path, fname) + + data = requests.get(url, stream=True) + try: + data.raise_for_status() + + with open(download_file, "wb") as updatefile: + for chunk in data.iter_content(chunk_size=1024): + if chunk: + updatefile.write(chunk) + return True, download_file + except Exception as exc: + print('There was a problem: %s' % (exc)) + return False, None + +def check_filename(name, extname = ['rpm','tar','tar.gz','gz'], is_url=True): + """ + 检查文件是否是需要的扩展名 + 返回: True/False, 文件名 + """ + if is_url: + #解析url + parse_url = urlparse(name) + url_path=parse_url.path + pkg_name = (os.path.basename(url_path)) + #file_name, extension_name = os.path.splitext(pkg_name) + extension_name = os.path.splitext(pkg_name)[-1][1:] + else: + pkg_name = (os.path.basename(name)) + extension_name = os.path.splitext(pkg_name)[-1][1:] + + if extension_name in extname: + return True, pkg_name + else: + return False, pkg_name + + +def get_logger(name=None): + return logging.getLogger('%s' % name) + +def get_clientip(request): + if 'HTTP_X_FORWARDED_FOR' in request.META: + clientip = request.META['HTTP_X_FORWARDED_FOR'] + else: + clientip = request.META['REMOTE_ADDR'] + + return {'clientip': clientip, 'user': request.user} + + +def timesince(dt, since='', default="just now"): + """ + Returns string representing "time since" e.g. + 3 days, 5 hours. + """ + + if since == '': + since = datetime.datetime.utcnow() + + if since == None: + return default + + diff = since - dt + + periods = ( + (diff.days / 365, "year", "years"), + (diff.days / 30, "month", "months"), + (diff.days / 7, "week", "weeks"), + (diff.days, "day", "days"), + (diff.seconds / 3600, "hour", "hours"), + (diff.seconds / 60, "minute", "minutes"), + (diff.seconds, "second", "seconds"), + ) + + for period, singular, plural in periods: + if period: + return "%d %s" % (period, singular if period == 1 else plural) + return default + +_STRPTIME_LOCK = threading.Lock() + +_GMT_FORMAT = "%a, %d %b %Y %H:%M:%S GMT" +_ISO8601_FORMAT = "%Y-%m-%dT%H:%M:%S.000Z" + +def now_date(): + """当前时间""" + return datetime.datetime.now() + +def date_to_unixtime(datetime): + """将datetime转换成unixtime""" + return time.mktime(datetime.timetuple()) + +def to_unixtime_old(time_string, format_string): + time_string = time_string.decode("ascii") + with _STRPTIME_LOCK: + return int(calendar.timegm(time.strptime(time_string, format_string))) + +def to_unixtime(time_string, format_string): + """ + 时间字符串转成unix时间戳 + """ + return int(calendar.timegm(time.strptime(time_string, format_string))) + +def http_date(timeval=None): + """返回符合HTTP标准的GMT时间字符串,用strftime的格式表示就是"%a, %d %b %Y %H:%M:%S GMT"。 + 但不能使用strftime,因为strftime的结果是和locale相关的。 + """ + return formatdate(timeval, usegmt=True) + + +def http_to_unixtime(time_string): + """把HTTP Date格式的字符串转换为UNIX时间(自1970年1月1日UTC零点的秒数)。 + + HTTP Date形如 `Sat, 05 Dec 2015 11:10:29 GMT` 。 + """ + return to_unixtime(time_string, _GMT_FORMAT) + +def cst_to_unixtime(time_string): + """ + 2018-10-17 12:39:30 CST+0800 + """ + _CST_FORMAT = "%Y-%m-%d %H:%M:%S CST%z" + return to_unixtime(time_string, _CST_FORMAT) + +def iso8601_to_unixtime(time_string): + """把ISO8601时间字符串(形如,2012-02-24T06:07:48.000Z)转换为UNIX时间,精确到秒。""" + return to_unixtime(time_string, _ISO8601_FORMAT) + +def str2datetime(st, s_format='%Y-%m-%d %H:%M:%S'): + """字符串转成datetime + CST_FORMAT = "%Y-%m-%d %H:%M:%S CST%z" + """ + return datetime.datetime.strptime(st, s_format) + +def str2utcdatetime(st,from_format='%Y-%m-%d %H:%M:%S', utc_format='%Y-%m-%dT%H:%M:%SZ',tz=True): + """字符串时间转UTC时间(-8:00)""" + local_st = datetime.datetime.strptime(st, from_format) + time_struct = time.mktime(local_st.timetuple()) + utc_st = datetime.datetime.utcfromtimestamp(time_struct) + if tz: + return utc_st.strftime(utc_format) + else: + return utc_st + +def datetime2utc(local_st): + """本地时间转UTC时间(-8:00)""" + time_struct = time.mktime(local_st.timetuple()) + utc_st = datetime.datetime.utcfromtimestamp(time_struct) + return utc_st + +def utc2datetime(utc_st): + """UTC时间转本地时间(+8:00)""" + now_stamp = time.time() + local_time = datetime.datetime.fromtimestamp(now_stamp) + utc_time = datetime.datetime.utcfromtimestamp(now_stamp) + offset = local_time - utc_time + local_st = utc_st + offset + return local_st + +def utc_to_local_unixtime(utc_time_str, utc_format='%Y-%m-%dT%H:%M:%SZ'): + """ + utc字符转转换成unix时间戳 + '2019-01-16T11:30:00Z' + '2019-01-16 11:30:00+00:00', utc_format='%Y-%m-%d %H:%M:%S+00:00' + """ + local_tz = pytz.timezone('Asia/Shanghai') + local_format = "%Y-%m-%d %H:%M:%S" + utc_dt = datetime.datetime.strptime(utc_time_str, utc_format) + local_dt = utc_dt.replace(tzinfo=pytz.utc).astimezone(local_tz) + time_str = local_dt.strftime(local_format) + return int(time.mktime(time.strptime(time_str, local_format))) + +def capacity_convert(size, expect='auto', rate=1000): + """ + :param size: '100MB', '1G' + :param expect: 'K, M, G, T + :param rate: Default 1000, may be 1024 + :return: + """ + rate_mapping = ( + ('K', rate), + ('KB', rate), + ('M', rate**2), + ('MB', rate**2), + ('G', rate**3), + ('GB', rate**3), + ('T', rate**4), + ('TB', rate**4), + ) + + rate_mapping = OrderedDict(rate_mapping) + + std_size = 0 # To KB + for unit in rate_mapping: + if size.endswith(unit): + try: + std_size = float(size.strip(unit).strip()) * rate_mapping[unit] + except ValueError: + pass + + if expect == 'auto': + for unit, rate_ in rate_mapping.items(): + if rate > std_size/rate_ > 1: + expect = unit + break + expect_size = std_size / rate_mapping[expect] + return expect_size, expect + +def filesizeformat(value, baseMB=False): + """Format the value like a 'human-readable' file size (i.e. 13 KB, + 4.1 MB, 102 bytes, etc). Per default decimal prefixes are used (mega, + giga etc.), if the second parameter is set to `True` the binary + prefixes are (mebi, gibi). + """ + try: + bytes = float(value) + except: + return 0 + + if baseMB is True: + bytes = bytes * 1024 * 1024 + + base = 1024 + + if bytes == 0: + return '0' + + ret = '0' + if bytes < base: + ret = '%d Bytes' % (bytes) + elif bytes < base * base: + ret = '%d KB' % (bytes / base) + elif bytes < base * base * base: + ret = '%d MB' % (bytes / (base * base)) + elif bytes < base * base * base * base: + if bytes % (base * base * base) == 0: + ret = '%d GB' % (bytes / (base * base * base)) + else: + ret = "%d MB" % (bytes / (base * base)) + else: + ret = '%.1f TB' % (bytes / (base * base * base * base)) + + return ret + +def sum_capacity(cap_list): + total = 0 + for cap in cap_list: + size, _ = capacity_convert(cap, expect='K') + total += size + total = '{} K'.format(total) + return capacity_convert(total, expect='auto') + + +def str_to_md5(strs): + m=hashlib.md5() + m.update(strs.encode("utf8")) + return m.hexdigest() + +def int_random(): + """使用uuid生产随机数,返回128位整数""" + u = uuid.uuid1() + return u.int + +def hex_random(): + """使用uuid生产随机数,返回32位十六进制""" + u = uuid.uuid1() + return u.hex + +def iso8601_to_time(time_string): + """把ISO8601时间字符串(形如,2012-02-24T06:07:48.000Z)转换为UNIX时间,精确到秒。""" + return time_string.strftime('%Y-%m-%d %H:%M:%S') + +def str_to_datetime(string): + """字符串 2017-10-21 00:00:00 格式化为datetime""" + return datetime.datetime.strptime(string,"%Y-%m-%d %H:%M:%S") + + +def sumplus(*args): + """求和""" + r = 0.0 + for n in args: + r += float(n) + return "%.2f" % r + +def sumplusInt(*args): + """求和""" + r = 0 + for n in args: + r += int(n) + return r + +class ImageStorage(FileSystemStorage): + """ + 图片上传 + """ + + def __init__(self, location=settings.MEDIA_ROOT, base_url=settings.MEDIA_URL): + #初始化 + super(ImageStorage, self).__init__(location, base_url) + + #重写 _save方法 + def _save(self, name, content): + + #文件扩展名 + ext = os.path.splitext(name)[1] + #文件目录 + d = os.path.dirname(name) + #定义文件名,年月日时分秒随机数 + year = time.strftime('%Y') + month = time.strftime('%m') + fn = time.strftime('%Y%m%d%H%M%S') + fn = fn + str(hex_random()) + #重写合成文件名 + name = os.path.join('images', year,month, d, fn + ext) + #调用父类方法 + return super(ImageStorage, self)._save(name, content) + +def htmlfilesave(name, content, gameid): + """ + 保存文件,返回url地址 + """ + + #文件扩展名 + ext = os.path.splitext(name)[1] + + #定义文件名,年月日时分秒随机数 + year = time.strftime('%Y') + month = time.strftime('%m') + fn = time.strftime('%Y%m%d%H%M%S') + fn = fn + str(hex_random()) + #重写合成文件名 + name = os.path.join('html', year,month, gameid, fn + ext) + #完整的文件路径 + full_name = os.path.join(settings.MEDIA_ROOT, name) + #得到url + url = filepath_to_uri(os.path.join(settings.MEDIA_URL, name)) + + directory = os.path.dirname(full_name) + if not os.path.exists(directory): + try: + os.makedirs(directory) + except FileNotFoundError: + pass + + if not os.path.isdir(directory): + raise IOError("%s exists and is not a directory." % directory) + + #写入文件 + #open(full_name,'w', encoding='utf8').writelines(content.replace(settings.HTML_HREF_REPLACE_KEY, app_url)) + open(full_name,'w', encoding='utf8').writelines(content) + + return url + +def writefile(content, apk_url, ext='html'): + """ + 写文件,返回文件路径 + """ + ext = '.' + ext + + fn = time.strftime('%Y%m%d%H%M%S') + fn = fn + str(hex_random()) + #重写合成文件名 + name = os.path.join('cdnfile', fn + ext) + #完整的文件路径 + full_name = os.path.join(settings.MEDIA_ROOT, name) + + directory = os.path.dirname(full_name) + if not os.path.exists(directory): + os.makedirs(directory) + + #写入文件 + open(full_name,'w', encoding='utf8').writelines(content.replace(settings.HTML_HREF_REPLACE_KEY, apk_url)) + + return full_name + +def ftpupload(host, port, username, password, upload_dir, upload_file, rmfile=False): + """上传文件到ftp""" + + timeout = 10 + ftp = ftplib.FTP() + filename = os.path.basename(upload_file) + try: + #连接ftp + ftp.connect(host=host, port=port, timeout=timeout) + except (socket.error, socket.gaierror): + return False, "连接FTP失败 %s" % host + + try: + #登录 + ftp.login(username, password) + except ftplib.error_perm: + return False, "ftp用户名或密码错误" + + try: + #切换到操作目录 + ftp.cwd(upload_dir) + except: + #切换目录失败,目录不存在 + #创建目录 + ftp.mkd(upload_dir) + #切换目录 + ftp.cwd(upload_dir) + + fd=open(upload_file,'rb') + #上传文件 + ftp.storbinary('STOR %s' % filename, fd) + + fd.close() + ftp.quit() + + #删除文件 + if rmfile: + os.remove(upload_file) + + #返回写入的ftp地址 + url = filepath_to_uri(os.path.join('/', upload_dir, filename)) + + return True,url + + +class ExcelStorage(FileSystemStorage): + """ + excel文件上传 + """ + + def __init__(self, location=settings.MEDIA_ROOT, base_url=settings.MEDIA_URL): + #初始化 + super(ExcelStorage, self).__init__(location, base_url) + + #重写 _save方法 + def _save(self, name, content): + + #文件扩展名 + ext = os.path.splitext(name)[1] + #文件目录 + d = os.path.dirname(name) + #定义文件名,年月日时分秒随机数 + year = time.strftime('%Y') + month = time.strftime('%m') + fn = time.strftime('%Y%m%d%H%M%S') + #fn = fn + str(hex_random()) + #重写合成文件名 + name = os.path.join('excel', year,month, d, fn + ext) + #调用父类方法 + return super(ExcelStorage, self)._save(name, content) + +def send_email(mail_type,smtp_user,smtp_password,receiver_users = [],send_data = [] ,isTest=False): + """ + 发送邮件 + """ + + now = datetime.datetime.now() + time = now.strftime("%Y-%m-%d %H:%M:%S") + + if isTest: + content = "

测试

" + else: + content = ''' + + + + + + + +

%s

+ %s + + + ''' % (time,"
".join(send_data)) + + + title = "PT助手" + #receiver = ["249545020@qq.com"] + receiver = receiver_users + msg = '' + msg = MIMEText(content, 'html', 'utf-8') + # 设置发送人 + msg['From'] = Header(smtp_user) + # 设置接收人 + msg['To'] = Header(str(";".join(receiver))) + # 设置邮件标题 + msg['Subject'] = Header(title) + + try: + if mail_type == "qq": + smtp_client = smtplib.SMTP_SSL("smtp.qq.com", 465) + elif mail_type == "163": + smtp_client = smtplib.SMTP_SSL("smtp.163.com", 465) + elif mail_type == "sina": + smtp_client = smtplib.SMTP_SSL("smtp.sina.com", 465) + else: + return False,"未知邮件类型" + + smtp_client.login(smtp_user, smtp_password) + smtp_client.sendmail(smtp_user, receiver, msg.as_string()) + smtp_client.quit() + + except smtplib.SMTPException as e: + logging.error(str(e)) + return False,str(e) + + return True,"发送成功" + +def send_telegram(tg_chat_id,tg_token,send_data = [] ,isTest=False): + + + bot = telegram.Bot(token = tg_token) + + now = datetime.datetime.now() + time = now.strftime("%Y-%m-%d %H:%M:%S") + + try: + if isTest: + bot.send_message(chat_id=tg_chat_id, + text='%s[测试]' % time, + parse_mode=telegram.ParseMode.HTML) + else: + bot.send_message(chat_id=tg_chat_id, + text='%s\r\n%s' % (time,"\r\n".join(send_data)), + parse_mode=telegram.ParseMode.HTML) + + except: + ex_type, ex_val, ex_stack = sys.exc_info() + + logging.error(str(ex_val)) + + if 'Chat not found' in str(ex_val): + msg = "发送测试失败,未找到频道ID" + elif 'Unauthorized' in str(ex_val): + msg = "发送测试失败,令牌配置错误" + else: + msg = "发送测试失败,请联系管理员" + return False, msg + + return True,"发送成功" + +def send_iyuu(iyuu_key,send_data = [] ,isTest=False): + + user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36' + + #针对不同通知类型进行处理 + now = datetime.datetime.now() + time = now.strftime("%Y-%m-%d %H:%M:%S") + headers = { + 'user_agent': user_agent, + 'Content-type': 'application/x-www-form-urlencoded' + } + now_time = parse.quote('
') + parse.unquote(time) + parse.quote('

') + api = 'https://iyuu.cn/' + iyuu_key + '.send' + if isTest: + send_txts = parse.quote('
[测试]

') + sen_url = api + '?text='+ parse.quote('测试提示') + '&desp=' + now_time + send_txts + else: + send_txts = parse.quote('
'.join(send_data)) + sen_url = api + '?text='+ parse.quote('签到提示') + '&desp=' + now_time + send_txts + + try: + response = requests.get(sen_url, headers=headers ,verify=False) + response_msg = json.loads(response.text) + if response.status_code == 200 and response_msg['errcode'] == 0: + msg = "发送成功" + else: + msg = "发送测试失败,%s" % response_msg['errmsg'] + return False, msg + except: + logging.error("连接IYUU失败") + return False, "连接IYUU失败" + + return True,"发送成功" + +def getSiteUrl(url): + """ + 获取官网url + """ + u = urlparse(url) + site_url = u.scheme + "://" + u.netloc + + return site_url + +def parseUrl(url): + """ + 拆解url + #scheme='https', netloc='tr.ikaixin.win:10443', path='/', params='', query='', fragment='' + """ + data = {} + u = urlparse(url) + + # http,https + data['protocol'] = u.scheme + data['url'] = u.netloc + data['host'] = u.netloc.split(":")[0] + data['port'] = u.port + data['path'] = u.path + data['params'] = u.params + data['query'] = u.query + + return data + +#def deal_img(img_name): + #'''图片进行预处理,并返回bytes类型数据''' + #img = Image.open(img_name) + #buf = BytesIO() + ## 将图片转化为灰度图像 + #image = img.convert('L') + ## 设置默认的阈值(可以根据阈值得到更加清晰的验证码图) + #threshold = 128 + #table = [] + #for i in range(256): + #if i < threshold: + #table.append(0) + #else: + #table.append(1) + ## 图片的像素点什么的 + #image = image.point(table,'1') + #image.save(buf,'png') + #return buf.getvalue() + +def cookie_parse(cookie_str): + cookie_dict = {} + cookies = cookie_str.split(";") + for cookie in cookies: + cookie = cookie.split("=") + cookie_dict[cookie[0]] = cookie[1] + return cookie_dict \ No newline at end of file diff --git a/apps/common/views.py b/apps/common/views.py new file mode 100644 index 0000000..98bb23b --- /dev/null +++ b/apps/common/views.py @@ -0,0 +1,91 @@ +from django.shortcuts import render +from django.views.generic import TemplateView +from django.contrib.auth.mixins import LoginRequiredMixin +from django.http import JsonResponse + +# Create your views here. +#------------------------------ +#class BaiDuOcrView(LoginRequiredMixin,TemplateView): + #""" + #百度OCR配置 + #""" + #template_name = 'common/baidu_ocr.html' + + ##显示添加模板 + #def get(self, request, *args, **kwargs): + #""" + #得到 + #""" + + #return super(BaiDuOcrView, self).get(request, *args, **kwargs) + + ##显示编辑模板 + #def get_context_data(self, **kwargs): + + ##获取记录 + #orm_count = BaiDuOcr.objects.count() + #if orm_count == 0: + + #context = {} + #else: + ##获取一条数据 + #ormdata = BaiDuOcr.objects.first() + #context = { + #'id': ormdata.id, + #'app_id': ormdata.app_id, + #'app_key': ormdata.app_key, + #'secret_key': ormdata.secret_key, + #} + #kwargs.update(context) + #return super(BaiDuOcrView, self).get_context_data(**kwargs) + + ##数据提交接收方法 + #def post(self, request, *args, **kwargs): + #""" + #数据提交 + #""" + #_id = request.POST.get('id','') + #app_id = request.POST.get("app_id").strip() + #app_key = request.POST.get("app_key").strip() + #secret_key = request.POST.get("secret_key").strip() + + #if _id == '': + #ormdata = BaiDuOcr.objects.create(app_id=app_id, + #app_key=app_key, + #secret_key=secret_key, + #) + #else: + #ormdata = BaiDuOcr.objects.get(id=_id) + #ormdata.app_id = app_id + #ormdata.app_key = app_key + #ormdata.secret_key = secret_key + + #ormdata.save() + + #response_data={"code":1,"msg":"添加成功"} + + #return JsonResponse(response_data) + +class BackupLoadView(LoginRequiredMixin,TemplateView): + """ + 备份恢复 + """ + template_name = 'common/backup_load.html' + + #显示添加模板 + def get(self, request, *args, **kwargs): + """ + 得到 + """ + + return super(BackupLoadView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + context = { + + } + kwargs.update(context) + return super(BackupLoadView, self).get_context_data(**kwargs) + diff --git a/apps/common/views_request.py b/apps/common/views_request.py new file mode 100644 index 0000000..974b991 --- /dev/null +++ b/apps/common/views_request.py @@ -0,0 +1,147 @@ +from django.shortcuts import render + +from django.http import JsonResponse +from django.contrib.auth.decorators import login_required +from django.conf import settings +from django.core.management import call_command +import logging +import tempfile +import os +import shutil + +from sites.models import SiteInfo,SiteConfig +from cron.models import Log +from .sites_sign import hdchina, general,noSign,keepfrds,tjupt,pterclub,hdarea,hdcity,btschool,hares,hd,ttg,pt52,greatposterwall,hdsky,opencd + +#================== +@login_required +def backup(request): + """ + 备份数据 + """ + logger = logging.getLogger('django') + + if request.method == "POST": + action = request.POST.get('action','') + + backup_file = os.path.join(settings.BACKUP_DIR,'pthelper.json') + logger.info("Starting backup process.") + + with tempfile.TemporaryDirectory() as d: + dump_path = os.path.join(d, 'dump.json') + logger.info("Starting data dump...") + #call_command('dumpdata', '--exclude','cron.Log',natural_foreign=True, output=dump_path) + call_command('dumpdata', exclude=['cron.Log'], natural_foreign=True, output=dump_path) + + logger.info("Data dumped.") + logger.info("Copying BACKUP_DIR to %s...", backup_file) + #shutil.copytree(settings.MEDIA_ROOT, os.path.join(d, 'media')) + shutil.copyfile(dump_path, backup_file) + + logger.info('Copy done.') + #backup_path = os.path.join(settings.BACKUP_DIR, datetime.date.today().strftime("%Y-%m-%d.zip")) + #with zipfile.ZipFile(backup_path, mode='w') as backup_zip: + #for root, dirs, files in os.walk(d): + #for file in files: + #filepath = os.path.join(root, file) + #logger.info("Compressing {}...".format(filepath)) + #backup_zip.write(filepath, + #arcname=os.path.relpath(filepath, d)) + #logger.info("{} created.".format(backup_path)) + + response_data={"code":0,"msg":"ok" } + + return JsonResponse(response_data) + +#================== +@login_required +def load(request): + """ + 数据恢复 + """ + logger = logging.getLogger('django') + + if request.method == "POST": + action = request.POST.get('action','') + + backup_file = os.path.join(settings.BACKUP_DIR,'pthelper.json') + logger.info("Starting load process.") + + call_command('loaddata', 'test_dump.json') + + +#================== +@login_required +def signAgain(request): + """ + 补签 + """ + if request.method == "POST": + action = request.POST.get('action') + name = request.POST.get('name') + _id = request.POST.get('id','') + + if action != 'again' or _id == "": + response_data={"code":0,"msg":"未知操作" } + + return JsonResponse(response_data) + + #获取配置的站点信息 + site_info = SiteInfo.objects.get(siteconfig_name=name) + + site_name = site_info.siteconfig_name + site_cookie = site_info.cookie + #获取站点配置信息 + site_config = SiteConfig.objects.get(name=site_name) + site_url = site_config.index_url + site_name_cn = site_config.name_cn + + + if site_name == 'hdchina': + flag, data = hdchina(site_name, site_name_cn, site_url, site_cookie) + elif site_name in ['hdfans','1ptba','ptchina','3wmg','discfan','hddolby','hdatmos','soulvoice', + 'pthome','hdtime','hdzone','htpt','audiences','nicept','hdhome','pttime', + 'lemonhd','ourbits','asf']: + flag, data = general(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'pterclub': + flag, data = pterclub(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'hdarea': + flag, data = hdarea(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'hdcity': + flag, data = hdcity(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'btschool': + flag, data = btschool(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'hares': + flag, data = hares(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'totheglory': + flag, data = ttg(site_name, site_name_cn, site_url, site_cookie) + elif site_name == '52pt': + flag, data = pt52(site_name, site_name_cn, site_url, site_cookie) + elif site_name in ['beitai','msg','hdmayi','oshen','avgv','eastgame']: + flag, data = noSign(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'keepfrds': + flag, data = keepfrds(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'tjupt': + flag, data = tjupt(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'hd': + flag, data = hd(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'greatposterwall': + flag, data = greatposterwall(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'open': + flag, data = opencd(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'hdsky': + flag, data = hdsky(site_name, site_name_cn, site_url, site_cookie) + else: + flag, data = (False,'%s 未匹配站点' % site_name) + + #补签成功,刷新状态 + if flag: + Log.objects.filter(id=_id).update(status=flag) + + print(site_name,data) + if flag: + response_data={"code":1,"msg":data } + else: + response_data={"code":0,"msg":data } + + return JsonResponse(response_data) diff --git a/apps/cron/__init__.py b/apps/cron/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/cron/admin.py b/apps/cron/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/apps/cron/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/cron/apps.py b/apps/cron/apps.py new file mode 100644 index 0000000..8ce9a35 --- /dev/null +++ b/apps/cron/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CronConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'cron' diff --git a/apps/cron/crontabs.py b/apps/cron/crontabs.py new file mode 100644 index 0000000..e877ddd --- /dev/null +++ b/apps/cron/crontabs.py @@ -0,0 +1,121 @@ +# coding=utf-8 +import requests +import datetime +from urllib import parse +import os +import logging +from bs4 import BeautifulSoup + +from common.utils import send_email,send_telegram,send_iyuu +from common.sites_sign import hdchina, general,noSign,keepfrds,tjupt,pterclub,hdarea,hdcity,btschool,hares,hd,ttg,pt52,greatposterwall,hdsky,opencd,haidan +from sites.models import SiteConfig, SiteInfo +from .models import Job, Log +from notify.models import NotifyConfig + + +user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36' + +def sign(crontab_id): + """ + 执行签到 + 参数: + crontab_id:执行任务的ID + """ + + logger = logging.getLogger('sign') + + #保存最后发送的结果 + send_data = [] + site_count = SiteInfo.objects.count() + if site_count == 0: + send_data.append('未配置任何站点') + else: + #获取所有已经配置的站点 + sites = SiteInfo.objects.all() + for i in sites: + site_name = i.siteconfig_name + site_cookie = i.cookie + #获取站点配置信息 + site_config = SiteConfig.objects.get(name=site_name) + site_url = site_config.index_url + site_name_cn = site_config.name_cn + + #headers = { + #'user-agent': user_agent, + #'referer': site_url, + #'cookie': site_cookie + #} + if site_name == 'hdchina': + flag, data = hdchina(site_name, site_name_cn, site_url, site_cookie) + elif site_name in ['hdfans','1ptba','ptchina','3wmg','discfan','hddolby','hdatmos','soulvoice', + 'pthome','hdtime','hdzone','htpt','audiences','nicept','hdhome','pttime', + 'lemonhd','ourbits','asf']: + flag, data = general(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'pterclub': + flag, data = pterclub(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'hdarea': + flag, data = hdarea(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'hdcity': + flag, data = hdcity(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'btschool': + flag, data = btschool(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'hares': + flag, data = hares(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'totheglory': + flag, data = ttg(site_name, site_name_cn, site_url, site_cookie) + elif site_name == '52pt': + flag, data = pt52(site_name, site_name_cn, site_url, site_cookie) + elif site_name in ['beitai','msg','hdmayi','oshen','avgv','eastgame','et8']: + flag, data = noSign(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'keepfrds': + flag, data = keepfrds(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'tjupt': + flag, data = tjupt(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'hd': + flag, data = hd(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'greatposterwall': + flag, data = greatposterwall(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'open': + flag, data = opencd(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'hdsky': + flag, data = hdsky(site_name, site_name_cn, site_url, site_cookie) + elif site_name == 'haidan': + flag, data = haidan(site_name, site_name_cn, site_url, site_cookie) + else: + flag, data = (False,'%s 未匹配站点' % site_name) + + #print(site_name,data) + try: + Log.objects.create(name = '签到',type_id = 1000, crontab_id = crontab_id, site_name=site_name, message = data, status = flag) + except: + logger.error("%s(%s)数据返回出错" % (site_name, site_name_cn)) + + send_data.append(data) + + + logger.info("-------------send_data-------------------") + logger.info(",".join(send_data)) + #获取对应任务ID的记录详情 + notify_ormdata = Job.objects.get(crontab_id = crontab_id) + if 'iyuu' in notify_ormdata.notifys: + + iyuu_ormdata = NotifyConfig.objects.get(name='iyuu') + + flag,msg = send_iyuu(iyuu_ormdata.iyuu_key, send_data) + logger.info("-------------iyuu-------------------") + logger.info(msg) + + if 'telegram' in notify_ormdata.notifys: + tg_ormdata = NotifyConfig.objects.get(name='telegram') + flag,msg = send_telegram(tg_ormdata.tg_chat_id,tg_ormdata.tg_token,send_data) + logger.info("-------------telegram-------------------") + logger.info(msg) + + if 'email' in notify_ormdata.notifys: + email_ormdata = NotifyConfig.objects.get(name='email') + + receiver_users = [x for x in email_ormdata.receive_user.split(',')] + flag,msg = send_email(email_ormdata.mail_type,email_ormdata.smtp_user,email_ormdata.smtp_password,receiver_users,send_data) + logger.info("-------------email-------------------") + logger.info(msg) + diff --git a/apps/cron/migrations/0001_initial.py b/apps/cron/migrations/0001_initial.py new file mode 100644 index 0000000..4a722b5 --- /dev/null +++ b/apps/cron/migrations/0001_initial.py @@ -0,0 +1,25 @@ +# Generated by Django 4.0.2 on 2022-02-23 06:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='JobType', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=20, verbose_name='名称')), + ('type_id', models.IntegerField(verbose_name='类型ID')), + ], + options={ + 'unique_together': {('name', 'type_id')}, + }, + ), + ] diff --git a/apps/cron/migrations/0002_alter_jobtype_unique_together_alter_jobtype_name_and_more.py b/apps/cron/migrations/0002_alter_jobtype_unique_together_alter_jobtype_name_and_more.py new file mode 100644 index 0000000..5422405 --- /dev/null +++ b/apps/cron/migrations/0002_alter_jobtype_unique_together_alter_jobtype_name_and_more.py @@ -0,0 +1,27 @@ +# Generated by Django 4.0.2 on 2022-02-23 06:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cron', '0001_initial'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='jobtype', + unique_together=set(), + ), + migrations.AlterField( + model_name='jobtype', + name='name', + field=models.CharField(max_length=20, unique=True, verbose_name='名称'), + ), + migrations.AlterField( + model_name='jobtype', + name='type_id', + field=models.IntegerField(unique=True, verbose_name='类型ID'), + ), + ] diff --git a/apps/cron/migrations/0003_job.py b/apps/cron/migrations/0003_job.py new file mode 100644 index 0000000..cb88bee --- /dev/null +++ b/apps/cron/migrations/0003_job.py @@ -0,0 +1,30 @@ +# Generated by Django 4.0.2 on 2022-02-24 11:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cron', '0002_alter_jobtype_unique_together_alter_jobtype_name_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='Job', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=30, unique=True, verbose_name='任务名称')), + ('jobtype_id', models.IntegerField(verbose_name='类型ID,签到任务只允许配置一个')), + ('cron_time_type', models.CharField(max_length=10, verbose_name='执行周期类型,秒,分,时,天,周,月')), + ('crontab', models.CharField(max_length=100, verbose_name='执行时间')), + ('crontab_id', models.CharField(max_length=64, verbose_name='执行时间')), + ('crontab_status', models.BooleanField(default=True, verbose_name='任务状态,运行,禁用')), + ('week', models.IntegerField(verbose_name='周')), + ('day', models.IntegerField(verbose_name='天')), + ('hour', models.IntegerField(verbose_name='时')), + ('minute', models.IntegerField(verbose_name='分')), + ('second', models.IntegerField(verbose_name='秒')), + ], + ), + ] diff --git a/apps/cron/migrations/0004_rename_cron_time_type_job_crontab_time_type_and_more.py b/apps/cron/migrations/0004_rename_cron_time_type_job_crontab_time_type_and_more.py new file mode 100644 index 0000000..4284bf5 --- /dev/null +++ b/apps/cron/migrations/0004_rename_cron_time_type_job_crontab_time_type_and_more.py @@ -0,0 +1,32 @@ +# Generated by Django 4.0.2 on 2022-02-25 01:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cron', '0003_job'), + ] + + operations = [ + migrations.RenameField( + model_name='job', + old_name='cron_time_type', + new_name='crontab_time_type', + ), + migrations.RemoveField( + model_name='job', + name='crontab', + ), + migrations.AddField( + model_name='job', + name='crontab_time', + field=models.CharField(max_length=100, null=True, verbose_name='执行周期'), + ), + migrations.AlterField( + model_name='job', + name='crontab_id', + field=models.CharField(max_length=64, verbose_name='执行ID'), + ), + ] diff --git a/apps/cron/migrations/0005_job_sites.py b/apps/cron/migrations/0005_job_sites.py new file mode 100644 index 0000000..d98a77e --- /dev/null +++ b/apps/cron/migrations/0005_job_sites.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.2 on 2022-02-25 11:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cron', '0004_rename_cron_time_type_job_crontab_time_type_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='job', + name='sites', + field=models.CharField(max_length=300, null=True, verbose_name='站点,使用,号分割'), + ), + ] diff --git a/apps/cron/migrations/0006_alter_job_day_alter_job_hour_alter_job_minute_and_more.py b/apps/cron/migrations/0006_alter_job_day_alter_job_hour_alter_job_minute_and_more.py new file mode 100644 index 0000000..9d34680 --- /dev/null +++ b/apps/cron/migrations/0006_alter_job_day_alter_job_hour_alter_job_minute_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 4.0.2 on 2022-02-26 10:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cron', '0005_job_sites'), + ] + + operations = [ + migrations.AlterField( + model_name='job', + name='day', + field=models.IntegerField(default=0, verbose_name='天'), + ), + migrations.AlterField( + model_name='job', + name='hour', + field=models.IntegerField(default=0, verbose_name='时'), + ), + migrations.AlterField( + model_name='job', + name='minute', + field=models.IntegerField(default=0, verbose_name='分'), + ), + migrations.AlterField( + model_name='job', + name='second', + field=models.IntegerField(default=0, verbose_name='秒'), + ), + migrations.AlterField( + model_name='job', + name='week', + field=models.IntegerField(default=0, verbose_name='周'), + ), + ] diff --git a/apps/cron/migrations/0007_job_notifys.py b/apps/cron/migrations/0007_job_notifys.py new file mode 100644 index 0000000..29242a4 --- /dev/null +++ b/apps/cron/migrations/0007_job_notifys.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.2 on 2022-02-28 00:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cron', '0006_alter_job_day_alter_job_hour_alter_job_minute_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='job', + name='notifys', + field=models.CharField(max_length=100, null=True, verbose_name='通知类型,使用,号分割'), + ), + ] diff --git a/apps/cron/migrations/0008_log.py b/apps/cron/migrations/0008_log.py new file mode 100644 index 0000000..8bd1a9a --- /dev/null +++ b/apps/cron/migrations/0008_log.py @@ -0,0 +1,25 @@ +# Generated by Django 4.0.2 on 2022-02-28 07:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cron', '0007_job_notifys'), + ] + + operations = [ + migrations.CreateModel( + name='Log', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=20, verbose_name='任务名称')), + ('type_id', models.IntegerField(verbose_name='类型ID')), + ('crontab_id', models.CharField(max_length=64, verbose_name='执行ID')), + ('message', models.CharField(max_length=300, verbose_name='日志记录')), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('status', models.BooleanField(default=True, verbose_name='是否执行成功')), + ], + ), + ] diff --git a/apps/cron/migrations/0009_alter_log_created_at.py b/apps/cron/migrations/0009_alter_log_created_at.py new file mode 100644 index 0000000..48c9123 --- /dev/null +++ b/apps/cron/migrations/0009_alter_log_created_at.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.2 on 2022-02-28 07:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cron', '0008_log'), + ] + + operations = [ + migrations.AlterField( + model_name='log', + name='created_at', + field=models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='创建时间'), + ), + ] diff --git a/apps/cron/migrations/0010_alter_job_crontab_time.py b/apps/cron/migrations/0010_alter_job_crontab_time.py new file mode 100644 index 0000000..fe362da --- /dev/null +++ b/apps/cron/migrations/0010_alter_job_crontab_time.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.2 on 2022-02-28 18:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cron', '0009_alter_log_created_at'), + ] + + operations = [ + migrations.AlterField( + model_name='job', + name='crontab_time', + field=models.CharField(max_length=100, null=True, verbose_name='执行周期,* * * * *'), + ), + ] diff --git a/apps/cron/migrations/0011_log_site_name.py b/apps/cron/migrations/0011_log_site_name.py new file mode 100644 index 0000000..cc122ce --- /dev/null +++ b/apps/cron/migrations/0011_log_site_name.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.2 on 2022-03-07 09:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cron', '0010_alter_job_crontab_time'), + ] + + operations = [ + migrations.AddField( + model_name='log', + name='site_name', + field=models.CharField(max_length=50, null=True, verbose_name='站点名称'), + ), + ] diff --git a/apps/cron/migrations/__init__.py b/apps/cron/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/cron/models.py b/apps/cron/models.py new file mode 100644 index 0000000..ececf0e --- /dev/null +++ b/apps/cron/models.py @@ -0,0 +1,41 @@ +from django.db import models + +# Create your models here. +class JobType(models.Model): + """ + 任务类型 + """ + name = models.CharField('名称', max_length=20, unique=True) + type_id = models.IntegerField("类型ID", unique=True) + + +class Job(models.Model): + """ + 任务 + """ + name = models.CharField('任务名称', max_length=30, unique=True) + jobtype_id = models.IntegerField("类型ID,签到任务只允许配置一个") + crontab_time_type = models.CharField('执行周期类型,秒,分,时,天,周,月',max_length=10) + crontab_time = models.CharField('执行周期,* * * * *',max_length=100,null=True) + crontab_id = models.CharField('执行ID',max_length=64) + crontab_status = models.BooleanField('任务状态,运行,禁用',default=True) + week = models.IntegerField('周',default=0) + day = models.IntegerField('天',default=0) + hour = models.IntegerField('时',default=0) + minute = models.IntegerField('分',default=0) + second = models.IntegerField('秒',default=0) + sites = models.CharField('站点,使用,号分割',max_length=300,null=True) + notifys = models.CharField('通知类型,使用,号分割',max_length=100,null=True) + + +class Log(models.Model): + """ + 任务日志 + """ + name = models.CharField('任务名称', max_length=20) + type_id = models.IntegerField("类型ID") + crontab_id = models.CharField('执行ID',max_length=64) + site_name = models.CharField('站点名称', max_length=50,null=True) + message = models.CharField('日志记录',max_length=300) + created_at = models.DateTimeField('创建时间', auto_now_add=True,db_index = True) + status = models.BooleanField('是否执行成功',default=True) \ No newline at end of file diff --git a/apps/cron/templates/cron/job.html b/apps/cron/templates/cron/job.html new file mode 100644 index 0000000..7921840 --- /dev/null +++ b/apps/cron/templates/cron/job.html @@ -0,0 +1,207 @@ + +
+
+
+ +
+
+ +
+ +
+
+ + + +
+
+
+ +
+ +
+
+
+ + + + + + + \ No newline at end of file diff --git a/apps/cron/templates/cron/job_one.html b/apps/cron/templates/cron/job_one.html new file mode 100644 index 0000000..852d7a5 --- /dev/null +++ b/apps/cron/templates/cron/job_one.html @@ -0,0 +1,715 @@ + +
+
+
+ +
+
+ +
+ +
+
+ + +
+
+
+ +
+ +
+ 新增/编辑计划任务 + +
+
+ + + +
+ +
+ +
+
+
+ +
+ +
+ +
+
+ +
+ +
+
+
+ +
+
+ + +
+
+
+
+
+
+
+
+ + + + + {% verbatim myblock %} + + + + + + + + + + + + + + + + + + + + + + + {% endverbatim myblock %} + + + \ No newline at end of file diff --git a/apps/cron/templates/cron/jobadd.html b/apps/cron/templates/cron/jobadd.html new file mode 100644 index 0000000..c51198e --- /dev/null +++ b/apps/cron/templates/cron/jobadd.html @@ -0,0 +1,321 @@ + +
{% csrf_token %} + +
+ +
+
+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+
+ +
+ +
+ + +
+
+ + + + +{% verbatim myblock %} + + + + + + + + {% endverbatim myblock %} \ No newline at end of file diff --git a/apps/cron/templates/cron/jobtype.html b/apps/cron/templates/cron/jobtype.html new file mode 100644 index 0000000..06be1df --- /dev/null +++ b/apps/cron/templates/cron/jobtype.html @@ -0,0 +1,197 @@ + +
+
+
+ +
+
+ +
+ +
+
+ + + +
+
+
+ +
+ +
+
+
+ + + + + + + \ No newline at end of file diff --git a/apps/cron/templates/cron/jobtypeadd.html b/apps/cron/templates/cron/jobtypeadd.html new file mode 100644 index 0000000..ff852a5 --- /dev/null +++ b/apps/cron/templates/cron/jobtypeadd.html @@ -0,0 +1,55 @@ + +
{% csrf_token %} + +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ + +
+
+ + \ No newline at end of file diff --git a/apps/cron/templates/cron/jobtypeedit.html b/apps/cron/templates/cron/jobtypeedit.html new file mode 100644 index 0000000..1d58bd4 --- /dev/null +++ b/apps/cron/templates/cron/jobtypeedit.html @@ -0,0 +1,54 @@ + +
{% csrf_token %} + + +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ + +
+
+ + \ No newline at end of file diff --git a/apps/cron/templates/cron/log.html b/apps/cron/templates/cron/log.html new file mode 100644 index 0000000..7699916 --- /dev/null +++ b/apps/cron/templates/cron/log.html @@ -0,0 +1,169 @@ + +
+
+
+ +
+
+ +
+ +
+
+ + +
+
+
+ +
+ +
+
+
+ + + + + + + \ No newline at end of file diff --git a/apps/cron/templates/cron/sign.html b/apps/cron/templates/cron/sign.html new file mode 100644 index 0000000..6af8c97 --- /dev/null +++ b/apps/cron/templates/cron/sign.html @@ -0,0 +1,183 @@ + + +
+
+
+ +
{% csrf_token %} + + + + +
+ +
+ +
+ +
+
点
+ +
+
+ +
+ +
+
分
+ +
+
+ + +
+ + {% if crontab_status %} +
+ + +
+ {% else %} +
+ + +
+ {% endif %} +
+
+ + +
+ {{ notifys|safe }} + +
+
+ +
+
+ + +
+
+
+ +
+
+ +
+
日志
+
+
+
+
+
+
+
+ +{% verbatim myblock %} + +{% endverbatim myblock %} + + + + diff --git a/apps/cron/tests.py b/apps/cron/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/cron/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/cron/urls/__init__.py b/apps/cron/urls/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/apps/cron/urls/__init__.py @@ -0,0 +1 @@ + diff --git a/apps/cron/urls/views_urls.py b/apps/cron/urls/views_urls.py new file mode 100644 index 0000000..47a1df9 --- /dev/null +++ b/apps/cron/urls/views_urls.py @@ -0,0 +1,33 @@ +# coding:utf-8 +from django.urls import path +from .. import views, views_request + +app_name = 'cron' + +urlpatterns = [ + # Resource asset path + + #任务类型 + path('jobtype', views.JobTypeListView.as_view()), + path('jobtype/add', views.JobTypeAddView.as_view()), + path('jobtype/edit', views.JobTypeEditView.as_view()), + path('jobtype/del', views.JobTypeDelView.as_view()), + path('jobtype/list', views_request.jobtype), + path('jobtypeinfo/list', views_request.jobtypeinfo), + + #任务 + path('job', views.JobListView.as_view()), + path('job/add', views.JobAddView.as_view()), + path('job/edit', views.JobEditView.as_view()), + path('job/del', views.JobDelView.as_view()), + path('job/list', views_request.job), + #签到任务 + path('sign', views.SignJobView.as_view()), + #日志 + path('log', views.LogListView.as_view()), + path('log/del', views.LogDelView.as_view()), + path('log/list', views_request.log), + path('loginfo', views_request.loginfo), + +] + diff --git a/apps/cron/views.py b/apps/cron/views.py new file mode 100644 index 0000000..2114225 --- /dev/null +++ b/apps/cron/views.py @@ -0,0 +1,557 @@ +from django.shortcuts import render +from django.views.generic import TemplateView +from django.contrib.auth.mixins import LoginRequiredMixin +from django.http import JsonResponse + +from django.conf import settings +import time +import sys +from apscheduler.schedulers.background import BackgroundScheduler +from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor +from django_apscheduler.jobstores import DjangoJobStore, register_job +from .models import JobType, Job +from notify.models import NotifyConfig +from .crontabs import * + +import logging +import uuid + +logger = logging.getLogger('django') + +executors = { + # 执行器的线程与进程数 + 'default': ThreadPoolExecutor(10), + 'processpool': ProcessPoolExecutor(10) +} + +jobstores = { + 'default': DjangoJobStore() +} + +job_defaults = { + # 最近多久时间内允许存在的任务数 + 'misfire_grace_time': 10, + # 该定时任务允许最大的实例个数 + 'max_instances': 2, + # 是否运行一次最新的任务,当多个任务堆积时 + #'coalesce': True, + #存在相同任务直接覆盖 + 'replace_existing': True, +} + +# 1.实例化调度器 +scheduler = BackgroundScheduler(timezone=settings.TIME_ZONE,job_defaults=job_defaults) +# 2.调度器使用DjangoJobStore() +scheduler.add_jobstore(DjangoJobStore(), "default") +# 5.开启定时任务 +scheduler.start() +#try: + ## 3.设置定时任务 + ## 另一种方式为每天固定时间执行任务,对应代码为 + #@register_job(scheduler,"cron", second="*/10",id="test", replace_existing=True) + #def my_job(): + ## 这里写你要执行的任务 + #format_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())) + #print(format_time) + ## 5.开启定时任务 + #scheduler.start() + #print("Scheduler started!") +#except Exception as e: + #print(e) + ## 有错误就停止定时器 + #scheduler.shutdown() + +class JobTypeListView(LoginRequiredMixin,TemplateView): + """ + 显示 + """ + template_name = 'cron/jobtype.html' + + def get(self, request, *args, **kwargs): + """ + + """ + return super(JobTypeListView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + context = { + # 'data': self.ormdata_game + } + kwargs.update(context) + return super(JobTypeListView, self).get_context_data(**kwargs) + + +class JobTypeAddView(LoginRequiredMixin,TemplateView): + """ + 添加 + """ + template_name = 'cron/jobtypeadd.html' + + #显示添加模板 + def get(self, request, *args, **kwargs): + """ + """ + return super(JobTypeAddView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + context = { + } + kwargs.update(context) + return super(JobTypeAddView, self).get_context_data(**kwargs) + + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + name = request.POST.get("name").strip() + type_id = request.POST.get("type_id").strip() + + + try: + ormdata = JobType.objects.create(name=name, + type_id=type_id, + ) + ormdata.save() + response_data={"code":1,"msg":"添加成功"} + except: + ex_type, ex_val, ex_stack = sys.exc_info() + + if 'UNIQUE' in str(ex_val): + response_data={"code":0,"msg":"存在相同记录"} + else: + response_data={"code":0,"msg":"操作失败,请联系管理员"} + + + return JsonResponse(response_data) + +class JobTypeEditView(LoginRequiredMixin,TemplateView): + """ + 编辑域名 + """ + template_name = 'cron/jobtypeedit.html' + + #显示添加模板 + def get(self, request, *args, **kwargs): + """ + 得到 + """ + + #记录ID + self._id = request.GET.get('id') + self.ormdata = JobType.objects.get(id=self._id) + + return super(JobTypeEditView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + context = { + 'id': self._id, + 'name': self.ormdata.name, + 'type_id': self.ormdata.type_id, + + } + kwargs.update(context) + return super(JobTypeEditView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + name = request.POST.get("name").strip() + type_id = request.POST.get("type_id").strip() + + _id = request.POST.get('id') + + ormdata = JobType.objects.get(id=_id) + + ormdata.name = name + ormdata.type_id = type_id + + ormdata.save() + + response_data={"code":1,"msg":"添加成功"} + + return JsonResponse(response_data) + +class JobTypeDelView(LoginRequiredMixin,TemplateView): + """ + 删除 + """ + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + + #得到批量或者单个要删除的id + ids = request.POST.getlist("ids[]") + #print("ids====>",ids) + + JobType.objects.filter(id__in=ids).delete() + + #Site.objects.filter(id=i).delete() + + response_data={"code":1,"msg":"操作成功"} + + + return JsonResponse(response_data) + +#=================================================================== +class JobListView(LoginRequiredMixin,TemplateView): + """ + 显示 + """ + template_name = 'cron/job.html' + + def get(self, request, *args, **kwargs): + """ + + """ + return super(JobListView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + context = { + # 'data': self.ormdata_game + } + kwargs.update(context) + return super(JobListView, self).get_context_data(**kwargs) + + +class JobAddView(LoginRequiredMixin,TemplateView): + """ + 添加 + """ + template_name = 'cron/jobadd.html' + + #显示添加模板 + def get(self, request, *args, **kwargs): + """ + """ + return super(JobAddView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + context = { + } + kwargs.update(context) + return super(JobAddView, self).get_context_data(**kwargs) + + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + name = request.POST.get("name").strip() + jobtype_id = request.POST.get("job_type") + crontab_status = request.POST.get("crontab_status") + week = request.POST.get("week",0) + day = request.POST.get("day",0) + hour = request.POST.get("hour",0) + minute = request.POST.get("minute",0) + second = request.POST.get("second",0) + crontab_time_type = request.POST.get("select") + sites = request.POST.getlist("sites") + + if crontab_time_type == 'day': + #每天几点几分运行 + crontab_time = minute + ' ' + hour + '* * *' + + + try: + ormdata = Job.objects.create(name=name, + jobtype_id=jobtype_id, + crontab_time_type = crontab_time_type, + crontab_status = crontab_status, + week = week, + day = day, + hour = hour, + minute = minute, + second = second, + sites = ",".join(sites), + crontab_time = crontab_time, + crontab_id = uuid.uuid1() + ) + ormdata.save() + response_data={"code":1,"msg":"添加成功"} + except: + ex_type, ex_val, ex_stack = sys.exc_info() + + if 'UNIQUE' in str(ex_val): + response_data={"code":0,"msg":"存在相同记录"} + else: + response_data={"code":0,"msg":"操作失败,请联系管理员"} + + + return JsonResponse(response_data) + +class JobEditView(LoginRequiredMixin,TemplateView): + """ + 编辑域名 + """ + template_name = 'cron/jobedit.html' + + #显示添加模板 + def get(self, request, *args, **kwargs): + """ + 得到 + """ + + #记录ID + self._id = request.GET.get('id') + self.ormdata = JobType.objects.get(id=self._id) + + return super(JobEditView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + context = { + 'id': self._id, + 'name': self.ormdata.name, + 'type_id': self.ormdata.type_id, + + } + kwargs.update(context) + return super(JobEditView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + name = request.POST.get("name").strip() + type_id = request.POST.get("type_id").strip() + + _id = request.POST.get('id') + + ormdata = JobType.objects.get(id=_id) + + ormdata.name = name + ormdata.type_id = type_id + + ormdata.save() + + response_data={"code":1,"msg":"添加成功"} + + return JsonResponse(response_data) + +class JobDelView(LoginRequiredMixin,TemplateView): + """ + 删除 + """ + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + + #得到批量或者单个要删除的id + ids = request.POST.getlist("ids[]") + #print("ids====>",ids) + + Job.objects.filter(id__in=ids).delete() + + #Site.objects.filter(id=i).delete() + + response_data={"code":1,"msg":"操作成功"} + + + return JsonResponse(response_data) + + +#------------------------------ +class SignJobView(LoginRequiredMixin,TemplateView): + """ + 签到任务 + """ + template_name = 'cron/sign.html' + + #显示添加模板 + def get(self, request, *args, **kwargs): + """ + 得到 + """ + + return super(SignJobView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + html = [] + notify_type = {'iyuu':'IYUU','telegram':'Telegram','email':'邮箱'} + #获取记录 + sign_count = Job.objects.filter(jobtype_id=1000).count() + if sign_count == 0: + + #获取通知配置 + notifys_count = NotifyConfig.objects.count() + if notifys_count == 0: + html.append('请先配置通知') + else: + for d in NotifyConfig.objects.all(): + html.append('') + + context = {'notifys':"".join(html), + } + else: + #获取通知配置 + notifys_count = NotifyConfig.objects.count() + ormdata = Job.objects.get(jobtype_id=1000) + if notifys_count == 0: + html.append('

请先配置通知

') + else: + #notify_ormdata = NotifyConfig.objects.all() + + for d in NotifyConfig.objects.all(): + + if ormdata.notifys == None: + html.append('') + elif d.name in ormdata.notifys: + html.append('') + else: + html.append('') + + + context = { + 'id': ormdata.id, + 'jobtype_id': ormdata.jobtype_id, + 'hour':ormdata.hour, + 'minute':ormdata.minute, + 'crontab_status':ormdata.crontab_status, + 'notifys':"".join(html), + 'crontab_id': ormdata.crontab_id + } + kwargs.update(context) + return super(SignJobView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + _id = request.POST.get('id','') + crontab_status = request.POST.get("crontab_status") + hour = request.POST.get("hour") + minute = request.POST.get("minute") + #需要html配合 + notifys = request.POST.getlist('notifys[]') + crontab_id = request.POST.get("crontab_id",'') + + if crontab_id == "" or crontab_id == None: + crontab_id = str(uuid.uuid1()) + + jobtype_id = 1000 + crontab_time_type = 'day' + #每天几点几分运行 + crontab_time = minute + ' ' + hour + '* * *' + name = "签到" + + #未配置消息通知不予通过 + if len(notifys) == 0: + response_data={"code":0,"msg":"请配置消息通知!"} + return JsonResponse(response_data) + + data_count = Job.objects.filter(jobtype_id=1000).count() + if data_count == 0: + ormdata = Job.objects.create(name=name, + crontab_status=crontab_status, + hour=hour, + minute=minute, + jobtype_id=jobtype_id, + crontab_time_type=crontab_time_type, + crontab_time=crontab_time, + crontab_id = crontab_id, + notifys = ",".join(notifys) + ) + else: + ormdata = Job.objects.get(id=_id) + ormdata.crontab_status = crontab_status + ormdata.hour = hour + ormdata.minute = minute + ormdata.crontab_time = crontab_time + ormdata.notifys = ",".join(notifys) + + ormdata.save() + + crontab_id = ormdata.crontab_id + + # 创建任务 + #scheduler.add_job(test, 'cron', hour=hour, minute=minute, second=second, args=[s]) + isJob = scheduler.get_job(crontab_id) + + if isJob != None: + + #激活 + if crontab_status == '1': + #已经存在的任务不能做时间修改,必须删除在添加 + scheduler.remove_job(crontab_id) + scheduler.add_job(sign, 'cron', hour=hour, minute=minute, id=crontab_id, args=[crontab_id]) + + else: + #禁用状态,直接删除任务 + scheduler.remove_job(crontab_id) + #暂停任务 + #scheduler.pause_job(crontab_id) + + else: + #启用添加,禁用忽略 + if crontab_status == '1': + scheduler.add_job(sign, 'cron', hour=hour, minute=minute, id=crontab_id, args=[crontab_id]) + + response_data={"code":1,"msg":"添加成功"} + + return JsonResponse(response_data) + +#=================================================================== +class LogListView(LoginRequiredMixin,TemplateView): + """ + 显示 + """ + template_name = 'cron/log.html' + + def get(self, request, *args, **kwargs): + """ + + """ + return super(LogListView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + context = { + # 'data': self.ormdata_game + } + kwargs.update(context) + return super(LogListView, self).get_context_data(**kwargs) + +class LogDelView(LoginRequiredMixin,TemplateView): + """ + 删除 + """ + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + + #得到批量或者单个要删除的id + ids = request.POST.getlist("ids[]") + #print("ids====>",ids) + + Log.objects.filter(id__in=ids).delete() + + #Site.objects.filter(id=i).delete() + + response_data={"code":1,"msg":"操作成功"} + + + return JsonResponse(response_data) \ No newline at end of file diff --git a/apps/cron/views_request.py b/apps/cron/views_request.py new file mode 100644 index 0000000..7aa6a16 --- /dev/null +++ b/apps/cron/views_request.py @@ -0,0 +1,311 @@ +#coding=utf-8 +from django.http import JsonResponse +from django.db.models import Q +from django.contrib.auth.decorators import login_required +import datetime +from .models import JobType, Job, Log + + +#直接使用检查是否管理员 +#from django.contrib.auth.decorators import user_passes_test +#@user_passes_test(lambda u: u.is_superuser) + +@login_required +def jobtype(request): + """ + 提供相应的数据 + """ + + #得到偏移量 + pageIndex= int(request.GET.get('page',1)) + #得到查询个数 + pageSize = int(request.GET.get('limit',10)) + + #搜索字段 + searchKey = request.GET.get('searchKey','') + searchValue = request.GET.get('searchValue','') + #去掉空格 + searchValue = searchValue.strip() + + #得到排序字段 + sort = request.GET.get('sort','id') + #得到排序规则 + order_by_type = request.GET.get('order','desc') + + if order_by_type == 'desc': + order_by = sort + else: + order_by = '-' + sort + + data = {} + data['code'] = 0 + data['msg'] = "" + data['data'] = [] + + if searchValue == '': + + data['count'] = JobType.objects.count() + if pageIndex == 1: + ormdata = JobType.objects.order_by(order_by)[:pageSize] #offset:limit + else: + ormdata = JobType.objects.order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize] + else: + + data['count'] = JobType.objects.filter(Q(name__icontains=searchValue)).count() + if pageIndex == 1: + ormdata = JobType.objects.filter(Q(name__icontains=searchValue)).order_by(order_by)[:pageSize] #offset:limit + else: + ormdata = JobType.objects.filter(Q(name__icontains=searchValue)).order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize] + + for i in ormdata: + + data['data'].append({"id":i.id, + "name":i.name, + "type_id":i.type_id, + }) + #返回json串 + #return HttpResponse(json.dumps(data,ensure_ascii = False), "application/json") + return JsonResponse(data) + +#------------------------------------------ +@login_required +def job(request): + """ + 提供相应的数据 + """ + + #得到偏移量 + pageIndex= int(request.GET.get('page',1)) + #得到查询个数 + pageSize = int(request.GET.get('limit',10)) + + #搜索字段 + searchKey = request.GET.get('searchKey','') + searchValue = request.GET.get('searchValue','') + #去掉空格 + searchValue = searchValue.strip() + + #得到排序字段 + sort = request.GET.get('sort','id') + #得到排序规则 + order_by_type = request.GET.get('order','desc') + + if order_by_type == 'desc': + order_by = sort + else: + order_by = '-' + sort + + data = {} + data['code'] = 0 + data['msg'] = "" + data['data'] = [] + + if searchValue == '': + + data['count'] = Job.objects.count() + if pageIndex == 1: + ormdata = Job.objects.order_by(order_by)[:pageSize] #offset:limit + else: + ormdata = Job.objects.order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize] + else: + + data['count'] = Job.objects.filter(Q(name__icontains=searchValue)).count() + if pageIndex == 1: + ormdata = Job.objects.filter(Q(name__icontains=searchValue)).order_by(order_by)[:pageSize] #offset:limit + else: + ormdata = Job.objects.filter(Q(name__icontains=searchValue)).order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize] + + for i in ormdata: + + data['data'].append({"id":i.id, + "name":i.name, + "jobtype_id":i.jobtype_id, + "crontab_time":i.crontab_time, + "crontab_status":i.crontab_status, + }) + #返回json串 + #return HttpResponse(json.dumps(data,ensure_ascii = False), "application/json") + return JsonResponse(data) + + +#------------------------------------------ +@login_required +def log(request): + """ + 提供相应的数据 + """ + + #得到偏移量 + pageIndex= int(request.GET.get('page',1)) + #得到查询个数 + pageSize = int(request.GET.get('limit',10)) + + #搜索字段 + searchKey = request.GET.get('searchKey','') + searchValue = request.GET.get('searchValue','') + #去掉空格 + searchValue = searchValue.strip() + + #得到排序字段 + sort = request.GET.get('sort','id') + #得到排序规则 + order_by_type = request.GET.get('order','desc') + + if order_by_type == 'desc': + order_by = sort + else: + order_by = '-' + sort + + data = {} + data['code'] = 0 + data['msg'] = "" + data['data'] = [] + + if searchValue == '': + + data['count'] = Log.objects.count() + if pageIndex == 1: + ormdata = Log.objects.order_by(order_by)[:pageSize] #offset:limit + else: + ormdata = Log.objects.order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize] + else: + + data['count'] = Log.objects.filter(Q(name__icontains=searchValue)).count() + if pageIndex == 1: + ormdata = Log.objects.filter(Q(name__icontains=searchValue)).order_by(order_by)[:pageSize] #offset:limit + else: + ormdata = Log.objects.filter(Q(name__icontains=searchValue)).order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize] + + for i in ormdata: + + data['data'].append({"id":i.id, + "name":i.name, + "type_id":i.type_id, + "crontab_id":i.crontab_id, + "site_name":i.site_name, + "message":i.message, + "created_at":i.created_at, + "status":i.status, + }) + #返回json串 + #return HttpResponse(json.dumps(data,ensure_ascii = False), "application/json") + return JsonResponse(data) + + +#================== +@login_required +def jobtypeinfo(request): + """ + 任务类型 + xmSelect + """ + + data = {} + data['code'] = 0 + data['msg'] = "" + data['data'] = [] + + #得到排序字段 + sort = request.GET.get('sort','type_id') + #得到排序规则 + order_by_type = request.GET.get('order','desc') + + if order_by_type == 'desc': + order_by = sort + else: + order_by = '-' + sort + + #获取任务信息中是否配置了签到 + qiandao_count = Job.objects.filter(jobtype_id=1000).count() + + #获取所有记录 + data['count'] = JobType.objects.count() + + ormdata = JobType.objects.order_by(order_by).all() + + for i in ormdata: + if qiandao_count == 0: + + data['data'].append({ + "name":i.name, + "type_id":i.type_id + }) + else: + if i.type_id != 1000: + + data['data'].append({ + "name":i.name, + "type_id":i.type_id + }) + + return JsonResponse(data) + +@login_required +def loginfo(request): + """ + 任务日志 + + """ + + data = {} + data['code'] = 0 + data['msg'] = "" + data['data'] = [] + + #GET用于签到任务日志展示 + if request.method == "GET": + #获取任务id + crontab_id = request.GET.get('crontab_id') + #得到排序字段 + sort = request.GET.get('sort','created_at') + #得到排序规则 + order_by_type = request.GET.get('order','desc') + + if order_by_type == 'desc': + order_by = sort + else: + order_by = '-' + sort + + if crontab_id == "" or crontab_id == None: + response_data={"code":0,"msg":"无任务记录"} + + return JsonResponse(response_data) + + #获取今天日期 + today = datetime.datetime.today() + #获取所有记录 + data['count'] = Log.objects.filter(crontab_id=crontab_id).filter(created_at__date=today).count() + + ormdata = Log.objects.filter(crontab_id=crontab_id).filter(created_at__date=today).order_by(order_by) + + for i in ormdata: + + data['data'].append({'id':i.id, + "name":i.name, + "type_id":i.type_id, + "message":i.message + }) + + return JsonResponse(data) + + #POST用于首页展示识别列表 + if request.method == "POST": + + order_by = 'created_at' + #获取今天日期 + today = datetime.datetime.today() + #获取今天签到失败记录 + data['count'] = Log.objects.filter(type_id=1000).filter(status=False).filter(created_at__date=today).count() + + ormdata = Log.objects.filter(type_id=1000).filter(status=False).filter(created_at__date=today).order_by(order_by) + + for i in ormdata: + + data['data'].append({'id':i.id, + "name":i.site_name, + "message":i.message + }) + + return JsonResponse(data) + + diff --git a/apps/dashboard/__init__.py b/apps/dashboard/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/dashboard/admin.py b/apps/dashboard/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/apps/dashboard/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/dashboard/apps.py b/apps/dashboard/apps.py new file mode 100644 index 0000000..7b1cc05 --- /dev/null +++ b/apps/dashboard/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class DashboardConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'dashboard' diff --git a/apps/dashboard/migrations/__init__.py b/apps/dashboard/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/dashboard/models.py b/apps/dashboard/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/apps/dashboard/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/apps/dashboard/templates/dashboard/dashboard.html b/apps/dashboard/templates/dashboard/dashboard.html new file mode 100644 index 0000000..4622d79 --- /dev/null +++ b/apps/dashboard/templates/dashboard/dashboard.html @@ -0,0 +1,184 @@ +{% load static %} + + + +
+
+
+
+
+ PT站数量个 +
+
+

{{sites_num}}

+
+
+
+ +
+ +
+ +
+
+
今日签到失败信息
+
+
+ +
+
+ +
+ +
+
+
已知问题
+
+
    +
  • + +
    +
    学校由于CF干扰,需要同一浏览器访问后在执行补签 +
    +
  • +
  • + +
    +
    暂不支持全站备份和恢复 +
    +
  • +
+
+
+
+
+ + +
+ + + + + + \ No newline at end of file diff --git a/apps/dashboard/tests.py b/apps/dashboard/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/dashboard/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/dashboard/urls/__init__.py b/apps/dashboard/urls/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/apps/dashboard/urls/__init__.py @@ -0,0 +1 @@ + diff --git a/apps/dashboard/urls/views_urls.py b/apps/dashboard/urls/views_urls.py new file mode 100644 index 0000000..2af17f5 --- /dev/null +++ b/apps/dashboard/urls/views_urls.py @@ -0,0 +1,12 @@ +# coding:utf-8 +from django.urls import path +from .. import views + +app_name = 'dashboard' + +urlpatterns = [ + # Resource asset url + path('', views.DashboardListView.as_view(), name='dashboard'), + +] + diff --git a/apps/dashboard/views.py b/apps/dashboard/views.py new file mode 100644 index 0000000..c4dfef9 --- /dev/null +++ b/apps/dashboard/views.py @@ -0,0 +1,23 @@ +from django.shortcuts import render +from django.contrib.auth.mixins import LoginRequiredMixin +from django.views.generic import TemplateView +from sites.models import SiteInfo + +# Create your views here. +class DashboardListView(LoginRequiredMixin, TemplateView): + """ + 打开首页的控制台 + """ + template_name = 'dashboard/dashboard.html' + + def get_context_data(self, **kwargs): + + #配置站点总数 + sites_count = SiteInfo.objects.count() + + context = { + 'sites_num': sites_count, + # 'action': 'Asset list', + } + kwargs.update(context) + return super(DashboardListView, self).get_context_data(**kwargs) diff --git a/apps/global_settings.py b/apps/global_settings.py new file mode 100644 index 0000000..d0bb7d6 --- /dev/null +++ b/apps/global_settings.py @@ -0,0 +1,16 @@ + +from django.conf import settings + +""" +模板直接使用settings变量 +""" + + +def get_global_settings(request): + context = { + 'SITE_NAME': settings.SITE_NAME, + 'SITE_URL': settings.SITE_URL, + 'SITE_VERSION': settings.SITE_VERSION, + 'SITE_COPYRIGHT': settings.SITE_COPYRIGHT, + } + return context diff --git a/apps/index/__init__.py b/apps/index/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/index/admin.py b/apps/index/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/apps/index/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/index/apps.py b/apps/index/apps.py new file mode 100644 index 0000000..ce71d20 --- /dev/null +++ b/apps/index/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class IndexConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'index' diff --git a/apps/index/migrations/__init__.py b/apps/index/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/index/models.py b/apps/index/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/apps/index/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/apps/index/templates/index/about.html b/apps/index/templates/index/about.html new file mode 100644 index 0000000..cea397b --- /dev/null +++ b/apps/index/templates/index/about.html @@ -0,0 +1,84 @@ + +
+ +
+ +
+
+

所有被管理主机需要安装agent

+
+
    +
  • + +
    +

    + Centos 安装 + 需要使用自定义yum源  +

    +
      +
    • yum install python36-setuptools python36-django python36-osquery + python36-requests python36-simplejson python36-djangorestframework + python36-gunicorn python36-greenlet python36-gevent python36-supervisor osquery
    • +
    • yum install opsadmin-agent
    • +
    +
    +
  • +
  • + +
    +

    + Debian 安装 + 需要使用自定义apt源  +

    +
      +
    • apt-get install python3-setuptools python3-django python3-osquery + python3-requests python3-simplejson python3-djangorestframework + python3-gunicorn python3-greenlet python3-gevent python3-supervisor osquery
    • +
    • apt-get install opsadmin-agent
    • +
    +
    +
  • +
  • + +
    +

    + Agent启动方式 +

    +
      +
    • systemctl start supervisord
    • +
    • 启动后使用端口20080,访问http://ip:20080检查是否安装成功
    • +
    +
    +
  • +
+
+
+
+
更新日志
+
+
    +
  • + +
    +

    + V1.0.0 + 基础功能实现  + 2019-01-08 +

    +
      +
    • 网易云ssh秘钥管理,直接可以操作网易云平台
    • +
    • 网易云VPC管理,只支持同步查看
    • +
    • 网易云DNS管理,支持同步和操作网易云平台
    • +
    • 根据项目分类云实例,并后台同步云实例
    • +
    • 对实例进行主机名修改以及DNS记录同步
    • +
    +
    +
  • +
+
+
+
+ +
+
+ diff --git a/apps/index/templates/index/index.html b/apps/index/templates/index/index.html new file mode 100644 index 0000000..838aa18 --- /dev/null +++ b/apps/index/templates/index/index.html @@ -0,0 +1 @@ +{% extends 'base.html' %} \ No newline at end of file diff --git a/apps/index/templates/index/tpl/lock-screen.html b/apps/index/templates/index/tpl/lock-screen.html new file mode 100644 index 0000000..122fb13 --- /dev/null +++ b/apps/index/templates/index/tpl/lock-screen.html @@ -0,0 +1,426 @@ +
+
+
+
+ + +
+
+
+
+
+ +
退出登录
+
+
+
+ + + + + + \ No newline at end of file diff --git a/apps/index/templates/index/tpl/message.html b/apps/index/templates/index/tpl/message.html new file mode 100644 index 0000000..21080e1 --- /dev/null +++ b/apps/index/templates/index/tpl/message.html @@ -0,0 +1,252 @@ +
+ +
+ + + + diff --git a/apps/index/templates/index/tpl/note.html b/apps/index/templates/index/tpl/note.html new file mode 100644 index 0000000..f167420 --- /dev/null +++ b/apps/index/templates/index/tpl/note.html @@ -0,0 +1,210 @@ +
本地便签
+
+
+ +

没有便签

+
+
+ +
+ + + + \ No newline at end of file diff --git a/apps/index/templates/index/tpl/password.html b/apps/index/templates/index/tpl/password.html new file mode 100644 index 0000000..8922e36 --- /dev/null +++ b/apps/index/templates/index/tpl/password.html @@ -0,0 +1,54 @@ +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/apps/index/tests.py b/apps/index/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/index/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/index/urls/__init__.py b/apps/index/urls/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/apps/index/urls/__init__.py @@ -0,0 +1 @@ + diff --git a/apps/index/urls/views_urls.py b/apps/index/urls/views_urls.py new file mode 100644 index 0000000..89c5b60 --- /dev/null +++ b/apps/index/urls/views_urls.py @@ -0,0 +1,15 @@ +# coding:utf-8 +from django.urls import path +from .. import views + +app_name = 'index' + +urlpatterns = [ + # Resource asset url + path('', views.IndexView.as_view()), + path('index/theme', views.IndexThemeView.as_view()), + path('index/note', views.IndexNoteView.as_view()), + path('index/message', views.IndexMessageView.as_view()), + path('index/about', views.IndexAboutView.as_view()), +] + diff --git a/apps/index/views.py b/apps/index/views.py new file mode 100644 index 0000000..b7ca39c --- /dev/null +++ b/apps/index/views.py @@ -0,0 +1,98 @@ +from django.views.generic import TemplateView +from django.utils import timezone +from django.db.models import Count +from django.contrib.auth.mixins import LoginRequiredMixin +from django.shortcuts import redirect +from django.http import HttpResponse, JsonResponse +from django.conf import settings + +class IndexView(LoginRequiredMixin,TemplateView): + template_name = 'index/index.html' + + def get(self, request, *args, **kwargs): + + + return super(IndexView, self).get(request, *args, **kwargs) + + def get_context_data(self, **kwargs): + context = { + # 'html': " ".join(self.html), + # 'key': settings.HTML_HREF_REPLACE_KEY, + # 'tag': self.group_tag + } + + kwargs.update(context) + return super(IndexView, self).get_context_data(**kwargs) + + +class IndexThemeView(LoginRequiredMixin,TemplateView): + template_name = 'index/tpl/theme.html' + + def get(self, request, *args, **kwargs): + + + return super(IndexThemeView, self).get(request, *args, **kwargs) + + def get_context_data(self, **kwargs): + context = { + # 'html': " ".join(self.html), + # 'key': settings.HTML_HREF_REPLACE_KEY, + # 'tag': self.group_tag + } + + kwargs.update(context) + return super(IndexThemeView, self).get_context_data(**kwargs) + +class IndexNoteView(LoginRequiredMixin,TemplateView): + template_name = 'index/tpl/note.html' + + def get(self, request, *args, **kwargs): + + + return super(IndexNoteView, self).get(request, *args, **kwargs) + + def get_context_data(self, **kwargs): + context = { + # 'html': " ".join(self.html), + # 'key': settings.HTML_HREF_REPLACE_KEY, + # 'tag': self.group_tag + } + + kwargs.update(context) + return super(IndexNoteView, self).get_context_data(**kwargs) + +class IndexMessageView(LoginRequiredMixin,TemplateView): + template_name = 'index/tpl/message.html' + + def get(self, request, *args, **kwargs): + + + return super(IndexMessageView, self).get(request, *args, **kwargs) + + def get_context_data(self, **kwargs): + context = { + # 'html': " ".join(self.html), + # 'key': settings.HTML_HREF_REPLACE_KEY, + # 'tag': self.group_tag + } + + kwargs.update(context) + return super(IndexMessageView, self).get_context_data(**kwargs) + +class IndexAboutView(LoginRequiredMixin,TemplateView): + template_name = 'index/about.html' + + def get(self, request, *args, **kwargs): + + + return super(IndexAboutView, self).get(request, *args, **kwargs) + + def get_context_data(self, **kwargs): + context = { + # 'html': " ".join(self.html), + # 'key': settings.HTML_HREF_REPLACE_KEY, + # 'tag': self.group_tag + } + + kwargs.update(context) + return super(IndexAboutView, self).get_context_data(**kwargs) \ No newline at end of file diff --git a/apps/initdata/__init__.py b/apps/initdata/__init__.py new file mode 100644 index 0000000..3d042ae --- /dev/null +++ b/apps/initdata/__init__.py @@ -0,0 +1,2 @@ + +default_app_config = 'initdata.apps.InitdataConfig' \ No newline at end of file diff --git a/apps/initdata/admin.py b/apps/initdata/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/apps/initdata/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/initdata/apps.py b/apps/initdata/apps.py new file mode 100644 index 0000000..eae0bc2 --- /dev/null +++ b/apps/initdata/apps.py @@ -0,0 +1,13 @@ +from django.apps import AppConfig +from django.db.models.signals import post_migrate + +def do_init_data(sender, **kwargs): + from initdata.initDatas import init_datas + init_datas() + +class InitdataConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'initdata' + + def ready(self): + post_migrate.connect(do_init_data, sender=self) \ No newline at end of file diff --git a/apps/initdata/initDatas.py b/apps/initdata/initDatas.py new file mode 100644 index 0000000..42f7c73 --- /dev/null +++ b/apps/initdata/initDatas.py @@ -0,0 +1,126 @@ +from myauth.models import User +from django.contrib.auth.hashers import make_password + +from sites.models import SiteConfig +from cron.models import JobType +from notify.models import MailType + +def init_datas(): + """ + 初始化数据库 + """ + print('开始初始化用户数据库...') + + if User.objects.count() == 0: + #添加管理员 + User.objects.create(name='admin', + nickname='admin', + email = 'admin@admin.com', + password = make_password('123456'), + mobile='13911111111', + is_superuser=True, + is_active=True + ) + print('初始化管理员成功.') + else: + print("管理员已经存在,忽略...") + + print('开始初始化站点配置...') + sites_data = [{'name':'hdfans','name_cn':'红豆饭','index_url':'https://hdfans.org'}, + {'name':'1ptba','name_cn':'壹PT吧','index_url':'https://1ptba.com'}, + {'name':'ptchina','name_cn':'铂金学院','index_url':'https://ptchina.org'}, + {'name':'hdchina','name_cn':'瓷器','index_url':'https://hdchina.org'}, + {'name':'hdmayi','name_cn':'小蚂蚁','index_url':'http://hdmayi.com'}, + {'name':'msg','name_cn':'马杀鸡','index_url':'https://pt.msg.vg'}, + {'name':'beitai','name_cn':'备胎','index_url':'https://www.beitai.pt'}, + {'name':'oshen','name_cn':'奥申','index_url':'http://www.oshen.win'}, + {'name':'avgv','name_cn':'爱薇','index_url':'http://avgv.cc'}, + {'name':'eastgame','name_cn':'吐鲁番','index_url':'https://pt.eastgame.org'}, + {'name':'keepfrds','name_cn':'朋友','index_url':'https://pt.keepfrds.com'}, + {'name':'tjupt','name_cn':'北洋园','index_url':'https://tjupt.org'}, + {'name':'itzmx','name_cn':'分享站','index_url':'https://pt.itzmx.com'}, + {'name':'greatposterwall','name_cn':'海豹','index_url':'https://greatposterwall.com'}, + {'name':'hd','name_cn':'海带','index_url':'https://www.hd.ai'}, + {'name':'m-team','name_cn':'馒头','index_url':'https://kp.m-team.cc'}, + {'name':'lemonhd','name_cn':'柠檬','index_url':'https://lemonhd.org'}, + {'name':'btschool','name_cn':'学校','index_url':'https://pt.btschool.club'}, + {'name':'pthome','name_cn':'铂金家','index_url':'https://pthome.net'}, + {'name':'totheglory','name_cn':'听听歌','index_url':'https://totheglory.im'}, + {'name':'hdhome','name_cn':'家园','index_url':'https://hdhome.org'}, + {'name':'pterclub','name_cn':'猫站','index_url':'https://pterclub.com'}, + {'name':'ourbits','name_cn':'我堡','index_url':'https://ourbits.club'}, + {'name':'hdsky','name_cn':'天空','index_url':'https://hdsky.me'}, + {'name':'hares','name_cn':'白兔','index_url':'https://club.hares.top'}, + {'name':'et8','name_cn':'他吹吹风','index_url':'https://et8.org'}, + {'name':'audiences','name_cn':'观众','index_url':'https://audiences.me'}, + {'name':'hdcity','name_cn':'城市','index_url':'https://hdcity.city'}, + {'name':'open','name_cn':'皇后','index_url':'https://open.cd'}, + {'name':'hdarea','name_cn':'高清视界','index_url':'https://www.hdarea.co'}, + {'name':'soulvoice','name_cn':'聆音','index_url':'https://pt.soulvoice.club'}, + {'name':'nicept','name_cn':'老师','index_url':'https://www.nicept.net'}, + {'name':'haidan','name_cn':'海胆','index_url':'https://www.haidan.video'}, + {'name':'3wmg','name_cn':'芒果','index_url':'https://www.3wmg.com'}, + {'name':'discfan','name_cn':'碟粉','index_url':'https://discfan.net'}, + {'name':'htpt','name_cn':'海棠','index_url':'https://www.htpt.cc'}, + {'name':'hddolby','name_cn':'杜比','index_url':'https://www.hddolby.com'}, + {'name':'hdzone','name_cn':'高清地带','index_url':'https://hdzone.me'}, + {'name':'pttime','name_cn':'时间','index_url':'https://www.pttime.org'}, + {'name':'hdatmos','name_cn':'阿童木','index_url':'https://hdatmos.club'}, + {'name':'52pt','name_cn':'52PT','index_url':'https://52pt.site'}, + {'name':'hdtime','name_cn':'高清时间','index_url':'https://hdtime.org'}, + {'name':'asf','name_cn':'A-SOUL','index_url':'https://p2p.bbs.asf.ink'}, + ] + + for site in sites_data: + get_site = SiteConfig.objects.filter(name=site['name']).count() + if get_site == 0: + SiteConfig.objects.create(name = site['name'], + name_cn = site['name_cn'], + index_url = site['index_url'], + ) + print('添加站点 [%s] 成功' % site['name']) + else: + #更新站点配置 + #SiteConfig.objects.filter(name=site['name']).update(name_cn = site['name_cn'], + #index_url=site['index_url'], + #) + #print('站点 [%s] 更新成功' % site['name']) + print('站点 [%s] 已经存在,忽略...' % site['name']) + + + print('开始初始化任务类型...') + jobtypes_data = [{'name':'签到','type_id':1000}, + {'name':'辅种','type_id':1001}, + {'name':'刷流','type_id':1002}, + ] + + for jobtype in jobtypes_data: + get_jobtype = JobType.objects.filter(name=jobtype['name']).count() + if get_jobtype == 0: + JobType.objects.create(name = jobtype['name'], + type_id = jobtype['type_id'], + ) + print('添加任务类型 [%s] 成功' % jobtype['name']) + else: + print('任务类型 [%s] 已经存在,忽略...' % jobtype['name']) + + print('开始初始化通知邮箱类型...') + mailtypes_data = [{'name':'QQ邮箱','alias_name':'qq','smtp_server':'smtp.qq.com','smtp_port':465}, + {'name':'新浪邮箱(非vip)','alias_name':'sina','smtp_server':'smtp.sina.com','smtp_port':465}, + {'name':'网易163邮箱','alias_name':'163','smtp_server':'smtp.163.com','smtp_port':465}, + ] + + for mailtype in mailtypes_data: + get_mailtype = MailType.objects.filter(name=mailtype['name']).count() + if get_mailtype == 0: + MailType.objects.create(name = mailtype['name'], + alias_name = mailtype['alias_name'], + smtp_server = mailtype['smtp_server'], + smtp_port = mailtype['smtp_port'], + ) + print('添加邮箱类型 [%s] 成功' % mailtype['name']) + else: + print('邮箱类型 [%s] 已经存在,忽略...' % mailtype['name']) + + print('初始化成功.') + \ No newline at end of file diff --git a/apps/initdata/migrations/__init__.py b/apps/initdata/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/initdata/models.py b/apps/initdata/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/apps/initdata/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/apps/initdata/tests.py b/apps/initdata/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/initdata/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/initdata/views.py b/apps/initdata/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/apps/initdata/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/apps/login/__init__.py b/apps/login/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/login/admin.py b/apps/login/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/apps/login/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/login/apps.py b/apps/login/apps.py new file mode 100644 index 0000000..be1c173 --- /dev/null +++ b/apps/login/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class LoginConfig(AppConfig): + name = 'login' diff --git a/apps/login/migrations/__init__.py b/apps/login/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/login/models.py b/apps/login/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/apps/login/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/apps/login/templates/login/forget.html b/apps/login/templates/login/forget.html new file mode 100644 index 0000000..8b62e19 --- /dev/null +++ b/apps/login/templates/login/forget.html @@ -0,0 +1,255 @@ + + + + + + + + 重置密码 + + + + + + +
+
+

重置密码

+
+ + +
+
+ + +
+
+ + +
+ + +
+ +
+
+
+ + + + + + + \ No newline at end of file diff --git a/apps/login/templates/login/login.html b/apps/login/templates/login/login.html new file mode 100644 index 0000000..f60915c --- /dev/null +++ b/apps/login/templates/login/login.html @@ -0,0 +1,230 @@ +{% load static %} + + + + + + + + + + 用户登录 + + + + + + +
+
+

用户登录

+
{% csrf_token %} + + +
+
+ + +
+
+ +
+
+ +
+
+
+ + + + + + + \ No newline at end of file diff --git a/apps/login/templates/login/reg.html b/apps/login/templates/login/reg.html new file mode 100644 index 0000000..78bad50 --- /dev/null +++ b/apps/login/templates/login/reg.html @@ -0,0 +1,225 @@ + + + + + + + + 用户注册 + + + + + + +
+
+

用户注册

+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + \ No newline at end of file diff --git a/apps/login/tests.py b/apps/login/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/login/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/login/urls/__init__.py b/apps/login/urls/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/apps/login/urls/__init__.py @@ -0,0 +1 @@ + diff --git a/apps/login/urls/views_urls.py b/apps/login/urls/views_urls.py new file mode 100644 index 0000000..b18b619 --- /dev/null +++ b/apps/login/urls/views_urls.py @@ -0,0 +1,14 @@ +# coding:utf-8 +from django.urls import path +from .. import views + +app_name = 'login' + +urlpatterns = [ + # Resource asset url + path('login', views.LoginView.as_view()), + path('login/', views.LoginView.as_view()), + path('logout', views.logout), + +] + diff --git a/apps/login/views.py b/apps/login/views.py new file mode 100644 index 0000000..ebddaea --- /dev/null +++ b/apps/login/views.py @@ -0,0 +1,90 @@ + +from myauth.models import User +from django.views.generic import TemplateView +from django.http import HttpResponse, JsonResponse, HttpResponseRedirect +from django.contrib import auth +from django.views.decorators.csrf import csrf_exempt +from django.template import RequestContext +from rest_framework.authtoken.models import Token + +# Create your views here. +class LoginView(TemplateView): + """ + 登录 + """ + template_name = 'login/login.html' + model = User + + def get(self, request, *args, **kwargs): + """ + 登录 + """ + #登录失效后,再次登录需要上次的url + self.next = request.GET.get('next') + + if self.next == None: + self.next = '/' + + return super(LoginView, self).get(request, *args, **kwargs) + + def get_context_data(self, **kwargs): + context = { + 'next': self.next + # 'action': 'Asset list', + } + kwargs.update(context) + return super(LoginView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + id = self.request.POST.get('id') + username = request.POST.get("username") + password = request.POST.get("password") + #记住密码 + remember = request.POST.get("remember") + + if username == "" or username == None: + data = {"code":0, "msg":"用户名不能为空"} + return JsonResponse(data) + + if password == "" or password == None: + data = {"code":0, "msg":"密码不能为空"} + return JsonResponse(data) + + user = auth.authenticate(username=username,password=password) + + if user is not None: + if user.is_active: + auth.login(request, user) + # 更新token值 + try: + token = Token.objects.get(user=user) + except: + #当用户token不存在,则创建token + token = Token.objects.create(user=user) + #token.delete() + #token = Token.objects.create(user=user) + + next = request.POST.get('next','/') + + data={"code": 1, "token":token.key, "url": next, "msg":"登录成功"} + + else: + msg="账户禁止登录" + data={"code": 0, "msg":msg} + else: + + msg="用户名或密码错误" + data={"code": 0, "msg":msg} + + return JsonResponse(data) + +def logout(request): + """ + 登出 + """ + auth.logout(request) + return HttpResponseRedirect('/login') \ No newline at end of file diff --git a/apps/myauth/__init__.py b/apps/myauth/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/myauth/admin.py b/apps/myauth/admin.py new file mode 100644 index 0000000..38bb454 --- /dev/null +++ b/apps/myauth/admin.py @@ -0,0 +1,86 @@ +#coding: utf-8 + +from django import forms +from django.contrib import admin +from django.contrib.auth.models import Group as DjangoGroup +from django.contrib.auth.admin import UserAdmin +from django.contrib.auth.forms import ReadOnlyPasswordHashField + +from myauth.models import User + +# 新增用户表单 +class UserCreateForm(forms.ModelForm): + """A form for creating new users. Includes all the required + fields, plus a repeated password.""" + password1 = forms.CharField(label='Password', widget=forms.PasswordInput) + password2 = forms.CharField( + label='Password confirmation', + widget=forms.PasswordInput, + ) + + class Meta: + model = User + fields = ('name', 'email') + + def clean_password2(self): + # Check that the two password entries match + password1 = self.cleaned_data.get("password1") + password2 = self.cleaned_data.get("password2") + if password1 and password2 and password1 != password2: + raise forms.ValidationError("Passwords don't match") + return password2 + + def save(self, commit=True): + # Save the provided password in hashed format + user = super(UserCreateForm, self).save(commit=False) + user.set_password(self.cleaned_data["password1"]) + if commit: + user.save() + return user + +# 修改用户表单 +class UserChangeForm(forms.ModelForm): + """A form for updating users. Includes all the fields on + the user, but replaces the password field with admin's + password hash display field. + """ + password = ReadOnlyPasswordHashField() + + class Meta: + model = User + fields = "__all__" + + def clean_password(self): + # Regardless of what the user provides, return the initial value. + # This is done here, rather than on the field, because the + # field does not have access to the initial value + return self.initial["password"] + +# 注册用户 +class MyUserAdmin(UserAdmin): + + form = UserChangeForm + add_form = UserCreateForm + + list_display = ('name', 'created_at', 'email', 'is_delete', 'is_superuser') + search_fields = ('name', 'email') + list_filter = ('is_superuser',) + fieldsets = ( + (None, {'fields': ('name', 'email', 'password', 'avatar',)}), + ('Permissions', {'fields': ('is_delete', 'is_superuser', 'is_active')}), + ('Important dates', {'fields': ('last_login',)}), + ) + add_fieldsets = ( + ( + None, + { + 'classes': ('wide',), + 'fields': ('name', 'email', 'password1', 'password2'), + } + ), + ) + ordering = ('created_at',) + filter_horizontal = () + + +admin.site.register(User, MyUserAdmin) \ No newline at end of file diff --git a/apps/myauth/api.py b/apps/myauth/api.py new file mode 100644 index 0000000..8b1322b --- /dev/null +++ b/apps/myauth/api.py @@ -0,0 +1,40 @@ +# ~*~ coding: utf-8 ~*~ + +from rest_framework import viewsets, generics +from rest_framework import mixins + +from .models import User +from .permissions import IsSuperUser,IsValidUser +from . import serializers + +class UserListViewSet(viewsets.ModelViewSet): + #查询所有的数据 + queryset = User.objects.all() + #序列化(表现层,将数据按照一定格式返回给用户) + serializer_class = serializers.UserSerializer + #可以通过id和name来筛选。 + #API后面为?id=1,就会抽选User id=1的用户。?name=sftang会抽选用户名是sftang的用户 (需要rest client,不能直接浏览器访问) + filter_fields = ('id', 'name') + +#使用mixin实现 +# class UserApi(mixins.RetrieveModelMixin, +# mixins.UpdateModelMixin, +# mixins.DestroyModelMixin, +# generics.GenericAPIView): +# queryset = User.objects.all() +# serializer_class = serializers.UserSerializer +# permission_classes = (IsSuperUser,) + +# def get(self, request, *args, **kwargs): +# return self.retrieve(request, *args, **kwargs) + +# def put(self, request, *args, **kwargs): +# return self.update(request, *args, **kwargs) + +# def delete(self, request, *args, **kwargs): +# return self.destroy(request, *args, **kwargs) + +class UserApi(generics.RetrieveUpdateDestroyAPIView): + queryset = User.objects.all() + serializer_class = serializers.UserSerializer + permission_classes = (IsSuperUser,) diff --git a/apps/myauth/apps.py b/apps/myauth/apps.py new file mode 100644 index 0000000..611e83d --- /dev/null +++ b/apps/myauth/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class MyauthConfig(AppConfig): + name = 'myauth' diff --git a/apps/myauth/backends.py b/apps/myauth/backends.py new file mode 100644 index 0000000..0e3e708 --- /dev/null +++ b/apps/myauth/backends.py @@ -0,0 +1,178 @@ +from django.contrib.auth import get_user_model +#from django.contrib.auth.models import Permission + +from mymenu.models import Menu + +UserModel = get_user_model() + + +class ModelBackend: + """ + Authenticates against settings.AUTH_USER_MODEL. + """ + + def authenticate(self, request, username=None, password=None, **kwargs): + if username is None: + username = kwargs.get(UserModel.USERNAME_FIELD) + try: + user = UserModel._default_manager.get_by_natural_key(username) + except UserModel.DoesNotExist: + # Run the default password hasher once to reduce the timing + # difference between an existing and a nonexistent user (#20760). + UserModel().set_password(password) + else: + if user.check_password(password) and self.user_can_authenticate(user): + return user + + def user_can_authenticate(self, user): + """ + Reject users with is_active=False. Custom user models that don't have + that attribute are allowed. + """ + is_active = getattr(user, 'is_active', None) + return is_active or is_active is None + + def _get_user_permissions(self, user_obj): + return user_obj.user_permissions.all() + + def _get_group_permissions(self, user_obj): + user_groups_field = get_user_model()._meta.get_field('groups') + user_groups_query = 'group__%s' % user_groups_field.related_query_name() + return Menu.objects.filter(**{user_groups_query: user_obj}) + + def _get_permissions(self, user_obj, obj, from_name): + """ + Return the permissions of `user_obj` from `from_name`. `from_name` can + be either "group" or "user" to return permissions from + `_get_group_permissions` or `_get_user_permissions` respectively. + """ + if not user_obj.is_active or user_obj.is_anonymous or obj is not None: + return set() + + perm_cache_name = '_%s_perm_cache' % from_name + if not hasattr(user_obj, perm_cache_name): + if user_obj.is_superuser: + perms = Menu.objects.all() + else: + perms = getattr(self, '_get_%s_permissions' % from_name)(user_obj) + perms = perms.values_list('content_type__app_label', 'codename').order_by() + setattr(user_obj, perm_cache_name, {"%s.%s" % (ct, name) for ct, name in perms}) + return getattr(user_obj, perm_cache_name) + + def get_user_permissions(self, user_obj, obj=None): + """ + Return a set of permission strings the user `user_obj` has from their + `user_permissions`. + """ + return self._get_permissions(user_obj, obj, 'user') + + def get_group_permissions(self, user_obj, obj=None): + """ + Return a set of permission strings the user `user_obj` has from the + groups they belong. + """ + return self._get_permissions(user_obj, obj, 'group') + + def get_all_permissions(self, user_obj, obj=None): + if not user_obj.is_active or user_obj.is_anonymous or obj is not None: + return set() + if not hasattr(user_obj, '_perm_cache'): + user_obj._perm_cache = { + *self.get_user_permissions(user_obj), + *self.get_group_permissions(user_obj), + } + return user_obj._perm_cache + + def has_perm(self, user_obj, perm, obj=None): + if not user_obj.is_active: + return False + return perm in self.get_all_permissions(user_obj, obj) + + def has_module_perms(self, user_obj, app_label): + """ + Return True if user_obj has any permissions in the given app_label. + """ + return user_obj.is_active and any( + perm[:perm.index('.')] == app_label + for perm in self.get_all_permissions(user_obj) + ) + + def get_user(self, user_id): + try: + user = UserModel._default_manager.get(pk=user_id) + except UserModel.DoesNotExist: + return None + return user if self.user_can_authenticate(user) else None + + +class AllowAllUsersModelBackend(ModelBackend): + def user_can_authenticate(self, user): + return True + + +class RemoteUserBackend(ModelBackend): + """ + This backend is to be used in conjunction with the ``RemoteUserMiddleware`` + found in the middleware module of this package, and is used when the server + is handling authentication outside of Django. + + By default, the ``authenticate`` method creates ``User`` objects for + usernames that don't already exist in the database. Subclasses can disable + this behavior by setting the ``create_unknown_user`` attribute to + ``False``. + """ + + # Create a User object if not already in the database? + create_unknown_user = True + + def authenticate(self, request, remote_user): + """ + The username passed as ``remote_user`` is considered trusted. Return + the ``User`` object with the given username. Create a new ``User`` + object if ``create_unknown_user`` is ``True``. + + Return None if ``create_unknown_user`` is ``False`` and a ``User`` + object with the given username is not found in the database. + """ + if not remote_user: + return + user = None + username = self.clean_username(remote_user) + + # Note that this could be accomplished in one try-except clause, but + # instead we use get_or_create when creating unknown users since it has + # built-in safeguards for multiple threads. + if self.create_unknown_user: + user, created = UserModel._default_manager.get_or_create(**{ + UserModel.USERNAME_FIELD: username + }) + if created: + user = self.configure_user(user) + else: + try: + user = UserModel._default_manager.get_by_natural_key(username) + except UserModel.DoesNotExist: + pass + return user if self.user_can_authenticate(user) else None + + def clean_username(self, username): + """ + Perform any cleaning on the "username" prior to using it to get or + create the user object. Return the cleaned username. + + By default, return the username unchanged. + """ + return username + + def configure_user(self, user): + """ + Configure a user after creation and return the updated user. + + By default, return the user unmodified. + """ + return user + + +class AllowAllUsersRemoteUserBackend(RemoteUserBackend): + def user_can_authenticate(self, user): + return True diff --git a/apps/myauth/decorators.py b/apps/myauth/decorators.py new file mode 100644 index 0000000..eb78d6e --- /dev/null +++ b/apps/myauth/decorators.py @@ -0,0 +1,19 @@ +#coding=utf-8 +from django.contrib.auth import REDIRECT_FIELD_NAME +from django.contrib.admin.views.decorators import user_passes_test + +#REDIRECT_FIELD_NAME 默认 next +def superuser_required(view_func=None, redirect_field_name=REDIRECT_FIELD_NAME, + login_url=None): + """ + Decorator for views that checks that the user is logged in and is a + superuser, redirecting to the login page if necessary. + """ + actual_decorator = user_passes_test( + lambda u: u.is_active and u.is_superuser, + login_url=login_url, + redirect_field_name=redirect_field_name + ) + if view_func: + return actual_decorator(view_func) + return actual_decorator \ No newline at end of file diff --git a/apps/myauth/migrations/0001_initial.py b/apps/myauth/migrations/0001_initial.py new file mode 100644 index 0000000..972102f --- /dev/null +++ b/apps/myauth/migrations/0001_initial.py @@ -0,0 +1,54 @@ +# Generated by Django 3.1.7 on 2021-03-26 03:36 + +from django.db import migrations, models +import myauth.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('mymenu', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Group', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=80, unique=True, verbose_name='name')), + ('tag', models.CharField(default='unknow', max_length=30, verbose_name='用于业务识别标签')), + ('permissions', models.ManyToManyField(blank=True, to='mymenu.Menu', verbose_name='permissions')), + ], + options={ + 'verbose_name': 'group', + 'verbose_name_plural': 'groups', + }, + managers=[ + ('objects', myauth.models.GroupManager()), + ], + ), + migrations.CreateModel( + name='User', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('name', models.CharField(max_length=30, unique=True, verbose_name='用户名')), + ('nickname', models.CharField(max_length=30, verbose_name='真实姓名')), + ('email', models.EmailField(max_length=40, unique=True, verbose_name='邮箱')), + ('is_delete', models.BooleanField(default=False)), + ('is_active', models.BooleanField(default=True)), + ('is_superuser', models.BooleanField(default=False)), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('avatar', models.URLField(blank=True, verbose_name='头像')), + ('mobile', models.CharField(max_length=15, null=True, verbose_name='移动电话')), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='myauth.Group', verbose_name='groups')), + ('permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='mymenu.Menu', verbose_name='user permissions')), + ], + options={ + 'ordering': ('created_at',), + }, + ), + ] diff --git a/apps/myauth/migrations/0002_alter_group_id_alter_user_id.py b/apps/myauth/migrations/0002_alter_group_id_alter_user_id.py new file mode 100644 index 0000000..53fd95f --- /dev/null +++ b/apps/myauth/migrations/0002_alter_group_id_alter_user_id.py @@ -0,0 +1,23 @@ +# Generated by Django 4.0.2 on 2022-02-22 08:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('myauth', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='group', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='user', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ] diff --git a/apps/myauth/migrations/__init__.py b/apps/myauth/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/myauth/mixins.py b/apps/myauth/mixins.py new file mode 100644 index 0000000..23307f2 --- /dev/null +++ b/apps/myauth/mixins.py @@ -0,0 +1,72 @@ +# ~*~ coding: utf-8 ~*~ + +from django.contrib.auth.mixins import UserPassesTestMixin, PermissionRequiredMixin +from django.core.exceptions import ImproperlyConfigured, PermissionDenied +from django.contrib.auth.views import redirect_to_login +from mymenu.models import Menu +from myauth.models import Group + +class AdminUserRequiredMixin(UserPassesTestMixin): + """验证用户是否管理员,非管理员返回403""" + def test_func(self): + if not self.request.user.is_authenticated: + return False + elif not self.request.user.is_superuser: + self.raise_exception = True + return False + return True + +class LoginPermissionsMixin(PermissionRequiredMixin): + """登录、权限验证""" + + def has_permission(self): + """ + Override this method to customize the way permissions are checked. + """ + #得到登录用户对象 + user = self.request.user + #管理员不检查权限 + if user.is_active and user.is_superuser: + return True + + #得到请求的访问url + visit_url = self.request.path + #得到访问url的id,返回[{'id':1}] + menuId = Menu.objects.filter(menuurl=visit_url).values('id')[0]['id'] + + #检查用户权限 得到用户所拥有的菜单id list + menus = user.get_permissions(user) + + + #标识 没有权限 + no_permission = True + if menuId in menus: + #访问的url在权限中 + return True + else: + #检查组权限 + #得到用户所在的所有组ID + usergroups = [p.id for p in user.groups.all()] + for g in usergroups: + #得到组对象 + group = Group.objects.get(id=g) + #得到组对应的权限 + menus = group.get_permissions() + if menuId in menus: + return True + + return False + + def dispatch(self, request, *args, **kwargs): + #验证用户是否登录 + if not self.request.user.is_authenticated: + return redirect_to_login(self.request.get_full_path(), + self.get_login_url(), self.get_redirect_field_name()) + + if not self.has_permission(): + # We could also use "return self.handle_no_permission()" here + #raise PermissionDenied(self.get_permission_denied_message()) + raise PermissionDenied + + return super().dispatch(request, *args, **kwargs) + #return super(LoginPermissionsMixin, self).dispatch(request, *args, **kwargs) \ No newline at end of file diff --git a/apps/myauth/models.py b/apps/myauth/models.py new file mode 100644 index 0000000..1b0f67d --- /dev/null +++ b/apps/myauth/models.py @@ -0,0 +1,284 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ +from django.db.models.signals import post_save +from django.dispatch import receiver +from django.conf import settings +from django.utils import timezone +# Create your models here. +from django.contrib.auth.models import BaseUserManager, AbstractBaseUser +from django.contrib import auth +from django.core.exceptions import PermissionDenied + +from rest_framework.authtoken.models import Token + +from mymenu.models import Menu + +def update_last_login(sender, user, **kwargs): + """ + A signal receiver which updates the last_login date for + the user logging in. + """ + user.last_login = timezone.now() + user.save(update_fields=['last_login']) + +class GroupManager(models.Manager): + """ + The manager for the auth's Group model. + """ + use_in_migrations = True + + def get_by_natural_key(self, name): + return self.get(name=name) + + +class Group(models.Model): + """ + Groups are a generic way of categorizing users to apply permissions, or + some other label, to those users. A user can belong to any number of + groups. + + A user in a group automatically has all the permissions granted to that + group. For example, if the group 'Site editors' has the permission + can_edit_home_page, any user in that group will have that permission. + + Beyond permissions, groups are a convenient way to categorize users to + apply some label, or extended functionality, to them. For example, you + could create a group 'Special users', and you could write code that would + do special things to those users -- such as giving them access to a + members-only portion of your site, or sending them members-only email + messages. + """ + name = models.CharField(_('name'), max_length=80, unique=True) + permissions = models.ManyToManyField( + Menu, + verbose_name=_('permissions'), + blank=True, + ) + tag = models.CharField('用于业务识别标签', max_length=30, default='unknow') + + objects = GroupManager() + + class Meta: + verbose_name = _('group') + verbose_name_plural = _('groups') + + def __str__(self): + return self.name + + def natural_key(self): + return (self.name,) + + def get_permissions(self): + """返回菜单id的list""" + return [p.id for p in self.permissions.all()] + + def get_permissions_name(self): + """返回菜单名称""" + return ",".join([p.name for p in self.permissions.all()]) + +# A few helper functions for common logic between User and AnonymousUser. +def _user_get_all_permissions(user, obj): + permissions = set() + for backend in auth.get_backends(): + if hasattr(backend, "get_all_permissions"): + permissions.update(backend.get_all_permissions(user, obj)) + return permissions + + +def _user_has_perm(user, perm, obj): + """ + A backend can raise `PermissionDenied` to short-circuit permission checking. + """ + for backend in auth.get_backends(): + if not hasattr(backend, 'has_perm'): + continue + try: + if backend.has_perm(user, perm, obj): + return True + except PermissionDenied: + return False + return False + + +def _user_has_module_perms(user, app_label): + """ + A backend can raise `PermissionDenied` to short-circuit permission checking. + """ + for backend in auth.get_backends(): + if not hasattr(backend, 'has_module_perms'): + continue + try: + if backend.has_module_perms(user, app_label): + return True + except PermissionDenied: + return False + return False + + +class PermissionsMixin(models.Model): + """ + Add the fields and methods necessary to support the Group and Permission + models using the ModelBackend. + """ + is_superuser = models.BooleanField( + _('superuser status'), + default=False, + help_text=_( + 'Designates that this user has all permissions without ' + 'explicitly assigning them.' + ), + ) + groups = models.ManyToManyField( + Group, + verbose_name=_('groups'), + blank=True, + help_text=_( + 'The groups this user belongs to. A user will get all permissions ' + 'granted to each of their groups.' + ), + related_name="user_set", + related_query_name="user", + ) + permissions = models.ManyToManyField( + Menu, + verbose_name=_('user permissions'), + blank=True, + help_text=_('Specific permissions for this user.'), + related_name="user_set", + related_query_name="user", + ) + + class Meta: + abstract = True + + def get_group_permissions(self, obj=None): + """ + Return a list of permission strings that this user has through their + groups. Query all available auth backends. If an object is passed in, + return only permissions matching this object. + """ + permissions = set() + for backend in auth.get_backends(): + if hasattr(backend, "get_group_permissions"): + permissions.update(backend.get_group_permissions(self, obj)) + return permissions + + def get_all_permissions(self, obj=None): + return _user_get_all_permissions(self, obj) + + def has_perm(self, perm, obj=None): + """ + Return True if the user has the specified permission. Query all + available auth backends, but return immediately if any backend returns + True. Thus, a user who has permission from a single auth backend is + assumed to have permission in general. If an object is provided, check + permissions for that object. + """ + # Active superusers have all permissions. + if self.is_active and self.is_superuser: + return True + + # Otherwise we need to check the backends. + return _user_has_perm(self, perm, obj) + + def has_perms(self, perm_list, obj=None): + """ + Return True if the user has each of the specified permissions. If + object is passed, check if the user has all required perms for it. + """ + return all(self.has_perm(perm, obj) for perm in perm_list) + + def has_module_perms(self, app_label): + """ + Return True if the user has any permissions in the given app label. + Use simlar logic as has_perm(), above. + """ + # Active superusers have all permissions. + if self.is_active and self.is_superuser: + return True + + return _user_has_module_perms(self, app_label) + + def get_permissions(self, obj=None): + """返回菜单id的list""" + return [p.id for p in obj.permissions.all()] + + def get_permissions_name(self, obj=None): + """返回菜单名称""" + return ",".join([p.name for p in obj.permissions.all()]) + + +class UserManager(BaseUserManager): + + def create_user(self, name, email, password=None): + + if not email: + raise ValueError('Users must have an email address') + + user = self.model( + name=name, + email=UserManager.normalize_email(email), + ) + + user.set_password(password) + user.save(using=self._db) + return user + + def create_superuser(self, name, email, password=None): + + user = self.create_user(name, email, password) + user.is_superuser = True + user.save(using=self._db) + return user + + +class User(AbstractBaseUser, PermissionsMixin): + '''用户表''' + + name = models.CharField('用户名', max_length=30, unique=True) + nickname = models.CharField('真实姓名', max_length=30) + email = models.EmailField('邮箱', max_length=40, unique=True) + #password = models.CharField('密码', max_length=128) + is_delete = models.BooleanField(default=False) + is_active = models.BooleanField(default=True) + is_superuser = models.BooleanField(default=False) + #last_login = models.DateTimeField('最后登录时间',null=True ) + created_at = models.DateTimeField('创建时间', auto_now_add=True) + #avatar = models.FileField('头像', max_length=256, null=True, upload_to='avatars') + avatar = models.URLField('头像', blank=True) + mobile = models.CharField('移动电话', max_length=15, null=True) + + objects = UserManager() + #设置认证标识,设置成标识的字段 unique=True + USERNAME_FIELD = 'name' + #当通过createsuperuser管理命令创建一个用户时,用于提示的一个字段名称列表 + REQUIRED_FIELDS = ('email',) + + class Meta: + ordering = ('created_at',) + + def __unicode__(self): + return self.name + + def get_full_name(self): + return self.email + + def get_short_name(self): + return self.name + + def has_perm(self, perm, obj=None): + return True + + def has_module_perms(self, app_label): + return True + + @property + def is_staff(self): + return self.is_superuser + +# 为每个用户添加token验证 +@receiver(post_save, sender=settings.AUTH_USER_MODEL) +def create_auth_token(sender, instance=None, created=False, **kwargs): + """创建用户的时候自动创建token""" + if created: + Token.objects.create(user=instance) \ No newline at end of file diff --git a/apps/myauth/permissions.py b/apps/myauth/permissions.py new file mode 100644 index 0000000..1b38aa1 --- /dev/null +++ b/apps/myauth/permissions.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from rest_framework import permissions + + +class IsValidUser(permissions.IsAuthenticated, permissions.BasePermission): + """Allows access to valid user, is active and not expired""" + + def has_permission(self, request, view): + return super(IsValidUser, self).has_permission(request, view) \ + and request.user.is_valid + + +class IsAppUser(IsValidUser): + """Allows access only to app user """ + + def has_permission(self, request, view): + return super(IsAppUser, self).has_permission(request, view) \ + and request.user.is_app + + +class IsSuperUser(IsValidUser): + """Allows access only to superuser""" + + def has_permission(self, request, view): + return super(IsSuperUser, self).has_permission(request, view) \ + and request.user.is_superuser + + +class IsSuperUserOrAppUser(IsValidUser): + """Allows access between superuser and app user""" + + def has_permission(self, request, view): + return super(IsSuperUserOrAppUser, self).has_permission(request, view) \ + and (request.user.is_superuser or request.user.is_app) + + +class IsSuperUserOrAppUserOrUserReadonly(IsSuperUserOrAppUser): + def has_permission(self, request, view): + if IsValidUser.has_permission(self, request, view) \ + and request.method in permissions.SAFE_METHODS: + return True + else: + return IsSuperUserOrAppUser.has_permission(self, request, view) + + +class IsCurrentUserOrReadOnly(permissions.BasePermission): + def has_object_permission(self, request, view, obj): + if request.method in permissions.SAFE_METHODS: + return True + return obj == request.user diff --git a/apps/myauth/readme.txt b/apps/myauth/readme.txt new file mode 100644 index 0000000..d7ef9f5 --- /dev/null +++ b/apps/myauth/readme.txt @@ -0,0 +1,3 @@ +×Զ¨ÒåÓû§Ñé֤ºó£¬ִÐÐÏÂÃæµÄÓï¾ä +python manage.py makemigrations + python manage.py migrate \ No newline at end of file diff --git a/apps/myauth/serializers.py b/apps/myauth/serializers.py new file mode 100644 index 0000000..737eade --- /dev/null +++ b/apps/myauth/serializers.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +from django.utils.translation import ugettext_lazy as _ +from django.core.cache import cache +from rest_framework import viewsets, serializers, generics +from .models import User + + +# class AdminUserSerializer(serializers.ModelSerializer): +# assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all()) + +# class Meta: +# model = AdminUser +# fields = '__all__' + +# def get_field_names(self, declared_fields, info): +# fields = super(AdminUserSerializer, self).get_field_names(declared_fields, info) +# fields.append('assets_amount') +# return fields + + +# class SystemUserSerializer(serializers.ModelSerializer): +# class Meta: +# model = SystemUser +# exclude = ('_password', '_private_key', '_public_key') + +# def get_field_names(self, declared_fields, info): +# fields = super(SystemUserSerializer, self).get_field_names(declared_fields, info) +# fields.extend(['assets_amount']) +# return fields + + +class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ('id', 'name', 'nickname', 'is_admin', 'mobile') diff --git a/apps/myauth/templates/myauth/group.html b/apps/myauth/templates/myauth/group.html new file mode 100644 index 0000000..6b2a493 --- /dev/null +++ b/apps/myauth/templates/myauth/group.html @@ -0,0 +1,80 @@ +{% extends 'base_right.html' %} + +{% block bodycontent %} + +
+ + + +
+ +
+ +{% endblock %} + +{% block footer %} + +{% endblock %} \ No newline at end of file diff --git a/apps/myauth/templates/myauth/groupadd.html b/apps/myauth/templates/myauth/groupadd.html new file mode 100644 index 0000000..29c51c7 --- /dev/null +++ b/apps/myauth/templates/myauth/groupadd.html @@ -0,0 +1,42 @@ +{% extends 'base_right.html' %} + +{% block bodycontent %} +
+
{% csrf_token %} +
+ +
+ +
+
+ + + + +
+
+{% endblock %} + +{% block footer %} + +{% endblock %} \ No newline at end of file diff --git a/apps/myauth/templates/myauth/groupedit.html b/apps/myauth/templates/myauth/groupedit.html new file mode 100644 index 0000000..dbac99d --- /dev/null +++ b/apps/myauth/templates/myauth/groupedit.html @@ -0,0 +1,41 @@ +{% extends 'base_right.html' %} + +{% block bodycontent %} +
+
{% csrf_token %} + +
+ +
+ +
+
+ + + +
+
+{% endblock %} + + +{% block footer %} + +{% endblock %} \ No newline at end of file diff --git a/apps/myauth/templates/myauth/groupusersadd.html b/apps/myauth/templates/myauth/groupusersadd.html new file mode 100644 index 0000000..8cd0957 --- /dev/null +++ b/apps/myauth/templates/myauth/groupusersadd.html @@ -0,0 +1,72 @@ +{% extends 'base_right.html' %} +{% load static %} + + +{% block header %} + +{% endblock %} + +{% block bodycontent %} +
+
{% csrf_token %} + +
+ +
+ +
+
+
+ +
+ +
+
+ + +
+
+{% endblock %} + + +{% block footer %} + + +{% endblock %} \ No newline at end of file diff --git a/apps/myauth/templates/myauth/permission.html b/apps/myauth/templates/myauth/permission.html new file mode 100644 index 0000000..dd00d77 --- /dev/null +++ b/apps/myauth/templates/myauth/permission.html @@ -0,0 +1,131 @@ +{% extends 'base_right.html' %} + +{% block bodycontent %} + + +
+
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+
+ +
+
+
+ +
+ +
+
+
+ +
+
+
+ + +{% endblock %} + +{% block footer %} + +{% endblock %} \ No newline at end of file diff --git a/apps/myauth/templates/myauth/permissiongroupadd.html b/apps/myauth/templates/myauth/permissiongroupadd.html new file mode 100644 index 0000000..fe5b71b --- /dev/null +++ b/apps/myauth/templates/myauth/permissiongroupadd.html @@ -0,0 +1,99 @@ +{% extends 'base_right.html' %} + +{% block bodycontent %} +
+
{% csrf_token %} + +
+ +
+ +
+
+
+ +
+ +
+
+ + +
+
+{% endblock %} + + +{% block footer %} + +{% endblock %} \ No newline at end of file diff --git a/apps/myauth/templates/myauth/permissionuseradd.html b/apps/myauth/templates/myauth/permissionuseradd.html new file mode 100644 index 0000000..8766144 --- /dev/null +++ b/apps/myauth/templates/myauth/permissionuseradd.html @@ -0,0 +1,99 @@ +{% extends 'base_right.html' %} + +{% block bodycontent %} +
+
{% csrf_token %} + +
+ +
+ +
+
+
+ +
+ +
+
+ + +
+
+{% endblock %} + + +{% block footer %} + +{% endblock %} \ No newline at end of file diff --git a/apps/myauth/templates/myauth/resetaccount.html b/apps/myauth/templates/myauth/resetaccount.html new file mode 100644 index 0000000..2ad3951 --- /dev/null +++ b/apps/myauth/templates/myauth/resetaccount.html @@ -0,0 +1,56 @@ +
{% csrf_token %} + +
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/apps/myauth/templates/myauth/resetpasswd.html b/apps/myauth/templates/myauth/resetpasswd.html new file mode 100644 index 0000000..1d0d700 --- /dev/null +++ b/apps/myauth/templates/myauth/resetpasswd.html @@ -0,0 +1,59 @@ + +
{% csrf_token %} + +
+ +
+ +
+
+
+ +
+ +
+
+
+ + +
+ +
+ + \ No newline at end of file diff --git a/apps/myauth/templates/myauth/user.html b/apps/myauth/templates/myauth/user.html new file mode 100644 index 0000000..cde4e7a --- /dev/null +++ b/apps/myauth/templates/myauth/user.html @@ -0,0 +1,223 @@ + +
+
+
+ +
+
+
+ +
+
+ +
+
+ + +
+
+
+ +
+ +
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/apps/myauth/templates/myauth/useradd.html b/apps/myauth/templates/myauth/useradd.html new file mode 100644 index 0000000..3ccd2aa --- /dev/null +++ b/apps/myauth/templates/myauth/useradd.html @@ -0,0 +1,109 @@ + +
{% csrf_token %} + +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+ +
+ + +
+
+ + \ No newline at end of file diff --git a/apps/myauth/templates/myauth/useredit.html b/apps/myauth/templates/myauth/useredit.html new file mode 100644 index 0000000..24d81ad --- /dev/null +++ b/apps/myauth/templates/myauth/useredit.html @@ -0,0 +1,100 @@ + +
{% csrf_token %} + +
+ +
+ +
+
+
+ +
+ +
+
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ {% if is_superuser %} + + + {% else %} + + + {% endif %} +
+
+
+ +
+ {% if is_active %} + + + {% else %} + + + {% endif %} +
+
+ +
+ + +
+
+ + \ No newline at end of file diff --git a/apps/myauth/tests.py b/apps/myauth/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/myauth/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/myauth/urls/__init__.py b/apps/myauth/urls/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/apps/myauth/urls/__init__.py @@ -0,0 +1 @@ + diff --git a/apps/myauth/urls/views_urls.py b/apps/myauth/urls/views_urls.py new file mode 100644 index 0000000..82f450f --- /dev/null +++ b/apps/myauth/urls/views_urls.py @@ -0,0 +1,36 @@ +# coding:utf-8 +from django.urls import path +from .. import views, views_request + +app_name = 'myauth' + +urlpatterns = [ + # Resource asset url + path('user', views.UserListView.as_view(), name='user'), + path('user/add', views.UserAddView.as_view(), name='useradd'), + path('user/edit', views.UserEditView.as_view(), name='useredit'), + path('user/del', views.UserDelView.as_view(), name='userdel'), + path('user/resetpasswd', views.UserResetPasswdView.as_view(), name='resetpasswd'), + path('user/resetpasswd2', views.UserResetPasswd2View.as_view()), + path('user/resetaccount', views.UserResetAccountView.as_view()), + + + path('group/', views.GroupListView.as_view()), + path('group/add', views.GroupAddView.as_view()), + path('group/edit', views.GroupEditView.as_view()), + #path('group/del', views.GroupDelView.as_view()), + path('group/groupusersadd', views.GroupUsersAddView.as_view()), + + path('permission/', views.PermissionListView.as_view()), + path('permissiongroup/add', views.PermissionGroupAddView.as_view()), + path('permissionuser/add', views.PermissionUserAddView.as_view()), + + # + path('myuser/list', views_request.user), + path('group/list', views_request.group), + path('permissiongroup/list', views_request.permissiongroup), + path('permissionuser/list', views_request.permissionuser), + path('myuser/setvalue', views_request.setValue), + +] + diff --git a/apps/myauth/views.py b/apps/myauth/views.py new file mode 100644 index 0000000..4d07a92 --- /dev/null +++ b/apps/myauth/views.py @@ -0,0 +1,673 @@ +from django.shortcuts import render +from django.views.generic import TemplateView +from django.http import HttpResponse, JsonResponse +from django.contrib.auth.hashers import check_password, make_password, is_password_usable +from django.db import IntegrityError +import sys +import traceback +from django.views.decorators.csrf import csrf_exempt +from django.contrib.auth.mixins import LoginRequiredMixin +import simplejson as json +from .models import User, Group +from mymenu.models import Menu +from myauth.mixins import AdminUserRequiredMixin +from rest_framework.authtoken.models import Token + +import logging +# 生成一个以当前文件名为名字的logger实例 +logger = logging.getLogger(__name__) + +# Create your views here. +#class UserListView(LoginRequiredMixin, TemplateView): +class UserListView(AdminUserRequiredMixin,TemplateView): + """ + 显示用户列表 + """ + template_name = 'myauth/user.html' + + def get_context_data(self, **kwargs): + context = { + # 'app': 'Assets', + # 'action': 'Asset list', + } + kwargs.update(context) + return super(UserListView, self).get_context_data(**kwargs) + +class UserAddView(AdminUserRequiredMixin,TemplateView): + """ + 添加用户 + """ + template_name = 'myauth/useradd.html' + model = User + + #显示添加模板 + def get_context_data(self, **kwargs): + context = { + # 'app': 'Assets', + # 'action': 'Asset list', + } + kwargs.update(context) + return super(UserAddView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + name = request.POST.get("name") + password = request.POST.get("password") + nickname = request.POST.get("nickname") + email = request.POST.get("email") + mobile = request.POST.get("mobile") + is_superuser = request.POST.get("is_superuser") + is_active = request.POST.get("is_active") + + msg="" + try: + ormdata = self.model.objects.create(name=name, + password=make_password(password), + nickname=nickname, + email=email, + mobile=mobile, + is_superuser=is_superuser, + is_active=is_active + ) + ormdata.save() + data={"code":1,"msg":"添加成功"} + except IntegrityError as e: + #得到异常消息 + msg=str(e) + #msg = "用户或邮箱已经存在" + logger.error(e.__cause__) + data={"code":0,"msg":msg} + + return JsonResponse(data) + +class UserEditView(AdminUserRequiredMixin,TemplateView): + """ + 编辑用户 + """ + template_name = 'myauth/useredit.html' + model = User + + def get(self, request, *args, **kwargs): + """ + 得到提交的id + """ + _id = request.GET.get('id') + #根据id得到数据 + ormdata = self.model.objects.get(id=_id) + self.id = ormdata.id + self.name = ormdata.name + self.nickname = ormdata.nickname + self.email = ormdata.email + self.mobile = ormdata.mobile + self.is_superuser = ormdata.is_superuser + self.is_active = ormdata.is_active + return super(UserEditView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + context = { + 'id': self.id, + 'name': self.name, + 'nickname': self.nickname, + 'email': self.email, + 'mobile': self.mobile, + 'is_superuser': self.is_superuser, + 'is_active': self.is_active, + } + kwargs.update(context) + return super(UserEditView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + _id = request.POST.get('id') + name = request.POST.get("name") + nickname = request.POST.get("nickname") + email = request.POST.get("email") + mobile = request.POST.get("mobile") + is_superuser = request.POST.get("is_superuser") + is_active = request.POST.get("is_active") + + msg="" + try: + ormdata = self.model.objects.get(id=_id) + ormdata.name=name + ormdata.nickname=nickname + ormdata.email=email + ormdata.mobile=mobile + ormdata.is_superuser=is_superuser + ormdata.is_active=is_active + + ormdata.save() + data={"code":1,"msg":"修改成功"} + except IntegrityError as e: + #得到异常消息 + msg=str(e) + logger.error(e) + data={"code":0,"msg":msg} + + return JsonResponse(data) + +class UserDelView(AdminUserRequiredMixin,TemplateView): + """ + 删除用户(用户只能禁用,不能删除) + """ + model = User + + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + #得到批量或者单个要删除的id + ids = request.POST.getlist("ids[]") + + #删除管理员id,管理员id为1禁止删除 + admin_id = 1 + if admin_id in ids: + ids.remove(admin_id) + + #禁止删除自身 + my_id = request.user.id + if my_id in ids: + ids.remove(my_id) + + msg="" + #批量删除,使用in方法 + #self.model.objects.filter(id__in=ids).delete() + self.model.objects.filter(id__in=ids).update(is_active=0,is_delete=1) + Token.objects.filter(user_id__in=ids).delete() + data={"code":1,"msg":"删除成功"} + + return JsonResponse(data) + +class UserResetPasswdView(AdminUserRequiredMixin,TemplateView): + """ + 用户密码重置 + """ + template_name = 'myauth/resetpasswd.html' + model = User + + def get(self, request, *args, **kwargs): + """ + 得到提交的ids + """ + self.id = request.GET.get('id') + + return super(UserResetPasswdView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + context = { + 'id': self.id + } + kwargs.update(context) + return super(UserResetPasswdView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + _id = request.POST.get('id') + password = request.POST.get("password") + + msg="" + try: + # ormdata = self.model.objects.get(id=_id) + # ormdata.password=make_password(password) + + # ormdata.save() + self.model.objects.filter(id=_id).update(password=make_password(password)) + data={"code":1,"msg":"密码修改成功"} + except IntegrityError as e: + #得到异常消息 + msg=str(e) + logger.error(msg) + data={"code":0,"msg":msg} + + return JsonResponse(data) + #return HttpResponse(json.dumps(data,ensure_ascii = False), "text/html") + +class UserResetPasswd2View(AdminUserRequiredMixin,TemplateView): + """ + 用户密码重置 + """ + template_name = 'myauth/resetpasswd.html' + model = User + + def get(self, request, *args, **kwargs): + """ + 得到提交的ids + """ + self.user = request.user + + return super(UserResetPasswd2View, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + context = { + 'id': self.user.id + } + kwargs.update(context) + return super(UserResetPasswd2View, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + _id = request.POST.get('id') + password = request.POST.get("password") + + msg="" + try: + # ormdata = self.model.objects.get(id=_id) + # ormdata.password=make_password(password) + + # ormdata.save() + self.model.objects.filter(id=_id).update(password=make_password(password)) + data={"code":1,"msg":"密码修改成功"} + except IntegrityError as e: + #得到异常消息 + msg=str(e) + logger.error(msg) + data={"code":0,"msg":msg} + + return JsonResponse(data) + +#-------------------------------------------------------------- +class UserResetAccountView(LoginRequiredMixin,TemplateView): + """ + 重置账号信息 + """ + template_name = 'myauth/resetaccount.html' + model = User + + def get(self, request, *args, **kwargs): + """ + 得到提交的ids + """ + self.id = request.user.id + + return super(UserResetAccountView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + context = { + 'id': self.id + } + kwargs.update(context) + return super(UserResetAccountView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + _id = request.POST.get('id') + name = request.POST.get("name") + password = request.POST.get("password") + + msg="" + try: + # ormdata = self.model.objects.get(id=_id) + # ormdata.password=make_password(password) + + # ormdata.save() + self.model.objects.filter(id=_id).update(name=name,password=make_password(password)) + data={"code":1,"msg":"账户重置成功"} + except IntegrityError as e: + #得到异常消息 + msg=str(e) + logger.error(msg) + data={"code":0,"msg":msg} + + return JsonResponse(data) + #return HttpResponse(json.dumps(data,ensure_ascii = False), "text/html") + +#用户组 +class GroupListView(AdminUserRequiredMixin,TemplateView): + """ + 显示 + """ + template_name = 'myauth/group.html' + + def get_context_data(self, **kwargs): + context = { + # 'app': 'Assets', + # 'action': 'Asset list', + } + kwargs.update(context) + return super(GroupListView, self).get_context_data(**kwargs) + +class GroupAddView(AdminUserRequiredMixin,TemplateView): + """ + 添加 + """ + template_name = 'myauth/groupadd.html' + model = Group + + #显示添加模板 + def get_context_data(self, **kwargs): + context = { + # 'app': 'Assets', + # 'action': 'Asset list', + } + kwargs.update(context) + return super(GroupAddView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + name = request.POST.get("row[name]") + + msg="" + try: + ormdata = self.model.objects.create(name=name) + ormdata.save() + data={"code":1,"msg":msg} + except IntegrityError as e: + #得到异常消息 + #msg=str(e.__cause__) + msg = "用户组已经存在" + logger.error(e.__cause__) + data={"code":0,"msg":msg} + + return JsonResponse(data) + +class GroupEditView(AdminUserRequiredMixin,TemplateView): + """ + 编辑 + """ + template_name = 'myauth/groupedit.html' + model = Group + + def get(self, request, *args, **kwargs): + """ + 得到提交的ids + """ + _id = request.GET.get('id') + #根据id得到数据 + ormdata = self.model.objects.get(id=_id) + self.id = ormdata.id + self.name = ormdata.name + + return super(GroupEditView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + context = { + 'id': self.id, + 'name': self.name + } + kwargs.update(context) + return super(GroupEditView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + id = request.POST.get('id') + name = request.POST.get("row[name]") + + msg="" + try: + ormdata = self.model.objects.get(id=id) + ormdata.name=name + + ormdata.save() + data={"code":1,"msg":msg} + except IntegrityError as e: + #得到异常消息 + #msg=str(e.__cause__) + msg = "用户组已经存在" + logger.error(e.__cause__) + data={"code":0,"msg":msg} + + return JsonResponse(data) + +class GroupDelView(AdminUserRequiredMixin,TemplateView): + """ + 删除 + """ + model = Group + + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + #得到批量或者单个要删除的id + ids = request.POST.getlist("ids[]") + + msg="" + #批量删除,使用in方法 + self.model.objects.filter(id__in=ids).delete() + data={"code":1,"msg":msg} + + return JsonResponse(data) + + +class GroupUsersAddView(AdminUserRequiredMixin,TemplateView): + """ + 添加组成员 + """ + template_name = 'myauth/groupusersadd.html' + #model = Group + + def get(self, request, *args, **kwargs): + """ + + """ + self.groupid = int(request.GET.get('groupid')) + + return super(GroupUsersAddView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + #获取设置组信息 + group=Group.objects.get(id=self.groupid) + #获取非管理员切激活的用户 + user_ormdata = User.objects.filter(is_active=1).filter(is_superuser=0)#.values('id', 'name', 'nickname') + html = [] + + for data in user_ormdata: + groups = [] + for i in data.groups.all(): + groups.append(i.id) + #判断当前组是否在用户所在组 + if self.groupid in groups: + html.append('' % (data.id,data.name,data.nickname)) + else: + html.append('' % (data.id,data.name,data.nickname)) + + context = { + 'id': self.groupid, + 'group': group, + 'html':"".join(html) + } + kwargs.update(context) + return super(GroupUsersAddView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + _id = request.POST.get('id') + name = request.POST.get("row[name]") + users = request.POST.getlist("users[]") + + #未选择成员 + if users==[]: + data={"code":0,"msg":"请选择分配组成员"} + return JsonResponse(data) + + msg="" + try: + group_ormdata = Group.objects.get(id=_id) + isdel = False + for user in users: + + user_ormdata = User.objects.get(id=int(user)) + if not isdel: + #删除当前组下的所有用户 + user_ormdata.groups.through.objects.filter(group_id=_id).delete() + isdel=True + user_ormdata.groups.add(group_ormdata) + + # ormdata.save() + data={"code":1,"msg":msg} + except IntegrityError as e: + #得到异常消息 + msg=str(e.__cause__) + logger.error(e.__cause__) + data={"code":0,"msg":msg} + + return JsonResponse(data) + +#权限 +class PermissionListView(AdminUserRequiredMixin,TemplateView): + """ + 显示 + """ + template_name = 'myauth/permission.html' + + def get_context_data(self, **kwargs): + context = { + # 'app': 'Assets', + # 'action': 'Asset list', + } + kwargs.update(context) + return super(PermissionListView, self).get_context_data(**kwargs) + +class PermissionGroupAddView(AdminUserRequiredMixin,TemplateView): + """ + 添加 + """ + template_name = 'myauth/permissiongroupadd.html' + + def get(self, request, *args, **kwargs): + + self.groupid = int(request.GET.get('id')) + + return super(PermissionGroupAddView, self).get(request, *args, **kwargs) + + #显示添加模板 + def get_context_data(self, **kwargs): + + group = Group.objects.get(id=self.groupid) + + context = { + 'groupname': group.name, + 'id': self.groupid, + } + kwargs.update(context) + return super(PermissionGroupAddView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + #组id + _id = request.POST.get('id') + + #菜单id,使用,号连接 + menusid = request.POST.get("menusid") + + #未选择成员 + if menusid=="": + data={"code":0,"msg":"请选择分配菜单权限"} + return JsonResponse(data) + + msg="" + try: + + isdel = False + group_ormdata = Group.objects.get(id=_id) + + for perms in menusid.split(','): + perms_ormdata = Menu.objects.get(id=perms) + if not isdel: + #删除当前组下的所有菜单权限 + group_ormdata.permissions.through.objects.filter(group_id=_id).delete() + isdel=True + group_ormdata.permissions.add(perms_ormdata) + data={"code":1,"msg":msg} + except IntegrityError as e: + #得到异常消息 + msg=str(e.__cause__) + logger.error(e.__cause__) + data={"code":0,"msg":msg} + + return JsonResponse(data) + +class PermissionUserAddView(AdminUserRequiredMixin,TemplateView): + """ + 添加 + """ + template_name = 'myauth/permissionuseradd.html' + + def get(self, request, *args, **kwargs): + + self.id = int(request.GET.get('id')) + + return super(PermissionUserAddView, self).get(request, *args, **kwargs) + + #显示添加模板 + def get_context_data(self, **kwargs): + + user = User.objects.get(id=self.id) + + context = { + 'name': user.name, + 'id': self.id, + } + kwargs.update(context) + return super(PermissionUserAddView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + #组id + _id = request.POST.get('id') + + #菜单id,使用,号连接 + menusid = request.POST.get("menusid") + + #未选择成员 + if menusid=="": + data={"code":0,"msg":"请选择分配菜单权限"} + return JsonResponse(data) + + msg="" + try: + + isdel = False + user_ormdata = User.objects.get(id=_id) + + for perms in menusid.split(','): + perms_ormdata = Menu.objects.get(id=perms) + if not isdel: + #删除当前组下的所有菜单权限 + user_ormdata.permissions.through.objects.filter(user_id=_id).delete() + isdel=True + user_ormdata.permissions.add(perms_ormdata) + data={"code":1,"msg":msg} + except IntegrityError as e: + #得到异常消息 + msg=str(e.__cause__) + logger.error(e.__cause__) + data={"code":0,"msg":msg} + + return JsonResponse(data) diff --git a/apps/myauth/views_request.py b/apps/myauth/views_request.py new file mode 100644 index 0000000..af62a2c --- /dev/null +++ b/apps/myauth/views_request.py @@ -0,0 +1,289 @@ +#coding=utf-8 +from django.http import JsonResponse +from django.db.models import Q +from django.contrib.auth.decorators import login_required +from myauth.decorators import superuser_required + +from .models import User, Group +from common.utils import iso8601_to_time +import logging +# 生成一个以当前文件名为名字的logger实例 +logger = logging.getLogger(__name__) + + +#直接使用检查是否管理员 +#from django.contrib.auth.decorators import user_passes_test +#@user_passes_test(lambda u: u.is_superuser) + +@login_required +@superuser_required +def user(request): + """ + 所有用户的数据,用于bootstrap-table + """ + + #得到偏移量 + pageIndex= int(request.GET.get('page',1)) + #得到查询个数 + pageSize = int(request.GET.get('limit',10)) + + #搜索字段 + searchKey = request.GET.get('searchKey','') + searchValue = request.GET.get('searchValue','') + + #得到排序字段 + sort = request.GET.get('sort','id') + #得到排序规则 + order_by_type = request.GET.get('order','asc') + + if order_by_type == 'asc': + order_by = sort + else: + order_by = '-' + sort + + data = {} + data['code'] = 0 + data['msg'] = "" + data['data'] = [] + + if searchKey == '' or searchValue == '': + #获取所有记录 + data['count'] = User.objects.all().count() + if pageIndex == 1: + ormdata = User.objects.all().order_by(order_by)[:pageSize] #offset:limit + else: + ormdata = User.objects.all().order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize] + else: + if searchKey == 'name': + data['count'] = User.objects.filter(Q(name__icontains=searchValue)|Q(nickname__icontains=searchValue)|Q(email__icontains=searchValue)).count() + if pageIndex == 1: + ormdata = User.objects.filter(Q(name__icontains=searchValue)|Q(nickname__icontains=searchValue)|Q(email__icontains=searchValue)).order_by(order_by)[:pageSize] #offset:limit + else: + ormdata = User.objects.filter(Q(name__icontains=searchValue)|Q(nickname__icontains=searchValue)|Q(email__icontains=searchValue)).order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize] + elif searchKey == 'mobile': + data['count'] = User.objects.filter(mobile__icontains=searchValue).count() + if pageIndex == 1: + ormdata = User.objects.filter(mobile__icontains=searchValue).order_by(order_by)[:pageSize] #offset:limit + else: + ormdata = User.objects.filter(mobile__icontains=searchValue).order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize] + else: + return JsonResponse(data) + + #获取所有记录 + #data['count'] = User.objects.all().count() + #if pageIndex == 1: + #ormdata = User.objects.all().order_by(order_by)[:pageSize] #offset:limit + #else: + #ormdata = User.objects.all().order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize] + + for i in ormdata: + + last_login = i.last_login + if last_login != None: + last_login = iso8601_to_time(last_login) + + data['data'].append({"id":i.id, + "name":i.name, + "nickname":i.nickname, + "email":i.email, + "last_login":last_login, + "is_superuser":i.is_superuser, + "mobile":i.mobile, + "is_active":i.is_active + }) + #返回json串 + #return HttpResponse(json.dumps(data,ensure_ascii = False), "application/json") + return JsonResponse(data) + +@login_required +@superuser_required +def group(request): + """ + 所有用户的数据,用于bootstrap-table + """ + + #得到偏移量 + pageIndex= int(request.GET.get('offset',0)) + #得到查询个数 + pageSize = int(request.GET.get('limit',10)) + #增加状态后解决清空,搜索的limit为搜索的值 + if pageSize < 10: + pageSize = 10 + + #得到排序字段 + sort = request.GET.get('sort','id') + #得到排序规则 + order_by_type = request.GET.get('order','asc') + + if order_by_type == 'asc': + order_by = sort + else: + order_by = '-' + sort + + data = {} + data['rows'] = [] + + #获取所有记录 + data['total'] = Group.objects.all().count() + #判断起始查询如果是ALL,表示显示全部 + if pageSize != -1: + ormdata = Group.objects.all().order_by(order_by)[pageIndex:pageIndex + pageSize] + else: + ormdata = Group.objects.all().order_by(order_by) + + #组ID为key,用户登录名和用户姓名为value + group_users = {} + #获取所有用户数据 + user_ormdata = User.objects.filter(is_active=1).filter(is_superuser=0) + for u in user_ormdata: + groups = [] + #得到该用户所在的组 + for g in u.groups.all(): + if g.id not in group_users: + group_users[g.id] = [] + + group_users[g.id].append("%s(%s)" % (u.name,u.nickname)) + #groups.append(i.id for i in u.groups.all()]) + + for i in ormdata: + + if i.id in group_users: + data['rows'].append({"id":i.id, + "name":i.name, + "users":",".join(group_users[i.id]) + }) + else: + data['rows'].append({"id":i.id, + "name":i.name, + "users":"" + }) + #返回json串 + #return HttpResponse(json.dumps(data,ensure_ascii = False), "application/json") + return JsonResponse(data) + +@login_required +@superuser_required +def permissiongroup(request): + """ + 所有用户的数据,用于bootstrap-table + """ + + #得到偏移量 + pageIndex= int(request.GET.get('offset',0)) + #得到查询个数 + pageSize = int(request.GET.get('limit',10)) + #增加状态后解决清空,搜索的limit为搜索的值 + if pageSize < 10: + pageSize = 10 + + #得到排序字段 + sort = request.GET.get('sort','id') + #得到排序规则 + order_by_type = request.GET.get('order','asc') + + if order_by_type == 'asc': + order_by = sort + else: + order_by = '-' + sort + + data = {} + data['rows'] = [] + data['total'] = 0 + #获取所有记录 + data['total'] = Group.objects.all().count() + #判断起始查询如果是ALL,表示显示全部 + if pageSize != -1: + ormdata = Group.objects.all().order_by(order_by)[pageIndex:pageIndex + pageSize] + else: + ormdata = Group.objects.all().order_by(order_by) + + for i in ormdata: + #得到组对应的所有菜单权限,返回一个list + #menu_ids = i.get_permissions() + menu_names = i.get_permissions_name() + data['rows'].append({"id":i.id, + "name":i.name, + "menus":menu_names + }) + + #返回json串 + #return HttpResponse(json.dumps(data,ensure_ascii = False), "application/json") + return JsonResponse(data) + +@login_required +@superuser_required +def permissionuser(request): + """ + 所有用户的数据,用于bootstrap-table + """ + + #得到偏移量 + pageIndex= int(request.GET.get('offset',0)) + #得到查询个数 + pageSize = int(request.GET.get('limit',10)) + #增加状态后解决清空,搜索的limit为搜索的值 + if pageSize < 10: + pageSize = 10 + + #得到排序字段 + sort = request.GET.get('sort','id') + #得到排序规则 + order_by_type = request.GET.get('order','asc') + + if order_by_type == 'asc': + order_by = sort + else: + order_by = '-' + sort + + data = {} + data['rows'] = [] + + #获取所有记录 + data['total'] = User.objects.filter(is_active=1).filter(is_superuser=0).count() + #判断起始查询如果是ALL,表示显示全部 + if pageSize != -1: + ormdata = User.objects.filter(is_active=1).filter(is_superuser=0).order_by(order_by)[pageIndex:pageIndex + pageSize] + else: + ormdata = User.objects.filter(is_active=1).filter(is_superuser=0).order_by(order_by) + + for i in ormdata: + #得到组对应的所有菜单权限,返回一个list + #menu_ids = i.get_permissions() + menu_names = i.get_permissions_name(i) + data['rows'].append({"id":i.id, + "name":i.name, + "menus":menu_names + }) + + return JsonResponse(data) + + +@login_required +@superuser_required +def setValue(request): + """ + 修改用户状态和管理员状态 + """ + + _id = request.GET.get('id') + value = request.GET.get('value') + tag = request.GET.get('tag') + + if request.user.id == int(_id): + data = {'code':0,'msg':'禁止操作自身'} + return JsonResponse(data) + + if tag == 'status': + #修改状态 + User.objects.filter(id=_id).update(is_active=value) + data = {'code':1,'msg':'操作成功'} + elif tag == 'super': + #修改是否管理员 + User.objects.filter(id=_id).update(is_superuser=value) + data = {'code':1,'msg':'操作成功'} + else: + data = {'code':0,'msg':'未知操作'} + + #返回json串 + #return HttpResponse(json.dumps(data,ensure_ascii = False), "application/json") + return JsonResponse(data) \ No newline at end of file diff --git a/apps/mymenu/__init__.py b/apps/mymenu/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/mymenu/admin.py b/apps/mymenu/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/apps/mymenu/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/mymenu/apps.py b/apps/mymenu/apps.py new file mode 100644 index 0000000..9735821 --- /dev/null +++ b/apps/mymenu/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class MymenuConfig(AppConfig): + name = 'mymenu' diff --git a/apps/mymenu/migrations/0001_initial.py b/apps/mymenu/migrations/0001_initial.py new file mode 100644 index 0000000..b4e1fa9 --- /dev/null +++ b/apps/mymenu/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 3.1.7 on 2021-03-26 03:36 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Menu', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=50, unique=True, verbose_name='菜单名')), + ('menuurl', models.CharField(max_length=150, verbose_name='url地址')), + ('menupid', models.IntegerField(verbose_name='父ID')), + ('menuseq', models.IntegerField(verbose_name='菜单排序')), + ('menupath', models.CharField(max_length=100, verbose_name='菜单级别序号,父id,使用逗号分割')), + ('menutype', models.IntegerField(verbose_name='菜单类型 1、菜单 2、功能')), + ], + ), + ] diff --git a/apps/mymenu/migrations/0002_alter_menu_id.py b/apps/mymenu/migrations/0002_alter_menu_id.py new file mode 100644 index 0000000..6ea9396 --- /dev/null +++ b/apps/mymenu/migrations/0002_alter_menu_id.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.2 on 2022-02-22 08:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mymenu', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='menu', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ] diff --git a/apps/mymenu/migrations/__init__.py b/apps/mymenu/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/mymenu/models.py b/apps/mymenu/models.py new file mode 100644 index 0000000..2c8b886 --- /dev/null +++ b/apps/mymenu/models.py @@ -0,0 +1,17 @@ +from django.db import models + +# Create your models here. +class Menu(models.Model): + """ + 菜单 + """ + name = models.CharField('菜单名', max_length=50, unique=True) + menuurl = models.CharField('url地址', max_length=150) + menupid = models.IntegerField(verbose_name='父ID') + menuseq = models.IntegerField('菜单排序') + menupath = models.CharField(verbose_name='菜单级别序号,父id,使用逗号分割', max_length=100) + menutype = models.IntegerField('菜单类型 1、菜单 2、功能') + + #class Meta: + #db_table = 'menu' #自定义表名称为mytable + #unique_together=("name","menuurl") \ No newline at end of file diff --git a/apps/mymenu/templates/mymenu/mymenu.html b/apps/mymenu/templates/mymenu/mymenu.html new file mode 100644 index 0000000..28741fc --- /dev/null +++ b/apps/mymenu/templates/mymenu/mymenu.html @@ -0,0 +1,141 @@ +{% extends 'base_right.html' %} + +{% block bodycontent %} + +
+
+
+
+ +
+ +
+
+
+ + 添加 +
+ +
+
+
+
+ +{% endblock %} + +{% block footer %} + +{% endblock %} \ No newline at end of file diff --git a/apps/mymenu/templates/mymenu/mymenuadd.html b/apps/mymenu/templates/mymenu/mymenuadd.html new file mode 100644 index 0000000..d523ed1 --- /dev/null +++ b/apps/mymenu/templates/mymenu/mymenuadd.html @@ -0,0 +1,70 @@ +{% extends 'base_right.html' %} + +{% block bodycontent %} +
+
{% csrf_token %} +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ + +
+
+{% endblock %} + +{% block footer %} + +{% endblock %} \ No newline at end of file diff --git a/apps/mymenu/templates/mymenu/mymenuedit.html b/apps/mymenu/templates/mymenu/mymenuedit.html new file mode 100644 index 0000000..0445395 --- /dev/null +++ b/apps/mymenu/templates/mymenu/mymenuedit.html @@ -0,0 +1,86 @@ +{% extends 'base_right.html' %} + +{% block bodycontent %} +
+
{% csrf_token %} + + +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+{% endblock %} + + +{% block footer %} + +{% endblock %} \ No newline at end of file diff --git a/apps/mymenu/tests.py b/apps/mymenu/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/mymenu/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/mymenu/urls/__init__.py b/apps/mymenu/urls/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/apps/mymenu/urls/__init__.py @@ -0,0 +1 @@ + diff --git a/apps/mymenu/urls/views_urls.py b/apps/mymenu/urls/views_urls.py new file mode 100644 index 0000000..8ccd8c7 --- /dev/null +++ b/apps/mymenu/urls/views_urls.py @@ -0,0 +1,21 @@ +# coding:utf-8 +from django.urls import path +from .. import views, views_request + +app_name = 'mymenu' + +urlpatterns = [ + # Resource asset url + + path('index/', views.MenuListView.as_view()), + path('add', views.MenuAddView.as_view()), + path('edit', views.MenuEditView.as_view()), + path('del', views.MenuDelView.as_view()), + + path('list/', views_request.menu), + path('groupmenuztree/', views_request.groupmenuztree), + path('usermenuztree/', views_request.usermenuztree), + path('menuztree/', views_request.menuztree), + +] + diff --git a/apps/mymenu/views.py b/apps/mymenu/views.py new file mode 100644 index 0000000..22a814c --- /dev/null +++ b/apps/mymenu/views.py @@ -0,0 +1,194 @@ +from django.views.generic import TemplateView +from django.http import JsonResponse +#from django.db import IntegrityError +from django.db.utils import IntegrityError +from .models import Menu +from myauth.mixins import AdminUserRequiredMixin +from common.utils import get_logger + +logger = get_logger("view") + +class MenuListView(AdminUserRequiredMixin,TemplateView): + """ + 显示 + """ + template_name = 'mymenu/mymenu.html' + + def get(self, request, *args, **kwargs): + """ + 得到提交的id + """ + + return super(MenuListView, self).get(request, *args, **kwargs) + + def get_context_data(self, **kwargs): + context = { + #'gameid': self.gameid, + # 'action': 'Asset list', + } + kwargs.update(context) + return super(MenuListView, self).get_context_data(**kwargs) + +class MenuAddView(AdminUserRequiredMixin,TemplateView): + """ + 添加 + """ + template_name = 'mymenu/mymenuadd.html' + model = Menu + + def get(self, request, *args, **kwargs): + """ + 得到提交的id + """ + return super(MenuAddView, self).get(request, *args, **kwargs) + + #显示添加模板 + def get_context_data(self, **kwargs): + #查询非功能的菜单 + menu = self.model.objects.filter(menutype=1) + self.html_menu = [] + for data in menu: + line = '' % (str(data.id), data.name) + self.html_menu.append(line) + + context = { + 'html_menu': self.html_menu, + } + kwargs.update(context) + return super(MenuAddView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + + name = request.POST.get("row[name]") + menuurl = request.POST.get("row[menuurl]") + menutype = request.POST.get("row[menutype]") + menuseq = request.POST.get("row[menuseq]") + menupid = request.POST.get("row[menupid]") + + msg="" + if name == "": + data={"code":0,"msg":"菜单名称不能为空"} + return JsonResponse(data) + + if menuurl == "": + data={"code":0,"msg":"菜单地址不能为空"} + return JsonResponse(data) + + if int(menupid) == 0: + menupath = menupid + else: + #得到父菜单信息 + Pmenu = Menu.objects.get(id=menupid) + menupath = Pmenu.menupath + ',' + menupid + + try: + ormdata = self.model.objects.create(name=name, + menuurl=menuurl, + menutype=menutype, + menuseq=menuseq, + menupid=menupid, + menupath=menupath + ) + ormdata.save() + data={"code":1,"msg":msg} + except IntegrityError as e: + #得到异常消息 + msg=str(e.__cause__) + logger.error(e.__cause__) + data={"code":0,"msg":msg} + + return JsonResponse(data) + +class MenuEditView(AdminUserRequiredMixin,TemplateView): + """ + 编辑 + """ + template_name = 'mymenu/mymenuedit.html' + model = Menu + + def get(self, request, *args, **kwargs): + """ + 得到提交的ids + """ + self.id = request.GET.get('id') + #根据id得到数据 + self.ormdata = self.model.objects.get(id=self.id) + return super(MenuEditView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + #查询非功能的菜单 + menu = self.model.objects.filter(menutype=1) + context = { + 'id': self.id, + 'mymenu': self.ormdata, + 'menu':menu + } + kwargs.update(context) + return super(MenuEditView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + _id = request.POST.get('id') + name = request.POST.get("row[name]") + menuurl = request.POST.get("row[menuurl]") + menutype = request.POST.get("row[menutype]") + menuseq = request.POST.get("row[menuseq]") + menupid = request.POST.get("row[menupid]") + + if name == "": + data={"code":0,"msg":"菜单名称不能为空"} + return JsonResponse(data) + + if menuurl == "": + data={"code":0,"msg":"菜单地址不能为空"} + return JsonResponse(data) + + msg="" + + try: + ormdata = self.model.objects.get(id=_id) + ormdata.name=name + ormdata.menuurl=menuurl + ormdata.menutype=menutype + ormdata.menuseq=menuseq + ormdata.menupid=menupid + + ormdata.save() + data={"code":1,"msg":msg} + except IntegrityError as e: + #得到异常消息 + msg=str(e.__cause__) + #msg = "游戏名已经存在" + logger.error(e.__cause__) + data={"code":0,"msg":msg} + + return JsonResponse(data) + +class MenuDelView(AdminUserRequiredMixin,TemplateView): + """ + 删除 + """ + model = Menu + + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + #得到单个要删除的id + _id = request.POST.get("id") + + msg="" + #删除 + self.model.objects.filter(id=_id).delete() + data={"code":1,"msg":msg} + + return JsonResponse(data) \ No newline at end of file diff --git a/apps/mymenu/views_request.py b/apps/mymenu/views_request.py new file mode 100644 index 0000000..31a16d5 --- /dev/null +++ b/apps/mymenu/views_request.py @@ -0,0 +1,172 @@ +#coding=utf-8 +from django.http import JsonResponse +from django.db.models import Q +from django.contrib.auth.decorators import login_required +from myauth.decorators import superuser_required + +from .models import Menu +from myauth.models import Group,User + +@login_required +@superuser_required +def menu(request): + """ + 所有的数据,用于bootstrap-table + """ + + #得到偏移量 + pageIndex= int(request.GET.get('offset',0)) + #得到查询个数 + pageSize = int(request.GET.get('limit',10)) + #增加状态后解决清空,搜索的limit为搜索的值 + if pageSize < 10: + pageSize = 10 + + #得到搜索字段 + search_key = request.GET.get('search') + #得到搜索类型,html中自定义 + search_type = request.GET.get('search_type') + + #得到排序字段 + sort = request.GET.get('sort','id') + #得到排序规则 + order_by_type = request.GET.get('order','asc') + + if order_by_type == 'asc': + order_by = sort + else: + order_by = '-' + sort + + data = {} + data['rows'] = [] + + if search_key=="" or search_key == None: + #获取所有记录 + data['total'] = Menu.objects.all().count() + #判断起始查询如果是ALL,表示显示全部 + if pageSize != -1: + ormdata = Menu.objects.all().order_by(order_by)[pageIndex:pageIndex + pageSize] + else: + ormdata = Menu.objects.all().order_by(order_by) + else: + #拼接搜索 + key = "name like '%%" +search_key +"%%'" + + #获取所有记录 + data['total'] = Menu.objects.filter(Q(name__contains=search_key)).count() + #判断起始查询如果是ALL,表示显示全部 + if pageSize != -1: + ormdata = Menu.objects.filter(Q(name__contains=search_key)).order_by(order_by)[pageIndex:pageSize] + else: + ormdata = Menu.objects.filter(Q(name__contains=search_key)).order_by(order_by) + + for i in ormdata: + #如果没有上级菜单,就是顶级,如果存在上级菜单,获取上级菜单的中文名称 + if i.menupid == 0: + menupid = "顶级" + else: + get_menu_ormdata = Menu.objects.get(id=i.menupid) + menupid = get_menu_ormdata.name + + if i.menutype == 1: + menutype = "菜单" + else: + menutype = "功能" + + data['rows'].append({"id": i.id, + "name": i.name, + "menuurl": i.menuurl, + "menupid":menupid, + "menuseq": i.menuseq, + "menutype": menutype + }) + #返回json串 + #return HttpResponse(json.dumps(data,ensure_ascii = False), "application/json") + return JsonResponse(data) + +@login_required +@superuser_required +def menuztree(request): + """ + 用于ztree 菜单树 + """ + + data = [] + + #得到所有菜单 + ormdata = Menu.objects.all().order_by('menuseq') + + for i in ormdata: + data.append({"name":i.name, + "id":i.id, + "pId":i.menupid, + "open":True + }) + + return JsonResponse(data, safe=False) + +@login_required +@superuser_required +def groupmenuztree(request): + """ + 用于ztree 用户组菜单权限树 + """ + #接收组权限ID + _id = request.GET.get("id") + data = [] + + #得到所有菜单 + ormdata = Menu.objects.all() + #组 + group = Group.objects.get(id=_id) + + for i in ormdata: + #得到组所在的菜单id list + menu_ids = group.get_permissions() + if i.id in menu_ids: + data.append({"name":i.name, + "id":i.id, + "pId":i.menupid, + "checked":True, + "open":True + }) + else: + data.append({"name":i.name, + "id":i.id, + "pId":i.menupid + }) + + return JsonResponse(data, safe=False) + +@login_required +@superuser_required +def usermenuztree(request): + """ + 用于ztree 用户菜单权限树 + """ + #接收组权限ID + _id = request.GET.get("id") + data = [] + + #得到所有菜单 + ormdata = Menu.objects.all() + #用户 + user = User.objects.get(id=_id) + + for i in ormdata: + #得到组所在的菜单id list + menu_ids = user.get_permissions(user) + if i.id in menu_ids: + data.append({"name":i.name, + "id":i.id, + "pId":i.menupid, + "checked":True, + "open":True + }) + else: + data.append({"name":i.name, + "id":i.id, + "pId":i.menupid + }) + + return JsonResponse(data, safe=False) \ No newline at end of file diff --git a/apps/notify/__init__.py b/apps/notify/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/notify/admin.py b/apps/notify/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/apps/notify/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/notify/apps.py b/apps/notify/apps.py new file mode 100644 index 0000000..a2eab6b --- /dev/null +++ b/apps/notify/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class NotifyConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'notify' diff --git a/apps/notify/migrations/0001_initial.py b/apps/notify/migrations/0001_initial.py new file mode 100644 index 0000000..e8eea05 --- /dev/null +++ b/apps/notify/migrations/0001_initial.py @@ -0,0 +1,38 @@ +# Generated by Django 4.0.2 on 2022-02-23 09:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='NotifyConfig', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=20, unique=True, verbose_name='名称,特定:iyuu,telegram,email')), + ('iyuu_key', models.CharField(max_length=200, verbose_name='IYUU令牌')), + ('tg_chat_id', models.BigIntegerField(verbose_name='频道ID或自身id')), + ('tg_token', models.CharField(max_length=200, verbose_name='telegram令牌')), + ('smtp_server', models.CharField(max_length=100, verbose_name='发信地址')), + ('smtp_port', models.IntegerField(verbose_name='发信端口')), + ('smtp_user', models.CharField(max_length=100, verbose_name='发信账号')), + ('smtp_password', models.CharField(max_length=128, verbose_name='发信账号密码')), + ('smtp_is_tls', models.BooleanField(default=False, verbose_name='是否启用tls')), + ('receive_user', models.CharField(max_length=100, verbose_name='接收账号,默认为发信账号')), + ], + ), + migrations.CreateModel( + name='NotifyType', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=20, unique=True, verbose_name='名称')), + ('type_id', models.IntegerField(unique=True, verbose_name='类型ID')), + ], + ), + ] diff --git a/apps/notify/migrations/0002_alter_notifyconfig_smtp_port_and_more.py b/apps/notify/migrations/0002_alter_notifyconfig_smtp_port_and_more.py new file mode 100644 index 0000000..10c2bb2 --- /dev/null +++ b/apps/notify/migrations/0002_alter_notifyconfig_smtp_port_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.0.2 on 2022-02-23 10:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('notify', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='notifyconfig', + name='smtp_port', + field=models.IntegerField(default=25, verbose_name='发信端口'), + ), + migrations.AlterField( + model_name='notifyconfig', + name='tg_chat_id', + field=models.BigIntegerField(default=0, verbose_name='频道ID或自身id'), + ), + ] diff --git a/apps/notify/migrations/0003_notifyconfig_mail_type.py b/apps/notify/migrations/0003_notifyconfig_mail_type.py new file mode 100644 index 0000000..06e1a65 --- /dev/null +++ b/apps/notify/migrations/0003_notifyconfig_mail_type.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.2 on 2022-02-24 00:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('notify', '0002_alter_notifyconfig_smtp_port_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='notifyconfig', + name='mail_type', + field=models.CharField(max_length=10, null=True, verbose_name='发件邮箱类型:qq,sina,163'), + ), + ] diff --git a/apps/notify/migrations/0004_mailtype_remove_notifyconfig_smtp_is_tls_and_more.py b/apps/notify/migrations/0004_mailtype_remove_notifyconfig_smtp_is_tls_and_more.py new file mode 100644 index 0000000..8488e9f --- /dev/null +++ b/apps/notify/migrations/0004_mailtype_remove_notifyconfig_smtp_is_tls_and_more.py @@ -0,0 +1,35 @@ +# Generated by Django 4.0.2 on 2022-02-24 07:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('notify', '0003_notifyconfig_mail_type'), + ] + + operations = [ + migrations.CreateModel( + name='MailType', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=20, unique=True, verbose_name='类型名称,QQ邮箱')), + ('alias_name', models.CharField(max_length=20, verbose_name='类型别名,qq,sina,163')), + ('smtp_server', models.CharField(max_length=100, verbose_name='发信地址')), + ('smtp_port', models.IntegerField(default=465, verbose_name='发信端口')), + ], + ), + migrations.RemoveField( + model_name='notifyconfig', + name='smtp_is_tls', + ), + migrations.RemoveField( + model_name='notifyconfig', + name='smtp_port', + ), + migrations.RemoveField( + model_name='notifyconfig', + name='smtp_server', + ), + ] diff --git a/apps/notify/migrations/0005_delete_notifytype.py b/apps/notify/migrations/0005_delete_notifytype.py new file mode 100644 index 0000000..e1432cf --- /dev/null +++ b/apps/notify/migrations/0005_delete_notifytype.py @@ -0,0 +1,16 @@ +# Generated by Django 4.0.2 on 2022-02-28 03:02 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('notify', '0004_mailtype_remove_notifyconfig_smtp_is_tls_and_more'), + ] + + operations = [ + migrations.DeleteModel( + name='NotifyType', + ), + ] diff --git a/apps/notify/migrations/__init__.py b/apps/notify/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/notify/models.py b/apps/notify/models.py new file mode 100644 index 0000000..7b346e3 --- /dev/null +++ b/apps/notify/models.py @@ -0,0 +1,23 @@ +from django.db import models + +# Create your models here. + + +class MailType(models.Model): + name = models.CharField('类型名称,QQ邮箱', max_length=20, unique=True) + alias_name = models.CharField("类型别名,qq,sina,163", max_length=20) + smtp_server = models.CharField("发信地址", max_length=100) + smtp_port = models.IntegerField("发信端口",default=465) + +class NotifyConfig(models.Model): + """ + 通知配置 + """ + name = models.CharField('名称,特定:iyuu,telegram,email', max_length=20, unique=True) + iyuu_key = models.CharField("IYUU令牌", max_length=200) + tg_chat_id = models.BigIntegerField('频道ID或自身id',default=0) + tg_token = models.CharField("telegram令牌", max_length=200) + mail_type = models.CharField("发件邮箱类型:qq,sina,163", max_length=10,null=True) + smtp_user = models.CharField("发信账号", max_length=100) + smtp_password = models.CharField("发信账号密码", max_length=128) + receive_user = models.CharField("接收账号,默认为发信账号", max_length=100) \ No newline at end of file diff --git a/apps/notify/templates/notify/email.html b/apps/notify/templates/notify/email.html new file mode 100644 index 0000000..a772d79 --- /dev/null +++ b/apps/notify/templates/notify/email.html @@ -0,0 +1,183 @@ + + +
+
+
+ +
{% csrf_token %} + + + +
+ +
+
+
+
+ +
+ +
+ +
+
+ +
+ +
+ + + +
+
+ +
+ +
+ +
+ +
+ +
+
+ + + +
+
+
+ +
+
+ +
+
说明
+
+
目前支持邮箱:腾讯,新浪,网易163 +
+
+
+
+ + + \ No newline at end of file diff --git a/apps/notify/templates/notify/iyuu.html b/apps/notify/templates/notify/iyuu.html new file mode 100644 index 0000000..30b4f26 --- /dev/null +++ b/apps/notify/templates/notify/iyuu.html @@ -0,0 +1,89 @@ + + +
+
+
+ +
{% csrf_token %} + + + +
+ +
+ +
+
+ +
+
+ + + +
+
+
+ +
+
+ +
+
说明
+
+
IYUU令牌申请地址: https://iyuu.cn
+
+
+
+ + + \ No newline at end of file diff --git a/apps/notify/templates/notify/telegram.html b/apps/notify/templates/notify/telegram.html new file mode 100644 index 0000000..cd88b63 --- /dev/null +++ b/apps/notify/templates/notify/telegram.html @@ -0,0 +1,113 @@ + + +
+
+
+ +
{% csrf_token %} + + + +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+ + + +
+
+
+ +
+
+ +
+
说明
+
+
+ 1、自备扶手梯
+ 2、关注@BotFather用户  点击这里创建token
+
依次输入:
+                    /start
+                    /newbot
+                    # 然后输入机器人的名字,注意必须以 bot 结尾!
+                    # `987654321:FEDCBA_dfoiuweSWEczgxT7-l4r9Y` 收到类似这样的就是你的token
+                
+ 3、获取个人chat_id   点击这里获取chat_id

+ 4、获取频道chat_id    +
创建新的频道,并把创建的机器人拉到频道里,然后@你的机器人并随意发送一条信息,然后访问 https://api.telegram.org/bot/getUpdates 获取,-100开头的id就是.
+                
+ +
+
+
+
+ + + \ No newline at end of file diff --git a/apps/notify/tests.py b/apps/notify/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/notify/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/notify/urls/__init__.py b/apps/notify/urls/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/apps/notify/urls/__init__.py @@ -0,0 +1 @@ + diff --git a/apps/notify/urls/views_urls.py b/apps/notify/urls/views_urls.py new file mode 100644 index 0000000..ea347df --- /dev/null +++ b/apps/notify/urls/views_urls.py @@ -0,0 +1,19 @@ +# coding:utf-8 +from django.urls import path +from .. import views, views_request + +app_name = 'notify' + +urlpatterns = [ + # Resource asset path + + #消息通知 + path('iyuu', views.IyuuView.as_view()), + path('iyuutest', views_request.iyuutest), + path('telegram', views.TelegramView.as_view()), + path('telegramtest', views_request.telegramtest), + path('email', views.EmailView.as_view()), + path('emailtest', views_request.emailtest), + path('mailtype/list', views_request.mailtypeinfo), +] + diff --git a/apps/notify/views.py b/apps/notify/views.py new file mode 100644 index 0000000..fb000a1 --- /dev/null +++ b/apps/notify/views.py @@ -0,0 +1,192 @@ +from django.shortcuts import render +from django.views.generic import TemplateView +from django.contrib.auth.mixins import LoginRequiredMixin +from django.http import JsonResponse +from .models import NotifyConfig + +# Create your views here. +class IyuuView(LoginRequiredMixin,TemplateView): + """ + iyuu配置 + """ + template_name = 'notify/iyuu.html' + + #显示添加模板 + def get(self, request, *args, **kwargs): + """ + 得到 + """ + + return super(IyuuView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + #获取iyuu记录 + iyuu_count = NotifyConfig.objects.filter(name='iyuu').count() + if iyuu_count == 0: + + context = {} + else: + ormdata = NotifyConfig.objects.get(name='iyuu') + context = { + 'id': ormdata.id, + 'name': ormdata.name, + 'iyuu_key': ormdata.iyuu_key, + } + kwargs.update(context) + return super(IyuuView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + _id = request.POST.get('id','') + iyuu_key = request.POST.get("iyuu_key").strip() + + iyuu_count = NotifyConfig.objects.filter(name='iyuu').count() + if iyuu_count == 0: + ormdata = NotifyConfig.objects.create(name='iyuu', + iyuu_key=iyuu_key, + ) + else: + ormdata = NotifyConfig.objects.get(id=_id) + ormdata.iyuu_key = iyuu_key + + ormdata.save() + + response_data={"code":1,"msg":"添加成功"} + + return JsonResponse(response_data) + +#------------------------------ +class TelegramView(LoginRequiredMixin,TemplateView): + """ + Telegram配置 + """ + template_name = 'notify/telegram.html' + + #显示添加模板 + def get(self, request, *args, **kwargs): + """ + 得到 + """ + + return super(TelegramView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + #获取记录 + telegram_count = NotifyConfig.objects.filter(name='telegram').count() + if telegram_count == 0: + + context = {} + else: + ormdata = NotifyConfig.objects.get(name='telegram') + context = { + 'id': ormdata.id, + 'name': ormdata.name, + 'tg_chat_id': ormdata.tg_chat_id, + 'tg_token': ormdata.tg_token, + } + kwargs.update(context) + return super(TelegramView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + _id = request.POST.get('id','') + tg_chat_id = request.POST.get("tg_chat_id").strip() + tg_token = request.POST.get("tg_token").strip() + + telegram_count = NotifyConfig.objects.filter(name='telegram').count() + if telegram_count == 0: + ormdata = NotifyConfig.objects.create(name='telegram', + tg_chat_id=tg_chat_id, + tg_token=tg_token, + ) + else: + ormdata = NotifyConfig.objects.get(id=_id) + ormdata.tg_chat_id = tg_chat_id + ormdata.tg_token = tg_token + + ormdata.save() + + response_data={"code":1,"msg":"添加成功"} + + return JsonResponse(response_data) + +#------------------------------ +class EmailView(LoginRequiredMixin,TemplateView): + """ + Email配置 + """ + template_name = 'notify/email.html' + + #显示添加模板 + def get(self, request, *args, **kwargs): + """ + 得到 + """ + + return super(EmailView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + #获取记录 + telegram_count = NotifyConfig.objects.filter(name='email').count() + if telegram_count == 0: + + context = {} + else: + ormdata = NotifyConfig.objects.get(name='email') + context = { + 'id': ormdata.id, + 'name': ormdata.name, + 'mail_type': ormdata.mail_type, + 'smtp_user': ormdata.smtp_user, + 'smtp_password': ormdata.smtp_password, + 'receive_user': ormdata.receive_user, + } + kwargs.update(context) + return super(EmailView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + _id = request.POST.get('id','') + mail_type = request.POST.get("mail_type").strip() + smtp_user = request.POST.get("smtp_user").strip() + smtp_password = request.POST.get("smtp_password").strip() + receive_user = request.POST.get("receive_user","").strip() + + if receive_user == '': + receive_user = smtp_user + + email_count = NotifyConfig.objects.filter(name='email').count() + if email_count == 0: + ormdata = NotifyConfig.objects.create(name='email', + mail_type=mail_type, + smtp_user=smtp_user, + smtp_password=smtp_password, + receive_user=receive_user, + ) + else: + ormdata = NotifyConfig.objects.get(id=_id) + ormdata.mail_type = mail_type + ormdata.smtp_user = smtp_user + ormdata.smtp_password = smtp_password + ormdata.receive_user = receive_user + + ormdata.save() + + response_data={"code":1,"msg":"添加成功"} + + return JsonResponse(response_data) \ No newline at end of file diff --git a/apps/notify/views_request.py b/apps/notify/views_request.py new file mode 100644 index 0000000..e20443a --- /dev/null +++ b/apps/notify/views_request.py @@ -0,0 +1,179 @@ +#coding=utf-8 +from django.http import JsonResponse +from django.db.models import Q +from django.contrib.auth.decorators import login_required +from .models import NotifyConfig,MailType + +import requests +import datetime +from urllib import parse +import simplejson as json +import telegram +import sys +from common.utils import send_email + +#直接使用检查是否管理员 +#from django.contrib.auth.decorators import user_passes_test +#@user_passes_test(lambda u: u.is_superuser) + +@login_required +def iyuutest(request): + """ + 提供相应的数据 + """ + + _id = request.POST.get('id','') + + if _id == '': + + response_data={"code":0,"msg":"请先配置IYUU令牌"} + + else: + + ormdata = NotifyConfig.objects.get(id=_id) + iyuu_key = ormdata.iyuu_key + + + user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36' + + now = datetime.datetime.now() + time = now.strftime("%Y-%m-%d %H:%M:%S") + headers = { + 'user_agent': user_agent, + 'Content-type': 'application/x-www-form-urlencoded' + } + + now_time = parse.quote('
') + parse.unquote(time) + parse.quote('

') + send_txts = parse.quote('
[测试]

') + api = 'https://iyuu.cn/' + iyuu_key + '.send' + sen_url = api + '?text='+ parse.quote('测试提示') + '&desp=' + now_time + send_txts + response = requests.get(sen_url, headers=headers ,verify=False) + + response_msg = json.loads(response.text) + if response.status_code == 200 and response_msg['errcode'] == 0: + response_data={"code":1,"msg":"发送测试成功"} + else: + response_data={"code":0,"msg":"发送测试失败,%s" % response_msg['errmsg'] } + + return JsonResponse(response_data) + +@login_required +def telegramtest(request): + """ + 提供相应的数据 + """ + + _id = request.POST.get('id','') + + if _id == '': + + response_data={"code":0,"msg":"请先配置Telegram"} + + else: + + ormdata = NotifyConfig.objects.get(id=_id) + tg_chat_id = ormdata.tg_chat_id + tg_token = ormdata.tg_token + + bot = telegram.Bot(token = tg_token) + + now = datetime.datetime.now() + time = now.strftime("%Y-%m-%d %H:%M:%S") + + try: + + response = bot.send_message(chat_id=tg_chat_id, + text='%s[测试]' % time, + parse_mode=telegram.ParseMode.HTML) + + #response_msg = json.loads(response) + response_data={"code":1,"msg":"发送测试成功"} + except: + ex_type, ex_val, ex_stack = sys.exc_info() + + if 'Chat not found' in str(ex_val): + response_data={"code":0,"msg":"发送测试失败,未找到频道ID" } + elif 'Unauthorized' in str(ex_val): + response_data={"code":0,"msg":"发送测试失败,令牌配置错误" } + else: + response_data={"code":0,"msg":"发送测试失败,请联系管理员" } + + return JsonResponse(response_data) + +@login_required +def emailtest(request): + """ + 提供相应的数据 + """ + + _id = request.POST.get('id','') + + if _id == '': + + response_data={"code":0,"msg":"请先配置邮箱"} + + else: + + ormdata = NotifyConfig.objects.get(id=_id) + mail_type = ormdata.mail_type + smtp_user = ormdata.smtp_user + smtp_password = ormdata.smtp_password + receive_user = ormdata.receive_user + + receiver_users = [] + for i in receive_user.split(","): + receiver_users.append(i) + + now = datetime.datetime.now() + time = now.strftime("%Y-%m-%d %H:%M:%S") + + try: + + sendata='%s[测试]' % time + + flag,response = send_email(mail_type,smtp_user,smtp_password,receiver_users,sendata,isTest=True) + + #response_msg = json.loads(response) + response_data={"code":1,"msg":response} + except: + response_data={"code":0,"msg":response } + + return JsonResponse(response_data) + + +#================== +@login_required +def mailtypeinfo(request): + """ + 邮箱类型 + xmSelect + """ + + data = {} + data['code'] = 0 + data['msg'] = "" + data['data'] = [] + + #得到排序字段 + sort = request.GET.get('sort','name') + #得到排序规则 + order_by_type = request.GET.get('order','desc') + + if order_by_type == 'desc': + order_by = sort + else: + order_by = '-' + sort + + #获取所有记录 + data['count'] = MailType.objects.count() + + ormdata = MailType.objects.order_by(order_by).all() + + for i in ormdata: + + data['data'].append({ + "name":i.name, + "value":i.alias_name + }) + + return JsonResponse(data) \ No newline at end of file diff --git a/apps/sites/__init__.py b/apps/sites/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/sites/admin.py b/apps/sites/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/apps/sites/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/sites/apps.py b/apps/sites/apps.py new file mode 100644 index 0000000..8d8aedb --- /dev/null +++ b/apps/sites/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class SitesConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'sites' diff --git a/apps/sites/migrations/0001_initial.py b/apps/sites/migrations/0001_initial.py new file mode 100644 index 0000000..0bc6785 --- /dev/null +++ b/apps/sites/migrations/0001_initial.py @@ -0,0 +1,43 @@ +# Generated by Django 4.0.2 on 2022-02-22 08:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='SiteRank', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=50, verbose_name='级别名称')), + ('site_name', models.CharField(max_length=50, verbose_name='网站名简称,英文')), + ('download', models.CharField(max_length=50, verbose_name='下载量')), + ('up_time', models.CharField(max_length=50, verbose_name='升级时间')), + ('upload', models.CharField(max_length=50, verbose_name='上传量')), + ('ratio', models.CharField(max_length=20, verbose_name='魔力')), + ('privilege', models.CharField(max_length=200, verbose_name='信息说明')), + ('serial_number', models.IntegerField(verbose_name='排序序号')), + ], + ), + migrations.CreateModel( + name='Site', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=50, verbose_name='网站名简称,英文')), + ('name_cn', models.CharField(max_length=50, verbose_name='网站名简称,中文')), + ('index_url', models.CharField(max_length=200, verbose_name='首页地址')), + ('torrent_url', models.CharField(max_length=200, verbose_name='种子地址')), + ('bonus_url', models.CharField(max_length=200, verbose_name='魔力地址')), + ('sign_url', models.CharField(max_length=200, verbose_name='签到地址')), + ], + options={ + 'unique_together': {('name', 'name_cn', 'index_url')}, + }, + ), + ] diff --git a/apps/sites/migrations/0002_alter_siterank_up_time.py b/apps/sites/migrations/0002_alter_siterank_up_time.py new file mode 100644 index 0000000..9fea427 --- /dev/null +++ b/apps/sites/migrations/0002_alter_siterank_up_time.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.2 on 2022-02-22 09:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sites', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='siterank', + name='up_time', + field=models.IntegerField(verbose_name='升级时间,单位周'), + ), + ] diff --git a/apps/sites/migrations/0003_alter_siterank_ratio.py b/apps/sites/migrations/0003_alter_siterank_ratio.py new file mode 100644 index 0000000..ae1d5a0 --- /dev/null +++ b/apps/sites/migrations/0003_alter_siterank_ratio.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.2 on 2022-02-22 10:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sites', '0002_alter_siterank_up_time'), + ] + + operations = [ + migrations.AlterField( + model_name='siterank', + name='ratio', + field=models.CharField(max_length=20, verbose_name='分享率'), + ), + ] diff --git a/apps/sites/migrations/0004_alter_siterank_unique_together.py b/apps/sites/migrations/0004_alter_siterank_unique_together.py new file mode 100644 index 0000000..29d8582 --- /dev/null +++ b/apps/sites/migrations/0004_alter_siterank_unique_together.py @@ -0,0 +1,17 @@ +# Generated by Django 4.0.2 on 2022-02-22 10:59 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('sites', '0003_alter_siterank_ratio'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='siterank', + unique_together={('name', 'site_name')}, + ), + ] diff --git a/apps/sites/migrations/0005_rename_site_siteconfig_and_more.py b/apps/sites/migrations/0005_rename_site_siteconfig_and_more.py new file mode 100644 index 0000000..3b5b6c1 --- /dev/null +++ b/apps/sites/migrations/0005_rename_site_siteconfig_and_more.py @@ -0,0 +1,26 @@ +# Generated by Django 4.0.2 on 2022-02-23 00:34 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('sites', '0004_alter_siterank_unique_together'), + ] + + operations = [ + migrations.RenameModel( + old_name='Site', + new_name='SiteConfig', + ), + migrations.RenameField( + model_name='siterank', + old_name='site_name', + new_name='siteconfig_name', + ), + migrations.AlterUniqueTogether( + name='siterank', + unique_together={('name', 'siteconfig_name')}, + ), + ] diff --git a/apps/sites/migrations/0006_siteinfo.py b/apps/sites/migrations/0006_siteinfo.py new file mode 100644 index 0000000..2afd745 --- /dev/null +++ b/apps/sites/migrations/0006_siteinfo.py @@ -0,0 +1,22 @@ +# Generated by Django 4.0.2 on 2022-02-23 00:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sites', '0005_rename_site_siteconfig_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='SiteInfo', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('siteconfig_name', models.CharField(max_length=50, unique=True, verbose_name='网站名简称,英文')), + ('cookie', models.CharField(max_length=300, verbose_name='网站cookie信息')), + ('passkey', models.CharField(max_length=100, verbose_name='网站秘钥')), + ], + ), + ] diff --git a/apps/sites/migrations/0007_alter_siteinfo_cookie_alter_siteinfo_passkey.py b/apps/sites/migrations/0007_alter_siteinfo_cookie_alter_siteinfo_passkey.py new file mode 100644 index 0000000..e2de451 --- /dev/null +++ b/apps/sites/migrations/0007_alter_siteinfo_cookie_alter_siteinfo_passkey.py @@ -0,0 +1,23 @@ +# Generated by Django 4.0.2 on 2022-02-23 01:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sites', '0006_siteinfo'), + ] + + operations = [ + migrations.AlterField( + model_name='siteinfo', + name='cookie', + field=models.CharField(max_length=800, verbose_name='网站cookie信息'), + ), + migrations.AlterField( + model_name='siteinfo', + name='passkey', + field=models.CharField(max_length=200, verbose_name='网站秘钥'), + ), + ] diff --git a/apps/sites/migrations/0008_alter_siteconfig_unique_together_and_more.py b/apps/sites/migrations/0008_alter_siteconfig_unique_together_and_more.py new file mode 100644 index 0000000..921fea0 --- /dev/null +++ b/apps/sites/migrations/0008_alter_siteconfig_unique_together_and_more.py @@ -0,0 +1,32 @@ +# Generated by Django 4.0.2 on 2022-02-23 06:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sites', '0007_alter_siteinfo_cookie_alter_siteinfo_passkey'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='siteconfig', + unique_together=set(), + ), + migrations.AlterField( + model_name='siteconfig', + name='index_url', + field=models.CharField(max_length=200, unique=True, verbose_name='首页地址'), + ), + migrations.AlterField( + model_name='siteconfig', + name='name', + field=models.CharField(max_length=50, unique=True, verbose_name='网站名简称,英文'), + ), + migrations.AlterField( + model_name='siteconfig', + name='name_cn', + field=models.CharField(max_length=50, unique=True, verbose_name='网站名简称,中文'), + ), + ] diff --git a/apps/sites/migrations/0009_siteconfig_action.py b/apps/sites/migrations/0009_siteconfig_action.py new file mode 100644 index 0000000..685f80d --- /dev/null +++ b/apps/sites/migrations/0009_siteconfig_action.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.2 on 2022-02-27 02:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sites', '0008_alter_siteconfig_unique_together_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='siteconfig', + name='action', + field=models.CharField(max_length=20, null=True, verbose_name='判断使用post还是get'), + ), + ] diff --git a/apps/sites/migrations/0010_siteconfig_is_sign.py b/apps/sites/migrations/0010_siteconfig_is_sign.py new file mode 100644 index 0000000..3e30db1 --- /dev/null +++ b/apps/sites/migrations/0010_siteconfig_is_sign.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.2 on 2022-02-28 18:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sites', '0009_siteconfig_action'), + ] + + operations = [ + migrations.AddField( + model_name='siteconfig', + name='is_sign', + field=models.BooleanField(default=True, verbose_name='是否有签到功能'), + ), + ] diff --git a/apps/sites/migrations/0011_remove_siteconfig_action_remove_siteconfig_is_sign_and_more.py b/apps/sites/migrations/0011_remove_siteconfig_action_remove_siteconfig_is_sign_and_more.py new file mode 100644 index 0000000..3e9a1a8 --- /dev/null +++ b/apps/sites/migrations/0011_remove_siteconfig_action_remove_siteconfig_is_sign_and_more.py @@ -0,0 +1,25 @@ +# Generated by Django 4.0.2 on 2022-03-01 16:10 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('sites', '0010_siteconfig_is_sign'), + ] + + operations = [ + migrations.RemoveField( + model_name='siteconfig', + name='action', + ), + migrations.RemoveField( + model_name='siteconfig', + name='is_sign', + ), + migrations.RemoveField( + model_name='siteconfig', + name='sign_url', + ), + ] diff --git a/apps/sites/migrations/__init__.py b/apps/sites/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/sites/models.py b/apps/sites/models.py new file mode 100644 index 0000000..98f85f3 --- /dev/null +++ b/apps/sites/models.py @@ -0,0 +1,40 @@ +from django.db import models + +# Create your models here. +class SiteConfig(models.Model): + """ + 站点配置信息 + """ + name = models.CharField('网站名简称,英文', max_length=50, unique=True) + name_cn = models.CharField('网站名简称,中文', max_length=50, unique=True) + index_url = models.CharField('首页地址', max_length=200, unique=True) + torrent_url = models.CharField('种子地址', max_length=200) + bonus_url = models.CharField('魔力地址', max_length=200) + + #class Meta: + ##db_table = 'menu' #自定义表名称为mytable + #unique_together=("name","name_cn","index_url") + +class SiteRank(models.Model): + """ + 站点军衔,升级信息 + """ + name= models.CharField('级别名称', max_length=50) + siteconfig_name = models.CharField('网站名简称,英文', max_length=50) + download = models.CharField('下载量', max_length=50) + up_time = models.IntegerField('升级时间,单位周') + upload = models.CharField('上传量', max_length=50) + ratio = models.CharField('分享率', max_length=20) + privilege = models.CharField('信息说明', max_length=200) + serial_number = models.IntegerField('排序序号') + + class Meta: + unique_together=("name","siteconfig_name") + +class SiteInfo(models.Model): + """ + 站点信息 + """ + siteconfig_name = models.CharField('网站名简称,英文', max_length=50, unique=True) + cookie = models.CharField("网站cookie信息", max_length=800) + passkey = models.CharField("网站秘钥", max_length=200) \ No newline at end of file diff --git a/apps/sites/templates/sites/siteconfig.html b/apps/sites/templates/sites/siteconfig.html new file mode 100644 index 0000000..2319cb7 --- /dev/null +++ b/apps/sites/templates/sites/siteconfig.html @@ -0,0 +1,197 @@ + +
+
+
+ +
+
+ +
+ +
+
+ + + +
+
+
+ +
+ +
+
+
+ + + + + + + \ No newline at end of file diff --git a/apps/sites/templates/sites/siteconfigadd.html b/apps/sites/templates/sites/siteconfigadd.html new file mode 100644 index 0000000..fb93a13 --- /dev/null +++ b/apps/sites/templates/sites/siteconfigadd.html @@ -0,0 +1,62 @@ + +
{% csrf_token %} + +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ + +
+
+ + \ No newline at end of file diff --git a/apps/sites/templates/sites/siteconfigedit.html b/apps/sites/templates/sites/siteconfigedit.html new file mode 100644 index 0000000..f6465a2 --- /dev/null +++ b/apps/sites/templates/sites/siteconfigedit.html @@ -0,0 +1,61 @@ + +
{% csrf_token %} + + +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ + +
+
+ + \ No newline at end of file diff --git a/apps/sites/templates/sites/siteinfo.html b/apps/sites/templates/sites/siteinfo.html new file mode 100644 index 0000000..df8163d --- /dev/null +++ b/apps/sites/templates/sites/siteinfo.html @@ -0,0 +1,279 @@ + +
+
+
+ +
+
+ +
+ +
+
+ + + + + +
+
+
+ +
+ +
+
+
+ + + + + + + \ No newline at end of file diff --git a/apps/sites/templates/sites/siteinfoadd.html b/apps/sites/templates/sites/siteinfoadd.html new file mode 100644 index 0000000..85aac1e --- /dev/null +++ b/apps/sites/templates/sites/siteinfoadd.html @@ -0,0 +1,94 @@ + +
{% csrf_token %} + +
+ +
+
+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ + +
+
+ + \ No newline at end of file diff --git a/apps/sites/templates/sites/siteinfoedit.html b/apps/sites/templates/sites/siteinfoedit.html new file mode 100644 index 0000000..ea6081c --- /dev/null +++ b/apps/sites/templates/sites/siteinfoedit.html @@ -0,0 +1,99 @@ + +
{% csrf_token %} + + +
+ +
+
+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ + +
+
+ + \ No newline at end of file diff --git a/apps/sites/templates/sites/siterankconfig.html b/apps/sites/templates/sites/siterankconfig.html new file mode 100644 index 0000000..27b4e65 --- /dev/null +++ b/apps/sites/templates/sites/siterankconfig.html @@ -0,0 +1,202 @@ + +
+
+
+ +
+
+ +
+ +
+
+ + + +
+
+
+ +
+ +
+
+
+ + + + + + + \ No newline at end of file diff --git a/apps/sites/templates/sites/siterankconfigadd.html b/apps/sites/templates/sites/siterankconfigadd.html new file mode 100644 index 0000000..60ee9f6 --- /dev/null +++ b/apps/sites/templates/sites/siterankconfigadd.html @@ -0,0 +1,128 @@ + +
{% csrf_token %} + +
+ +
+ +
+
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ + +
+
+ + \ No newline at end of file diff --git a/apps/sites/templates/sites/siterankconfigedit.html b/apps/sites/templates/sites/siterankconfigedit.html new file mode 100644 index 0000000..07ffeb9 --- /dev/null +++ b/apps/sites/templates/sites/siterankconfigedit.html @@ -0,0 +1,133 @@ + +
{% csrf_token %} + + +
+ +
+ +
+
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ + +
+
+ + \ No newline at end of file diff --git a/apps/sites/tests.py b/apps/sites/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/sites/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/sites/urls/__init__.py b/apps/sites/urls/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/apps/sites/urls/__init__.py @@ -0,0 +1 @@ + diff --git a/apps/sites/urls/views_urls.py b/apps/sites/urls/views_urls.py new file mode 100644 index 0000000..cc9407d --- /dev/null +++ b/apps/sites/urls/views_urls.py @@ -0,0 +1,34 @@ +# coding:utf-8 +from django.urls import path +from .. import views, views_request + +app_name = 'sites' + +urlpatterns = [ + # Resource asset path + + #站点配置 + path('siteconfig', views.SiteConfigListView.as_view()), + path('siteconfig/add', views.SiteConfigAddView.as_view()), + path('siteconfig/edit', views.SiteConfigEditView.as_view()), + path('siteconfig/del', views.SiteConfigDelView.as_view()), + path('siteconfig/list', views_request.siteconfig), + path('siteconfigname/list', views_request.siteconfigname), + path('siteconfigname2siteinfo/list', views_request.siteconfigname2siteinfo), + #站点升级配置 + path('siterankconfig', views.SiteRankConfigListView.as_view()), + path('siterankconfig/add', views.SiteRankConfigAddView.as_view()), + path('siterankconfig/edit', views.SiteRankConfigEditView.as_view()), + path('siterankconfig/del', views.SiteRankConfigDelView.as_view()), + path('siterankconfig/list', views_request.siterankconfig), + #站点信息 + path('siteinfo', views.SiteInfoListView.as_view()), + path('siteinfo/add', views.SiteInfoAddView.as_view()), + path('siteinfo/edit', views.SiteInfoEditView.as_view()), + path('siteinfo/del', views.SiteInfoDelView.as_view()), + path('siteinfo/list', views_request.siteinfo), + path('siteinfo/export', views_request.export), + path('siteinfo/import', views_request.importJson), + +] + diff --git a/apps/sites/views.py b/apps/sites/views.py new file mode 100644 index 0000000..6009c7d --- /dev/null +++ b/apps/sites/views.py @@ -0,0 +1,454 @@ +from django.shortcuts import render +from django.views.generic import TemplateView +from django.contrib.auth.mixins import LoginRequiredMixin +from django.http import JsonResponse + +from .models import SiteConfig, SiteRank, SiteInfo + +# Create your views here. +class SiteConfigListView(LoginRequiredMixin,TemplateView): + """ + 显示 + """ + template_name = 'sites/siteconfig.html' + + def get(self, request, *args, **kwargs): + """ + + """ + return super(SiteConfigListView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + context = { + # 'data': self.ormdata_game + } + kwargs.update(context) + return super(SiteConfigListView, self).get_context_data(**kwargs) + + +class SiteConfigAddView(LoginRequiredMixin,TemplateView): + """ + 添加 + """ + template_name = 'sites/siteconfigadd.html' + + #显示添加模板 + def get(self, request, *args, **kwargs): + """ + """ + return super(SiteConfigAddView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + context = { + } + kwargs.update(context) + return super(SiteConfigAddView, self).get_context_data(**kwargs) + + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + name = request.POST.get("name").strip() + name_cn = request.POST.get("name_cn").strip() + index_url = request.POST.get("index_url").strip() + + ormdata = SiteConfig.objects.create(name=name, + name_cn=name_cn, + index_url=index_url, + ) + + ormdata.save() + + response_data={"code":1,"msg":"添加成功"} + + + return JsonResponse(response_data) + +class SiteConfigEditView(LoginRequiredMixin,TemplateView): + """ + 编辑域名 + """ + template_name = 'sites/siteconfigedit.html' + + #显示添加模板 + def get(self, request, *args, **kwargs): + """ + 得到 + """ + + #记录ID + self._id = request.GET.get('id') + self.ormdata = SiteConfig.objects.get(id=self._id) + + return super(SiteConfigEditView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + context = { + 'id': self._id, + 'name': self.ormdata.name, + 'name_cn': self.ormdata.name_cn, + 'index_url': self.ormdata.index_url, + + } + kwargs.update(context) + return super(SiteConfigEditView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + name = request.POST.get("name").strip() + name_cn = request.POST.get("name_cn").strip() + index_url = request.POST.get("index_url").strip() + + _id = request.POST.get('id') + + ormdata = SiteConfig.objects.get(id=_id) + + ormdata.name = name + ormdata.name_cn = name_cn + ormdata.index_url = index_url + + ormdata.save() + + response_data={"code":1,"msg":"添加成功"} + + return JsonResponse(response_data) + +class SiteConfigDelView(LoginRequiredMixin,TemplateView): + """ + 删除 + """ + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + + #得到批量或者单个要删除的id + ids = request.POST.getlist("ids[]") + #print("ids====>",ids) + + SiteConfig.objects.filter(id__in=ids).delete() + + #Site.objects.filter(id=i).delete() + + response_data={"code":1,"msg":"操作成功"} + + + return JsonResponse(response_data) + +#================== +class SiteRankConfigListView(LoginRequiredMixin,TemplateView): + """ + 显示 + """ + template_name = 'sites/siterankconfig.html' + + def get(self, request, *args, **kwargs): + """ + + """ + return super(SiteRankConfigListView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + context = { + # 'data': self.ormdata_game + } + kwargs.update(context) + return super(SiteRankConfigListView, self).get_context_data(**kwargs) + + +class SiteRankConfigAddView(LoginRequiredMixin,TemplateView): + """ + 添加 + """ + template_name = 'sites/siterankconfigadd.html' + + #显示添加模板 + def get(self, request, *args, **kwargs): + """ + """ + return super(SiteRankConfigAddView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + context = { + } + kwargs.update(context) + return super(SiteRankConfigAddView, self).get_context_data(**kwargs) + + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + name = request.POST.get("name").strip() + siteconfig_name = request.POST.get("siteconfig_name") + download = request.POST.get("download").strip() + up_time = request.POST.get("up_time") + upload = request.POST.get("upload",0) + ratio = request.POST.get("ratio").strip() + privilege = request.POST.get("privilege").strip() + serial_number = request.POST.get("serial_number") + + ormdata = SiteRank.objects.create(name=name, + siteconfig_name=siteconfig_name, + download=download, + up_time=up_time, + upload=upload, + ratio=ratio, + privilege=privilege, + serial_number=serial_number, + ) + + ormdata.save() + + response_data={"code":1,"msg":"添加成功"} + + + return JsonResponse(response_data) + +class SiteRankConfigEditView(LoginRequiredMixin,TemplateView): + """ + 编辑域名 + """ + template_name = 'sites/siterankconfigedit.html' + + #显示添加模板 + def get(self, request, *args, **kwargs): + """ + 得到 + """ + + #记录ID + self._id = request.GET.get('id') + self.ormdata = SiteRank.objects.get(id=self._id) + + return super(SiteRankConfigEditView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + context = { + 'id': self._id, + 'name': self.ormdata.name, + 'siteconfig_name': self.ormdata.siteconfig_name, + 'download': self.ormdata.download, + 'up_time': self.ormdata.up_time, + 'upload': self.ormdata.upload, + 'ratio': self.ormdata.ratio, + 'privilege': self.ormdata.privilege, + 'serial_number': self.ormdata.serial_number, + + } + kwargs.update(context) + return super(SiteRankConfigEditView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + name = request.POST.get("name").strip() + siteconfig_name = request.POST.get("siteconfig_name") + download = request.POST.get("download").strip() + up_time = request.POST.get("up_time") + upload = request.POST.get("upload",0) + ratio = request.POST.get("ratio").strip() + privilege = request.POST.get("privilege").strip() + serial_number = request.POST.get("serial_number") + + _id = request.POST.get('id') + + ormdata = SiteRank.objects.get(id=_id) + + ormdata.name = name + ormdata.siteconfig_name = siteconfig_name + ormdata.download = download + ormdata.up_time = up_time + ormdata.upload = upload + ormdata.ratio = ratio + ormdata.privilege = privilege + ormdata.serial_number = serial_number + + ormdata.save() + + response_data={"code":1,"msg":"添加成功"} + + return JsonResponse(response_data) + +class SiteRankConfigDelView(LoginRequiredMixin,TemplateView): + """ + 删除 + """ + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + + #得到批量或者单个要删除的id + ids = request.POST.getlist("ids[]") + #print("ids====>",ids) + + SiteRank.objects.filter(id__in=ids).delete() + + #Site.objects.filter(id=i).delete() + + response_data={"code":1,"msg":"操作成功"} + + + return JsonResponse(response_data) + +#================== +class SiteInfoListView(LoginRequiredMixin,TemplateView): + """ + 显示 + """ + template_name = 'sites/siteinfo.html' + + def get(self, request, *args, **kwargs): + """ + + """ + return super(SiteInfoListView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + context = { + # 'data': self.ormdata_game + } + kwargs.update(context) + return super(SiteInfoListView, self).get_context_data(**kwargs) + + +class SiteInfoAddView(LoginRequiredMixin,TemplateView): + """ + 添加 + """ + template_name = 'sites/siteinfoadd.html' + + #显示添加模板 + def get(self, request, *args, **kwargs): + """ + """ + return super(SiteInfoAddView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + context = { + } + kwargs.update(context) + return super(SiteInfoAddView, self).get_context_data(**kwargs) + + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + siteconfig_name = request.POST.get("siteconfig_name").strip() + cookie = request.POST.get("cookie").strip() + passkey = request.POST.get("passkey").strip() + + ormdata = SiteInfo.objects.create(siteconfig_name=siteconfig_name, + cookie=cookie, + passkey=passkey, + ) + + ormdata.save() + + response_data={"code":1,"msg":"添加成功"} + + + return JsonResponse(response_data) + +class SiteInfoEditView(LoginRequiredMixin,TemplateView): + """ + 编辑域名 + """ + template_name = 'sites/siteinfoedit.html' + + #显示添加模板 + def get(self, request, *args, **kwargs): + """ + 得到 + """ + + #记录ID + self._id = request.GET.get('id') + self.ormdata = SiteInfo.objects.get(id=self._id) + + return super(SiteInfoEditView, self).get(request, *args, **kwargs) + + #显示编辑模板 + def get_context_data(self, **kwargs): + + context = { + 'id': self._id, + 'siteconfig_name': self.ormdata.siteconfig_name, + 'cookie': self.ormdata.cookie, + 'passkey': self.ormdata.passkey, + } + kwargs.update(context) + return super(SiteInfoEditView, self).get_context_data(**kwargs) + + #数据提交接收方法 + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + siteconfig_name = request.POST.get("siteconfig_name").strip() + cookie = request.POST.get("cookie").strip() + passkey = request.POST.get("passkey").strip() + + _id = request.POST.get('id') + + ormdata = SiteInfo.objects.get(id=_id) + + ormdata.siteconfig_name = siteconfig_name + ormdata.cookie = cookie + ormdata.passkey = passkey + + + ormdata.save() + + response_data={"code":1,"msg":"添加成功"} + + return JsonResponse(response_data) + +class SiteInfoDelView(LoginRequiredMixin,TemplateView): + """ + 删除 + """ + def post(self, request, *args, **kwargs): + """ + 数据提交 + """ + + #得到批量或者单个要删除的id + ids = request.POST.getlist("ids[]") + #print("ids====>",ids) + + SiteInfo.objects.filter(id__in=ids).delete() + + #Site.objects.filter(id=i).delete() + + response_data={"code":1,"msg":"操作成功"} + + + return JsonResponse(response_data) \ No newline at end of file diff --git a/apps/sites/views_request.py b/apps/sites/views_request.py new file mode 100644 index 0000000..c8febd5 --- /dev/null +++ b/apps/sites/views_request.py @@ -0,0 +1,374 @@ +#coding=utf-8 +from django.http import JsonResponse, StreamingHttpResponse,HttpResponse +from django.db.models import Q +from django.contrib.auth.decorators import login_required + +from .models import SiteConfig, SiteRank, SiteInfo +import simplejson as json +import io +from urllib import parse +import tempfile + +#直接使用检查是否管理员 +#from django.contrib.auth.decorators import user_passes_test +#@user_passes_test(lambda u: u.is_superuser) + +@login_required +def siteconfig(request): + """ + 提供相应的数据 + """ + + #得到偏移量 + pageIndex= int(request.GET.get('page',1)) + #得到查询个数 + pageSize = int(request.GET.get('limit',10)) + + #搜索字段 + searchKey = request.GET.get('searchKey','') + searchValue = request.GET.get('searchValue','') + #去掉空格 + searchValue = searchValue.strip() + + #得到排序字段 + sort = request.GET.get('sort','id') + #得到排序规则 + order_by_type = request.GET.get('order','desc') + + if order_by_type == 'desc': + order_by = sort + else: + order_by = '-' + sort + + data = {} + data['code'] = 0 + data['msg'] = "" + data['data'] = [] + + if searchValue == '': + + data['count'] = SiteConfig.objects.count() + if pageIndex == 1: + ormdata = SiteConfig.objects.order_by(order_by)[:pageSize] #offset:limit + else: + ormdata = SiteConfig.objects.order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize] + else: + + data['count'] = SiteConfig.objects.filter(Q(name__icontains=searchValue)|Q(name_cn__icontains=searchValue)).count() + if pageIndex == 1: + ormdata = SiteConfig.objects.filter(Q(name__icontains=searchValue)|Q(name_cn__icontains=searchValue)).order_by(order_by)[:pageSize] #offset:limit + else: + ormdata = SiteConfig.objects.filter(Q(name__icontains=searchValue)|Q(name_cn__icontains=searchValue)).order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize] + + for i in ormdata: + + data['data'].append({"id":i.id, + "name":i.name, + "name_cn":i.name_cn, + "index_url":i.index_url, + }) + #返回json串 + #return HttpResponse(json.dumps(data,ensure_ascii = False), "application/json") + return JsonResponse(data) + +@login_required +def siterankconfig(request): + """ + 提供相应的数据 + """ + + #得到偏移量 + pageIndex= int(request.GET.get('page',1)) + #得到查询个数 + pageSize = int(request.GET.get('limit',10)) + + #搜索字段 + searchKey = request.GET.get('searchKey','') + searchValue = request.GET.get('searchValue','') + #去掉空格 + searchValue = searchValue.strip() + + #得到排序字段 + sort = request.GET.get('sort','id') + #得到排序规则 + order_by_type = request.GET.get('order','desc') + + if order_by_type == 'desc': + order_by = sort + else: + order_by = '-' + sort + + data = {} + data['code'] = 0 + data['msg'] = "" + data['data'] = [] + + if searchValue == '': + + data['count'] = SiteRank.objects.count() + if pageIndex == 1: + ormdata = SiteRank.objects.order_by(order_by)[:pageSize] #offset:limit + else: + ormdata = SiteRank.objects.order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize] + else: + + data['count'] = SiteRank.objects.filter(Q(name__icontains=searchValue)|Q(siteconfig_name__icontains=searchValue)).count() + if pageIndex == 1: + ormdata = SiteRank.objects.filter(Q(name__icontains=searchValue)|Q(siteconfig_name__icontains=searchValue)).order_by(order_by)[:pageSize] #offset:limit + else: + ormdata = SiteRank.objects.filter(Q(name__icontains=searchValue)|Q(siteconfig_name__icontains=searchValue)).order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize] + + for i in ormdata: + + data['data'].append({"id":i.id, + "name":i.name, + "siteconfig_name":i.siteconfig_name, + "download":i.download, + "up_time":i.up_time, + "upload":i.upload, + "ratio":i.ratio, + "privilege":i.privilege, + "serial_number":i.serial_number, + }) + #返回json串 + #return HttpResponse(json.dumps(data,ensure_ascii = False), "application/json") + return JsonResponse(data) + + + +@login_required +def siteinfo(request): + """ + 提供相应的数据 + """ + + #得到偏移量 + pageIndex= int(request.GET.get('page',1)) + #得到查询个数 + pageSize = int(request.GET.get('limit',10)) + + #搜索字段 + searchKey = request.GET.get('searchKey','') + searchValue = request.GET.get('searchValue','') + #去掉空格 + searchValue = searchValue.strip() + + #得到排序字段 + sort = request.GET.get('sort','id') + #得到排序规则 + order_by_type = request.GET.get('order','desc') + + if order_by_type == 'desc': + order_by = sort + else: + order_by = '-' + sort + + data = {} + data['code'] = 0 + data['msg'] = "" + data['data'] = [] + + if searchValue == '': + + data['count'] = SiteInfo.objects.count() + if pageIndex == 1: + ormdata = SiteInfo.objects.order_by(order_by)[:pageSize] #offset:limit + else: + ormdata = SiteInfo.objects.order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize] + else: + + data['count'] = SiteInfo.objects.filter(Q(siteconfig_name__icontains=searchValue)).count() + if pageIndex == 1: + ormdata = SiteInfo.objects.filter(Q(siteconfig_name__icontains=searchValue)).order_by(order_by)[:pageSize] #offset:limit + else: + ormdata = SiteInfo.objects.filter(Q(siteconfig_name__icontains=searchValue)).order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize] + + for i in ormdata: + + siteconfig_ormdata = SiteConfig.objects.get(name=i.siteconfig_name) + + data['data'].append({"id":i.id, + "siteconfig_name":i.siteconfig_name, + 'siteconfig_name_cn':siteconfig_ormdata.name_cn, + "cookie":i.cookie, + "passkey":i.passkey, + }) + #返回json串 + #return HttpResponse(json.dumps(data,ensure_ascii = False), "application/json") + return JsonResponse(data) + +def get_stream(data): + # 开始这里我用ByteIO流总是出错,但是后来参考廖雪峰网站用StringIO就没问题 + file = io.StringIO() + data=json.dumps(data) + file.write(data) + res=file.getvalue() + file.close() + return res + +#================== +@login_required +def export(request): + """ + 文件导出 + """ + + #得到批量或者单个要删除的id + #ids = request.POST.getlist("ids[]") + #print("ids====>",ids) + ids = [] + #fetch方法 + if request.method == "PUT": + body = request.body + json_body = json.loads(body) + + ids = json_body['ids'] + + data = [] + + if len(ids) == 0: + ormdata = SiteInfo.objects.all() + else: + ormdata = SiteInfo.objects.filter(id__in=ids) + + for i in ormdata: + + data.append({"name":i.siteconfig_name, + "cookie":i.cookie, + "passkey":i.passkey, + }) + #print(data) + json_stream=get_stream(data) + response = HttpResponse(content_type='application/json') + + response['Content-Disposition'] = 'attachment;filename=' + parse.quote('pthelper站点配置信息') + '.json' + response.write(json_stream) + + return response + +#================== +@login_required +def importJson(request): + """ + 文件上传 + """ + data = {} + data['code'] = 0 + data['msg'] = "" + data['data'] = [] + + if request.method == 'POST': + file_obj = request.FILES.get('file') + #data = get_stream(file_obj) + + file_temp = tempfile.NamedTemporaryFile() + file_temp.write(file_obj.read()) + #res=file_temp.getvalue() + file_temp.seek(0) + data = file_temp.read() + file_temp.close() + + #print(data) + try: + json_data = json.loads(data) + for i in json_data: + siteconfig_name = i['name'] + cookie = i['cookie'] + passkey = i['passkey'] + + get_site = SiteInfo.objects.filter(siteconfig_name=siteconfig_name).count() + if get_site == 0: + SiteInfo.objects.create(siteconfig_name = siteconfig_name, + cookie = cookie, + passkey = passkey, + ) + else: + #更新站点配置 + SiteInfo.objects.filter(siteconfig_name=siteconfig_name).update(cookie = cookie, + passkey=passkey, + ) + response_data={"code":1,"msg":"更新成功"} + + except: + response_data={"code":0,"msg":"非法文件"} + + return JsonResponse(response_data) + +#================== +@login_required +def siteconfigname(request): + """ + 网站集合 + xmSelect + """ + + data = {} + data['code'] = 0 + data['msg'] = "" + data['data'] = [] + + #得到排序字段 + sort = request.GET.get('sort','name') + #得到排序规则 + order_by_type = request.GET.get('order','desc') + + if order_by_type == 'desc': + order_by = sort + else: + order_by = '-' + sort + + #获取所有记录 + data['count'] = SiteConfig.objects.count() + + ormdata = SiteConfig.objects.order_by(order_by).all() + + + for i in ormdata: + + data['data'].append({ + "name":i.name, + "name_en_cn":i.name + "(" + i.name_cn + ")" + }) + + return JsonResponse(data) + +#================== +@login_required +def siteconfigname2siteinfo(request): + """ + 网站集合,去掉已经创建的站点名称 + xmSelect + """ + + data = {} + data['code'] = 0 + data['msg'] = "" + data['data'] = [] + + #得到排序字段 + sort = request.GET.get('sort','name') + #得到排序规则 + order_by_type = request.GET.get('order','desc') + + if order_by_type == 'desc': + order_by = sort + else: + order_by = '-' + sort + + #获取所有记录 + data['count'] = SiteConfig.objects.count() + + ormdata = SiteConfig.objects.order_by(order_by).all() + + #获取siteinfo中配置的站点名 + ormdata_siteinfo = list(SiteInfo.objects.values_list('siteconfig_name', flat=True)) + + for i in ormdata: + #忽略已经配置的站点 + if i.name not in ormdata_siteinfo: + data['data'].append({ + "name":i.name, + "name_en_cn":i.name + "(" + i.name_cn + ")" + }) + + return JsonResponse(data) \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..d1bb882 --- /dev/null +++ b/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker build -t pthelper:v1.0 . \ No newline at end of file diff --git a/conf/nginx.conf b/conf/nginx.conf new file mode 100644 index 0000000..1e7ba2a --- /dev/null +++ b/conf/nginx.conf @@ -0,0 +1,61 @@ + +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + use epoll; + worker_connections 51200; + multi_accept on; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + server_names_hash_bucket_size 128; + client_header_buffer_size 64k; + large_client_header_buffers 4 64k; + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 60; + server_tokens off; + proxy_redirect off; + + client_max_body_size 20m; + client_body_temp_path /dev/shm; + client_body_buffer_size 256k; + proxy_connect_timeout 300; + proxy_send_timeout 300; + proxy_read_timeout 300; + proxy_buffer_size 64k; + proxy_buffers 4 32k; + proxy_busy_buffers_size 64k; + proxy_temp_file_write_size 128k; + proxy_max_temp_file_size 256m; + proxy_buffering off; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + gzip on; + #指定的客户端禁用gzip功能。我们设置成IE6或者更低版本以使我们的方案能够广泛兼容。 + gzip_disable 'msie6'; + gzip_min_length 1000; + gzip_buffers 4 8k; + gzip_http_version 1.1; + gzip_comp_level 2; + gzip_vary on; + #设置需要压缩的数据格式 + gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; + + + include /etc/nginx/conf.d/*.conf; +} diff --git a/conf/supervisord.conf b/conf/supervisord.conf new file mode 100644 index 0000000..31d24ee --- /dev/null +++ b/conf/supervisord.conf @@ -0,0 +1,13 @@ +[supervisord] +nodaemon = true + +[program:nginx] +command=nginx -c /etc/nginx/nginx.conf -g 'daemon off;' +user=root + +[program:gunicorn] +#command=gunicorn pthelper.wsgi:application --bind 0.0.0.0:8000 -w 2 --threads 2 --chdir=/home/data/www +command=gunicorn pthelper.wsgi:application --bind 0.0.0.0:8000 -w 4 --sleep=3 --tries=2 --daemon +directory=/home/data/www +user=root +#startsecs = 0 diff --git a/conf/vhost.conf b/conf/vhost.conf new file mode 100644 index 0000000..6bdf226 --- /dev/null +++ b/conf/vhost.conf @@ -0,0 +1,20 @@ +upstream pthelper { + server 127.0.0.1:8000; +} + +server { + + listen 80; + + location / { + proxy_pass http://pthelper; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_redirect off; + } + + location /static/ { + alias /home/data/www/static/; + } + +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..2c7ce5e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,12 @@ +version: '3.7' +services: + flexget: + image: pthelper:v1.0 + container_name: pthelper + restart: always + volumes: + - 本地目录/db:/db + - 本地目录/logs:/logs + - 本地目录/backups:/backups + ports: + - "58000:80" diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..df4d061 --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pthelper.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/nginx_signing.key b/nginx_signing.key new file mode 100644 index 0000000..d2258b8 --- /dev/null +++ b/nginx_signing.key @@ -0,0 +1,28 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v2.0.22 (GNU/Linux) + +mQENBE5OMmIBCAD+FPYKGriGGf7NqwKfWC83cBV01gabgVWQmZbMcFzeW+hMsgxH +W6iimD0RsfZ9oEbfJCPG0CRSZ7ppq5pKamYs2+EJ8Q2ysOFHHwpGrA2C8zyNAs4I +QxnZZIbETgcSwFtDun0XiqPwPZgyuXVm9PAbLZRbfBzm8wR/3SWygqZBBLdQk5TE +fDR+Eny/M1RVR4xClECONF9UBB2ejFdI1LD45APbP2hsN/piFByU1t7yK2gpFyRt +97WzGHn9MV5/TL7AmRPM4pcr3JacmtCnxXeCZ8nLqedoSuHFuhwyDnlAbu8I16O5 +XRrfzhrHRJFM1JnIiGmzZi6zBvH0ItfyX6ttABEBAAG0KW5naW54IHNpZ25pbmcg +a2V5IDxzaWduaW5nLWtleUBuZ2lueC5jb20+iQE+BBMBAgAoAhsDBgsJCAcDAgYV +CAIJCgsEFgIDAQIeAQIXgAUCV2K1+AUJGB4fQQAKCRCr9b2Ce9m/YloaB/9XGrol +kocm7l/tsVjaBQCteXKuwsm4XhCuAQ6YAwA1L1UheGOG/aa2xJvrXE8X32tgcTjr +KoYoXWcdxaFjlXGTt6jV85qRguUzvMOxxSEM2Dn115etN9piPl0Zz+4rkx8+2vJG +F+eMlruPXg/zd88NvyLq5gGHEsFRBMVufYmHtNfcp4okC1klWiRIRSdp4QY1wdrN +1O+/oCTl8Bzy6hcHjLIq3aoumcLxMjtBoclc/5OTioLDwSDfVx7rWyfRhcBzVbwD +oe/PD08AoAA6fxXvWjSxy+dGhEaXoTHjkCbz/l6NxrK3JFyauDgU4K4MytsZ1HDi +MgMW8hZXxszoICTTiQEcBBABAgAGBQJOTkelAAoJEKZP1bF62zmo79oH/1XDb29S +YtWp+MTJTPFEwlWRiyRuDXy3wBd/BpwBRIWfWzMs1gnCjNjk0EVBVGa2grvy9Jtx +JKMd6l/PWXVucSt+U/+GO8rBkw14SdhqxaS2l14v6gyMeUrSbY3XfToGfwHC4sa/ +Thn8X4jFaQ2XN5dAIzJGU1s5JA0tjEzUwCnmrKmyMlXZaoQVrmORGjCuH0I0aAFk +RS0UtnB9HPpxhGVbs24xXZQnZDNbUQeulFxS4uP3OLDBAeCHl+v4t/uotIad8v6J +SO93vc1evIje6lguE81HHmJn9noxPItvOvSMb2yPsE8mH4cJHRTFNSEhPW6ghmlf +Wa9ZwiVX5igxcvaIRgQQEQIABgUCTk5b0gAKCRDs8OkLLBcgg1G+AKCnacLb/+W6 +cflirUIExgZdUJqoogCeNPVwXiHEIVqithAM1pdY/gcaQZmIRgQQEQIABgUCTk5f +YQAKCRCpN2E5pSTFPnNWAJ9gUozyiS+9jf2rJvqmJSeWuCgVRwCcCUFhXRCpQO2Y +Va3l3WuB+rgKjsQ= +=EWWI +-----END PGP PUBLIC KEY BLOCK----- diff --git a/pthelper/__init__.py b/pthelper/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pthelper/asgi.py b/pthelper/asgi.py new file mode 100644 index 0000000..673df45 --- /dev/null +++ b/pthelper/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for pthelper project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pthelper.settings') + +application = get_asgi_application() diff --git a/pthelper/local_settings.py b/pthelper/local_settings.py new file mode 100644 index 0000000..9654733 --- /dev/null +++ b/pthelper/local_settings.py @@ -0,0 +1,266 @@ +""" +Django settings for pthelper project. + +Generated by 'django-admin startproject' using Django 4.0.2. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.0/ref/settings/ +""" + +from pathlib import Path +import os +import sys +#使用pymysql代替mysqlclient +import pymysql +pymysql.install_as_MySQLdb() + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-j2in)-m(_2f7#o059j-of8ao^s6j07d1w9rx1#cxeh#f2&)b_3' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = ['*'] + +#自定义app目录的时候需要添加,以下两种方法都可以 +sys.path.insert(0,os.path.join(BASE_DIR, "apps")) +#sys.path.append(os.path.join(BASE_DIR, 'apps')) + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'rest_framework', + 'rest_framework.authtoken', + #'rest_framework_swagger', + 'django_filters', + 'django_apscheduler',#定时执行任务 + 'common', + 'login', + 'mymenu', + 'myauth', + 'index', + 'dashboard', + 'sites', + 'cron', #计划任务 + 'notify', #消息通知 + 'initdata', #数据初始化 +] + +AUTH_USER_MODEL = 'myauth.User' +#请求登录地址,默认/accounts/login/ +LOGIN_URL = '/login/' +#/accounts/profile/ +#LOGIN_REDIRECT_URL = '/myauth/user' + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + #'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework.authentication.SessionAuthentication', + 'rest_framework.authentication.TokenAuthentication', + ), + 'DEFAULT_FILTER_BACKENDS': ( + 'django_filters.rest_framework.DjangoFilterBackend', + ) +} + +ROOT_URLCONF = 'pthelper.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(BASE_DIR, 'templates'),], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + #引入全局settings变量,方便模板直接引用 + 'apps.global_settings.get_global_settings', + ], + }, + }, +] + +WSGI_APPLICATION = 'pthelper.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/4.0/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', + #'ENGINE': 'mysql.connector.django', #使用mysql官方连接模块https://dev.mysql.com/downloads/connector/python/ + 'NAME': 'pthelper', + 'USER': 'root', + 'PASSWORD': '123456', + 'HOST': '127.0.0.1', + 'PORT': '3306', + 'CHARSET': 'utf8mb4', + 'COLLATION': 'utf8mb4_unicode_ci', + 'OPTIONS': {'sql_mode': 'TRADITIONAL', 'use_unicode': True, 'charset': 'utf8mb4'}, + #mysqlclient使用8.0(docker) + #'OPTIONS': {'sql_mode': 'TRADITIONAL', 'use_unicode': True, 'charset': 'utf8mb4', 'ssl_mode': 'DISABLED'}, + } +} + +# Password validation +# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + 'OPTIONS': { + 'min_length': 6, + } + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/4.0/topics/i18n/ + +LANGUAGE_CODE = 'zh-hans' + +TIME_ZONE = 'Asia/Shanghai' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = False + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.0/howto/static-files/ + +STATIC_URL = 'static/' + +STATICFILES_DIRS = ( + os.path.join(BASE_DIR, "static"), +) + +# Default primary key field type +# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +#模板使用变量 +SITE_NAME = 'PT助手' +SITE_URL = 'https://www.baidu.com' +SITE_VERSION = 'Version 1.0.0' +SITE_COPYRIGHT = 'copyright © 2022' + +#https://docs.djangoproject.com/en/dev/ref/templates/builtins/#std:templatefilter-date +APSCHEDULER_DATETIME_FORMAT = "Y-m-d H:i:s" +APSCHEDULER_RUN_NOW_TIMEOUT = 25 # Seconds + +#临时目录 +TMP_LOG_DIR = os.path.join(BASE_DIR, "tmp") +if not os.path.exists(TMP_LOG_DIR): os.mkdir(TMP_LOG_DIR) + +BACKUP_DIR = os.path.join(BASE_DIR, 'backups') +if not os.path.exists(BACKUP_DIR): os.mkdir(BACKUP_DIR) + +#日志 +BASE_LOG_DIR = os.path.join(BASE_DIR, "logs") +if not os.path.exists(BASE_LOG_DIR): os.mkdir(BASE_LOG_DIR) +LOGGING = { + 'version': 1, # 保留字 + 'disable_existing_loggers': False, # 禁用已经存在的logger实例 + # 日志文件的格式 + 'formatters': { + # 详细的日志格式 + 'standard': { + 'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d][%(levelname)s][%(message)s]' + }, + # 简单的日志格式 + 'simple': { + 'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' + }, + # 定义一个特殊的日志格式 + 'collect': { + 'format': '%(message)s' + } + }, + # 过滤器 + 'filters': { + 'require_debug_true': { + '()': 'django.utils.log.RequireDebugTrue', + }, + }, + # 处理器 + 'handlers': { + # 在终端打印 + 'console': { + 'level': 'DEBUG', + #'filters': ['require_debug_true'], # 只有在Django debug为True时才在屏幕打印日志 + 'class': 'logging.StreamHandler', # + 'formatter': 'simple' + }, + # 默认的 + 'default': { + 'level': 'INFO', + 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切 + 'filename': os.path.join(BASE_LOG_DIR, "info.log"), # 日志文件 + 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M + 'backupCount': 3, # 最多备份几个 + 'formatter': 'standard', + 'encoding': 'utf-8', + }, + # 专门用来记错误日志 + 'error': { + 'level': 'ERROR', + 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切 + 'filename': os.path.join(BASE_LOG_DIR, "error.log"), # 日志文件 + 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M + 'backupCount': 5, + 'formatter': 'standard', + 'encoding': 'utf-8', + }, + }, + 'loggers': { + # 默认的logger应用如下配置 + 'default': { + 'handlers': ['default', 'console', 'error'], # 上线之后可以把'console'移除 + 'level': 'DEBUG', + 'propagate': True, # 向不向更高级别的logger传递 + }, + }, +} \ No newline at end of file diff --git a/pthelper/settings.py b/pthelper/settings.py new file mode 100644 index 0000000..a603667 --- /dev/null +++ b/pthelper/settings.py @@ -0,0 +1,266 @@ +""" +Django settings for pthelper project. + +Generated by 'django-admin startproject' using Django 4.0.2. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.0/ref/settings/ +""" + +from pathlib import Path +import os +import sys + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-j2in)-m(_2f7#o059j-of8ao^s6j07d1w9rx1#cxeh#f2&)b_3' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = ['*'] + +#自定义app目录的时候需要添加,以下两种方法都可以 +sys.path.insert(0,os.path.join(BASE_DIR, "apps")) +#sys.path.append(os.path.join(BASE_DIR, 'apps')) + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'rest_framework', + 'rest_framework.authtoken', + #'rest_framework_swagger', + 'django_filters', + 'django_apscheduler',#定时执行任务 + 'common', + 'login', + 'mymenu', + 'myauth', + 'index', + 'dashboard', + 'sites', + 'cron', #计划任务 + 'notify', #消息通知 + 'initdata', #数据初始化 +] + +AUTH_USER_MODEL = 'myauth.User' +#请求登录地址,默认/accounts/login/ +LOGIN_URL = '/login/' +#/accounts/profile/ +#LOGIN_REDIRECT_URL = '/myauth/user' + +#跨域增加忽略 +#CORS_ALLOW_CREDENTIALS = True # 指明在跨域访问中,后端是否支持对cookie的操作 +#CORS_ORIGIN_ALLOW_ALL = False #允許所有跨站請求, 且whitelist不會被使用 +CORS_ORIGIN_ALLOW_ALL = True + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + #'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework.authentication.SessionAuthentication', + 'rest_framework.authentication.TokenAuthentication', + ), + 'DEFAULT_FILTER_BACKENDS': ( + 'django_filters.rest_framework.DjangoFilterBackend', + ) +} + +ROOT_URLCONF = 'pthelper.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(BASE_DIR, 'templates'),], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + #引入全局settings变量,方便模板直接引用 + 'apps.global_settings.get_global_settings', + ], + }, + }, +] + +WSGI_APPLICATION = 'pthelper.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/4.0/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR,'db','pthelper.sqlite3'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + 'OPTIONS': { + 'min_length': 6, + } + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/4.0/topics/i18n/ + +LANGUAGE_CODE = 'zh-hans' + +TIME_ZONE = 'Asia/Shanghai' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = False + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.0/howto/static-files/ + +STATIC_URL = 'static/' + +STATICFILES_DIRS = ( + os.path.join(BASE_DIR, "static"), +) + +# Default primary key field type +# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +#模板使用变量 +SITE_NAME = 'PT助手' +SITE_URL = 'https://www.baidu.com' +SITE_VERSION = 'Version 1.0.0' +SITE_COPYRIGHT = 'copyright © 2022' + +#https://docs.djangoproject.com/en/dev/ref/templates/builtins/#std:templatefilter-date +APSCHEDULER_DATETIME_FORMAT = "Y-m-d H:i:s" +APSCHEDULER_RUN_NOW_TIMEOUT = 25 # Seconds + +#临时目录 +TMP_LOG_DIR = os.path.join(BASE_DIR, "tmp") +if not os.path.exists(TMP_LOG_DIR): os.mkdir(TMP_LOG_DIR) + +BACKUP_DIR = os.path.join(BASE_DIR, 'backups') +if not os.path.exists(BACKUP_DIR): os.mkdir(BACKUP_DIR) + +#日志 +BASE_LOG_DIR = os.path.join(BASE_DIR, "logs") +if not os.path.exists(BASE_LOG_DIR): os.mkdir(BASE_LOG_DIR) +LOGGING = { + 'version': 1, # 保留字 + 'disable_existing_loggers': False, # 禁用已经存在的logger实例 + # 日志文件的格式 + 'formatters': { + # 详细的日志格式 + 'standard': { + 'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d][%(levelname)s][%(message)s]' + }, + # 简单的日志格式 + 'simple': { + 'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' + } + }, + # 过滤器 + 'filters': { + }, + # 处理器 + 'handlers': { + # 在终端打印 + 'console': { + 'level': 'DEBUG', + #'filters': ['require_debug_true'], # 只有在Django debug为True时才在屏幕打印日志 + 'class': 'logging.StreamHandler', # + 'formatter': 'simple' + }, + # 默认的 + 'info': { + 'level': 'INFO', + 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切 + 'filename': os.path.join(BASE_LOG_DIR, "info.log"), # 日志文件 + 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M + 'backupCount': 3, # 最多备份几个 + 'formatter': 'standard', + 'encoding': 'utf-8', + }, + # 专门用来记错误日志 + 'error': { + 'level': 'ERROR', + 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切 + 'filename': os.path.join(BASE_LOG_DIR, "error.log"), # 日志文件 + 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M + 'backupCount': 5, + 'formatter': 'standard', + 'encoding': 'utf-8', + }, + 'sign': { + 'level': 'INFO', + 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切 + 'filename': os.path.join(BASE_LOG_DIR, "sign.log"), # 日志文件 + 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M + 'backupCount': 3, # 最多备份几个 + 'formatter': 'standard', + 'encoding': 'utf-8', + }, + }, + 'loggers': { + # 默认的logger应用如下配置 + 'django': { + 'handlers': ['info', 'console', 'error'], # 上线之后可以把'console'移除 + 'level': 'INFO', + 'propagate': True, # 向不向更高级别的logger传递 + }, + 'sign': { #记录站点签到日志 + 'handlers': ['sign', 'error'], + 'level': 'INFO', + 'propagate': True, # 向不向更高级别的logger传递 + }, + }, +} \ No newline at end of file diff --git a/pthelper/urls.py b/pthelper/urls.py new file mode 100644 index 0000000..5e7b55b --- /dev/null +++ b/pthelper/urls.py @@ -0,0 +1,43 @@ +"""pthelper URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path +from django.conf.urls import include + +urlpatterns = [ + path('admin/', admin.site.urls), + + #path('api-auth/', include('rest_framework.urls')), + + #index + path('', include('index.urls.views_urls')), + #登录 + path('', include('login.urls.views_urls')), + #Dashboard + path('dashboard', include('dashboard.urls.views_urls')), + #用户、组、权限 + path('myauth/', include('myauth.urls.views_urls')), + #菜单 + path('mymenu', include('mymenu.urls.views_urls')), + #站点管理 + path('sites/', include('sites.urls.views_urls')), + #任务管理 + path('cron/', include('cron.urls.views_urls')), + #消息通知 + path('notify/', include('notify.urls.views_urls')), + #通用配置 + path('common/', include('common.urls.views_urls')), +] diff --git a/pthelper/wsgi.py b/pthelper/wsgi.py new file mode 100644 index 0000000..57a3be0 --- /dev/null +++ b/pthelper/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for pthelper project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pthelper.settings') + +application = get_wsgi_application() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ce67008 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,17 @@ +Django==4.0.2 +djangorestframework==3.13.1 +django-filter==21.1 +simplejson==3.17.6 +requests==2.27.1 +django-apscheduler==0.6.0 +python-telegram-bot==13.11 +PyMySQL >= 1.0.2 +lxml >= 4.7.1 +beautifulsoup4 >= 4.10.0 +ddddocr >= 1.4.3 +##### +#gunicorn + +#django-unixtimestampfield +#gevent + diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..526592d --- /dev/null +++ b/start.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +#初始化数据库 +python3 manage.py migrate +python3 manage.py runserver 0.0.0.0:80 +#gunicorn pthelper.wsgi:application --bind 0.0.0.0:8000 -w 4 --threads 2 --daemon + +#nginx -g 'daemon off;' \ No newline at end of file diff --git a/static/assets/images/bg-login.jpg b/static/assets/images/bg-login.jpg new file mode 100644 index 0000000..0b2d25e Binary files /dev/null and b/static/assets/images/bg-login.jpg differ diff --git a/static/assets/images/bg-screen.jpg b/static/assets/images/bg-screen.jpg new file mode 100644 index 0000000..20490c3 Binary files /dev/null and b/static/assets/images/bg-screen.jpg differ diff --git a/static/assets/images/favicon.ico b/static/assets/images/favicon.ico new file mode 100644 index 0000000..3aabbcb Binary files /dev/null and b/static/assets/images/favicon.ico differ diff --git a/static/assets/images/head.png b/static/assets/images/head.png new file mode 100644 index 0000000..4d461fb Binary files /dev/null and b/static/assets/images/head.png differ diff --git a/static/assets/images/ic_403.png b/static/assets/images/ic_403.png new file mode 100644 index 0000000..72221be Binary files /dev/null and b/static/assets/images/ic_403.png differ diff --git a/static/assets/images/ic_404.png b/static/assets/images/ic_404.png new file mode 100644 index 0000000..e2eb683 Binary files /dev/null and b/static/assets/images/ic_404.png differ diff --git a/static/assets/images/ic_500.png b/static/assets/images/ic_500.png new file mode 100644 index 0000000..b937e94 Binary files /dev/null and b/static/assets/images/ic_500.png differ diff --git a/static/assets/images/icon-invisible.png b/static/assets/images/icon-invisible.png new file mode 100644 index 0000000..475087d Binary files /dev/null and b/static/assets/images/icon-invisible.png differ diff --git a/static/assets/images/icon-visible.png b/static/assets/images/icon-visible.png new file mode 100644 index 0000000..fcb1df0 Binary files /dev/null and b/static/assets/images/icon-visible.png differ diff --git a/static/assets/images/logo.png b/static/assets/images/logo.png new file mode 100644 index 0000000..6cb5430 Binary files /dev/null and b/static/assets/images/logo.png differ diff --git a/static/assets/js/main.js b/static/assets/js/main.js new file mode 100644 index 0000000..7cd06fd --- /dev/null +++ b/static/assets/js/main.js @@ -0,0 +1 @@ +layui.config({version:"v1",base:"static/assets/module/"}).extend({steps:"steps/steps",notice:"notice/notice",cascader:"cascader/cascader",dropdown:"dropdown/dropdown",fileChoose:"fileChoose/fileChoose",Split:"Split/Split",Cropper:"Cropper/Cropper",tagsInput:"tagsInput/tagsInput",citypicker:"city-picker/city-picker",introJs:"introJs/introJs",zTree:"zTree/zTree",cascadeSelect:"cascadeSelect/cascadeSelect",miniPage:"layuimini/miniPage"}).use(["layer","setter","index","admin"],function(){var d=layui.jquery;var c=layui.layer;var e=layui.setter;var b=layui.index;var a=layui.admin;if(!e.getToken()){return location.replace("/login")}a.req("/static/json/menus.json",function(f){if(0===f.code){b.regRouter(f.data,function(g){g.name=g.title;g.url=g.path;g.iframe=g.component;g.show=!g.hide;g.subMenus=g.children;return g});b.renderSide(f.data);b.loadHome({url:"#/dashboard",name:''})}else{c.msg("获取菜单列表失败",{icon:2,anim:6})}})}); \ No newline at end of file diff --git a/static/assets/libs/echarts/china.js b/static/assets/libs/echarts/china.js new file mode 100644 index 0000000..7fc1bad --- /dev/null +++ b/static/assets/libs/echarts/china.js @@ -0,0 +1 @@ +(function(root,factory){if(typeof define==='function'&&define.amd){define(['exports','echarts'],factory);}else if(typeof exports === 'object' && typeof exports.nodeName !== 'string') {factory(exports, require('echarts'));} else {factory({}, root.echarts);}}(this, function (exports, echarts) {var log = function (msg) {if (typeof console !== 'undefined') {console && console.error && console.error(msg);}};if (!echarts) {log('ECharts is not Loaded');return;}if (!echarts.registerMap) {log('ECharts Map is not loaded');return;}echarts.registerMap('china', {"type":"FeatureCollection","features":[{"type":"Feature","id":"710000","properties":{"id":"710000","cp":[121.509062,24.044332],"name":"台湾","childNum":6},"geometry":{"type":"MultiPolygon","coordinates":[["@@°Ü¯Û"],["@@ƛĴÕƊÉɼģºðʀ\\ƎsÆNŌÔĚäœnÜƤɊĂǀĆĴžĤNJŨxĚĮǂƺòƌ‚–âÔ®ĮXŦţƸZûЋƕƑGđ¨ĭMó·ęcëƝɉlÝƯֹÅŃ^Ó·śŃNjƏďíåɛGɉ™¿@ăƑŽ¥ĘWǬÏĶŁâ"],["@@\\p|WoYG¿¥I†j@¢"],["@@…¡‰@ˆV^RqˆBbAŒnTXeRz¤Lž«³I"],["@@ÆEE—„kWqë @œ"],["@@fced"],["@@„¯ɜÄèaì¯ØǓIġĽ"],["@@çûĖ롖hòř "]],"encodeOffsets":[[[122886,24033]],[[123335,22980]],[[122375,24193]],[[122518,24117]],[[124427,22618]],[[124862,26043]],[[126259,26318]],[[127671,26683]]]}},{"type":"Feature","id":"130000","properties":{"id":"130000","cp":[114.502461,38.045474],"name":"河北","childNum":3},"geometry":{"type":"MultiPolygon","coordinates":[["@@o~†Z]‚ªr‰ºc_ħ²G¼s`jΟnüsœłNX_“M`ǽÓnUK…Ĝēs¤­©yrý§uģŒc†JŠ›e"],["@@U`Ts¿m‚"],["@@oºƋÄd–eVŽDJj£€J|Ådz•Ft~žKŨ¸IÆv|”‡¢r}膎onb˜}`RÎÄn°ÒdÞ²„^®’lnÐèĄlðӜ×]ªÆ}LiĂ±Ö`^°Ç¶p®đDcœŋ`–ZÔ’¶êqvFƚ†N®ĆTH®¦O’¾ŠIbÐã´BĐɢŴÆíȦp–ĐÞXR€·nndOž¤’OÀĈƒ­Qg˜µFo|gȒęSWb©osx|hYh•gŃfmÖĩnº€T̒Sp›¢dYĤ¶UĈjl’ǐpäìë|³kÛfw²Xjz~ÂqbTŠÑ„ěŨ@|oM‡’zv¢ZrÃVw¬ŧˏfŒ°ÐT€ªqŽs{Sž¯r æÝlNd®²Ğ džiGʂJ™¼lr}~K¨ŸƐÌWö€™ÆŠzRš¤lêmĞL΄’@¡|q]SvK€ÑcwpÏρ†ĿćènĪWlĄkT}ˆJ”¤~ƒÈT„d„™pddʾĬŠ”ŽBVt„EÀ¢ôPĎƗè@~‚k–ü\\rÊĔÖæW_§¼F˜†´©òDòj’ˆYÈrbĞāøŀG{ƀ|¦ðrb|ÀH`pʞkv‚GpuARhÞÆǶgƊTǼƹS£¨¡ù³ŘÍ]¿Ây™ôEP xX¶¹܇O¡“gÚ¡IwÃ鑦ÅB‡Ï|ǰ…N«úmH¯‹âŸDùŽyŜžŲIÄuШDž•¸dɂ‡‚FŸƒ•›Oh‡đ©OŸ›iÃ`ww^ƒÌkŸ‘ÑH«ƇǤŗĺtFu…{Z}Ö@U‡´…ʚLg®¯Oı°ÃwŸ ^˜—€VbÉs‡ˆmA…ê]]w„§›RRl£‡ȭµu¯b{ÍDěïÿȧŽuT£ġƒěŗƃĝ“Q¨fV†Ƌ•ƅn­a@‘³@šď„yýIĹÊKšŭfċŰóŒxV@tˆƯŒJ”]eƒR¾fe|rHA˜|h~Ėƍl§ÏŠlTíb ØoˆÅbbx³^zÃ͚¶Sj®A”yÂhðk`š«P€”ˈµEF†Û¬Y¨Ļrõqi¼‰Wi°§’б´°^[ˆÀ|ĠO@ÆxO\\tŽa\\tĕtû{ġŒȧXýĪÓjùÎRb›š^ΛfK[ݏděYfíÙTyŽuUSyŌŏů@Oi½’éŅ­aVcř§ax¹XŻác‡žWU£ôãºQ¨÷Ñws¥qEH‰Ù|‰›šYQoŕÇyáĂ£MðoťÊ‰P¡mšWO¡€v†{ôvîēÜISpÌhp¨ ‘j†deŔQÖj˜X³à™Ĉ[n`Yp@Už–cM`’RKhŒEbœ”pŞlNut®Etq‚nsÁŠgA‹iú‹oH‡qCX‡”hfgu“~ϋWP½¢G^}¯ÅīGCŸÑ^ãziMáļMTÃƘrMc|O_ž¯Ŏ´|‡morDkO\\mĆJfl@c̬¢aĦtRıҙ¾ùƀ^juųœK­ƒUFy™—Ɲ…›īÛ÷ąV×qƥV¿aȉd³B›qPBm›aËđŻģm“Å®Vйd^K‡KoŸnYg“¯Xhqa”Ldu¥•ÍpDž¡KąÅƒkĝęěhq‡}HyÓ]¹ǧ£…Í÷¿qáµ§š™g‘¤o^á¾ZE‡¤i`ij{n•ƒOl»ŸWÝĔįhg›F[¿¡—ßkOüš_‰€ū‹i„DZàUtėGylƒ}ŒÓM}€jpEC~¡FtoQi‘šHkk{Ãmï‚"]],"encodeOffsets":[[[119712,40641]],[[121616,39981]],[[116462,37237]]]}},{"type":"Feature","id":"140000","properties":{"id":"140000","cp":[111.849248,36.857014],"name":"山西","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@Þĩ҃S‰ra}Á€yWix±Üe´lè“ßÓǏok‘ćiµVZģ¡coœ‘TS˹ĪmnÕńe–hZg{gtwªpXaĚThȑp{¶Eh—®RćƑP¿£‘Pmc¸mQÝW•ďȥoÅîɡųAďä³aωJ‘½¥PG­ąSM­™…EÅruµé€‘Yӎ•Ō_d›ĒCo­Èµ]¯_²ÕjāŽK~©ÅØ^ԛkïçămϑk]­±ƒcݯÑÃmQÍ~_a—pm…~ç¡q“ˆu{JÅŧ·Ls}–EyÁÆcI{¤IiCfUc•ƌÃp§]웫vD@¡SÀ‘µM‚ÅwuŽYY‡¡DbÑc¡hƒ×]nkoQdaMç~eD•ÛtT‰©±@¥ù@É¡‰ZcW|WqOJmĩl«ħşvOÓ«IqăV—¥ŸD[mI~Ó¢cehiÍ]Ɠ~ĥqXŠ·eƷœn±“}v•[ěďŽŕ]_‘œ•`‰¹ƒ§ÕōI™o©b­s^}Ét±ū«³p£ÿ·Wµ|¡¥ăFÏs׌¥ŅxŸÊdÒ{ºvĴÎêÌɊ²¶€ü¨|ÞƸµȲ‘LLúÉƎ¤ϊęĔV`„_bª‹S^|ŸdŠzY|dz¥p†ZbÆ£¶ÒK}tĦÔņƠ‚PYzn€ÍvX¶Ěn ĠÔ„zý¦ª˜÷žÑĸَUȌ¸‚dòÜJð´’ìúNM¬ŒXZ´‘¤ŊǸ_tldIš{¦ƀðĠȤ¥NehXnYG‚‡R° ƬDj¬¸|CĞ„Kq‚ºfƐiĺ©ª~ĆOQª ¤@ìǦɌ²æBŒÊ”TœŸ˜ʂōĖ’šĴŞ–ȀœÆÿȄlŤĒö„t”νî¼ĨXhŒ‘˜|ªM¤Ðz"],"encodeOffsets":[[116874,41716]]}},{"type":"Feature","id":"150000","properties":{"id":"150000","cp":[111.670801,41.818311],"name":"内蒙古","childNum":2},"geometry":{"type":"MultiPolygon","coordinates":[["@@¯PqƒFB…‰|S•³C|kñ•H‹d‘iÄ¥sˆʼnő…PóÑÑE^‘ÅPpy_YtS™hQ·aHwsOnʼnÚs©iqj›‰€USiº]ïWš‰«gW¡A–Rë¥_ŽsgÁnUI«m‰…„‹]j‡vV¼euhwqA„aW˜ƒ_µj…»çjioQR¹ēÃßt@r³[ÛlćË^ÍÉáG“›OUۗOB±•XŸkŇ¹£k|e]ol™ŸkVͼÕqtaÏõjgÁ£§U^Œ”RLˆËnX°Ç’Bz†^~wfvˆypV ¯„ƫĉ˭ȫƗŷɿÿĿƑ˃ĝÿÃǃßËőó©ǐȍŒĖM×ÍEyx‹þp]Évïè‘vƀnÂĴÖ@‚‰†V~Ĉv¦wĖt—ējyÄDXÄxGQuv_›i¦aBçw‘˛wD™©{ŸtāmQ€{EJ§KPśƘƿ¥@‰sCT•É}ɃwˆƇy±ŸgÑ“}T[÷kÐ禫…SÒ¥¸ëBX½‰HáŵÀğtSÝÂa[ƣ°¯¦P]£ġ“–“Òk®G²„èQ°óMq}EŠóƐÇ\\ƒ‡@áügQ͋u¥Fƒ“T՛¿Jû‡]|mvāÎYua^WoÀa·­ząÒot×¶CLƗi¯¤mƎHNJ¤îìɾŊìTdåwsRÖgĒųúÍġäÕ}Q¶—ˆ¿A•†‹[¡Œ{d×uQAƒ›M•xV‹vMOmăl«ct[wº_šÇʊŽŸjb£ĦS_é“QZ“_lwgOiýe`YYLq§IÁˆdz£ÙË[ÕªuƏ³ÍT—s·bÁĽäė[›b[ˆŗfãcn¥îC¿÷µ[ŏÀQ­ōšĉm¿Á^£mJVm‡—L[{Ï_£›F¥Ö{ŹA}…×Wu©ÅaųijƳhB{·TQqÙIķˑZđ©Yc|M¡…L•eVUóK_QWk’_ĥ‘¿ãZ•»X\\ĴuUƒè‡lG®ěłTĠğDєOrÍd‚ÆÍz]‹±…ŭ©ŸÅ’]ŒÅÐ}UË¥©Tċ™ïxgckfWgi\\ÏĒ¥HkµE˜ë{»ÏetcG±ahUiñiWsɁˆ·c–C‚Õk]wȑ|ća}w…VaĚá žŒG°ùnM¬¯†{ÈˆÐÆA’¥ÄêJxÙ¢”hP¢Ûˆº€µwWOŸóFŽšÁz^ÀŗÎú´§¢T¤ǻƺSė‰ǵhÝÅQgvBHouʝl_o¿Ga{ïq{¥|ſĿHĂ÷aĝÇq‡Z‘ñiñC³ª—…»E`¨åXēÕqÉû[l•}ç@čƘóO¿¡ƒFUsA‰“ʽīccšocƒ‚ƒÇS}„“£‡IS~ălkĩXçmĈ…ŀЂoÐdxÒuL^T{r@¢‘žÍƒĝKén£kQ™‰yšÅõËXŷƏL§~}kqš»IHėDžjĝŸ»ÑÞoŸå°qTt|r©ÏS‹¯·eŨĕx«È[eMˆ¿yuˆ‘pN~¹ÏyN£{©’—g‹ħWí»Í¾s“əšDž_ÃĀɗ±ą™ijĉʍŌŷ—S›É“A‹±åǥɋ@럣R©ąP©}ĹªƏj¹erƒLDĝ·{i«ƫC£µsKCš…GS|úþX”gp›{ÁX¿Ÿć{ƱȏñZáĔyoÁhA™}ŅĆfdʼn„_¹„Y°ėǩÑ¡H¯¶oMQqð¡Ë™|‘Ñ`ƭŁX½·óۓxğįÅcQ‡ˆ“ƒs«tȋDžF“Ÿù^i‘t«Č¯[›hAi©á¥ÇĚ×l|¹y¯YȵƓ‹ñǙµï‚ċ™Ļ|Dœ™üȭ¶¡˜›oŽäÕG\\ďT¿Òõr¯œŸLguÏYęRƩšɷŌO\\İТæ^Ŋ IJȶȆbÜGŽĝ¬¿ĚVĎgª^íu½jÿĕęjık@Ľƒ]ėl¥Ë‡ĭûÁ„ƒėéV©±ćn©­ȇžÍq¯½•YÃÔʼn“ÉNѝÅÝy¹NqáʅDǡËñ­ƁYÅy̱os§ȋµʽǘǏƬɱà‘ưN¢ƔÊuľýľώȪƺɂļžxœZĈ}ÌʼnŪ˜ĺœŽĭFЛĽ̅ȣͽÒŵìƩÇϋÿȮǡŏçƑůĕ~Ǎ›¼ȳÐUf†dIxÿ\\G ˆzâɏÙOº·pqy£†@ŒŠqþ@Ǟ˽IBäƣzsÂZ†ÁàĻdñ°ŕzéØűzșCìDȐĴĺf®ŽÀľưø@ɜÖÞKĊŇƄ§‚͑těï͡VAġÑÑ»d³öǍÝXĉĕÖ{þĉu¸ËʅğU̎éhɹƆ̗̮ȘNJ֥ड़ࡰţાíϲäʮW¬®ҌeרūȠkɬɻ̼ãüfƠSצɩςåȈHϚÎKdzͲOðÏȆƘ¼CϚǚ࢚˼ФԂ¤ƌžĞ̪Qʤ´¼mȠJˀŸƲÀɠmǐnǔĎȆÞǠN~€ʢĜ‚¶ƌĆĘźʆȬ˪ĚǏĞGȖƴƀj`ĢçĶāàŃºē̃ĖćšYŒÀŎüôQÐÂŎŞdžŞêƖš˜oˆDĤÕºÑǘÛˤ³̀gńƘĔÀ^žªƂ`ªt¾äƚêĦĀ¼Ð€Ĕǎ¨Ȕ»͠^ˮÊȦƤøxRrŜH¤¸ÂxDĝŒ|ø˂˜ƮÐ¬ɚwɲFjĔ²Äw°dždÀɞ_ĸdîàŎjʜêTĞªŌ‡ŜWÈ|tqĢUB~´°ÎFC•ŽU¼pĀēƄN¦¾O¶ŠłKĊOj“Ě”j´ĜYp˜{¦„ˆSĚÍ\\Tš×ªV–÷Ší¨ÅDK°ßtŇĔKš¨ǵÂcḷ̌ĚǣȄĽF‡lġUĵœŇ‹ȣFʉɁƒMğįʏƶɷØŭOǽ«ƽū¹Ʊő̝Ȩ§ȞʘĖiɜɶʦ}¨֪ࠜ̀ƇǬ¹ǨE˦ĥªÔêFŽxúQ„Er´W„rh¤Ɛ \\talĈDJ˜Ü|[Pll̚¸ƎGú´Pž¬W¦†^¦–H]prR“n|or¾wLVnÇIujkmon£cX^Bh`¥V”„¦U¤¸}€xRj–[^xN[~ªŠxQ„‚[`ªHÆÂExx^wšN¶Ê˜|¨ì†˜€MrœdYp‚oRzNy˜ÀDs~€bcfÌ`L–¾n‹|¾T‚°c¨È¢a‚r¤–`[|òDŞĔöxElÖdH„ÀI`„Ď\\Àì~ƎR¼tf•¦^¢ķ¶e”ÐÚMŒptgj–„ɡČÅyġLû™ŇV®ŠÄÈƀ†Ď°P|ªVV†ªj–¬ĚÒêp¬–E|ŬÂc|ÀtƐK fˆ{ĘFǜƌXƲąo½Ę‘\\¥–o}›Ûu£ç­kX‘{uĩ«āíÓUŅßŢq€Ť¥lyň[€oi{¦‹L‡ń‡ðFȪȖ”ĒL„¿Ì‹ˆfŒ£K£ʺ™oqNŸƒwğc`ue—tOj×°KJ±qƒÆġm‰Ěŗos¬…qehqsuœƒH{¸kH¡Š…ÊRǪÇƌbȆ¢´ä܍¢NìÉʖ¦â©Ġu¦öČ^â£Ăh–šĖMÈÄw‚\\fŦ°W ¢¾luŸD„wŠ\\̀ʉÌÛM…Ā[bӞEn}¶Vc…ê“sƒ"]],"encodeOffsets":[[[129102,52189]]]}},{"type":"Feature","id":"210000","properties":{"id":"210000","cp":[123.429096,41.796767],"name":"辽宁","childNum":16},"geometry":{"type":"MultiPolygon","coordinates":[["@@L–Ž@@s™a"],["@@MnNm"],["@@d‚c"],["@@eÀ‚C@b‚“‰"],["@@f‡…Xwkbr–Ä`qg"],["@@^jtW‘Q"],["@@~ Y]c"],["@@G`ĔN^_¿Z‚ÃM"],["@@iX¶B‹Y"],["@@„YƒZ"],["@@L_{Epf"],["@@^WqCT\\"],["@@\\[“‹§t|”¤_"],["@@m`n_"],["@@Ïxnj{q_×^Giip"],["@@@œé^B†‡ntˆaÊU—˜Ÿ]x ¯ÄPIJ­°h€ʙK³†VˆÕ@Y~†|EvĹsDŽ¦­L^p²ŸÒG ’Ël]„xxÄ_˜fT¤Ď¤cŽœP„–C¨¸TVjbgH²sdÎdHt`Bˆ—²¬GJję¶[ÐhjeXdlwhšðSȦªVÊπ‹Æ‘Z˜ÆŶ®²†^ŒÎyÅÎcPqń“ĚDMħĜŁH­ˆk„çvV[ij¼W–‚YÀäĦ’‘`XlžR`žôLUVžfK–¢†{NZdĒª’YĸÌÚJRr¸SA|ƴgŴĴÆbvªØX~†źBŽ|¦ÕœEž¤Ð`\\|Kˆ˜UnnI]¤ÀÂĊnŎ™R®Ő¿¶\\ÀøíDm¦ÎbŨab‰œaĘ\\ľã‚¸a˜tÎSƐ´©v\\ÖÚÌǴ¤Â‡¨JKr€Z_Z€fjþhPkx€`Y”’RIŒjJcVf~sCN¤ ˆE‚œhæm‰–sHy¨SðÑÌ\\\\ŸĐRZk°IS§fqŒßýáЍÙÉÖ[^¯ǤŲ„ê´\\¦¬ĆPM¯£Ÿˆ»uïpùzEx€žanµyoluqe¦W^£ÊL}ñrkqWňûP™‰UP¡ôJŠoo·ŒU}£Œ„[·¨@XŒĸŸ“‹‹DXm­Ûݏº‡›GU‹CÁª½{íĂ^cj‡k“¶Ã[q¤“LÉö³cux«zZfƒ²BWÇ®Yß½ve±ÃC•ý£W{Ú^’q^sÑ·¨‹ÍOt“¹·C¥‡GD›rí@wÕKţ݋˜Ÿ«V·i}xËÍ÷‘i©ĝ‡ɝǡ]ƒˆ{c™±OW‹³Ya±Ÿ‰_穂Hžĕoƫ€Ňqƒr³‰Lys[„ñ³¯OS–ďOMisZ†±ÅFC¥Pq{‚Ã[Pg}\\—¿ghćO…•k^ģÁFıĉĥM­oEqqZûěʼn³F‘¦oĵ—hŸÕP{¯~TÍlª‰N‰ßY“Ð{Ps{ÃVU™™eĎwk±ʼnVÓ½ŽJãÇÇ»Jm°dhcÀff‘dF~ˆ€ĀeĖ€d`sx² šƒ®EżĀdQ‹Âd^~ăÔHˆ¦\\›LKpĄVez¤NP ǹӗR™ÆąJSh­a[¦´Âghwm€BÐ¨źhI|žVVŽ—Ž|p] ¼èNä¶ÜBÖ¼“L`‚¼bØæŒKV”ŸpoœúNZÞÒKxpw|ÊEMnzEQšŽIZ”ŽZ‡NBˆčÚFÜçmĩ‚WĪñt‘ÞĵÇñZ«uD‚±|Əlij¥ãn·±PmÍa‰–da‡ CL‡Ǒkùó¡³Ï«QaċϑOÃ¥ÕđQȥċƭy‹³ÃA"]],"encodeOffsets":[[[123686,41445]],[[126019,40435]],[[124393,40128]],[[126117,39963]],[[125322,40140]],[[126686,40700]],[[126041,40374]],[[125584,40168]],[[125453,40165]],[[125362,40214]],[[125280,40291]],[[125774,39997]],[[125976,40496]],[[125822,39993]],[[125509,40217]],[[122731,40949]]]}},{"type":"Feature","id":"220000","properties":{"id":"220000","cp":[125.3245,43.886841],"name":"吉林","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@‘p䔳PClƒFbbÍzš€wBG’ĭ€Z„Åi“»ƒlY­ċ²SgŽkÇ£—^S‰“qd¯•‹R…©éŽ£¯S†\\cZ¹iűƏCuƍÓX‡oR}“M^o•£…R}oªU­F…uuXHlEŕ‡€Ï©¤ÛmTŽþ¤D–²ÄufàÀ­XXȱAe„yYw¬dvõ´KÊ£”\\rµÄl”iˆdā]|DÂVŒœH¹ˆÞ®ÜWnŒC”Œķ W‹§@\\¸‹ƒ~¤‹Vp¸‰póIO¢ŠVOšŇürXql~òÉK]¤¥Xrfkvzpm¶bwyFoúvð‡¼¤ N°ąO¥«³[ƒéǡű_°Õ\\ÚÊĝŽþâőàerR¨­JYlďQ[ ÏYëЧTGz•tnŠß¡gFkMŸāGÁ¤ia ɉ™È¹`\\xs€¬dĆkNnuNUŠ–užP@‚vRY¾•–\\¢…ŒGªóĄ~RãÖÎĢù‚đŴÕhQŽxtcæëSɽʼníëlj£ƍG£nj°KƘµDsØÑpyƸ®¿bXp‚]vbÍZuĂ{nˆ^IüœÀSք”¦EŒvRÎûh@℈[‚Əȉô~FNr¯ôçR±ƒ­HÑl•’Ģ–^¤¢‚OðŸŒævxsŒ]ÞÁTĠs¶¿âƊGW¾ìA¦·TѬ†è¥€ÏÐJ¨¼ÒÖ¼ƒƦɄxÊ~S–tD@ŠĂ¼Ŵ¡jlºWžvЉˆzƦZЎ²CH— „Axiukd‹ŒGgetqmcžÛ£Ozy¥cE}|…¾cZ…k‚‰¿uŐã[oxGikfeäT@…šSUwpiÚFM©’£è^ڟ‚`@v¶eň†f h˜eP¶žt“äOlÔUgƒÞzŸU`lœ}ÔÆUvØ_Ō¬Öi^ĉi§²ÃŠB~¡Ĉ™ÚEgc|DC_Ȧm²rBx¼MÔ¦ŮdĨÃâYx‘ƘDVÇĺĿg¿cwÅ\\¹˜¥Yĭlœ¤žOv†šLjM_a W`zļMž·\\swqÝSA‡š—q‰Śij¯Š‘°kŠRē°wx^Đkǂғ„œž“œŽ„‹\\]˜nrĂ}²ĊŲÒøãh·M{yMzysěnĒġV·°“G³¼XÀ““™¤¹i´o¤ŃšŸÈ`̃DzÄUĞd\\i֚ŒˆmÈBĤÜɲDEh LG¾ƀľ{WaŒYÍȏĢĘÔRîĐj‹}Ǟ“ccj‡oUb½š{“h§Ǿ{K‹ƖµÎ÷žGĀÖŠåưÎs­l›•yiē«‹`姝H¥Ae^§„GK}iã\\c]v©ģZ“mÃ|“[M}ģTɟĵ‘Â`À–çm‰‘FK¥ÚíÁbXš³ÌQґHof{‰]e€pt·GŋĜYünĎųVY^’˜ydõkÅZW„«WUa~U·Sb•wGçǑ‚“iW^q‹F‚“›uNĝ—·Ew„‹UtW·Ýďæ©PuqEzwAV•—XR‰ãQ`­©GŒM‡ehc›c”ďϝd‡©ÑW_ϗYƅŒ»…é\\ƒɹ~ǙG³mØ©BšuT§Ĥ½¢Ã_ý‘L¡‘ýŸqT^rme™\\Pp•ZZbƒyŸ’uybQ—efµ]UhĿDCmûvašÙNSkCwn‰cćfv~…Y‹„ÇG"],"encodeOffsets":[[130196,42528]]}},{"type":"Feature","id":"230000","properties":{"id":"230000","cp":[128.642464,46.756967],"name":"黑龙江","childNum":2},"geometry":{"type":"MultiPolygon","coordinates":[["@@UƒµNÿ¥īè灋•HÍøƕ¶LŒǽ|g¨|”™Ža¾pViˆdd”~ÈiŒíďÓQġėǐZ΋ŽXb½|ſÃH½ŸKFgɱCģÛÇA‡n™‹jÕc[VĝDZÃ˄Ç_™ £ń³pŽj£º”š¿”»WH´¯”U¸đĢmžtĜyzzNN|g¸÷äűѱĉā~mq^—Œ[ƒ”››”ƒǁÑďlw]¯xQĔ‰¯l‰’€°řĴrŠ™˜BˆÞTxr[tޏĻN_yŸX`biN™Ku…P›£k‚ZĮ—¦[ºxÆÀdhŽĹŀUÈƗCw’áZħÄŭcÓ¥»NAw±qȥnD`{ChdÙFćš}¢‰A±Äj¨]ĊÕjŋ«×`VuÓś~_kŷVÝyh„“VkÄãPs”Oµ—fŸge‚Ň…µf@u_Ù ÙcŸªNªÙEojVx™T@†ãSefjlwH\\pŏäÀvŠŽlY†½d{†F~¦dyz¤PÜndsrhf‹HcŒvlwjFœ£G˜±DύƥY‡yϊu¹XikĿ¦ÏqƗǀOŜ¨LI|FRĂn sª|Cš˜zxAè¥bœfudTrFWÁ¹Am|˜ĔĕsķÆF‡´Nš‰}ć…UŠÕ@Áijſmužç’uð^ÊýowŒFzØÎĕNőžǏȎôªÌŒDŽàĀÄ˄ĞŀƒʀĀƘŸˮȬƬĊ°ƒUŸzou‡xe]}Ž…AyȑW¯ÌmK‡“Q]‹Īºif¸ÄX|sZt|½ÚUÎ lkš^p{f¤lˆºlÆW –€A²˜PVܜPH”Êâ]ÎĈÌÜk´\\@qàsĔÄQºpRij¼èi†`¶—„bXƒrBgxfv»ŽuUiˆŒ^v~”J¬mVp´£Œ´VWrnP½ì¢BX‚¬h™ŠðX¹^TjVœŠriªj™tŊÄm€tPGx¸bgRšŽsT`ZozÆO]’ÒFô҆Oƒ‡ŊŒvŞ”p’cGŒêŠsx´DR–Œ{A†„EOr°Œ•žx|íœbˆ³Wm~DVjºéNN†Ëܲɶ­GƒxŷCStŸ}]ûō•SmtuÇÃĕN•™āg»šíT«u}ç½BĵÞʣ¥ëÊ¡Mێ³ãȅ¡ƋaǩÈÉQ‰†G¢·lG|›„tvgrrf«†ptęŘnŠÅĢr„I²¯LiØsPf˜_vĠd„xM prʹšL¤‹¤‡eˌƒÀđK“žïÙVY§]I‡óáĥ]ķ†Kˆ¥Œj|pŇ\\kzţ¦šnņäÔVĂîά|vW’®l¤èØr‚˜•xm¶ă~lÄƯĄ̈́öȄEÔ¤ØQĄ–Ą»ƢjȦOǺ¨ìSŖÆƬy”Qœv`–cwƒZSÌ®ü±DŽ]ŀç¬B¬©ńzƺŷɄeeOĨS’Œfm Ċ‚ƀP̎ēz©Ċ‚ÄÕÊmgŸÇsJ¥ƔˆŊśæ’΁Ñqv¿íUOµª‰ÂnĦÁ_½ä@ê텣P}Ġ[@gġ}g“ɊדûÏWXá¢užƻÌsNͽƎÁ§č՛AēeL³àydl›¦ĘVçŁpśdžĽĺſʃQíÜçÛġԏsĕ¬—Ǹ¯YßċġHµ ¡eå`ļƒrĉŘóƢFì“ĎWøxÊk†”ƈdƬv|–I|·©NqńRŀƒ¤é”eŊœŀ›ˆàŀU²ŕƀB‚Q£Ď}L¹Îk@©ĈuǰųǨ”Ú§ƈnTËÇéƟÊcfčŤ^Xm‡—HĊĕË«W·ċëx³ǔķÐċJā‚wİ_ĸ˜Ȁ^ôWr­°oú¬Ħ…ŨK~”ȰCĐ´Ƕ£’fNÎèâw¢XnŮeÂÆĶŽ¾¾xäLĴĘlļO¤ÒĨA¢Êɚ¨®‚ØCÔ ŬGƠ”ƦYĜ‡ĘÜƬDJ—g_ͥœ@čŅĻA“¶¯@wÎqC½Ĉ»NŸăëK™ďÍQ“Ùƫ[«Ãí•gßÔÇOÝáW‘ñuZ“¯ĥ€Ÿŕā¡ÑķJu¤E Ÿå¯°WKɱ_d_}}vyŸõu¬ï¹ÓU±½@gÏ¿rýD‰†g…Cd‰µ—°MFYxw¿CG£‹Rƛ½Õ{]L§{qqąš¿BÇƻğëšܭNJË|c²}Fµ}›ÙRsÓpg±ŠQNqǫŋRwŕnéÑÉKŸ†«SeYR…ŋ‹@{¤SJ}šD Ûǖ֍Ÿ]gr¡µŷjqWÛham³~S«“„›Þ]"]],"encodeOffsets":[[[134456,44547]]]}},{"type":"Feature","id":"320000","properties":{"id":"320000","cp":[119.767413,33.041544],"name":"江苏","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@cþÅPiŠ`ZŸRu¥É\\]~°ŽY`µ†Óƒ^phÁbnÀşúŽòa–ĬºTÖŒb‚˜e¦¦€{¸ZâćNpŒ©žHr|^ˆmjhŠSEb\\afv`sz^lkŽlj‹Ätg‹¤D˜­¾Xš¿À’|ДiZ„ȀåB·î}GL¢õcßjaŸyBFµÏC^ĭ•cÙt¿sğH]j{s©HM¢ƒQnDÀ©DaÜތ·jgàiDbPufjDk`dPOîƒhw¡ĥ‡¥šG˜ŸP²ĐobºrY†„î¶aHŢ´ ]´‚rılw³r_{£DB_Ûdåuk|ˆŨ¯F Cºyr{XFy™e³Þċ‡¿Â™kĭB¿„MvÛpm`rÚã”@ƹhågËÖƿxnlč¶Åì½Ot¾dJlŠVJʜǀœŞqvnOŠ^ŸJ”Z‘ż·Q}ê͎ÅmµÒ]Žƍ¦Dq}¬R^èĂ´ŀĻĊIԒtžIJyQŐĠMNtœR®òLh‰›Ěs©»œ}OӌGZz¶A\\jĨFˆäOĤ˜HYš†JvÞHNiÜaϚɖnFQlšNM¤ˆB´ĄNöɂtp–Ŭdf先‹qm¿QûŠùއÚb¤uŃJŴu»¹Ą•lȖħŴw̌ŵ²ǹǠ͛hĭłƕrçü±Y™xci‡tğ®jű¢KOķ•Coy`å®VTa­_Ā]ŐÝɞï²ʯÊ^]afYǸÃĆēĪȣJđ͍ôƋĝÄ͎ī‰çÛɈǥ£­ÛmY`ó£Z«§°Ó³QafusNıDž_k}¢m[ÝóDµ—¡RLčiXy‡ÅNïă¡¸iĔϑNÌŕoēdōîåŤûHcs}~Ûwbù¹£¦ÓCt‹OPrƒE^ÒoŠg™ĉIµžÛÅʹK…¤½phMŠü`o怆ŀ"],"encodeOffsets":[[121740,32276]]}},{"type":"Feature","id":"330000","properties":{"id":"330000","cp":[120.153576,29.287459],"name":"浙江","childNum":45},"geometry":{"type":"MultiPolygon","coordinates":[["@@E^dQ]K"],["@@jX^j‡"],["@@sfŠbU‡"],["@@qP\\xz[ck"],["@@‘Rƒ¢‚FX}°[s_"],["@@Cbœ\\—}"],["@@e|v\\la{u"],["@@v~u}"],["@@QxÂF¯}"],["@@¹nŒvÞs¯o"],["@@rSkUEj"],["@@bi­ZŒP"],["@@p[}INf"],["@@À¿€"],["@@¹dnbŒ…"],["@@rSŸBnR"],["@@g~h}"],["@@FlEk"],["@@OdPc"],["@@v[u\\"],["@@FjâL~wyoo~›sµL–\\"],["@@¬e¹aNˆ"],["@@\\nÔ¡q]L³ë\\ÿ®ŒQ֎"],["@@ÊA­©[¬"],["@@KxŒv­"],["@@@hlIk]"],["@@pW{o||j"],["@@Md|_mC"],["@@¢…X£ÏylD¼XˆtH"],["@@hlÜ[LykAvyfw^Ež›¤"],["@@fp¤Mus“R"],["@@®_ma~•LÁ¬šZ"],["@@iM„xZ"],["@@ZcYd"],["@@Z~dOSo|A¿qZv"],["@@@`”EN¡v"],["@@|–TY{"],["@@@n@m"],["@@XWkCT\\"],["@@ºwšZRkĕWO¢"],["@@™X®±Grƪ\\ÔáXq{‹"],["@@ůTG°ĄLHm°UC‹"],["@@¤Ž€aÜx~}dtüGæţŎíĔcŖpMËВj碷ðĄÆMzˆjWKĎ¢Q¶˜À_꒔_Bı€i«pZ€gf€¤Nrq]§ĂN®«H±‡yƳí¾×ŸīàLłčŴǝĂíÀBŖÕªˆŠÁŖHŗʼnåqûõi¨hÜ·ƒñt»¹ýv_[«¸m‰YL¯‰Qª…mĉÅdMˆ•gÇjcº«•ęœ¬­K­´ƒB«Âącoċ\\xKd¡gěŧ«®á’[~ıxu·Å”KsËɏc¢Ù\\ĭƛëbf¹­ģSƒĜkáƉÔ­ĈZB{ŠaM‘µ‰fzʼnfåÂŧįƋǝÊĕġć£g³ne­ą»@­¦S®‚\\ßðCšh™iqªĭiAu‡A­µ”_W¥ƣO\\lċĢttC¨£t`ˆ™PZäuXßBs‡Ļyek€OđġĵHuXBšµ]׌‡­­\\›°®¬F¢¾pµ¼kŘó¬Wät’¸|@ž•L¨¸µr“ºù³Ù~§WI‹ŸZWŽ®’±Ð¨ÒÉx€`‰²pĜ•rOògtÁZ}þÙ]„’¡ŒŸFK‚wsPlU[}¦Rvn`hq¬\\”nQ´ĘRWb”‚_ rtČFI֊kŠŠĦPJ¶ÖÀÖJĈĄTĚòžC ²@Pú…Øzœ©PœCÈÚœĒ±„hŖ‡l¬â~nm¨f©–iļ«m‡nt–u†ÖZÜÄj“ŠLŽ®E̜Fª²iÊxبžIÈhhst"],["@@o\\V’zRZ}y"],["@@†@°¡mۛGĕ¨§Ianá[ýƤjfæ‡ØL–•äGr™"]],"encodeOffsets":[[[125592,31553]],[[125785,31436]],[[125729,31431]],[[125513,31380]],[[125223,30438]],[[125115,30114]],[[124815,29155]],[[124419,28746]],[[124095,28635]],[[124005,28609]],[[125000,30713]],[[125111,30698]],[[125078,30682]],[[125150,30684]],[[124014,28103]],[[125008,31331]],[[125411,31468]],[[125329,31479]],[[125626,30916]],[[125417,30956]],[[125254,30976]],[[125199,30997]],[[125095,31058]],[[125083,30915]],[[124885,31015]],[[125218,30798]],[[124867,30838]],[[124755,30788]],[[124802,30809]],[[125267,30657]],[[125218,30578]],[[125200,30562]],[[124968,30474]],[[125167,30396]],[[124955,29879]],[[124714,29781]],[[124762,29462]],[[124325,28754]],[[123990,28459]],[[125366,31477]],[[125115,30363]],[[125369,31139]],[[122495,31878]],[[125329,30690]],[[125192,30787]]]}},{"type":"Feature","id":"340000","properties":{"id":"340000","cp":[117.283042,31.26119],"name":"安徽","childNum":3},"geometry":{"type":"MultiPolygon","coordinates":[["@@^iuLX^"],["@@‚e©Ehl"],["@@°ZÆëϵmkǀwÌÕæhºgBĝâqÙĊz›ÖgņtÀÁÊÆá’hEz|WzqD¹€Ÿ°E‡ŧl{ævÜcA`¤C`|´qžxIJkq^³³ŸGšµbƒíZ…¹qpa±ď OH—¦™Ħˆx¢„gPícOl_iCveaOjCh߸i݋bÛªCC¿€m„RV§¢A|t^iĠGÀtÚs–d]ĮÐDE¶zAb àiödK¡~H¸íæAžǿYƒ“j{ď¿‘™À½W—®£ChŒÃsiŒkkly]_teu[bFa‰Tig‡n{]Gqªo‹ĈMYá|·¥f¥—őaSÕė™NµñĞ«ImŒ_m¿Âa]uĜp …Z_§{Cƒäg¤°r[_Yj‰ÆOdý“[ŽI[á·¥“Q_n‡ùgL¾mv™ˊBÜÆ¶ĊJhšp“c¹˜O]iŠ]œ¥ jtsggJǧw×jÉ©±›EFˍ­‰Ki”ÛÃÕYv…s•ˆm¬njĻª•§emná}k«ŕˆƒgđ²Ù›DǤ›í¡ªOy›†×Où±@DŸñSęćăÕIÕ¿IµĥO‰‰jNÕËT¡¿tNæŇàåyķrĕq§ÄĩsWÆßŽF¶žX®¿‰mŒ™w…RIޓfßoG‘³¾©uyH‘į{Ɓħ¯AFnuP…ÍÔzšŒV—dàôº^Ðæd´€‡oG¤{S‰¬ćxã}›ŧ×Kǥĩ«žÕOEзÖdÖsƘѨ[’Û^Xr¢¼˜§xvěƵ`K”§ tÒ´Cvlo¸fzŨð¾NY´ı~ÉĔē…ßúLÃϖ_ÈÏ|]ÂÏFl”g`bšežž€n¾¢pU‚h~ƴ˶_‚r sĄ~cž”ƈ]|r c~`¼{À{ȒiJjz`îÀT¥Û³…]’u}›f…ïQl{skl“oNdŸjŸäËzDvčoQŠďHI¦rb“tHĔ~BmlRš—V_„ħTLnñH±’DžœL‘¼L˜ªl§Ťa¸ŒĚlK²€\\RòvDcÎJbt[¤€D@®hh~kt°ǾzÖ@¾ªdb„YhüóZ ň¶vHrľ\\ʗJuxAT|dmÀO„‹[ÃԋG·ĚąĐlŪÚpSJ¨ĸˆLvÞcPæķŨŽ®mАˆálŸwKhïgA¢ųƩޖ¤OȜm’°ŒK´"]],"encodeOffsets":[[[121722,32278]],[[119475,30423]],[[119168,35472]]]}},{"type":"Feature","id":"350000","properties":{"id":"350000","cp":[118.306239,26.075302],"name":"福建","childNum":18},"geometry":{"type":"MultiPolygon","coordinates":[["@@“zht´‡]"],["@@aj^~ĆG—©O"],["@@ed¨„C}}i"],["@@@vˆPGsQ"],["@@‰sBz‚ddW]Q"],["@@SލQ“{"],["@@NŽVucW"],["@@qptBAq"],["@@‰’¸[mu"],["@@Q\\pD]_"],["@@jSwUadpF"],["@@eXª~ƒ•"],["@@AjvFso"],["@@fT–›_Çí\\Ÿ™—v|ba¦jZÆy€°"],["@@IjJi"],["@@wJI€ˆxš«¼AoNe{M­"],["@@K‰±¡Óˆ”ČäeZ"],["@@k¡¹Eh~c®wBk‹UplÀ¡I•~Māe£bN¨gZý¡a±Öcp©PhžI”Ÿ¢Qq…ÇGj‹|¥U™ g[Ky¬ŏ–v@OpˆtÉEŸF„\\@ åA¬ˆV{Xģ‰ĐBy…cpě…¼³Ăp·¤ƒ¥o“hqqÚ¡ŅLsƒ^ᗞ§qlŸÀhH¨MCe»åÇGD¥zPO£čÙkJA¼ß–ėu›ĕeûҍiÁŧSW¥˜QŠûŗ½ùěcݧSùĩąSWó«íęACµ›eR—åǃRCÒÇZÍ¢‹ź±^dlsŒtjD¸•‚ZpužÔâÒH¾oLUêÃÔjjēò´ĄW‚ƛ…^Ñ¥‹ĦŸ@Çò–ŠmŒƒOw¡õyJ†yD}¢ďÑÈġfŠZd–a©º²z£šN–ƒjD°Ötj¶¬ZSÎ~¾c°¶Ðm˜x‚O¸¢Pl´žSL|¥žA†ȪĖM’ņIJg®áIJČĒü` ŽQF‡¬h|ÓJ@zµ |ê³È ¸UÖŬŬÀEttĸr‚]€˜ðŽM¤ĶIJHtÏ A’†žĬkvsq‡^aÎbvŒd–™fÊòSD€´Z^’xPsÞrv‹ƞŀ˜jJd×ŘÉ ®A–ΦĤd€xĆqAŒ†ZR”ÀMźŒnĊ»ŒİÐZ— YX–æJŠyĊ²ˆ·¶q§·–K@·{s‘Xãô«lŗ¶»o½E¡­«¢±¨Yˆ®Ø‹¶^A™vWĶGĒĢžPlzfˆļŽtàAvWYãšO_‡¤sD§ssČġ[kƤPX¦Ž`¶“ž®ˆBBvĪjv©šjx[L¥àï[F…¼ÍË»ğV`«•Ip™}ccÅĥZE‹ãoP…´B@ŠD—¸m±“z«Ƴ—¿å³BRضˆœWlâþäą`“]Z£Tc— ĹGµ¶H™m@_©—kŒ‰¾xĨ‡ôȉðX«½đCIbćqK³Á‹Äš¬OAwã»aLʼn‡ËĥW[“ÂGI—ÂNxij¤D¢ŽîĎÎB§°_JœGsƒ¥E@…¤uć…P‘å†cuMuw¢BI¿‡]zG¹guĮck\\_"]],"encodeOffsets":[[[123250,27563]],[[122541,27268]],[[123020,27189]],[[122916,27125]],[[122887,26845]],[[122808,26762]],[[122568,25912]],[[122778,26197]],[[122515,26757]],[[122816,26587]],[[123388,27005]],[[122450,26243]],[[122578,25962]],[[121255,25103]],[[120987,24903]],[[122339,25802]],[[121042,25093]],[[122439,26024]]]}},{"type":"Feature","id":"360000","properties":{"id":"360000","cp":[115.592151,27.676493],"name":"江西","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@ĢĨƐgÂMD~ņªe^\\^§„ý©j׍cZ†Ø¨zdÒa¶ˆlҍJŒìõ`oz÷@¤u޸´†ôęöY¼‰HČƶajlÞƩ¥éZ[”|h}^U Œ ¥p„ĄžƦO lt¸Æ €Q\\€ŠaÆ|CnÂOjt­ĚĤd’ÈŒF`’¶„@Ðë” ¦ōҞ¨Sêv†HĢûXD®…QgėWiØPÞìºr¤dž€NĠ¢l–•ĄtZoœCƞÔºCxrpĠV®Ê{f_Y`_ƒeq’’®Aot`@o‚DXfkp¨|Šs¬\\D‘ÄSfè©Hn¬…^DhÆyøJh“ØxĢĀLʈ„ƠPżċĄwȠ̦G®ǒĤäTŠÆ~ĦwŠ«|TF¡Šn€c³Ïå¹]ĉđxe{ÎӐ†vOEm°BƂĨİ|G’vz½ª´€H’àp”eJ݆Qšxn‹ÀŠW­žEµàXÅĪt¨ÃĖrÄwÀFÎ|ňÓMå¼ibµ¯»åDT±m[“r«_gŽmQu~¥V\\OkxtL E¢‹ƒ‘Ú^~ýê‹Pó–qo슱_Êw§ÑªåƗ⼋mĉŹ‹¿NQ“…YB‹ąrwģcÍ¥B•Ÿ­ŗÊcØiI—žƝĿuŒqtāwO]‘³YCñTeɕš‹caub͈]trlu€ī…B‘ПGsĵıN£ï—^ķqss¿FūūV՟·´Ç{éĈý‰ÿ›OEˆR_ŸđûIċâJh­ŅıN‘ȩĕB…¦K{Tk³¡OP·wn—µÏd¯}½TÍ«YiµÕsC¯„iM•¤™­•¦¯P|ÿUHv“he¥oFTu‰õ\\ŽOSs‹MòđƇiaºćXŸĊĵà·çhƃ÷ǜ{‘ígu^›đg’m[×zkKN‘¶Õ»lčÓ{XSƉv©_ÈëJbVk„ĔVÀ¤P¾ºÈMÖxlò~ªÚàGĂ¢B„±’ÌŒK˜y’áV‡¼Ã~­…`g›ŸsÙfI›Ƌlę¹e|–~udjˆuTlXµf`¿JdŠ[\\˜„L‚‘²"],"encodeOffsets":[[116689,26234]]}},{"type":"Feature","id":"370000","properties":{"id":"370000","cp":[118.000923,36.275807],"name":"山东","childNum":13},"geometry":{"type":"MultiPolygon","coordinates":[["@@Xjd]{K"],["@@itbFHy"],["@@HlGk"],["@@T‚ŒGŸy"],["@@K¬˜•‹U"],["@@WdXc"],["@@PtOs"],["@@•LnXhc"],["@@ppVƒu]Or"],["@@cdzAUa"],["@@udRhnCI‡"],["@@ˆoIƒpR„"],["@@Ľč{fzƤî’Kš–ÎMĮ]†—ZFˆ½Y]â£ph’™š¶¨râøÀ†ÎǨ¤^ºÄ”Gzˆ~grĚĜlĞÆ„LĆdž¢Îo¦–cv“Kb€gr°Wh”mZp ˆL]LºcU‰Æ­n”żĤÌǜbAnrOAœ´žȊcÀbƦUØrĆUÜøœĬƞ†š˜Ez„VL®öØBkŖÝĐ˹ŧ̄±ÀbÎɜnb²ĦhņBĖ›žįĦåXćì@L¯´ywƕCéõė ƿ¸‘lµ¾Z|†ZWyFYŸ¨Mf~C¿`€à_RÇzwƌfQnny´INoƬˆèôº|sT„JUš›‚L„îVj„ǎ¾Ē؍‚Dz²XPn±ŴPè¸ŔLƔÜƺ_T‘üÃĤBBċȉöA´fa„˜M¨{«M`‡¶d¡ô‰Ö°šmȰBÔjjŒ´PM|”c^d¤u•ƒ¤Û´Œä«ƢfPk¶Môlˆ]Lb„}su^ke{lC‘…M•rDŠÇ­]NÑFsmoõľH‰yGă{{çrnÓE‰‹ƕZGª¹Fj¢ïW…uøCǷ돡ąuhÛ¡^Kx•C`C\\bÅxì²ĝÝ¿_N‰īCȽĿåB¥¢·IŖÕy\\‡¹kx‡Ã£Č×GDyÕ¤ÁçFQ¡„KtŵƋ]CgÏAùSed‡cÚź—ŠuYfƒyMmhUWpSyGwMPqŀ—›Á¼zK›¶†G•­Y§Ëƒ@–´śÇµƕBmœ@Io‚g——Z¯u‹TMx}C‘‰VK‚ï{éƵP—™_K«™pÛÙqċtkkù]gŽ‹Tğwo•ɁsMõ³ă‡AN£™MRkmEʕč™ÛbMjÝGu…IZ™—GPģ‡ãħE[iµBEuŸDPԛ~ª¼ętŠœ]ŒûG§€¡QMsğNPŏįzs£Ug{đJĿļā³]ç«Qr~¥CƎÑ^n¶ÆéÎR~ݏY’I“] P‰umŝrƿ›‰›Iā‹[x‰edz‹L‘¯v¯s¬ÁY…~}…ťuٌg›ƋpÝĄ_ņī¶ÏSR´ÁP~ž¿Cyžċßdwk´Ss•X|t‰`Ä Èð€AªìÎT°¦Dd–€a^lĎDĶÚY°Ž`ĪŴǒˆ”àŠv\\ebŒZH„ŖR¬ŢƱùęO•ÑM­³FۃWp[ƒ"]],"encodeOffsets":[[[123806,39303]],[[123821,39266]],[[123742,39256]],[[123702,39203]],[[123649,39066]],[[123847,38933]],[[123580,38839]],[[123894,37288]],[[123043,36624]],[[123344,38676]],[[123522,38857]],[[123628,38858]],[[118260,36742]]]}},{"type":"Feature","id":"410000","properties":{"id":"410000","cp":[113.665412,33.757975],"name":"河南","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@•ýL™ùµP³swIÓxcŢĞð†´E®žÚPt†ĴXØx¶˜@«ŕŕQGƒ‹Yfa[şu“ßǩ™đš_X³ijÕčC]kbc•¥CS¯ëÍB©÷‹–³­Siˆ_}m˜YTtž³xlàcȂzÀD}ÂOQ³ÐTĨ¯†ƗòËŖ[hœł‹Ŧv~††}ÂZž«¤lPǕ£ªÝŴÅR§ØnhcŒtâk‡nύ­ľŹUÓÝdKuķ‡I§oTũÙďkęĆH¸ÓŒ\\ăŒ¿PcnS{wBIvɘĽ[GqµuŸŇôYgûƒZcaŽ©@½Õǽys¯}lgg@­C\\£as€IdÍuCQñ[L±ęk·‹ţb¨©kK—’»›KC²‘òGKmĨS`ƒ˜UQ™nk}AGē”sqaJ¥ĐGR‰ĎpCuÌy ã iMc”plk|tRk†ðœev~^‘´†¦ÜŽSí¿_iyjI|ȑ|¿_»d}qŸ^{“Ƈdă}Ÿtqµ`Ƴĕg}V¡om½fa™Ço³TTj¥„tĠ—Ry”K{ùÓjuµ{t}uËR‘iŸvGŠçJFjµŠÍyqΘàQÂFewixGw½Yŷpµú³XU›½ġy™łå‰kÚwZXˆ·l„¢Á¢K”zO„Λ΀jc¼htoDHr…|­J“½}JZ_¯iPq{tę½ĕ¦Zpĵø«kQ…Ťƒ]MÛfaQpě±ǽ¾]u­Fu‹÷nƒ™čįADp}AjmcEǒaª³o³ÆÍSƇĈÙDIzˑ赟^ˆKLœ—i—Þñ€[œƒaA²zz‰Ì÷Dœ|[šíijgf‚ÕÞd®|`ƒĆ~„oĠƑô³Ŋ‘D×°¯CsŠøÀ«ì‰UMhTº¨¸ǡîS–Ô„DruÂÇZ•ÖEŽ’vPZ„žW”~؋ÐtĄE¢¦Ðy¸bŠô´oŬ¬Ž²Ês~€€]®tªašpŎJ¨Öº„_ŠŔ–`’Ŗ^Ѝ\\Ĝu–”~m²Ƹ›¸fW‰ĦrƔ}Î^gjdfÔ¡J}\\n C˜¦þWxªJRÔŠu¬ĨĨmF†dM{\\d\\ŠYÊ¢ú@@¦ª²SŠÜsC–}fNècbpRmlØ^g„d¢aÒ¢CZˆZxvÆ¶N¿’¢T@€uCœ¬^ĊðÄn|žlGl’™Rjsp¢ED}€Fio~ÔNŽ‹„~zkĘHVsDzßjƒŬŒŠŢ`Pûàl¢˜\\ÀœEhŽİgÞē X¼Pk–„|m"],"encodeOffsets":[[118256,37017]]}},{"type":"Feature","id":"420000","properties":{"id":"420000","cp":[113.298572,30.684355],"name":"湖北","childNum":3},"geometry":{"type":"MultiPolygon","coordinates":[["@@AB‚"],["@@lskt"],["@@¾«}{ra®pîÃ\\™›{øCŠËyyB±„b\\›ò˜Ý˜jK›‡L ]ĎĽÌ’JyÚCƈćÎT´Å´pb©È‘dFin~BCo°BĎĚømvŒ®E^vǾ½Ĝ²Ro‚bÜeNŽ„^ĺ£R†¬lĶ÷YoĖ¥Ě¾|sOr°jY`~I”¾®I†{GqpCgyl{‡£œÍƒÍyPL“¡ƒ¡¸kW‡xYlÙæŠšŁĢzœ¾žV´W¶ùŸo¾ZHxjwfx„GNÁ•³Xéæl¶‰EièIH‰ u’jÌQ~v|sv¶Ôi|ú¢Fh˜Qsğ¦ƒSiŠBg™ÐE^ÁÐ{–čnOÂȞUÎóĔ†ÊēIJ}Z³½Mŧïeyp·uk³DsѨŸL“¶_œÅuèw»—€¡WqÜ]\\‘Ò§tƗcÕ¸ÕFÏǝĉăxŻČƟO‡ƒKÉġÿ×wg”÷IÅzCg†]m«ªGeçÃTC’«[‰t§{loWeC@ps_Bp‘­r‘„f_``Z|ei¡—oċMqow€¹DƝӛDYpûs•–‹Ykıǃ}s¥ç³[§ŸcYЧHK„«Qy‰]¢“wwö€¸ïx¼ņ¾Xv®ÇÀµRĠЋžHMž±cÏd„ƒǍũȅȷ±DSyúĝ£ŤĀàtÖÿï[îb\\}pĭÉI±Ñy…¿³x¯N‰o‰|¹H™ÏÛm‹júË~Tš•u˜ęjCöAwě¬R’đl¯ Ñb­‰ŇT†Ŀ_[Œ‘IčĄʿnM¦ğ\\É[T·™k¹œ©oĕ@A¾w•ya¥Y\\¥Âaz¯ãÁ¡k¥ne£Ûw†E©Êō¶˓uoj_Uƒ¡cF¹­[Wv“P©w—huÕyBF“ƒ`R‹qJUw\\i¡{jŸŸEPïÿ½fć…QÑÀQ{ž‚°‡fLԁ~wXg—ītêݾ–ĺ‘Hdˆ³fJd]‹HJ²…E€ƒoU¥†HhwQsƐ»Xmg±çve›]Dm͂PˆoCc¾‹_h”–høYrŊU¶eD°Č_N~øĹĚ·`z’]Äþp¼…äÌQŒv\\rCŒé¾TnkžŐڀÜa‡“¼ÝƆ̶Ûo…d…ĔňТJq’Pb ¾|JŒ¾fXŠƐîĨ_Z¯À}úƲ‹N_ĒĊ^„‘ĈaŐyp»CÇĕKŠšñL³ŠġMŒ²wrIÒŭxjb[œžn«øœ˜—æˆàƒ ^²­h¯Ú€ŐªÞ¸€Y²ĒVø}Ā^İ™´‚LŠÚm„¥ÀJÞ{JVŒųÞŃx×sxxƈē ģMř–ÚðòIf–Ċ“Œ\\Ʈ±ŒdʧĘD†vČ_Àæ~DŒċ´A®µ†¨ØLV¦êHÒ¤"]],"encodeOffsets":[[[113712,34000]],[[115612,30507]],[[113649,34054]]]}},{"type":"Feature","id":"430000","properties":{"id":"430000","cp":[111.782279,28.09409],"name":"湖南","childNum":3},"geometry":{"type":"MultiPolygon","coordinates":[["@@—n„FTs"],["@@ßÅÆá‰½ÔXr—†CO™“…ËR‘ïÿĩ­TooQyšÓ[‹ŅBE¬–ÎÓXa„į§Ã¸G °ITxp‰úxÚij¥Ïš–̾ŠedžÄ©ĸG…œàGh‚€M¤–Â_U}Ċ}¢pczfŠþg¤€”ÇòAV‘‹M"],["@@©K—ƒA·³CQ±Á«³BUŠƑ¹AŠtćOw™D]ŒJiØSm¯b£‘ylƒ›X…HËѱH•«–‘C^õľA–Å§¤É¥„ïyuǙuA¢^{ÌC´­¦ŷJ£^[†“ª¿‡ĕ~•Ƈ…•N… skóā‡¹¿€ï]ă~÷O§­@—Vm¡‹Qđ¦¢Ĥ{ºjԏŽŒª¥nf´•~ÕoŸž×Ûą‹MąıuZœmZcÒ IJβSÊDŽŶ¨ƚƒ’CÖŎªQؼrŭŽ­«}NÏürʬŒmjr€@ĘrTW ­SsdHzƓ^ÇÂyUi¯DÅYlŹu{hTœ}mĉ–¹¥ě‰Dÿë©ıÓ[Oº£ž“¥ót€ł¹MՄžƪƒ`Pš…Di–ÛUоÅ‌ìˆU’ñB“È£ýhe‰dy¡oċ€`pfmjP~‚kZa…ZsÐd°wj§ƒ@€Ĵ®w~^‚kÀÅKvNmX\\¨a“”сqvíó¿F„¤¡@ũÑVw}S@j}¾«pĂr–ªg àÀ²NJ¶¶Dô…K‚|^ª†Ž°LX¾ŴäPᜣEXd›”^¶›IJÞܓ~‘u¸ǔ˜Ž›MRhsR…e†`ÄofIÔ\\Ø i”ćymnú¨cj ¢»–GČìƊÿШXeĈ¾Oð Fi ¢|[jVxrIQŒ„_E”zAN¦zLU`œcªx”OTu RLÄ¢dV„i`p˔vŎµªÉžF~ƒØ€d¢ºgİàw¸Áb[¦Zb¦–z½xBĖ@ªpº›šlS¸Ö\\Ĕ[N¥ˀmĎă’J\\‹ŀ`€…ňSڊĖÁĐiO“Ĝ«BxDõĚiv—ž–S™Ì}iùŒžÜnšÐºGŠ{Šp°M´w†ÀÒzJ²ò¨ oTçüöoÛÿñŽőФ‚ùTz²CȆȸǎۃƑÐc°dPÎŸğ˶[Ƚu¯½WM¡­Éž“’B·rížnZŸÒ `‡¨GA¾\\pē˜XhÆRC­üWGġu…T靧Ŏѝ©ò³I±³}_‘‹EÃħg®ęisÁPDmÅ{‰b[Rşs·€kPŸŽƥƒóRo”O‹ŸVŸ~]{g\\“êYƪ¦kÝbiċƵŠGZ»Ěõ…ó·³vŝž£ø@pyö_‹ëŽIkѵ‡bcѧy…×dY؎ªiþž¨ƒ[]f]Ņ©C}ÁN‡»hĻħƏ’ĩ"]],"encodeOffsets":[[[115640,30489]],[[112543,27312]],[[116690,26230]]]}},{"type":"Feature","id":"440000","properties":{"id":"440000","cp":[113.280637,23.125178],"name":"广东","childNum":24},"geometry":{"type":"MultiPolygon","coordinates":[["@@QdˆAua"],["@@ƒlxDLo"],["@@sbhNLo"],["@@Ă āŸ"],["@@WltO[["],["@@Krœ]S"],["@@e„„I]y"],["@@I|„Mym"],["@@ƒÛ³LSŒž¼Y"],["@@nvºB–ëui©`¾"],["@@zdšÛ›Jw®"],["@@†°…¯"],["@@a yAª¸ËJIx،@€ĀHAmßV¡o•fu•o"],["@@šs‰ŗÃÔėAƁ›ZšÄ ~°ČP‚‹äh"],["@@‹¶Ý’Ì‚vmĞh­ı‡Q"],["@@HœŠdSjĒ¢D}war…“u«ZqadYM"],["@@elŒ\\LqqU"],["@@~rMo\\"],["@@f„^ƒC"],["@@øPªoj÷ÍÝħXČx”°Q¨ıXNv"],["@@gÇƳˆŽˆ”oˆŠˆ[~tly"],["@@E–ÆC¿‘"],["@@OŽP"],["@@w‹†đóg‰™ĝ—[³‹¡VÙæÅöM̳¹pÁaËýý©D©Ü“JŹƕģGą¤{Ùū…ǘO²«BƱéA—Ò‰ĥ‡¡«BhlmtÃPµyU¯uc“d·w_bŝcīímGOŽ|KP’ȏ‡ŹãŝIŕŭŕ@Óoo¿ē‹±ß}Ž…ŭ‚ŸIJWÈCőâUâǙI›ğʼn©I›ijEׅÁ”³Aó›wXJþ±ÌŒÜӔĨ£L]ĈÙƺZǾĆĖMĸĤfŒÎĵl•ŨnȈ‘ĐtF”Š–FĤ–‚êk¶œ^k°f¶gŠŽœ}®Fa˜f`vXŲxl˜„¦–ÔÁ²¬ÐŸ¦pqÊ̲ˆi€XŸØRDÎ}†Ä@ZĠ’s„x®AR~®ETtĄZ†–ƈfŠŠHâÒÐA†µ\\S¸„^wĖkRzŠalŽŜ|E¨ÈNĀňZTŒ’pBh£\\ŒĎƀuXĖtKL–¶G|Ž»ĺEļĞ~ÜĢÛĊrˆO˜Ùîvd]nˆ¬VœÊĜ°R֟pM††–‚ƂªFbwžEÀˆ˜©Œž\\…¤]ŸI®¥D³|ˎ]CöAŤ¦…æ’´¥¸Lv¼€•¢ĽBaô–F~—š®²GÌҐEY„„œzk¤’°ahlV՞I^‹šCxĈPŽsB‰ƒºV‰¸@¾ªR²ĨN]´_eavSi‡vc•}p}Đ¼ƌkJœÚe thœ†_¸ ºx±ò_xN›Ë‹²‘@ƒă¡ßH©Ùñ}wkNÕ¹ÇO½¿£ĕ]ly_WìIžÇª`ŠuTÅxYĒÖ¼k֞’µ‚MžjJÚwn\\h‘œĒv]îh|’È›Ƅøègž¸Ķß ĉĈWb¹ƀdéƌNTtP[ŠöSvrCZžžaGuœbo´ŖÒÇА~¡zCI…özx¢„Pn‹•‰Èñ @ŒĥÒ¦†]ƞŠV}³ăĔñiiÄÓVépKG½Ä‘ÓávYo–C·sit‹iaÀy„ŧΡÈYDÑům}‰ý|m[węõĉZÅxUO}÷N¹³ĉo_qtă“qwµŁYلǝŕ¹tïÛUïmRCº…ˆĭ|µ›ÕÊK™½R‘ē ó]‘–GªęAx–»HO£|ām‡¡diď×YïYWªʼnOeÚtĐ«zđ¹T…ā‡úE™á²\\‹ķÍ}jYàÙÆſ¿Çdğ·ùTßÇţʄ¡XgWÀLJğ·¿ÃˆOj YÇ÷Qě‹i"]],"encodeOffsets":[[[117381,22988]],[[116552,22934]],[[116790,22617]],[[116973,22545]],[[116444,22536]],[[116931,22515]],[[116496,22490]],[[116453,22449]],[[113301,21439]],[[118726,21604]],[[118709,21486]],[[113210,20816]],[[115482,22082]],[[113171,21585]],[[113199,21590]],[[115232,22102]],[[115739,22373]],[[115134,22184]],[[113056,21175]],[[119573,21271]],[[119957,24020]],[[115859,22356]],[[116561,22649]],[[116285,22746]]]}},{"type":"Feature","id":"450000","properties":{"id":"450000","cp":[108.320004,22.82402],"name":"广西","childNum":2},"geometry":{"type":"MultiPolygon","coordinates":[["@@H– TQ§•A"],["@@ĨʪƒLƒƊDÎĹĐCǦė¸zÚGn£¾›rªŀÜt¬@֛ڈSx~øOŒ˜ŶÐÂæȠ\\„ÈÜObĖw^oބLf¬°bI lTØB̈F£Ć¹gñĤaY“t¿¤VSñœK¸¤nM†¼‚JE±„½¸šŠño‹ÜCƆæĪ^ŠĚQÖ¦^‡ˆˆf´Q†üÜʝz¯šlzUĺš@쇀p¶n]sxtx¶@„~ÒĂJb©gk‚{°‚~c°`ԙ¬rV\\“la¼¤ôá`¯¹LC†ÆbŒxEræO‚v[H­˜„[~|aB£ÖsºdAĐzNÂðsŽÞƔ…Ĥªbƒ–ab`ho¡³F«èVloޤ™ÔRzpp®SŽĪº¨ÖƒºN…ij„d`’a”¦¤F³ºDÎńĀìŠCžĜº¦Ċ•~nS›|gźvZkCÆj°zVÈÁƔ]LÊFZg…čP­kini«‹qǀcz͔Y®¬Ů»qR×ō©DՄ‘§ƙǃŵTÉĩ±ŸıdÑnYY›IJvNĆÌØÜ Öp–}e³¦m‹©iÓ|¹Ÿħņ›|ª¦QF¢Â¬ʖovg¿em‡^ucà÷gՎuŒíÙćĝ}FϼĹ{µHK•sLSđƃr‹č¤[Ag‘oS‹ŇYMÿ§Ç{Fśbky‰lQxĕƒ]T·¶[B…ÑÏGáşşƇe€…•ăYSs­FQ}­Bƒw‘tYğÃ@~…C̀Q ×W‡j˱rÉ¥oÏ ±«ÓÂ¥•ƒ€k—ŽwWűŒmcih³K›~‰µh¯e]lµ›él•E쉕E“ďs‡’mǖŧē`ãògK_ÛsUʝ“ćğ¶hŒöŒO¤Ǜn³Žc‘`¡y‹¦C‘ez€YŠwa™–‘[ďĵűMę§]X˜Î_‚훘Û]é’ÛUćİÕBƣ±…dƒy¹T^džûÅÑŦ·‡PĻþÙ`K€¦˜…¢ÍeœĥR¿Œ³£[~Œäu¼dl‰t‚†W¸oRM¢ď\\zœ}Æzdvň–{ÎXF¶°Â_„ÒÂÏL©Ö•TmuŸ¼ãl‰›īkiqéfA„·Êµ\\őDc¥ÝF“y›Ôć˜c€űH_hL܋êĺШc}rn`½„Ì@¸¶ªVLŒŠhŒ‹\\•Ţĺk~ŽĠið°|gŒtTĭĸ^x‘vK˜VGréAé‘bUu›MJ‰VÃO¡…qĂXËS‰ģãlýàŸ_ju‡YÛÒB†œG^˜é֊¶§ŽƒEG”ÅzěƒƯ¤Ek‡N[kdåucé¬dnYpAyČ{`]þ¯T’bÜÈk‚¡Ġ•vŒàh„ÂƄ¢Jî¶²"]],"encodeOffsets":[[[111707,21520]],[[107619,25527]]]}},{"type":"Feature","id":"460000","properties":{"id":"460000","cp":[109.83119,19.031971],"name":"海南","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@š¦Ŝil¢”XƦ‘ƞò–ïè§ŞCêɕrŧůÇąĻõ™·ĉ³œ̅kÇm@ċȧƒŧĥ‰Ľʉ­ƅſ“ȓÒ˦ŝE}ºƑ[ÍĜȋ gÎfǐÏĤ¨êƺ\\Ɔ¸ĠĎvʄȀœÐ¾jNðĀÒRŒšZdž™zÐŘΰH¨Ƣb²_Ġ "],"encodeOffsets":[[112750,20508]]}},{"type":"Feature","id":"510000","properties":{"id":"510000","cp":[104.065735,30.659462],"name":"四川","childNum":2},"geometry":{"type":"MultiPolygon","coordinates":[["@@LqKr"],["@@Š[ĻéV£ž_ţġñpG •réÏ·~ąSfy×͂·ºſƽiÍıƣıĻmHH}siaX@iǰÁÃ×t«ƒ­Tƒ¤J–JJŒyJ•ÈŠ`Ohߦ¡uËhIyCjmÿw…ZG……Ti‹SˆsO‰žB²ŸfNmsPaˆ{M{ŠõE‘^Hj}gYpaeuž¯‘oáwHjÁ½M¡pM“–uå‡mni{fk”\\oƒÎqCw†EZ¼K›ĝŠƒAy{m÷L‡wO×SimRI¯rK™õBS«sFe‡]fµ¢óY_ÆPRcue°Cbo׌bd£ŌIHgtrnyPt¦foaXďx›lBowz‹_{ÊéWiêE„GhܸºuFĈIxf®Ž•Y½ĀǙ]¤EyŸF²ċ’w¸¿@g¢§RGv»–áŸW`ÃĵJwi]t¥wO­½a[׈]`Ãi­üL€¦LabbTÀå’c}Íh™Æhˆ‹®BH€î|Ék­¤S†y£„ia©taį·Ɖ`ō¥Uh“O…ƒĝLk}©Fos‰´›Jm„µlŁu—…ø–nÑJWΪ–YÀïAetTžŅ‚ӍG™Ë«bo‰{ıwodƟ½ƒžOġܑµxàNÖ¾P²§HKv¾–]|•B‡ÆåoZ`¡Ø`ÀmºĠ~ÌЧnDž¿¤]wğ@sƒ‰rğu‰~‘Io”[é±¹ ¿žſđӉ@q‹gˆ¹zƱřaí°KtǤV»Ã[ĩǭƑ^ÇÓ@ỗs›Zϕ‹œÅĭ€Ƌ•ěpwDóÖሯneQˌq·•GCœýS]xŸ·ý‹q³•O՜Œ¶Qzßti{ř‰áÍÇWŝŭñzÇW‹pç¿JŒ™‚Xœĩè½cŒF–ÂLiVjx}\\N†ŇĖ¥Ge–“JA¼ÄHfÈu~¸Æ«dE³ÉMA|b˜Ò…˜ćhG¬CM‚õŠ„ƤąAvƒüV€éŀ‰_V̳ĐwQj´·ZeÈÁ¨X´Æ¡Qu·»Ÿ“˜ÕZ³ġqDo‰y`L¬gdp°şŠp¦ėìÅĮZްIä”h‚‘ˆzŠĵœf²å ›ĚрKp‹IN|‹„Ñz]ń……·FU×é»R³™MƒÉ»GM«€ki€™ér™}Ã`¹ăÞmȝnÁîRǀ³ĜoİzŔwǶVÚ£À]ɜ»ĆlƂ²Ġ…þTº·àUȞÏʦ¶†I’«dĽĢdĬ¿–»Ĕ׊h\\c¬†ä²GêëĤł¥ÀǿżÃÆMº}BÕĢyFVvw–ˆxBèĻĒ©Ĉ“tCĢɽŠȣ¦āæ·HĽî“ôNԓ~^¤Ɗœu„œ^s¼{TA¼ø°¢İªDè¾Ň¶ÝJ‘®Z´ğ~Sn|ªWÚ©òzPOȸ‚bð¢|‹øĞŠŒœŒQìÛÐ@Ğ™ǎRS¤Á§d…i“´ezÝúØã]Hq„kIŸþËQǦÃsǤ[E¬ÉŪÍxXƒ·ÖƁİlƞ¹ª¹|XÊwn‘ÆƄmÀêErĒtD®ċæcQƒ”E®³^ĭ¥©l}äQto˜ŖÜqƎkµ–„ªÔĻĴ¡@Ċ°B²Èw^^RsºT£ڿœQP‘JvÄz„^Đ¹Æ¯fLà´GC²‘dt˜­ĀRt¼¤ĦOðğfÔðDŨŁĞƘïžPȆ®âbMüÀXZ ¸£@Ś›»»QÉ­™]d“sÖ×_͖_ÌêŮPrĔĐÕGĂeZÜîĘqBhtO ¤tE[h|Y‹Ô‚ZśÎs´xº±UŒ’ñˆt|O’ĩĠºNbgþŠJy^dÂY Į„]Řz¦gC‚³€R`Šz’¢AjŒ¸CL„¤RÆ»@­Ŏk\\Ç´£YW}z@Z}‰Ã¶“oû¶]´^N‡Ò}èN‚ª–P˜Íy¹`S°´†ATe€VamdUĐwʄvĮÕ\\ƒu‹Æŗ¨Yp¹àZÂm™Wh{á„}WØǍ•Éüw™ga§áCNęÎ[ĀÕĪgÖɪX˜øx¬½Ů¦¦[€—„NΆL€ÜUÖ´òrÙŠxR^–†J˜k„ijnDX{Uƒ~ET{ļº¦PZc”jF²Ė@Žp˜g€ˆ¨“B{ƒu¨ŦyhoÚD®¯¢˜ WòàFΤ¨GDäz¦kŮPœġq˚¥À]€Ÿ˜eŽâÚ´ªKxī„Pˆ—Ö|æ[xäJÞĥ‚s’NÖ½ž€I†¬nĨY´®Ð—ƐŠ€mD™ŝuäđđEb…e’e_™v¡}ìęNJē}q”É埁T¯µRs¡M@}ůa†a­¯wvƉåZwž\\Z{åû^›"]],"encodeOffsets":[[[108815,30935]],[[110617,31811]]]}},{"type":"Feature","id":"520000","properties":{"id":"520000","cp":[106.713478,26.578343],"name":"贵州","childNum":3},"geometry":{"type":"MultiPolygon","coordinates":[["@@†G\\†lY£‘in"],["@@q‚|ˆ‚mc¯tχVSÎ"],["@@hÑ£Is‡NgßH†›HªķÃh_¹ƒ¡ĝħń¦uيùŽgS¯JHŸ|sÝÅtÁïyMDč»eÕtA¤{b\\}—ƒG®u\\åPFq‹wÅaD…žK°ºâ_£ùbµ”mÁ‹ÛœĹM[q|hlaªāI}тƒµ@swtwm^oµˆD鼊yV™ky°ÉžûÛR…³‚‡eˆ‡¥]RՋěħ[ƅåÛDpŒ”J„iV™™‰ÂF²I…»mN·£›LbÒYb—WsÀbŽ™pki™TZĄă¶HŒq`……ĥ_JŸ¯ae«ƒKpÝx]aĕÛPƒÇȟ[ÁåŵÏő—÷Pw}‡TœÙ@Õs«ĿÛq©½œm¤ÙH·yǥĘĉBµĨÕnđ]K„©„œá‹ŸG纍§Õßg‡ǗĦTèƤƺ{¶ÉHÎd¾ŚÊ·OÐjXWrãLyzÉAL¾ę¢bĶėy_qMĔąro¼hĊžw¶øV¤w”²Ĉ]ʚKx|`ź¦ÂÈdr„cȁbe¸›`I¼čTF´¼Óýȃr¹ÍJ©k_șl³´_pН`oÒh޶pa‚^ÓĔ}D»^Xyœ`d˜[Kv…JPhèhCrĂĚÂ^Êƌ wˆZL­Ġ£šÁbrzOIl’MM”ĪŐžËr×ÎeŦŽtw|Œ¢mKjSǘňĂStÎŦEtqFT†¾†E쬬ôxÌO¢Ÿ KгŀºäY†„”PVgŎ¦Ŋm޼VZwVlŒ„z¤…ž£Tl®ctĽÚó{G­A‡ŒÇgeš~Αd¿æaSba¥KKûj®_ć^\\ؾbP®¦x^sxjĶI_Ä X‚⼕Hu¨Qh¡À@Ëô}ޱžGNìĎlT¸ˆ…`V~R°tbÕĊ`¸úÛtπFDu€[ƒMfqGH·¥yA‰ztMFe|R‚_Gk†ChZeÚ°to˜v`x‹b„ŒDnÐ{E}šZ˜è€x—†NEފREn˜[Pv@{~rĆAB§‚EO¿|UZ~ì„Uf¨J²ĂÝÆ€‚sª–B`„s¶œfvö¦ŠÕ~dÔq¨¸º»uù[[§´sb¤¢zþFœ¢Æ…Àhˆ™ÂˆW\\ıŽËI݊o±ĭŠ£þˆÊs}¡R]ŒěƒD‚g´VG¢‚j±®è†ºÃmpU[Á›‘Œëº°r›ÜbNu¸}Žº¼‡`ni”ºÔXĄ¤¼Ôdaµ€Á_À…†ftQQgœR—‘·Ǔ’v”}Ýלĵ]µœ“Wc¤F²›OĩųãW½¯K‚©…]€{†LóµCIµ±Mß¿hŸ•©āq¬o‚½ž~@i~TUxŪÒ¢@ƒ£ÀEîôruń‚”“‚b[§nWuMÆLl¿]x}ij­€½"]],"encodeOffsets":[[[112158,27383]],[[112105,27474]],[[112095,27476]]]}},{"type":"Feature","id":"530000","properties":{"id":"530000","cp":[101.512251,24.740609],"name":"云南","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@[„ùx½}ÑRH‘YīĺûsÍn‘iEoã½Ya²ė{c¬ĝg•ĂsA•ØÅwď‚õzFjw}—«Dx¿}UũlŸê™@•HÅ­F‰¨ÇoJ´Ónũuą¡Ã¢pÒŌ“Ø TF²‚xa²ËX€‚cʋlHîAßËŁkŻƑŷÉ©h™W­æßU‡“Ës¡¦}•teèÆ¶StǀÇ}Fd£j‹ĈZĆÆ‹¤T‚č\\Dƒ}O÷š£Uˆ§~ŃG™‚åŃDĝ¸œTsd¶¶Bªš¤u¢ŌĎo~t¾ÍŶÒtD¦Ú„iôö‰€z›ØX²ghįh½Û±¯€ÿm·zR¦Ɵ`ªŊÃh¢rOԍ´£Ym¼èêf¯ŪĽn„†cÚbŒw\\zlvWžªâˆ ¦g–mĿBş£¢ƹřbĥkǫßeeZkÙIKueT»sVesb‘aĕ ¶®dNœĄÄpªyސ¼—„³BE˜®l‡ŽGœŭCœǶwêżĔÂe„pÍÀQƞpC„–¼ŲÈ­AÎô¶R„ä’Q^Øu¬°š_Èôc´¹ò¨P΢hlϦ´Ħ“Æ´sâDŽŲPnÊD^¯°’Upv†}®BP̪–jǬx–Söwlfòªv€qĸ|`H€­viļ€ndĜ­Ćhň•‚em·FyށqóžSᝑ³X_ĞçêtryvL¤§z„¦c¦¥jnŞk˜ˆlD¤øz½ĜàžĂŧMÅ|áƆàÊcðÂF܎‚áŢ¥\\\\º™İøÒÐJĴ‡„îD¦zK²ǏÎEh~’CD­hMn^ÌöÄ©ČZÀžaü„fɭyœpį´ěFűk]Ôě¢qlÅĆÙa¶~Äqššê€ljN¬¼H„ÊšNQ´ê¼VظE††^ŃÒyŒƒM{ŒJLoÒœęæŸe±Ķ›y‰’‡gã“¯JYÆĭĘëo¥Š‰o¯hcK«z_pŠrC´ĢÖY”—¼ v¸¢RŽÅW³Â§fǸYi³xR´ďUˊ`êĿU„û€uĆBƒƣö‰N€DH«Ĉg†——Ñ‚aB{ÊNF´¬c·Åv}eÇÃGB»”If•¦HňĕM…~[iwjUÁKE•Ž‹¾dĪçW›šI‹èÀŒoÈXòyŞŮÈXâÎŚŠj|àsRy‹µÖ›–Pr´þŒ ¸^wþTDŔ–Hr¸‹žRÌmf‡żÕâCôox–ĜƌÆĮŒ›Ð–œY˜tâŦÔ@]ÈǮƒ\\μģUsȯLbîƲŚºyh‡rŒŠ@ĒԝƀŸÀ²º\\êp“’JŠ}ĠvŠqt„Ġ@^xÀ£È†¨mËÏğ}n¹_¿¢×Y_æpˆÅ–A^{½•Lu¨GO±Õ½ßM¶w’ÁĢۂP‚›Ƣ¼pcIJxŠ|ap̬HšÐŒŊSfsðBZ¿©“XÏÒK•k†÷Eû¿‰S…rEFsÕūk”óVǥʼniTL‚¡n{‹uxţÏh™ôŝ¬ğōN“‘NJkyPaq™Âğ¤K®‡YŸxÉƋÁ]āęDqçgOg†ILu—\\_gz—]W¼ž~CÔē]bµogpў_oď`´³Țkl`IªºÎȄqÔþž»E³ĎSJ»œ_f·‚adÇqƒÇc¥Á_Źw{™L^ɱćx“U£µ÷xgĉp»ĆqNē`rĘzaĵĚ¡K½ÊBzyäKXqiWPÏɸ½řÍcÊG|µƕƣG˛÷Ÿk°_^ý|_zċBZocmø¯hhcæ\\lˆMFlư£Ĝ„ÆyH“„F¨‰µêÕ]—›HA…àӄ^it `þßäkŠĤÎT~Wlÿ¨„ÔPzUC–NVv [jâôDôď[}ž‰z¿–msSh‹¯{jïğl}šĹ[–őŒ‰gK‹©U·µË@¾ƒm_~q¡f¹…ÅË^»‘f³ø}Q•„¡Ö˳gͱ^ǁ…\\ëÃA_—¿bW›Ï[¶ƛ鏝£F{īZgm@|kHǭƁć¦UĔťƒ×ë}ǝƒeďºȡȘÏíBə£āĘPªij¶“ʼnÿ‡y©n‰ď£G¹¡I›Š±LÉĺÑdĉ܇W¥˜‰}g˜Á†{aqÃ¥aŠıęÏZ—ï`"],"encodeOffsets":[[104636,22969]]}},{"type":"Feature","id":"540000","properties":{"id":"540000","cp":[89.132212,30.860361],"name":"西藏","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@hžľxŽŖ‰xƒÒVކºÅâAĪÝȆµę¯Ňa±r_w~uSÕň‘qOj]ɄQ…£Z……UDûoY’»©M[‹L¼qãË{V͕çWViŽ]ë©Ä÷àyƛh›ÚU°ŒŒa”d„cQƒ~Mx¥™cc¡ÙaSyF—ցk­ŒuRýq¿Ôµ•QĽ³aG{¿FµëªéĜÿª@¬·–K‰·àariĕĀ«V»Ŷ™Ĵū˜gèLǴŇƶaf‹tŒèBŚ£^Šâ†ǐÝ®–šM¦ÁǞÿ¬LhŸŽJ¾óƾƺcxw‹f]Y…´ƒ¦|œQLn°aœdĊ…œ\\¨o’œǀÍŎœ´ĩĀd`tÊQŞŕ|‚¨C^©œĈ¦„¦ÎJĊ{ŽëĎjª²rЉšl`¼Ą[t|¦St辉PŒÜK¸€d˜Ƅı]s¤—î_v¹ÎVòŦj˜£Əsc—¬_Ğ´|٘¦Avަw`ăaÝaa­¢e¤ı²©ªSªšÈMĄwžÉØŔì@T‘¤—Ę™\\õª@”þo´­xA s”ÂtŎKzó´ÇĊµ¢rž^nĊ­Æ¬×üGž¢‚³ {âĊ]š™G‚~bÀgVjzlhǶf€žOšfdЉªB]pj„•TO–tĊ‚n¤}®¦ƒČ¥d¢¼»ddš”Y¼Žt—¢eȤJ¤}Ǿ¡°§¤AГlc@ĝ”sªćļđAç‡wx•UuzEÖġ~AN¹ÄÅȀݦ¿ģŁéì±H…ãd«g[؉¼ēÀ•cīľġ¬cJ‘µ…ÐʥVȝ¸ßS¹†ý±ğkƁ¼ą^ɛ¤Ûÿ‰b[}¬ōõÃ]ËNm®g@•Bg}ÍF±ǐyL¥íCˆƒIij€Ï÷њį[¹¦[⚍EÛïÁÉdƅß{âNÆāŨߝ¾ě÷yC£‡k­´ÓH@¹†TZ¥¢įƒ·ÌAЧ®—Zc…v½ŸZ­¹|ŕWZqgW“|ieZÅYVӁqdq•bc²R@†c‡¥Rã»Ge†ŸeƃīQ•}J[ғK…¬Ə|o’ėjġĠÑN¡ð¯EBčnwôɍėªƒ²•CλŹġǝʅįĭạ̃ūȹ]ΓͧgšsgȽóϧµǛ†ęgſ¶ҍć`ĘąŌJޚä¤rÅň¥ÖÁUětęuůÞiĊÄÀ\\Æs¦ÓRb|Â^řÌkÄŷ¶½÷‡f±iMݑ›‰@ĥ°G¬ÃM¥n£Øą‚ğ¯ß”§aëbéüÑOčœk£{\\‘eµª×M‘šÉfm«Ƒ{Å׃Gŏǩãy³©WÑăû‚··‘Q—òı}¯ã‰I•éÕÂZ¨īès¶ZÈsŽæĔTŘvŽgÌsN@îá¾ó@‰˜ÙwU±ÉT廣TđŸWxq¹Zo‘b‹s[׌¯cĩv‡Œėŧ³BM|¹k‰ªħ—¥TzNYnݍßpęrñĠĉRS~½ŠěVVе‚õ‡«ŒM££µB•ĉ¥áºae~³AuĐh`Ü³ç@BۘïĿa©|z²Ý¼D”£à貋ŸƒIƒû›I ā€óK¥}rÝ_Á´éMaň¨€~ªSĈ½Ž½KÙóĿeƃÆBŽ·¬ën×W|Uº}LJrƳ˜lŒµ`bÔ`QˆˆÐÓ@s¬ñIŒÍ@ûws¡åQÑßÁ`ŋĴ{Ī“T•ÚÅTSij‚‹Yo|Ç[ǾµMW¢ĭiÕØ¿@˜šMh…pÕ]j†éò¿OƇĆƇp€êĉâlØw–ěsˆǩ‚ĵ¸c…bU¹ř¨WavquSMzeo_^gsÏ·¥Ó@~¯¿RiīB™Š\\”qTGªÇĜçPoŠÿfñòą¦óQīÈáP•œābß{ƒZŗĸIæÅ„hnszÁCËìñšÏ·ąĚÝUm®ó­L·ăU›Èíoù´Êj°ŁŤ_uµ^‘°Œìǖ@tĶĒ¡Æ‡M³Ģ«˜İĨÅ®ğ†RŽāð“ggheÆ¢z‚Ê©Ô\\°ÝĎz~ź¤Pn–MĪÖB£Ÿk™n鄧żćŠ˜ĆK„ǰ¼L¶è‰âz¨u¦¥LDĘz¬ýÎmĘd¾ß”Fz“hg²™Fy¦ĝ¤ċņbΛ@y‚Ąæm°NĮZRÖíŽJ²öLĸÒ¨Y®ƌÐV‰à˜tt_ڀÂyĠzž]Ţh€zĎ{†ĢX”ˆc|šÐqŽšfO¢¤ög‚ÌHNŽ„PKŖœŽ˜Uú´xx[xˆvĐCûŠìÖT¬¸^}Ìsòd´_އKgžLĴ…ÀBon|H@–Êx˜—¦BpŰˆŌ¿fµƌA¾zLjRxжF”œkĄźRzŀˆ~¶[”´Hnª–VƞuĒ­È¨ƎcƽÌm¸ÁÈM¦x͊ëÀxdžB’šú^´W†£–d„kɾĬpœw‚˂ØɦļĬIŚœÊ•n›Ŕa¸™~J°î”lɌxĤÊÈðhÌ®‚g˜T´øŽàCˆŽÀ^ªerrƘdž¢İP|Ė ŸWœªĦ^¶´ÂL„aT±üWƜ˜ǀRšŶUńšĖ[QhlLüA†‹Ü\\†qR›Ą©"],"encodeOffsets":[[90849,37210]]}},{"type":"Feature","id":"610000","properties":{"id":"610000","cp":[108.948024,34.263161],"name":"陕西","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@˜p¢—ȮµšûG™Ħ}Ħšðǚ¶òƄ€jɂz°{ºØkÈęâ¦jª‚Bg‚\\œċ°s¬Ž’]jžú ‚E”Ȍdž¬s„t‡”RˆÆdĠݎwܔ¸ôW¾ƮłÒ_{’Ìšû¼„jº¹¢GǪÒ¯ĘƒZ`ºŊƒecņąš~BÂgzpâēòYǠȰÌTΨÂWœ|fcŸă§uF—Œ@NŸ¢XLƒŠRMº[ğȣſï|¥J™kc`sʼnǷ’Y¹‹W@µ÷K…ãï³ÛIcñ·VȋڍÒķø©—þ¥ƒy‚ÓŸğęmWµÎumZyOŅƟĥÓ~sÑL¤µaŅY¦ocyZ{‰y c]{ŒTa©ƒ`U_Ěē£ωÊƍKù’K¶ȱÝƷ§{û»ÅÁȹÍéuij|¹cÑd‘ŠìUYƒŽO‘uF–ÕÈYvÁCqӃT•Ǣí§·S¹NgŠV¬ë÷Át‡°Dد’C´ʼnƒópģ}„ċcE˅FŸŸéGU¥×K…§­¶³B‹Č}C¿åċ`wġB·¤őcƭ²ő[Å^axwQO…ÿEËߌ•ĤNĔŸwƇˆÄŠńwĪ­Šo[„_KÓª³“ÙnK‰Çƒěœÿ]ď€ă_d©·©Ýŏ°Ù®g]±„Ÿ‡ß˜å›—¬÷m\\›iaǑkěX{¢|ZKlçhLt€Ňîŵ€œè[€É@ƉĄEœ‡tƇÏ˜³­ħZ«mJ…›×¾‘MtÝĦ£IwÄå\\Õ{‡˜ƒOwĬ©LÙ³ÙgBƕŀr̛ĢŭO¥lãyC§HÍ£ßEñŸX¡—­°ÙCgpťz‘ˆb`wI„vA|§”‡—hoĕ@E±“iYd¥OϹS|}F@¾oAO²{tfžÜ—¢Fǂ҈W²°BĤh^Wx{@„¬‚­F¸¡„ķn£P|ŸªĴ@^ĠĈæb–Ôc¶l˜Yi…–^Mi˜cϰÂ[ä€vï¶gv@À“Ĭ·lJ¸sn|¼u~a]’ÆÈtŌºJp’ƒþ£KKf~ЦUbyäIšĺãn‡Ô¿^­žŵMT–hĠܤko¼Ŏìąǜh`[tŒRd²IJ_œXPrɲ‰l‘‚XžiL§àƒ–¹ŽH˜°Ȧqº®QC—bA†„ŌJ¸ĕÚ³ĺ§ `d¨YjžiZvRĺ±öVKkjGȊĐePОZmļKÀ€‚[ŠŽ`ösìh†ïÎoĬdtKÞ{¬èÒÒBŒÔpIJÇĬJŊ¦±J«ˆY§‹@·pH€µàåVKe›pW†ftsAÅqC·¬ko«pHÆuK@oŸHĆۄķhx“e‘n›S³àǍrqƶRbzy€¸ËАl›¼EºpĤ¼Œx¼½~Ğ’”à@†ÚüdK^ˆmÌSj"],"encodeOffsets":[[110234,38774]]}},{"type":"Feature","id":"620000","properties":{"id":"620000","cp":[103.823557,36.058039],"name":"甘肃","childNum":2},"geometry":{"type":"MultiPolygon","coordinates":[["@@VuUv"],["@@ũ‹EĠtt~nkh`Q‰¦ÅÄÜdw˜Ab×ĠąJˆ¤DüègĺqBqœj°lI¡ĨÒ¤úSHbš‡ŠjΑBаaZˆ¢KJŽ’O[|A£žDx}Nì•HUnrk„ kp€¼Y kMJn[aG‚áÚÏ[½rc†}aQxOgsPMnUs‡nc‹Z…ž–sKúvA›t„Þġ’£®ĀYKdnFwš¢JE°”Latf`¼h¬we|€Æ‡šbj}GA€·~WŽ”—`†¢MC¤tL©IJ°qdf”O‚“bÞĬ¹ttu`^ZúE`Œ[@„Æsîz®¡’C„ƳƜG²“R‘¢R’m”fŽwĸg܃‚ą G@pzJM½mŠhVy¸uÈÔO±¨{LfæU¶ßGĂq\\ª¬‡²I‚¥IʼnÈīoı‹ÓÑAçÑ|«LÝcspīðÍg…të_õ‰\\ĉñLYnĝg’ŸRǡÁiHLlõUĹ²uQjYi§Z_c¨Ÿ´ĹĖÙ·ŋI…ƒaBD˜­R¹ȥr—¯G•ºß„K¨jWk’ɱŠOq›Wij\\a­‹Q\\sg_ĆǛōëp»£lğۀgS•ŶN®À]ˆÓäm™ĹãJaz¥V}‰Le¤L„ýo‘¹IsŋÅÇ^‘Žbz…³tmEÁ´aйcčecÇN•ĊãÁ\\蝗dNj•]j†—ZµkÓda•ćå]ğij@ ©O{¤ĸm¢ƒE·®ƒ«|@Xwg]A챝‡XǁÑdzªc›wQÚŝñsÕ³ÛV_ýƒ˜¥\\ů¥©¾÷w—Ž©WÕÊĩhÿÖÁRo¸V¬âDb¨šhûx–Ê×nj~Zâƒg|šXÁnßYoº§ZÅŘvŒ[„ĭÖʃuďxcVbnUSf…B¯³_Tzº—ΕO©çMÑ~Mˆ³]µ^püµ”ŠÄY~y@X~¤Z³€[Èōl@®Å¼£QKƒ·Di‹¡By‘ÿ‰Q_´D¥hŗyƒ^ŸĭÁZ]cIzý‰ah¹MĪğP‘s{ò‡‹‘²Vw¹t³Ŝˁ[ŽÑ}X\\gsFŸ£sPAgěp×ëfYHāďÖqēŭOÏë“dLü•\\iŒ”t^c®šRʺ¶—¢H°mˆ‘rYŸ£BŸ¹čIoľu¶uI]vģSQ{ƒUŻ”Å}QÂ|̋°ƅ¤ĩŪU ꡞÌZҞ\\v˜²PĔ»ƢNHƒĂyAmƂwVmž`”]ȏb•”H`‰Ì¢²ILvĜ—H®¤Dlt_„¢JJÄämèÔDëþgºƫ™”aʎÌrêYi~ ÎݤNpÀA¾Ĕ¼b…ð÷’Žˆ‡®‚”üs”zMzÖĖQdȨý†v§Tè|ªH’þa¸|šÐ ƒwKĢx¦ivr^ÿ ¸l öæfƟĴ·PJv}n\\h¹¶v†·À|\\ƁĚN´Ĝ€çèÁz]ġ¤²¨QÒŨTIl‡ªťØ}¼˗ƦvÄùØE‹’«Fï˛Iq”ōŒTvāÜŏ‚íÛߜÛV—j³âwGăÂíNOŠˆŠPìyV³ʼnĖýZso§HіiYw[߆\\X¦¥c]ÔƩÜ·«j‡ÐqvÁ¦m^ċ±R™¦΋ƈťĚgÀ»IïĨʗƮްƝ˜ĻþÍAƉſ±tÍEÕÞāNU͗¡\\ſčåÒʻĘm ƭÌŹöʥ’ëQ¤µ­ÇcƕªoIýˆ‰Iɐ_mkl³ă‰Ɠ¦j—¡Yz•Ňi–}Msßõ–īʋ —}ƒÁVmŸ_[n}eı­Uĥ¼‘ª•I{ΧDӜƻėoj‘qYhĹT©oūĶ£]ďxĩ‹ǑMĝ‰q`B´ƃ˺Ч—ç~™²ņj@”¥@đ´ί}ĥtPńǾV¬ufӃÉC‹tÓ̻‰…¹£G³€]ƖƾŎĪŪĘ̖¨ʈĢƂlɘ۪üºňUðǜȢƢż̌ȦǼ‚ĤŊɲĖ­Kq´ï¦—ºĒDzņɾªǀÞĈĂD†½ĄĎÌŗĞrôñnŽœN¼â¾ʄľԆ|DŽŽ֦ज़ȗlj̘̭ɺƅêgV̍ʆĠ·ÌĊv|ýĖÕWĊǎÞ´õ¼cÒÒBĢ͢UĜð͒s¨ňƃLĉÕÝ@ɛƯ÷¿Ľ­ĹeȏijëCȚDŲyê×Ŗyò¯ļcÂßY…tÁƤyAã˾J@ǝrý‹‰@¤…rz¸oP¹ɐÚyᐇHŸĀ[Jw…cVeȴϜ»ÈŽĖ}ƒŰŐèȭǢόĀƪÈŶë;Ñ̆ȤМľĮEŔ—ĹŊũ~ËUă{ŸĻƹɁύȩþĽvĽƓÉ@ē„ĽɲßǐƫʾǗĒpäWÐxnsÀ^ƆwW©¦cÅ¡Ji§vúF¶Ž¨c~c¼īŒeXǚ‹\\đ¾JŽwÀďksãA‹fÕ¦L}wa‚o”Z’‹D½†Ml«]eÒÅaɲáo½FõÛ]ĻÒ¡wYR£¢rvÓ®y®LF‹LzĈ„ôe]gx}•|KK}xklL]c¦£fRtív¦†PĤoH{tK"]],"encodeOffsets":[[[108619,36299]],[[108589,36341]]]}},{"type":"Feature","id":"630000","properties":{"id":"630000","cp":[96.778916,35.623178],"name":"青海","childNum":2},"geometry":{"type":"MultiPolygon","coordinates":[["@@InJm"],["@@CƒÆ½OŃĦsΰ~dz¦@@“Ņiš±è}ؘƄ˹A³r_ĞŠǒNΌĐw¤^ŬĵªpĺSZg’rpiƼĘԛ¨C|͖J’©Ħ»®VIJ~f\\m `Un„˜~ʌŸ•ĬàöNt•~ňjy–¢Zi˜Ɣ¥ĄŠk´nl`JʇŠJþ©pdƖ®È£¶ìRʦ‘źõƮËnŸʼėæÑƀĎ[‚˜¢VÎĂMÖÝÎF²sƊƀÎBļýƞ—¯ʘƭðħ¼Jh¿ŦęΌƇš¥²Q]Č¥nuÂÏriˆ¸¬ƪÛ^Ó¦d€¥[Wà…x\\ZŽjҕ¨GtpþYŊĕ´€zUO뇉P‰îMĄÁxH´á˜iÜUà›îÜՁĂÛSuŎ‹r“œJð̬EŒ‘FÁú×uÃÎkr“Ē{V}İ«O_ÌËĬ©ŽÓŧSRѱ§Ģ£^ÂyèçěM³Ƃę{[¸¿u…ºµ[gt£¸OƤĿéYŸõ·kŸq]juw¥Dĩƍ€õÇPéĽG‘ž©ã‡¤G…uȧþRcÕĕNy“yût“ˆ­‡ø‘†ï»a½ē¿BMoᣟÍj}éZËqbʍš“Ƭh¹ìÿÓAçãnIáI`ƒks£CG­ě˜Uy×Cy•…’Ÿ@¶ʡÊBnāzG„ơMē¼±O÷õJËĚăVŸĪũƆ£Œ¯{ËL½Ìzż“„VR|ĠTbuvJvµhĻĖH”Aëáa…­OÇðñęNw‡…œľ·L›mI±íĠĩPÉ×®ÿs—’cB³±JKßĊ«`…ađ»·QAmO’‘Vţéÿ¤¹SQt]]Çx€±¯A@ĉij¢Ó祖•ƒl¶ÅÛr—ŕspãRk~¦ª]Į­´“FR„åd­ČsCqđéFn¿Åƃm’Éx{W©ºƝºįkÕƂƑ¸wWūЩÈFž£\\tÈ¥ÄRÈýÌJ ƒlGr^×äùyÞ³fj”c†€¨£ÂZ|ǓMĝšÏ@ëÜőR‹›ĝ‰Œ÷¡{aïȷPu°ËXÙ{©TmĠ}Y³’­ÞIňµç½©C¡į÷¯B»|St»›]vƒųƒs»”}MÓ ÿʪƟǭA¡fs˜»PY¼c¡»¦c„ċ­¥£~msĉP•–Siƒ^o©A‰Šec‚™PeǵŽkg‚yUi¿h}aH™šĉ^|ᴟ¡HØûÅ«ĉ®]m€¡qĉ¶³ÈyôōLÁst“BŸ®wn±ă¥HSò뚣˜S’ë@לÊăxÇN©™©T±ª£IJ¡fb®ÞbŽb_Ą¥xu¥B—ž{łĝ³«`d˜Ɛt—¤ťiñžÍUuºí`£˜^tƃIJc—·ÛLO‹½Šsç¥Ts{ă\\_»™kϊ±q©čiìĉ|ÍIƒ¥ć¥›€]ª§D{ŝŖÉR_sÿc³Īō›ƿΑ›§p›[ĉ†›c¯bKm›R¥{³„Z†e^ŽŒwx¹dƽŽôIg §Mĕ ƹĴ¿—ǣÜ̓]‹Ý–]snåA{‹eŒƭ`ǻŊĿ\\ijŬű”YÂÿ¬jĖqŽßbЏ•L«¸©@ěĀ©ê¶ìÀEH|´bRľž–Ó¶rÀQþ‹vl®Õ‚E˜TzÜdb ˜hw¤{LR„ƒd“c‹b¯‹ÙVgœ‚ƜßzÃô쮍^jUèXΖ|UäÌ»rKŽ\\ŒªN‘¼pZCü†VY††¤ɃRi^rPҒTÖ}|br°qňb̰ªiƶGQ¾²„x¦PœmlŜ‘[Ĥ¡ΞsĦŸÔÏâ\\ªÚŒU\\f…¢N²§x|¤§„xĔsZPòʛ²SÐqF`ª„VƒÞŜĶƨVZŒÌL`ˆ¢dŐIqr\\oäõ–F礻Ŷ×h¹]Clـ\\¦ďÌį¬řtTӺƙgQÇÓHţĒ”´ÃbEÄlbʔC”|CˆŮˆk„Ʈ[ʼ¬ňœ´KŮÈΰÌζƶlð”ļA†TUvdTŠG†º̼ŠÔ€ŒsÊDԄveOg"]],"encodeOffsets":[[[105308,37219]],[[95370,40081]]]}},{"type":"Feature","id":"640000","properties":{"id":"640000","cp":[106.278179,37.26637],"name":"宁夏","childNum":2},"geometry":{"type":"MultiPolygon","coordinates":[["@@KëÀęĞ«OęȿȕŸı]ʼn¡åįÕÔ«Ǵõƪ™ĚQÐZhv K°›öqÀѐS[ÃÖHƖčË‡nL]ûc…Ùß@‚“ĝ‘¾}w»»‹oģF¹œ»kÌÏ·{zPƒ§B­¢íyÅt@ƒ@áš]Yv_ssģ¼i߁”ĻL¾ġsKD£¡N_…“˜X¸}B~Haiˆ™Åf{«x»ge_bs“KF¯¡Ix™mELcÿZ¤­Ģ‘ƒÝœsuBLù•t†ŒYdˆmVtNmtOPhRw~bd…¾qÐ\\âÙH\\bImlNZŸ»loƒŸqlVm–Gā§~QCw¤™{A\\‘PKŸNY‡¯bF‡kC¥’sk‹Šs_Ã\\ă«¢ħkJi¯r›rAhĹûç£CU‡ĕĊ_ԗBixÅُĄnªÑaM~ħpOu¥sîeQ¥¤^dkKwlL~{L~–hw^‚ófćƒKyEŒ­K­zuÔ¡qQ¤xZÑ¢^ļöܾEpž±âbÊÑÆ^fk¬…NC¾‘Œ“YpxbK~¥Že֎ŒäBlt¿Đx½I[ĒǙŒWž‹f»Ĭ}d§dµùEuj¨‚IÆ¢¥dXªƅx¿]mtÏwßR͌X¢͎vÆzƂZò®ǢÌʆCrâºMÞzžÆMҔÊÓŊZľ–r°Î®Ȉmª²ĈUªĚøºˆĮ¦ÌĘk„^FłĬhĚiĀ˾iİbjÕ"],["@@mfwěwMrŢªv@G‰"]],"encodeOffsets":[[[109366,40242]],[[108600,36303]]]}},{"type":"Feature","id":"650000","properties":{"id":"650000","cp":[85.617733,40.792818],"name":"新疆","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@QØĔ²X¨”~ǘBºjʐߨvK”ƔX¨vĊOžÃƒ·¢i@~c—‡ĝe_«”Eš“}QxgɪëÏÃ@sÅyXoŖ{ô«ŸuX…ê•Îf`œC‚¹ÂÿÐGĮÕĞXŪōŸMźÈƺQèĽôe|¿ƸJR¤ĘEjcUóº¯Ĩ_ŘÁMª÷Ð¥Oéȇ¿ÖğǤǷÂF҇zÉx[]­Ĥĝ‰œ¦EP}ûƥé¿İƷTėƫœŕƅ™ƱB»Đ±’ēO…¦E–•}‘`cȺrĦáŖuҞª«IJ‡πdƺÏØZƴwʄ¤ĖGЙǂZ̓èH¶}ÚZצʥĪï|ÇĦMŔ»İĝLj‹ì¥Βœba­¯¥ǕǚkĆŵĦɑĺƯxūД̵nơʃĽá½M»›òmqóŘĝč˾ăC…ćāƿÝɽ©DZŅ¹đ¥˜³ðLrÁ®ɱĕģʼnǻ̋ȥơŻǛȡVï¹Ň۩ûkɗġƁ§ʇė̕ĩũƽō^ƕŠUv£ƁQï“Ƶkŏ½ΉÃŭdzLқʻ«ƭ\\lƒ‡ŭD‡“{ʓDkaFÃÄa“³ŤđÔGRÈƚhSӹŚsİ«ĐË[¥ÚDkº^Øg¼ŵ¸£EÍö•€ůʼnT¡c_‡ËKY‹ƧUśĵ„݃U_©rETÏʜ±OñtYw獃{£¨uM³x½şL©Ùá[ÓÐĥ Νtģ¢\\‚ś’nkO›w¥±ƒT»ƷFɯàĩÞáB¹Æ…ÑUw„੍žĽw[“mG½Èå~‡Æ÷QyŠěCFmĭZī—ŵVÁ™ƿQƛ—ûXS²‰b½KϽĉS›©ŷXĕŸ{ŽĕK·¥Ɨcqq©f¿]‡ßDõU³h—­gËÇïģÉɋw“k¯í}I·šœbmœÉ–ř›īJɥĻˁ×xo›ɹī‡l•c…¤³Xù]‘™DžA¿w͉ì¥wÇN·ÂËnƾƍdǧđ®Ɲv•Um©³G\\“}µĿ‡QyŹl㓛µEw‰LJQ½yƋBe¶ŋÀů‡ož¥A—˜Éw@•{Gpm¿Aij†ŽKLhˆ³`ñcËtW‚±»ÕS‰ëüÿďD‡u\\wwwù³—V›LŕƒOMËGh£õP¡™er™Ïd{“‡ġWÁ…č|yšg^ğyÁzÙs`—s|ÉåªÇ}m¢Ń¨`x¥’ù^•}ƒÌ¥H«‰Yªƅ”Aйn~Ꝛf¤áÀz„gŠÇDIԝ´AňĀ҄¶ûEYospõD[{ù°]u›Jq•U•|Soċxţ[õÔĥkŋÞŭZ˺óYËüċrw €ÞkrťË¿XGÉbřaDü·Ē÷Aê[Ää€I®BÕИÞ_¢āĠpŠÛÄȉĖġDKwbm‡ÄNô‡ŠfœƫVÉvi†dz—H‘‹QµâFšù­Âœ³¦{YGžƒd¢ĚÜO „€{Ö¦ÞÍÀPŒ^b–ƾŠlŽ[„vt×ĈÍE˨¡Đ~´î¸ùÎh€uè`¸ŸHÕŔVºwĠââWò‡@{œÙNÝ´ə²ȕn{¿¥{l—÷eé^e’ďˆXj©î\\ªÑò˜Üìc\\üqˆÕ[Č¡xoÂċªbØ­Œø|€¶ȴZdÆÂšońéŒGš\\”¼C°ÌƁn´nxšÊOĨ’ہƴĸ¢¸òTxÊǪMīИÖŲÃɎOvˆʦƢ~FއRěò—¿ġ~åŊœú‰Nšžš¸qŽ’Ę[Ĕ¶ÂćnÒPĒÜvúĀÊbÖ{Äî¸~Ŕünp¤ÂH¾œĄYÒ©ÊfºmԈĘcDoĬMŬ’˜S¤„s²‚”ʘچžȂVŦ –ŽèW°ªB|IJXŔþÈJĦÆæFĚêŠYĂªĂ]øªŖNÞüA€’fɨJ€˜¯ÎrDDšĤ€`€mz\\„§~D¬{vJÂ˜«lµĂb–¤p€ŌŰNĄ¨ĊXW|ų ¿¾ɄĦƐMT”‡òP˜÷fØĶK¢ȝ˔Sô¹òEð­”`Ɩ½ǒÂň×äı–§ĤƝ§C~¡‚hlå‚ǺŦŞkâ’~}ŽFøàIJaĞ‚fƠ¥Ž„Ŕdž˜®U¸ˆźXœv¢aƆúŪtŠųƠjd•ƺŠƺÅìnrh\\ĺ¯äɝĦ]èpĄ¦´LƞĬŠ´ƤǬ˼Ēɸ¤rºǼ²¨zÌPðŀbþ¹ļD¢¹œ\\ĜÑŚŸ¶ZƄ³àjĨoâŠȴLʉȮŒĐ­ĚăŽÀêZǚŐ¤qȂ\\L¢ŌİfÆs|zºeªÙæ§΢{Ā´ƐÚ¬¨Ĵà²łhʺKÞºÖTŠiƢ¾ªì°`öøu®Ê¾ãØ"],"encodeOffsets":[[88824,50096]]}},{"type":"Feature","id":"110000","properties":{"id":"110000","cp":[116.405285,39.904989],"name":"北京","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@ĽOÁ›ûtŷmiÍt_H»Ĩ±d`й­{bw…Yr“³S]§§o¹€qGtm_Sŧ€“oa›‹FLg‘QN_•dV€@Zom_ć\\ߚc±x¯oœRcfe…£’o§ËgToÛJíĔóu…|wP¤™XnO¢ÉˆŦ¯rNÄā¤zâŖÈRpŢZŠœÚ{GŠrFt¦Òx§ø¹RóäV¤XdˆżâºWbwڍUd®bêņ¾‘jnŎGŃŶŠnzÚSeîĜZczî¾i]͜™QaúÍÔiþĩȨWĢ‹ü|Ėu[qb[swP@ÅğP¿{\\‡¥A¨Ï‘Ѩj¯ŠX\\¯œMK‘pA³[H…īu}}"],"encodeOffsets":[[120023,41045]]}},{"type":"Feature","id":"120000","properties":{"id":"120000","cp":[117.190182,39.125596],"name":"天津","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@ŬgX§Ü«E…¶Ḟ“¬O_™ïlÁg“z±AXe™µÄĵ{¶]gitgšIj·›¥îakS€‰¨ÐƎk}ĕ{gB—qGf{¿a†U^fI“ư‹³õ{YƒıëNĿžk©ïËZŏ‘R§òoY×Ógc…ĥs¡bġ«@dekąI[nlPqCnp{ˆō³°`{PNdƗqSÄĻNNâyj]äžÒD ĬH°Æ]~¡HO¾ŒX}ÐxŒgp“gWˆrDGˆŒpù‚Š^L‚ˆrzWxˆZ^¨´T\\|~@I‰zƒ–bĤ‹œjeĊªz£®Ĕvě€L†mV¾Ô_ȔNW~zbĬvG†²ZmDM~”~"],"encodeOffsets":[[120237,41215]]}},{"type":"Feature","id":"310000","properties":{"id":"310000","cp":[121.472644,31.231706],"name":"上海","childNum":6},"geometry":{"type":"MultiPolygon","coordinates":[["@@ɧư¬EpƸÁxc‡"],["@@©„ªƒ"],["@@”MA‹‘š"],["@@Qp݁E§ÉC¾"],["@@bŝՕÕEȣÚƥêImɇǦèÜĠŒÚžÃƌÃ͎ó"],["@@ǜûȬɋŠŭ™×^‰sYŒɍDŋ‘ŽąñCG²«ªč@h–_p¯A{‡oloY€¬j@IJ`•gQڛhr|ǀ^MIJvtbe´R¯Ô¬¨YŽô¤r]ì†Ƭį"]],"encodeOffsets":[[[124702,32062]],[[124547,32200]],[[124808,31991]],[[124726,32110]],[[124903,32376]],[[124438,32149]]]}},{"type":"Feature","id":"500000","properties":{"id":"500000","cp":[107.304962,29.533155],"name":"重庆","childNum":2},"geometry":{"type":"MultiPolygon","coordinates":[["@@vjG~nGŘŬĶȂƀƾ¹¸ØÎezĆT¸}êЖqHŸðqĖ䒊¥^CƒIj–²p…\\_ æüY|[YxƊæuž°xb®…Űb@~¢NQt°¶‚Sæ “Ê~rljĔëĚ¢~šuf`‘‚†fa‚ĔJåĊ„nÖ]„jƎćÊ@Š£¾a®£Ű{ŶĕF‹ègLk{Y|¡ĜWƔtƬJÑxq‹±ĢN´‰òK‰™–LÈüD|s`ŋ’ć]ƒÃ‰`đŒMûƱ½~Y°ħ`ƏíW‰½eI‹½{aŸ‘OIrÏ¡ĕŇa†p†µÜƅġ‘œ^ÖÛbÙŽŏml½S‹êqDu[R‹ãË»†ÿw`»y‘¸_ĺę}÷`M¯ċfCVµqʼn÷Z•gg“Œ`d½pDO‡ÎCnœ^uf²ènh¼WtƏxRGg¦…pV„†FI±ŽG^ŒIc´ec‡’G•ĹÞ½sëĬ„h˜xW‚}Kӈe­Xsbk”F¦›L‘ØgTkïƵNï¶}Gy“w\\oñ¡nmĈzjŸ•@™Óc£»Wă¹Ój“_m»ˆ¹·~MvÛaqœ»­‰êœ’\\ÂoVnŽÓØÍ™²«‹bq¿efE „€‹Ĝ^Qž~ Évý‡ş¤²Į‰pEİ}zcĺƒL‹½‡š¿gņ›¡ýE¡ya£³t\\¨\\vú»¼§·Ñr_oÒý¥u‚•_n»_ƒ•At©Þűā§IVeëƒY}{VPÀFA¨ąB}q@|Ou—\\Fm‰QF݅Mw˜å}]•€|FmϋCaƒwŒu_p—¯sfÙgY…DHl`{QEfNysBЦzG¸rHe‚„N\\CvEsÐùÜ_·ÖĉsaQ¯€}_U‡†xÃđŠq›NH¬•Äd^ÝŰR¬ã°wećJEž·vÝ·Hgƒ‚éFXjÉê`|yŒpxkAwœWĐpb¥eOsmzwqChóUQl¥F^laf‹anòsr›EvfQdÁUVf—ÎvÜ^efˆtET¬ôA\\œ¢sJŽnQTjP؈xøK|nBz‰„œĞ»LY‚…FDxӄvr“[ehľš•vN”¢o¾NiÂxGp⬐z›bfZo~hGi’]öF|‰|Nb‡tOMn eA±ŠtPT‡LjpYQ|†SH††YĀxinzDJ€Ìg¢và¥Pg‰_–ÇzII‹€II•„£®S¬„Øs쐣ŒN"],["@@ifjN@s"]],"encodeOffsets":[[[109628,30765]],[[111725,31320]]]}},{"type":"Feature","id":"810000","properties":{"id":"810000","cp":[114.173355,22.320048],"name":"香港","childNum":5},"geometry":{"type":"MultiPolygon","coordinates":[["@@AlBk"],["@@mŽn"],["@@EpFo"],["@@ea¢pl¸Eõ¹‡hj[ƒ]ÔCΖ@lj˜¡uBXŸ…•´‹AI¹…[‹yDUˆ]W`çwZkmc–…M›žp€Åv›}I‹oJlcaƒfёKްä¬XJmÐ đhI®æÔtSHn€Eˆ„ÒrÈc"],["@@rMUw‡AS®€e"]],"encodeOffsets":[[[117111,23002]],[[117072,22876]],[[117045,22887]],[[116975,23082]],[[116882,22747]]]}},{"type":"Feature","id":"820000","properties":{"id":"820000","cp":[113.54909,22.198951],"name":"澳门","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@kÊd°å§s"],"encodeOffsets":[[116279,22639]]}}],"UTF8Encoding":true});})); \ No newline at end of file diff --git a/static/assets/libs/echarts/echarts-wordcloud.min.js b/static/assets/libs/echarts/echarts-wordcloud.min.js new file mode 100644 index 0000000..f66e691 --- /dev/null +++ b/static/assets/libs/echarts/echarts-wordcloud.min.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("echarts")):"function"==typeof define&&define.amd?define(["echarts"],e):"object"==typeof exports?exports["echarts-wordcloud"]=e(require("echarts")):t["echarts-wordcloud"]=e(t.echarts)}(this,function(t){return function(t){function e(n){if(r[n])return r[n].exports;var i=r[n]={i:n,l:!1,exports:{}};return t[n].call(i.exports,i,i.exports,e),i.l=!0,i.exports}var r={};return e.m=t,e.c=r,e.d=function(t,r,n){e.o(t,r)||Object.defineProperty(t,r,{configurable:!1,enumerable:!0,get:n})},e.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(r,"a",r),r},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=25)}([function(t,e){function r(t,e){G[t]=e}function n(t){if(null==t||"object"!=typeof t)return t;var e=t,r=W.call(t);if("[object Array]"===r){e=[];for(var i=0,a=t.length;i1e-10&&(i.width+=a/s,i.height+=a/s,i.x-=a/s/2,i.y-=a/s/2)}return i}return t},contain:function(t,e){var r=this.transformCoordToLocal(t,e),n=this.getBoundingRect(),i=this.style;if(t=r[0],e=r[1],n.contain(t,e)){var a=this.path.data;if(i.hasStroke()){var o=i.lineWidth,l=i.strokeNoScale?this.getLineScale():1;if(l>1e-10&&(i.hasFill()||(o=Math.max(o,this.strokeContainThreshold)),s.containStroke(a,o/l,t,e)))return!0}if(i.hasFill())return s.contain(a,t,e)}return!1},dirty:function(t){null==t&&(t=!0),t&&(this.__dirtyPath=t,this._rect=null),this.__dirty=!0,this.__zr&&this.__zr.refresh(),this.__clipTarget&&this.__clipTarget.dirty()},animateShape:function(t){return this.animate("shape",t)},attrKV:function(t,e){"shape"===t?(this.setShape(e),this.__dirtyPath=!0,this._rect=null):i.prototype.attrKV.call(this,t,e)},setShape:function(t,e){var r=this.shape;if(r){if(a.isObject(t))for(var n in t)t.hasOwnProperty(n)&&(r[n]=t[n]);else r[t]=e;this.dirty(!0)}return this},getLineScale:function(){var t=this.transform;return t&&u(t[0]-1)>1e-10&&u(t[3]-1)>1e-10?Math.sqrt(u(t[0]*t[3]-t[2]*t[1])):1}},n.extend=function(t){var e=function(e){n.call(this,e),t.style&&this.style.extendFrom(t.style,!1);var r=t.shape;if(r){this.shape=this.shape||{};var i=this.shape;for(var a in r)!i.hasOwnProperty(a)&&r.hasOwnProperty(a)&&(i[a]=r[a])}t.init&&t.init.call(this,e)};a.inherits(e,n);for(var r in t)"style"!==r&&"shape"!==r&&(e.prototype[r]=t[r]);return e},a.inherits(n,i);var f=n;t.exports=f},function(t,e){function r(t,e){var r=new S(2);return null==t&&(t=0),null==e&&(e=0),r[0]=t,r[1]=e,r}function n(t,e){return t[0]=e[0],t[1]=e[1],t}function i(t){var e=new S(2);return e[0]=t[0],e[1]=t[1],e}function a(t,e,r){return t[0]=e,t[1]=r,t}function o(t,e,r){return t[0]=e[0]+r[0],t[1]=e[1]+r[1],t}function s(t,e,r,n){return t[0]=e[0]+r[0]*n,t[1]=e[1]+r[1]*n,t}function l(t,e,r){return t[0]=e[0]-r[0],t[1]=e[1]-r[1],t}function h(t){return Math.sqrt(u(t))}function u(t){return t[0]*t[0]+t[1]*t[1]}function c(t,e,r){return t[0]=e[0]*r[0],t[1]=e[1]*r[1],t}function f(t,e,r){return t[0]=e[0]/r[0],t[1]=e[1]/r[1],t}function d(t,e){return t[0]*e[0]+t[1]*e[1]}function v(t,e,r){return t[0]=e[0]*r,t[1]=e[1]*r,t}function g(t,e){var r=h(e);return 0===r?(t[0]=0,t[1]=0):(t[0]=e[0]/r,t[1]=e[1]/r),t}function p(t,e){return Math.sqrt((t[0]-e[0])*(t[0]-e[0])+(t[1]-e[1])*(t[1]-e[1]))}function m(t,e){return(t[0]-e[0])*(t[0]-e[0])+(t[1]-e[1])*(t[1]-e[1])}function x(t,e){return t[0]=-e[0],t[1]=-e[1],t}function y(t,e,r,n){return t[0]=e[0]+n*(r[0]-e[0]),t[1]=e[1]+n*(r[1]-e[1]),t}function w(t,e,r){var n=e[0],i=e[1];return t[0]=r[0]*n+r[2]*i+r[4],t[1]=r[1]*n+r[3]*i+r[5],t}function _(t,e,r){return t[0]=Math.min(e[0],r[0]),t[1]=Math.min(e[1],r[1]),t}function b(t,e,r){return t[0]=Math.max(e[0],r[0]),t[1]=Math.max(e[1],r[1]),t}var S="undefined"==typeof Float32Array?Array:Float32Array,M=h,T=u,k=p,C=m;e.create=r,e.copy=n,e.clone=i,e.set=a,e.add=o,e.scaleAndAdd=s,e.sub=l,e.len=h,e.length=M,e.lenSquare=u,e.lengthSquare=T,e.mul=c,e.div=f,e.dot=d,e.scale=v,e.normalize=g,e.distance=p,e.dist=k,e.distanceSquare=m,e.distSquare=C,e.negate=x,e.lerp=y,e.applyTransform=w,e.min=_,e.max=b},function(t,e,r){function n(t,e,r,n){r<0&&(t+=r,r=-r),n<0&&(e+=n,n=-n),this.x=t,this.y=e,this.width=r,this.height=n}var i=r(2),a=r(8),o=i.applyTransform,s=Math.min,l=Math.max;n.prototype={constructor:n,union:function(t){var e=s(t.x,this.x),r=s(t.y,this.y);this.width=l(t.x+t.width,this.x+this.width)-e,this.height=l(t.y+t.height,this.y+this.height)-r,this.x=e,this.y=r},applyTransform:function(){var t=[],e=[],r=[],n=[];return function(i){if(i){t[0]=r[0]=this.x,t[1]=n[1]=this.y,e[0]=n[0]=this.x+this.width,e[1]=r[1]=this.y+this.height,o(t,t,i),o(e,e,i),o(r,r,i),o(n,n,i),this.x=s(t[0],e[0],r[0],n[0]),this.y=s(t[1],e[1],r[1],n[1]);var a=l(t[0],e[0],r[0],n[0]),h=l(t[1],e[1],r[1],n[1]);this.width=a-this.x,this.height=h-this.y}}}(),calculateTransform:function(t){var e=this,r=t.width/e.width,n=t.height/e.height,i=a.create();return a.translate(i,i,[-e.x,-e.y]),a.scale(i,i,[r,n]),a.translate(i,i,[t.x,t.y]),i},intersect:function(t){if(!t)return!1;t instanceof n||(t=n.create(t));var e=this,r=e.x,i=e.x+e.width,a=e.y,o=e.y+e.height,s=t.x,l=t.x+t.width,h=t.y,u=t.y+t.height;return!(i=r.x&&t<=r.x+r.width&&e>=r.y&&e<=r.y+r.height},clone:function(){return new n(this.x,this.y,this.width,this.height)},copy:function(t){this.x=t.x,this.y=t.y,this.width=t.width,this.height=t.height},plain:function(){return{x:this.x,y:this.y,width:this.width,height:this.height}}},n.create=function(t){return new n(t.x,t.y,t.width,t.height)};var h=n;t.exports=h},function(t,e,r){function n(t){return t>-b&&tb||t<-b}function a(t,e,r,n,i){var a=1-i;return a*a*(a*t+3*i*e)+i*i*(i*n+3*a*r)}function o(t,e,r,n,i){var a=1-i;return 3*(((e-t)*a+2*(r-e)*i)*a+(n-r)*i*i)}function s(t,e,r,i,a,o){var s=i+3*(e-r)-t,l=3*(r-2*e+t),h=3*(e-t),u=t-a,c=l*l-3*s*h,f=l*h-9*s*u,d=h*h-3*l*u,v=0;if(n(c)&&n(f))if(n(l))o[0]=0;else{var g=-h/l;g>=0&&g<=1&&(o[v++]=g)}else{var p=f*f-4*c*d;if(n(p)){var m=f/c,g=-l/s+m,x=-m/2;g>=0&&g<=1&&(o[v++]=g),x>=0&&x<=1&&(o[v++]=x)}else if(p>0){var y=_(p),b=c*l+1.5*s*(-f+y),S=c*l+1.5*s*(-f-y);b=b<0?-w(-b,T):w(b,T),S=S<0?-w(-S,T):w(S,T);var g=(-l-(b+S))/(3*s);g>=0&&g<=1&&(o[v++]=g)}else{var k=(2*c*l-3*s*f)/(2*_(c*c*c)),C=Math.acos(k)/3,P=_(c),I=Math.cos(C),g=(-l-2*P*I)/(3*s),x=(-l+P*(I+M*Math.sin(C)))/(3*s),A=(-l+P*(I-M*Math.sin(C)))/(3*s);g>=0&&g<=1&&(o[v++]=g),x>=0&&x<=1&&(o[v++]=x),A>=0&&A<=1&&(o[v++]=A)}}return v}function l(t,e,r,a,o){var s=6*r-12*e+6*t,l=9*e+3*a-3*t-9*r,h=3*e-3*t,u=0;if(n(l)){if(i(s)){var c=-h/s;c>=0&&c<=1&&(o[u++]=c)}}else{var f=s*s-4*l*h;if(n(f))o[0]=-s/(2*l);else if(f>0){var d=_(f),c=(-s+d)/(2*l),v=(-s-d)/(2*l);c>=0&&c<=1&&(o[u++]=c),v>=0&&v<=1&&(o[u++]=v)}}return u}function h(t,e,r,n,i,a){var o=(e-t)*i+t,s=(r-e)*i+e,l=(n-r)*i+r,h=(s-o)*i+o,u=(l-s)*i+s,c=(u-h)*i+h;a[0]=t,a[1]=o,a[2]=h,a[3]=c,a[4]=c,a[5]=u,a[6]=l,a[7]=n}function u(t,e,r,n,i,o,s,l,h,u,c){var f,d,v,g,p,m=.005,x=1/0;k[0]=h,k[1]=u;for(var w=0;w<1;w+=.05)C[0]=a(t,r,i,s,w),C[1]=a(e,n,o,l,w),(g=y(k,C))=0&&g=0&&c<=1&&(o[u++]=c)}}else{var f=l*l-4*s*h;if(n(f)){var c=-l/(2*s);c>=0&&c<=1&&(o[u++]=c)}else if(f>0){var d=_(f),c=(-l+d)/(2*s),v=(-l-d)/(2*s);c>=0&&c<=1&&(o[u++]=c),v>=0&&v<=1&&(o[u++]=v)}}return u}function v(t,e,r){var n=t+r-2*e;return 0===n?.5:(t-e)/n}function g(t,e,r,n,i){var a=(e-t)*n+t,o=(r-e)*n+e,s=(o-a)*n+a;i[0]=t,i[1]=a,i[2]=s,i[3]=s,i[4]=o,i[5]=r}function p(t,e,r,n,i,a,o,s,l){var h,u=.005,f=1/0;k[0]=o,k[1]=s;for(var d=0;d<1;d+=.05){C[0]=c(t,r,i,d),C[1]=c(e,n,a,d);var v=y(k,C);v=0&&vA&&(I=0,P={}),I++,P[r]=i,i}function a(t,e,r,n,i,a,l){return a?s(t,e,r,n,i,a,l):o(t,e,r,n,i,l)}function o(t,e,r,n,a,o){var s=m(t,e,a,o),u=i(t,e);a&&(u+=a[1]+a[3]);var c=s.outerHeight,f=l(0,u,r),d=h(0,c,n),v=new _(f,d,u,c);return v.lineHeight=s.lineHeight,v}function s(t,e,r,n,i,a,o){var s=x(t,{rich:a,truncate:o,font:e,textAlign:r,textPadding:i}),u=s.outerWidth,c=s.outerHeight,f=l(0,u,r),d=h(0,c,n);return new _(f,d,u,c)}function l(t,e,r){return"right"===r?t-=e:"center"===r&&(t-=e/2),t}function h(t,e,r){return"middle"===r?t-=e/2:"bottom"===r&&(t-=e),t}function u(t,e,r){var n=e.x,i=e.y,a=e.height,o=e.width,s=a/2,l="left",h="top";switch(t){case"left":n-=r,i+=s,l="right",h="middle";break;case"right":n+=r+o,i+=s,h="middle";break;case"top":n+=o/2,i-=r,l="center",h="bottom";break;case"bottom":n+=o/2,i+=a+r,l="center";break;case"inside":n+=o/2,i+=s,l="center",h="middle";break;case"insideLeft":n+=r,i+=s,h="middle";break;case"insideRight":n+=o-r,i+=s,l="right",h="middle";break;case"insideTop":n+=o/2,i+=r,l="center";break;case"insideBottom":n+=o/2,i+=a-r,l="center",h="bottom";break;case"insideTopLeft":n+=r,i+=r;break;case"insideTopRight":n+=o-r,i+=r,l="right";break;case"insideBottomLeft":n+=r,i+=a-r,h="bottom";break;case"insideBottomRight":n+=o-r,i+=a-r,l="right",h="bottom"}return{x:n,y:i,textAlign:l,textVerticalAlign:h}}function c(t,e,r,n,i){if(!e)return"";var a=(t+"").split("\n");i=f(e,r,n,i);for(var o=0,s=a.length;o=o;l++)s-=o;var h=i(r);return h>s&&(r="",h=0),s=t-h,n.ellipsis=r,n.ellipsisWidth=h,n.contentWidth=s,n.containerWidth=t,n}function d(t,e){var r=e.containerWidth,n=e.font,a=e.contentWidth;if(!r)return"";var o=i(t,n);if(o<=r)return t;for(var s=0;;s++){if(o<=a||s>=e.maxIterations){t+=e.ellipsis;break}var l=0===s?v(t,a,e.ascCharWidth,e.cnCharWidth):o>0?Math.floor(t.length*a/o):0;t=t.substr(0,l),o=i(t,n)}return""===t&&(t=e.placeholder),t}function v(t,e,r,n){for(var i=0,a=0,o=t.length;al)t="",a=[];else if(null!=h)for(var u=f(h-(r?r[1]+r[3]:0),e,n.ellipsis,{minChar:n.minChar,placeholder:n.placeholder}),c=0,v=a.length;ca&&y(r,t.substring(a,o)),y(r,n[2],n[1]),a=O.lastIndex}ap)return{lines:[],width:0,height:0};M.textWidth=i(M.text,I);var D=T.textWidth,L=null==D||"auto"===D;if("string"==typeof D&&"%"===D.charAt(D.length-1))M.percentWidth=D,u.push(M),D=0;else{if(L){D=M.textWidth;var F=T.textBackgroundColor,R=F&&F.image;R&&(R=b.findExistImage(R),b.isImageReady(R)&&(D=Math.max(D,R.width*A/R.height)))}var B=P?P[1]+P[3]:0;D+=B;var z=null!=v?v-_:null;null!=z&&zthis._ux||y(e-this._yi)>this._uy||this._len<5;return this.addData(h.L,t,e),this._ctx&&r&&(this._needsDash()?this._dashedLineTo(t,e):this._ctx.lineTo(t,e)),r&&(this._xi=t,this._yi=e),this},bezierCurveTo:function(t,e,r,n,i,a){return this.addData(h.C,t,e,r,n,i,a),this._ctx&&(this._needsDash()?this._dashedBezierTo(t,e,r,n,i,a):this._ctx.bezierCurveTo(t,e,r,n,i,a)),this._xi=i,this._yi=a,this},quadraticCurveTo:function(t,e,r,n){return this.addData(h.Q,t,e,r,n),this._ctx&&(this._needsDash()?this._dashedQuadraticTo(t,e,r,n):this._ctx.quadraticCurveTo(t,e,r,n)),this._xi=r,this._yi=n,this},arc:function(t,e,r,n,i,a){return this.addData(h.A,t,e,r,r,n,i-n,0,a?0:1),this._ctx&&this._ctx.arc(t,e,r,n,i,a),this._xi=p(i)*r+t,this._yi=m(i)*r+t,this},arcTo:function(t,e,r,n,i){return this._ctx&&this._ctx.arcTo(t,e,r,n,i),this},rect:function(t,e,r,n){return this._ctx&&this._ctx.rect(t,e,r,n),this.addData(h.R,t,e,r,n),this},closePath:function(){this.addData(h.Z);var t=this._ctx,e=this._x0,r=this._y0;return t&&(this._needsDash()&&this._dashedLineTo(e,r),t.closePath()),this._xi=e,this._yi=r,this},fill:function(t){t&&t.fill(),this.toStatic()},stroke:function(t){t&&t.stroke(),this.toStatic()},setLineDash:function(t){if(t instanceof Array){this._lineDash=t,this._dashIdx=0;for(var e=0,r=0;re.length&&(this._expandData(),e=this.data);for(var r=0;r0&&d<=t||u<0&&d>=t||0==u&&(c>0&&p<=e||c<0&&p>=e);)n=this._dashIdx,r=o[n],d+=u*r,p+=c*r,this._dashIdx=(n+1)%m,u>0&&dl||c>0&&ph||s[n%2?"moveTo":"lineTo"](u>=0?v(d,t):g(d,t),c>=0?v(p,e):g(p,e));u=d-t,c=p-e,this._dashOffset=-x(u*u+c*c)},_dashedBezierTo:function(t,e,r,i,a,o){var s,l,h,u,c,f=this._dashSum,d=this._dashOffset,v=this._lineDash,g=this._ctx,p=this._xi,m=this._yi,y=n.cubicAt,w=0,_=this._dashIdx,b=v.length,S=0;for(d<0&&(d=f+d),d%=f,s=0;s<1;s+=.1)l=y(p,t,r,a,s+.1)-y(p,t,r,a,s),h=y(m,e,i,o,s+.1)-y(m,e,i,o,s),w+=x(l*l+h*h);for(;_d);_++);for(s=(S-d)/w;s<=1;)u=y(p,t,r,a,s),c=y(m,e,i,o,s),_%2?g.moveTo(u,c):g.lineTo(u,c),s+=v[_]/w,_=(_+1)%b;_%2!=0&&g.lineTo(a,o),l=a-u,h=o-c,this._dashOffset=-x(l*l+h*h)},_dashedQuadraticTo:function(t,e,r,n){var i=r,a=n;r=(r+2*t)/3,n=(n+2*e)/3,t=(this._xi+2*t)/3,e=(this._yi+2*e)/3,this._dashedBezierTo(t,e,r,n,i,a)},toStatic:function(){var t=this.data;t instanceof Array&&(t.length=this._len,w&&(this.data=new Float32Array(t)))},getBoundingRect:function(){u[0]=u[1]=f[0]=f[1]=Number.MAX_VALUE,c[0]=c[1]=d[0]=d[1]=-Number.MAX_VALUE;for(var t=this.data,e=0,r=0,n=0,s=0,l=0;ll||y(o-i)>u||f===c-1)&&(t.lineTo(a,o),n=a,i=o);break;case h.C:t.bezierCurveTo(s[f++],s[f++],s[f++],s[f++],s[f++],s[f++]),n=s[f-2],i=s[f-1];break;case h.Q:t.quadraticCurveTo(s[f++],s[f++],s[f++],s[f++]),n=s[f-2],i=s[f-1];break;case h.A:var v=s[f++],g=s[f++],x=s[f++],w=s[f++],_=s[f++],b=s[f++],S=s[f++],M=s[f++],T=x>w?x:w,k=x>w?1:x/w,C=x>w?w/x:1,P=Math.abs(x-w)>.001,I=_+b;P?(t.translate(v,g),t.rotate(S),t.scale(k,C),t.arc(0,0,T,_,I,1-M),t.scale(1/k,1/C),t.rotate(-S),t.translate(-v,-g)):t.arc(v,g,T,_,I,1-M),1==f&&(e=p(_)*x+v,r=m(_)*w+g),n=p(I)*x+v,i=m(I)*w+g;break;case h.R:e=n=s[f],r=i=s[f+1],t.rect(s[f++],s[f++],s[f++],s[f++]);break;case h.Z:t.closePath(),n=e,i=r}}}},_.CMD=h;var b=_;t.exports=b},function(e,r){e.exports=t},function(t,e){function r(){var t=new u(6);return n(t),t}function n(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t}function i(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=e[4],t[5]=e[5],t}function a(t,e,r){var n=e[0]*r[0]+e[2]*r[1],i=e[1]*r[0]+e[3]*r[1],a=e[0]*r[2]+e[2]*r[3],o=e[1]*r[2]+e[3]*r[3],s=e[0]*r[4]+e[2]*r[5]+e[4],l=e[1]*r[4]+e[3]*r[5]+e[5];return t[0]=n,t[1]=i,t[2]=a,t[3]=o,t[4]=s,t[5]=l,t}function o(t,e,r){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=e[4]+r[0],t[5]=e[5]+r[1],t}function s(t,e,r){var n=e[0],i=e[2],a=e[4],o=e[1],s=e[3],l=e[5],h=Math.sin(r),u=Math.cos(r);return t[0]=n*u+o*h,t[1]=-n*h+o*u,t[2]=i*u+s*h,t[3]=-i*h+u*s,t[4]=u*a+h*l,t[5]=u*l-h*a,t}function l(t,e,r){var n=r[0],i=r[1];return t[0]=e[0]*n,t[1]=e[1]*i,t[2]=e[2]*n,t[3]=e[3]*i,t[4]=e[4]*n,t[5]=e[5]*i,t}function h(t,e){var r=e[0],n=e[2],i=e[4],a=e[1],o=e[3],s=e[5],l=r*o-a*n;return l?(l=1/l,t[0]=o*l,t[1]=-a*l,t[2]=-n*l,t[3]=r*l,t[4]=(n*s-o*i)*l,t[5]=(a*i-r*s)*l,t):null}var u="undefined"==typeof Float32Array?Array:Float32Array;e.create=r,e.identity=n,e.copy=i,e.mul=a,e.translate=o,e.rotate=s,e.scale=l,e.invert=h},function(t,e,r){function n(t){return t.replace(/^\s+/,"").replace(/\s+$/,"")}function i(t,e,r,n){var i=e[1]-e[0],a=r[1]-r[0];if(0===i)return 0===a?r[0]:(r[0]+r[1])/2;if(n)if(i>0){if(t<=e[0])return r[0];if(t>=e[1])return r[1]}else{if(t>=e[0])return r[0];if(t<=e[1])return r[1]}else{if(t===e[0])return r[0];if(t===e[1])return r[1]}return(t-e[0])/i*a+r[0]}function a(t,e){switch(t){case"center":case"middle":t="50%";break;case"left":case"top":t="0%";break;case"right":case"bottom":t="100%"}return"string"==typeof t?n(t).match(/%$/)?parseFloat(t)/100*e:parseFloat(t):null==t?NaN:+t}function o(t,e,r){return null==e&&(e=10),e=Math.min(Math.max(0,e),20),t=(+t).toFixed(e),r?t:+t}function s(t){return t.sort(function(t,e){return t-e}),t}function l(t){if(t=+t,isNaN(t))return 0;for(var e=1,r=0;Math.round(t*e)/e!==t;)e*=10,r++;return r}function h(t){var e=t.toString(),r=e.indexOf("e");if(r>0){var n=+e.slice(r+1);return n<0?-n:0}var i=e.indexOf(".");return i<0?0:e.length-1-i}function u(t,e){var r=Math.log,n=Math.LN10,i=Math.floor(r(t[1]-t[0])/n),a=Math.round(r(Math.abs(e[1]-e[0]))/n),o=Math.min(Math.max(-i+a,0),20);return isFinite(o)?o:20}function c(t,e,r){if(!t[e])return 0;var n=w.reduce(t,function(t,e){return t+(isNaN(e)?0:e)},0);if(0===n)return 0;for(var i=Math.pow(10,r),a=w.map(t,function(t){return(isNaN(t)?0:t)/n*i*100}),o=100*i,s=w.map(a,function(t){return Math.floor(t)}),l=w.reduce(s,function(t,e){return t+e},0),h=w.map(a,function(t,e){return t-s[e]});lu&&(u=h[f],c=f);++s[c],h[c]=0,++l}return s[e]/i}function f(t){var e=2*Math.PI;return(t%e+e)%e}function d(t){return t>-_&&t<_}function v(t){if(t instanceof Date)return t;if("string"==typeof t){var e=b.exec(t);if(!e)return new Date(NaN);if(e[8]){var r=+e[4]||0;return"Z"!==e[8].toUpperCase()&&(r-=e[8].slice(0,3)),new Date(Date.UTC(+e[1],+(e[2]||1)-1,+e[3]||1,r,+(e[5]||0),+e[6]||0,+e[7]||0))}return new Date(+e[1],+(e[2]||1)-1,+e[3]||1,+e[4]||0,+(e[5]||0),+e[6]||0,+e[7]||0)}return null==t?new Date(NaN):new Date(Math.round(t))}function g(t){return Math.pow(10,p(t))}function p(t){return Math.floor(Math.log(t)/Math.LN10)}function m(t,e){var r,n=p(t),i=Math.pow(10,n),a=t/i;return r=e?a<1.5?1:a<2.5?2:a<4?3:a<7?5:10:a<1?1:a<2?2:a<3?3:a<5?5:10,t=r*i,n>=-20?+t.toFixed(n<0?-n:0):t}function x(t){function e(t,r,n){return t.interval[n]=0}var w=r(0),_=1e-4,b=/^(?:(\d{4})(?:[-\/](\d{1,2})(?:[-\/](\d{1,2})(?:[T ](\d{1,2})(?::(\d\d)(?::(\d\d)(?:[.,](\d+))?)?)?(Z|[\+\-]\d\d:?\d\d)?)?)?)?)?$/;e.linearMap=i,e.parsePercent=a,e.round=o,e.asc=s,e.getPrecision=l,e.getPrecisionSafe=h,e.getPixelPrecision=u,e.getPercentWithPrecision=c,e.MAX_SAFE_INTEGER=9007199254740991,e.remRadian=f,e.isRadianAroundZero=d,e.parseDate=v,e.quantity=g,e.nice=m,e.reformIntervals=x,e.isNumeric=y},function(t,e,r){function n(t){if("string"==typeof t){var e=l.get(t);return e&&e.image}return t}function i(t,e,r,n,i){if(t){if("string"==typeof t){if(e&&e.__zrImageSrc===t||!r)return e;var s=l.get(t),h={hostEl:r,cb:n,cbPayload:i};return s?(e=s.image,!o(e)&&s.pending.push(h)):(!e&&(e=new Image),e.onload=a,l.put(t,e.__cachedImgObj={image:e,pending:[h]}),e.src=e.__zrImageSrc=t),e}return t}return e}function a(){var t=this.__cachedImgObj;this.onload=this.__cachedImgObj=null;for(var e=0;e=0||n&&i.indexOf(n,s)<0)){var l=e.getShallow(s);null!=l&&(a[t[o][0]]=l)}}return a}}var i=r(0);t.exports=n},function(t,e,r){function n(t){t=t||{},o.call(this,t);for(var e in t)t.hasOwnProperty(e)&&"style"!==e&&(this[e]=t[e]);this.style=new a(t.style,this),this._rect=null,this.__clipPaths=[]}var i=r(0),a=r(40),o=r(16),s=r(48);n.prototype={constructor:n,type:"displayable",__dirty:!0,invisible:!1,z:0,z2:0,zlevel:0,draggable:!1,dragging:!1,silent:!1,culling:!1,cursor:"pointer",rectHover:!1,progressive:-1,beforeBrush:function(t){},afterBrush:function(t){},brush:function(t,e){},getBoundingRect:function(){},contain:function(t,e){return this.rectContain(t,e)},traverse:function(t,e){t.call(e,this)},rectContain:function(t,e){var r=this.transformCoordToLocal(t,e);return this.getBoundingRect().contain(r[0],r[1])},dirty:function(){this.__dirty=!0,this._rect=null,this.__zr&&this.__zr.refresh()},animateStyle:function(t){return this.animate("style",t)},attrKV:function(t,e){"style"!==t?o.prototype.attrKV.call(this,t,e):this.style.set(e)},setStyle:function(t,e){return this.style.set(t,e),this.dirty(!1),this},useStyle:function(t){return this.style=new a(t,this),this.dirty(!1),this}},i.inherits(n,o),i.mixin(n,s);var l=n;t.exports=l},function(t,e,r){function n(t){return isNaN(t)?"-":(t=(t+"").split("."),t[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g,"$1,")+(t.length>1?"."+t[1]:""))}function i(t,e){return t=(t||"").toLowerCase().replace(/-(.)/g,function(t,e){return e.toUpperCase()}),e&&t&&(t=t.charAt(0).toUpperCase()+t.slice(1)),t}function a(t){return String(t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function o(t,e,r){c.isArray(e)||(e=[e]);var n=e.length;if(!n)return"";for(var i=e[0].$vars||[],o=0;o':""}function h(t,e,r){"week"!==t&&"month"!==t&&"quarter"!==t&&"half-year"!==t&&"year"!==t||(t="MM-dd\nyyyy");var n=d.parseDate(e),i=r?"UTC":"",a=n["get"+i+"FullYear"](),o=n["get"+i+"Month"]()+1,s=n["get"+i+"Date"](),l=n["get"+i+"Hours"](),h=n["get"+i+"Minutes"](),u=n["get"+i+"Seconds"]();return t=t.replace("MM",m(o)).replace("M",o).replace("yyyy",a).replace("yy",a%100).replace("dd",m(s)).replace("d",s).replace("hh",m(l)).replace("h",l).replace("mm",m(h)).replace("m",h).replace("ss",m(u)).replace("s",u)}function u(t){return t?t.charAt(0).toUpperCase()+t.substr(1):t}var c=r(0),f=r(5),d=r(9),v=c.normalizeCssArray,g=["a","b","c","d","e","f","g"],p=function(t,e){return"{"+t+(null==e?"":e)+"}"},m=function(t){return t<10?"0"+t:t},x=f.truncateText,y=f.getBoundingRect;e.addCommas=n,e.toCamelCase=i,e.normalizeCssArray=v,e.encodeHTML=a,e.formatTpl=o,e.formatTplSimple=s,e.getTooltipMarker=l,e.formatTime=h,e.capitalFirst=u,e.truncateText=x,e.getTextRect=y},function(t,e){var r=function(){this.head=null,this.tail=null,this._len=0},n=r.prototype;n.insert=function(t){var e=new i(t);return this.insertEntry(e),e},n.insertEntry=function(t){this.head?(this.tail.next=t,t.prev=this.tail,t.next=null,this.tail=t):this.head=this.tail=t,this._len++},n.remove=function(t){var e=t.prev,r=t.next;e?e.next=r:this.head=r,r?r.prev=e:this.tail=e,t.next=t.prev=null,this._len--},n.len=function(){return this._len},n.clear=function(){this.head=this.tail=null,this._len=0};var i=function(t){this.value=t,this.next,this.prev},a=function(t){this._list=new r,this._map={},this._maxSize=t||10,this._lastRemovedEntry=null},o=a.prototype;o.put=function(t,e){var r=this._list,n=this._map,a=null;if(null==n[t]){var o=r.len(),s=this._lastRemovedEntry;if(o>=this._maxSize&&o>0){var l=r.head;r.remove(l),delete n[l.key],a=l.value,this._lastRemovedEntry=l}s?s.value=e:s=new i(e),s.key=t,r.insertEntry(s),n[t]=s}return a},o.get=function(t){var e=this._map[t],r=this._list;if(null!=e)return e!==r.tail&&(r.remove(e),r.insertEntry(e)),e.value},o.clear=function(){this._list.clear(),this._map={}};var s=a;t.exports=s},function(t,e){var r={};r="undefined"==typeof navigator?{browser:{},os:{},node:!0,canvasSupported:!0,svgSupported:!0}:function(t){var e={},r={},n=t.match(/Firefox\/([\d.]+)/),i=t.match(/MSIE\s([\d.]+)/)||t.match(/Trident\/.+?rv:(([\d.]+))/),a=t.match(/Edge\/([\d.]+)/),o=/micromessenger/i.test(t);return n&&(r.firefox=!0,r.version=n[1]),i&&(r.ie=!0,r.version=i[1]),a&&(r.edge=!0,r.version=a[1]),o&&(r.weChat=!0),{browser:r,os:e,node:!1,canvasSupported:!!document.createElement("canvas").getContext,svgSupported:"undefined"!=typeof SVGRect,touchEventsSupported:"ontouchstart"in window&&!r.ie&&!r.edge,pointerEventsSupported:"onpointerdown"in window&&(r.edge||r.ie&&r.version>=11)}}(navigator.userAgent);var n=r;t.exports=n},function(t,e,r){var n=r(41),i=r(42),a=r(17),o=r(43),s=r(0),l=function(t){a.call(this,t),i.call(this,t),o.call(this,t),this.id=t.id||n()};l.prototype={type:"element",name:"",__zr:null,ignore:!1,clipPath:null,drift:function(t,e){switch(this.draggable){case"horizontal":e=0;break;case"vertical":t=0}var r=this.transform;r||(r=this.transform=[1,0,0,1,0,0]),r[4]+=t,r[5]+=e,this.decomposeTransform(),this.dirty(!1)},beforeUpdate:function(){},afterUpdate:function(){},update:function(){this.updateTransform()},traverse:function(t,e){},attrKV:function(t,e){if("position"===t||"scale"===t||"origin"===t){if(e){var r=this[t];r||(r=this[t]=[]),r[0]=e[0],r[1]=e[1]}}else this[t]=e},hide:function(){this.ignore=!0,this.__zr&&this.__zr.refresh()},show:function(){this.ignore=!1,this.__zr&&this.__zr.refresh()},attr:function(t,e){if("string"==typeof t)this.attrKV(t,e);else if(s.isObject(t))for(var r in t)t.hasOwnProperty(r)&&this.attrKV(r,t[r]);return this.dirty(!1),this},setClipPath:function(t){var e=this.__zr;e&&t.addSelfToZr(e),this.clipPath&&this.clipPath!==t&&this.removeClipPath(),this.clipPath=t,t.__zr=e,t.__clipTarget=this,this.dirty(!1)},removeClipPath:function(){var t=this.clipPath;t&&(t.__zr&&t.removeSelfFromZr(t.__zr),t.__zr=null,t.__clipTarget=null,this.clipPath=null,this.dirty(!1))},addSelfToZr:function(t){this.__zr=t;var e=this.animators;if(e)for(var r=0;rs||t<-s}var i=r(8),a=r(2),o=i.identity,s=5e-5,l=function(t){t=t||{},t.position||(this.position=[0,0]),null==t.rotation&&(this.rotation=0),t.scale||(this.scale=[1,1]),this.origin=this.origin||null},h=l.prototype;h.transform=null,h.needLocalTransform=function(){return n(this.rotation)||n(this.position[0])||n(this.position[1])||n(this.scale[0]-1)||n(this.scale[1]-1)},h.updateTransform=function(){var t=this.parent,e=t&&t.transform,r=this.needLocalTransform(),n=this.transform;if(!r&&!e)return void(n&&o(n));n=n||i.create(),r?this.getLocalTransform(n):o(n),e&&(r?i.mul(n,t.transform,n):i.copy(n,t.transform)),this.transform=n,this.invTransform=this.invTransform||i.create(),i.invert(this.invTransform,n)},h.getLocalTransform=function(t){return l.getLocalTransform(this,t)},h.setTransform=function(t){var e=this.transform,r=t.dpr||1;e?t.setTransform(r*e[0],r*e[1],r*e[2],r*e[3],r*e[4],r*e[5]):t.setTransform(r,0,0,r,0,0)},h.restoreTransform=function(t){var e=t.dpr||1;t.setTransform(e,0,0,e,0,0)};var u=[];h.decomposeTransform=function(){if(this.transform){var t=this.parent,e=this.transform;t&&t.transform&&(i.mul(u,t.invTransform,e),e=u);var r=e[0]*e[0]+e[1]*e[1],a=e[2]*e[2]+e[3]*e[3],o=this.position,s=this.scale;n(r-1)&&(r=Math.sqrt(r)),n(a-1)&&(a=Math.sqrt(a)),e[0]<0&&(r=-r),e[3]<0&&(a=-a),o[0]=e[4],o[1]=e[5],s[0]=r,s[1]=a,this.rotation=Math.atan2(-e[1]/a,e[0]/r)}},h.getGlobalScale=function(){var t=this.transform;if(!t)return[1,1];var e=Math.sqrt(t[0]*t[0]+t[1]*t[1]),r=Math.sqrt(t[2]*t[2]+t[3]*t[3]);return t[0]<0&&(e=-e),t[3]<0&&(r=-r),[e,r]},h.transformCoordToLocal=function(t,e){var r=[t,e],n=this.invTransform;return n&&a.applyTransform(r,r,n),r},h.transformCoordToGlobal=function(t,e){var r=[t,e],n=this.transform;return n&&a.applyTransform(r,r,n),r},l.getLocalTransform=function(t,e){e=e||[],o(e);var r=t.origin,n=t.scale||[1,1],a=t.rotation||0,s=t.position||[0,0];return r&&(e[4]-=r[0],e[5]-=r[1]),i.scale(e,e,n),a&&i.rotate(e,e,a),r&&(e[4]+=r[0],e[5]+=r[1]),e[4]+=s[0],e[5]+=s[1],e};var c=l;t.exports=c},function(t,e,r){function n(t){return t=Math.round(t),t<0?0:t>255?255:t}function i(t){return t=Math.round(t),t<0?0:t>360?360:t}function a(t){return t<0?0:t>1?1:t}function o(t){return n(t.length&&"%"===t.charAt(t.length-1)?parseFloat(t)/100*255:parseInt(t,10))}function s(t){return a(t.length&&"%"===t.charAt(t.length-1)?parseFloat(t)/100:parseFloat(t))}function l(t,e,r){return r<0?r+=1:r>1&&(r-=1),6*r<1?t+(e-t)*r*6:2*r<1?e:3*r<2?t+(e-t)*(2/3-r)*6:t}function h(t,e,r){return t+(e-t)*r}function u(t,e,r,n,i){return t[0]=e,t[1]=r,t[2]=n,t[3]=i,t}function c(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t}function f(t,e){k&&c(k,e),k=T.put(t,k||e.slice())}function d(t,e){if(t){e=e||[];var r=T.get(t);if(r)return c(e,r);t+="";var n=t.replace(/ /g,"").toLowerCase();if(n in M)return c(e,M[n]),f(t,e),e;if("#"!==n.charAt(0)){var i=n.indexOf("("),a=n.indexOf(")");if(-1!==i&&a+1===n.length){var l=n.substr(0,i),h=n.substr(i+1,a-(i+1)).split(","),d=1;switch(l){case"rgba":if(4!==h.length)return void u(e,0,0,0,1);d=s(h.pop());case"rgb":return 3!==h.length?void u(e,0,0,0,1):(u(e,o(h[0]),o(h[1]),o(h[2]),d),f(t,e),e);case"hsla":return 4!==h.length?void u(e,0,0,0,1):(h[3]=s(h[3]),v(h,e),f(t,e),e);case"hsl":return 3!==h.length?void u(e,0,0,0,1):(v(h,e),f(t,e),e);default:return}}u(e,0,0,0,1)}else{if(4===n.length){var g=parseInt(n.substr(1),16);return g>=0&&g<=4095?(u(e,(3840&g)>>4|(3840&g)>>8,240&g|(240&g)>>4,15&g|(15&g)<<4,1),f(t,e),e):void u(e,0,0,0,1)}if(7===n.length){var g=parseInt(n.substr(1),16);return g>=0&&g<=16777215?(u(e,(16711680&g)>>16,(65280&g)>>8,255&g,1),f(t,e),e):void u(e,0,0,0,1)}}}}function v(t,e){var r=(parseFloat(t[0])%360+360)%360/360,i=s(t[1]),a=s(t[2]),o=a<=.5?a*(i+1):a+i-a*i,h=2*a-o;return e=e||[],u(e,n(255*l(h,o,r+1/3)),n(255*l(h,o,r)),n(255*l(h,o,r-1/3)),1),4===t.length&&(e[3]=t[3]),e}function g(t){if(t){var e,r,n=t[0]/255,i=t[1]/255,a=t[2]/255,o=Math.min(n,i,a),s=Math.max(n,i,a),l=s-o,h=(s+o)/2;if(0===l)e=0,r=0;else{r=h<.5?l/(s+o):l/(2-s-o);var u=((s-n)/6+l/2)/l,c=((s-i)/6+l/2)/l,f=((s-a)/6+l/2)/l;n===s?e=f-c:i===s?e=1/3+u-f:a===s&&(e=2/3+c-u),e<0&&(e+=1),e>1&&(e-=1)}var d=[360*e,r,h];return null!=t[3]&&d.push(t[3]),d}}function p(t,e){var r=d(t);if(r){for(var n=0;n<3;n++)r[n]=e<0?r[n]*(1-e)|0:(255-r[n])*e+r[n]|0;return b(r,4===r.length?"rgba":"rgb")}}function m(t){var e=d(t);if(e)return((1<<24)+(e[0]<<16)+(e[1]<<8)+ +e[2]).toString(16).slice(1)}function x(t,e,r){if(e&&e.length&&t>=0&&t<=1){r=r||[];var i=t*(e.length-1),o=Math.floor(i),s=Math.ceil(i),l=e[o],u=e[s],c=i-o;return r[0]=n(h(l[0],u[0],c)),r[1]=n(h(l[1],u[1],c)),r[2]=n(h(l[2],u[2],c)),r[3]=a(h(l[3],u[3],c)),r}}function y(t,e,r){if(e&&e.length&&t>=0&&t<=1){var i=t*(e.length-1),o=Math.floor(i),s=Math.ceil(i),l=d(e[o]),u=d(e[s]),c=i-o,f=b([n(h(l[0],u[0],c)),n(h(l[1],u[1],c)),n(h(l[2],u[2],c)),a(h(l[3],u[3],c))],"rgba");return r?{color:f,leftIndex:o,rightIndex:s,value:i}:f}}function w(t,e,r,n){if(t=d(t))return t=g(t),null!=e&&(t[0]=i(e)),null!=r&&(t[1]=s(r)),null!=n&&(t[2]=s(n)),b(v(t),"rgba")}function _(t,e){if((t=d(t))&&null!=e)return t[3]=a(e),b(t,"rgba")}function b(t,e){if(t&&t.length){var r=t[0]+","+t[1]+","+t[2];return"rgba"!==e&&"hsva"!==e&&"hsla"!==e||(r+=","+t[3]),e+"("+r+")"}}var S=r(14),M={transparent:[0,0,0,0],aliceblue:[240,248,255,1],antiquewhite:[250,235,215,1],aqua:[0,255,255,1],aquamarine:[127,255,212,1],azure:[240,255,255,1],beige:[245,245,220,1],bisque:[255,228,196,1],black:[0,0,0,1],blanchedalmond:[255,235,205,1],blue:[0,0,255,1],blueviolet:[138,43,226,1],brown:[165,42,42,1],burlywood:[222,184,135,1],cadetblue:[95,158,160,1],chartreuse:[127,255,0,1],chocolate:[210,105,30,1],coral:[255,127,80,1],cornflowerblue:[100,149,237,1],cornsilk:[255,248,220,1],crimson:[220,20,60,1],cyan:[0,255,255,1],darkblue:[0,0,139,1],darkcyan:[0,139,139,1],darkgoldenrod:[184,134,11,1],darkgray:[169,169,169,1],darkgreen:[0,100,0,1],darkgrey:[169,169,169,1],darkkhaki:[189,183,107,1],darkmagenta:[139,0,139,1],darkolivegreen:[85,107,47,1],darkorange:[255,140,0,1],darkorchid:[153,50,204,1],darkred:[139,0,0,1],darksalmon:[233,150,122,1],darkseagreen:[143,188,143,1],darkslateblue:[72,61,139,1],darkslategray:[47,79,79,1],darkslategrey:[47,79,79,1],darkturquoise:[0,206,209,1],darkviolet:[148,0,211,1],deeppink:[255,20,147,1],deepskyblue:[0,191,255,1],dimgray:[105,105,105,1],dimgrey:[105,105,105,1],dodgerblue:[30,144,255,1],firebrick:[178,34,34,1],floralwhite:[255,250,240,1],forestgreen:[34,139,34,1],fuchsia:[255,0,255,1],gainsboro:[220,220,220,1],ghostwhite:[248,248,255,1],gold:[255,215,0,1],goldenrod:[218,165,32,1],gray:[128,128,128,1],green:[0,128,0,1],greenyellow:[173,255,47,1],grey:[128,128,128,1],honeydew:[240,255,240,1],hotpink:[255,105,180,1],indianred:[205,92,92,1],indigo:[75,0,130,1],ivory:[255,255,240,1],khaki:[240,230,140,1],lavender:[230,230,250,1],lavenderblush:[255,240,245,1],lawngreen:[124,252,0,1],lemonchiffon:[255,250,205,1],lightblue:[173,216,230,1],lightcoral:[240,128,128,1],lightcyan:[224,255,255,1],lightgoldenrodyellow:[250,250,210,1],lightgray:[211,211,211,1],lightgreen:[144,238,144,1],lightgrey:[211,211,211,1],lightpink:[255,182,193,1],lightsalmon:[255,160,122,1],lightseagreen:[32,178,170,1],lightskyblue:[135,206,250,1],lightslategray:[119,136,153,1],lightslategrey:[119,136,153,1],lightsteelblue:[176,196,222,1],lightyellow:[255,255,224,1],lime:[0,255,0,1],limegreen:[50,205,50,1],linen:[250,240,230,1],magenta:[255,0,255,1],maroon:[128,0,0,1],mediumaquamarine:[102,205,170,1],mediumblue:[0,0,205,1],mediumorchid:[186,85,211,1],mediumpurple:[147,112,219,1],mediumseagreen:[60,179,113,1],mediumslateblue:[123,104,238,1],mediumspringgreen:[0,250,154,1],mediumturquoise:[72,209,204,1],mediumvioletred:[199,21,133,1],midnightblue:[25,25,112,1],mintcream:[245,255,250,1],mistyrose:[255,228,225,1],moccasin:[255,228,181,1],navajowhite:[255,222,173,1],navy:[0,0,128,1],oldlace:[253,245,230,1],olive:[128,128,0,1],olivedrab:[107,142,35,1],orange:[255,165,0,1],orangered:[255,69,0,1],orchid:[218,112,214,1],palegoldenrod:[238,232,170,1],palegreen:[152,251,152,1],paleturquoise:[175,238,238,1],palevioletred:[219,112,147,1],papayawhip:[255,239,213,1],peachpuff:[255,218,185,1],peru:[205,133,63,1],pink:[255,192,203,1],plum:[221,160,221,1],powderblue:[176,224,230,1],purple:[128,0,128,1],red:[255,0,0,1],rosybrown:[188,143,143,1],royalblue:[65,105,225,1],saddlebrown:[139,69,19,1],salmon:[250,128,114,1],sandybrown:[244,164,96,1],seagreen:[46,139,87,1],seashell:[255,245,238,1],sienna:[160,82,45,1],silver:[192,192,192,1],skyblue:[135,206,235,1],slateblue:[106,90,205,1],slategray:[112,128,144,1],slategrey:[112,128,144,1],snow:[255,250,250,1],springgreen:[0,255,127,1],steelblue:[70,130,180,1],tan:[210,180,140,1],teal:[0,128,128,1],thistle:[216,191,216,1],tomato:[255,99,71,1],turquoise:[64,224,208,1],violet:[238,130,238,1],wheat:[245,222,179,1],white:[255,255,255,1],whitesmoke:[245,245,245,1],yellow:[255,255,0,1],yellowgreen:[154,205,50,1]},T=new S(20),k=null,C=x,P=y;e.parse=d,e.lift=p,e.toHex=m,e.fastLerp=x,e.fastMapToColor=C,e.lerp=y,e.mapToColor=P,e.modifyHSL=w,e.modifyAlpha=_,e.stringify=b},function(t,e){var r=1;"undefined"!=typeof window&&(r=Math.max(window.devicePixelRatio||1,1));var n=r;e.debugMode=0,e.devicePixelRatio=n},function(t,e,r){function n(t){return i(t),M(t.rich,i),t}function i(t){if(t){t.font=P.makeFont(t);var e=t.textAlign;"middle"===e&&(e="center"),t.textAlign=null==e||O[e]?e:"left";var r=t.textVerticalAlign||t.textBaseline;"center"===r&&(r="middle"),t.textVerticalAlign=null==r||D[r]?r:"top";t.textPadding&&(t.textPadding=T(t.textPadding))}}function a(t,e,r,n,i){n.rich?s(t,e,r,n,i):o(t,e,r,n,i)}function o(t,e,r,n,i){var a=g(e,"font",n.font||P.DEFAULT_FONT),o=n.textPadding,s=t.__textCotentBlock;s&&!t.__dirty||(s=t.__textCotentBlock=P.parsePlainText(r,a,o,n.truncate));var l=s.outerHeight,u=s.lines,d=s.lineHeight,x=v(l,n,i),w=x.baseX,_=x.baseY,b=x.textAlign,S=x.textVerticalAlign;h(e,n,i,w,_);var M=P.adjustTextY(_,l,S),T=w,k=M,C=c(n);if(C||o){var I=P.getWidth(r,a),A=I;o&&(A+=o[1]+o[3]);var O=P.adjustTextX(w,A,b);C&&f(t,e,n,O,M,A,l),o&&(T=y(w,b,o),k+=o[0])}g(e,"textAlign",b||"left"),g(e,"textBaseline","middle"),g(e,"shadowBlur",n.textShadowBlur||0),g(e,"shadowColor",n.textShadowColor||"transparent"),g(e,"shadowOffsetX",n.textShadowOffsetX||0),g(e,"shadowOffsetY",n.textShadowOffsetY||0),k+=d/2;var D=n.textStrokeWidth,L=p(n.textStroke,D),F=m(n.textFill);L&&(g(e,"lineWidth",D),g(e,"strokeStyle",L)),F&&g(e,"fillStyle",F);for(var R=0;R=0&&(T=C[R],"right"===T.textAlign);)u(t,e,T,n,A,b,F,"right"),O-=T.width,F-=T.width,R--;for(L+=(a-(L-_)-(S-F)-O)/2;D<=R;)T=C[D],u(t,e,T,n,A,b,L+T.width/2,"center"),L+=T.width,D++;b+=A}}function h(t,e,r,n,i){if(r&&e.textRotation){var a=e.textOrigin;"center"===a?(n=r.width/2+r.x,i=r.height/2+r.y):a&&(n=a[0]+r.x,i=a[1]+r.y),t.translate(n,i),t.rotate(-e.textRotation),t.translate(-n,-i)}}function u(t,e,r,n,i,a,o,s){var l=n.rich[r.styleName]||{},h=r.textVerticalAlign,u=a+i/2;"top"===h?u=a+r.height/2:"bottom"===h&&(u=a+i-r.height/2),!r.isLineHolder&&c(l)&&f(t,e,l,"right"===s?o-r.width:"center"===s?o-r.width/2:o,u-r.height/2,r.width,r.height);var d=r.textPadding;d&&(o=y(o,s,d),u-=r.height/2-d[2]-r.textHeight/2),g(e,"shadowBlur",S(l.textShadowBlur,n.textShadowBlur,0)),g(e,"shadowColor",l.textShadowColor||n.textShadowColor||"transparent"),g(e,"shadowOffsetX",S(l.textShadowOffsetX,n.textShadowOffsetX,0)),g(e,"shadowOffsetY",S(l.textShadowOffsetY,n.textShadowOffsetY,0)),g(e,"textAlign",s),g(e,"textBaseline","middle"),g(e,"font",r.font||P.DEFAULT_FONT);var v=p(l.textStroke||n.textStroke,w),x=m(l.textFill||n.textFill),w=b(l.textStrokeWidth,n.textStrokeWidth);v&&(g(e,"lineWidth",w),g(e,"strokeStyle",v),e.strokeText(r.text,o,u)),x&&(g(e,"fillStyle",x),e.fillText(r.text,o,u))}function c(t){return t.textBackgroundColor||t.textBorderWidth&&t.textBorderColor}function f(t,e,r,n,i,a,o){var s=r.textBackgroundColor,l=r.textBorderWidth,h=r.textBorderColor,u=k(s);if(g(e,"shadowBlur",r.textBoxShadowBlur||0),g(e,"shadowColor",r.textBoxShadowColor||"transparent"),g(e,"shadowOffsetX",r.textBoxShadowOffsetX||0),g(e,"shadowOffsetY",r.textBoxShadowOffsetY||0),u||l&&h){e.beginPath();var c=r.textBorderRadius;c?I.buildPath(e,{x:n,y:i,width:a,height:o,r:c}):e.rect(n,i,a,o),e.closePath()}if(u)g(e,"fillStyle",s),e.fill();else if(C(s)){var f=s.image;f=A.createOrUpdateImage(f,null,t,d,s),f&&A.isImageReady(f)&&e.drawImage(f,n,i,a,o)}l&&h&&(g(e,"lineWidth",l),g(e,"strokeStyle",h),e.stroke())}function d(t,e){e.image=t}function v(t,e,r){var n=e.x||0,i=e.y||0,a=e.textAlign,o=e.textVerticalAlign;if(r){var s=e.textPosition;if(s instanceof Array)n=r.x+x(s[0],r.width),i=r.y+x(s[1],r.height);else{var l=P.adjustTextPositionOnRect(s,r,e.textDistance);n=l.x,i=l.y,a=a||l.textAlign,o=o||l.textVerticalAlign}var h=e.textOffset;h&&(n+=h[0],i+=h[1])}return{baseX:n,baseY:i,textAlign:a,textVerticalAlign:o}}function g(t,e,r){return t[e]=r,t[e]}function p(t,e){return null==t||e<=0||"transparent"===t||"none"===t?null:t.image||t.colorStops?"#000":t}function m(t){return null==t||"none"===t?null:t.image||t.colorStops?"#000":t}function x(t,e){return"string"==typeof t?t.lastIndexOf("%")>=0?parseFloat(t)/100*e:parseFloat(t):t}function y(t,e,r){return"right"===e?t-r[1]:"center"===e?t+r[3]/2-r[1]/2:t+r[3]}function w(t,e){return null!=t&&(t||e.textBackgroundColor||e.textBorderWidth&&e.textBorderColor||e.textPadding)}var _=r(0),b=_.retrieve2,S=_.retrieve3,M=_.each,T=_.normalizeCssArray,k=_.isString,C=_.isObject,P=r(5),I=r(21),A=r(10),O={left:1,right:1,center:1},D={top:1,bottom:1,middle:1};e.normalizeTextStyle=n,e.renderText=a,e.getStroke=p,e.getFill=m,e.needDrawText=w},function(t,e){function r(t,e){var r,n,i,a,o=e.x,s=e.y,l=e.width,h=e.height,u=e.r;l<0&&(o+=l,l=-l),h<0&&(s+=h,h=-h),"number"==typeof u?r=n=i=a=u:u instanceof Array?1===u.length?r=n=i=a=u[0]:2===u.length?(r=i=u[0],n=a=u[1]):3===u.length?(r=u[0],n=a=u[1],i=u[2]):(r=u[0],n=u[1],i=u[2],a=u[3]):r=n=i=a=0;var c;r+n>l&&(c=r+n,r*=l/c,n*=l/c),i+a>l&&(c=i+a,i*=l/c,a*=l/c),n+i>h&&(c=n+i,n*=h/c,i*=h/c),r+a>h&&(c=r+a,r*=h/c,a*=h/c),t.moveTo(o+r,s),t.lineTo(o+l-n,s),0!==n&&t.quadraticCurveTo(o+l,s,o+l,s+n),t.lineTo(o+l,s+h-i),0!==i&&t.quadraticCurveTo(o+l,s+h,o+l-i,s+h),t.lineTo(o+a,s+h),0!==a&&t.quadraticCurveTo(o,s+h,o,s+h-a),t.lineTo(o,s+r),0!==r&&t.quadraticCurveTo(o,s,o+r,s)}e.buildPath=r},function(t,e){function r(t){return t%=n,t<0&&(t+=n),t}var n=2*Math.PI;e.normalizeRadian=r},function(t,e,r){function n(t,e,r){var n=e.points,o=e.smooth;if(n&&n.length>=2){if(o&&"spline"!==o){var s=a(n,o,r,e.smoothConstraint);t.moveTo(n[0][0],n[0][1]);for(var l=n.length,h=0;h<(r?l:l-1);h++){var u=s[2*h],c=s[2*h+1],f=n[(h+1)%l];t.bezierCurveTo(u[0],u[1],c[0],c[1],f[0],f[1])}}else{"spline"===o&&(n=i(n,r)),t.moveTo(n[0][0],n[0][1]);for(var h=1,d=n.length;h128){var l=r.data[o]+r.data[o+1]+r.data[o+2];i+=l,++a}}for(var h=i/a,o=0;oh?(n.data[o]=0,n.data[o+1]=0,n.data[o+2]=0,n.data[o+3]=0):(n.data[o]=255,n.data[o+1]=255,n.data[o+2]=255,n.data[o+3]=255)}e.putImageData(n,0,0)}var i=r(7),a=r(27);r(28),r(77);var o=r(78);if(!o.isSupported)throw new Error("Sorry your browser not support wordCloud");i.registerLayout(function(t,e){t.eachSeriesByType("wordCloud",function(r){function s(t){var e=t.detail.item;t.detail.drawn&&r.layoutInstance.ondraw&&(t.detail.drawn.gx+=l.x/m,t.detail.drawn.gy+=l.y/m,r.layoutInstance.ondraw(e[0],e[1],e[2],t.detail.drawn))}var l=a.getLayoutRect(r.getBoxLayoutParams(),{width:e.getWidth(),height:e.getHeight()}),h=r.getData(),u=document.createElement("canvas");u.width=l.width,u.height=l.height;var c=u.getContext("2d"),f=r.get("maskImage");if(f)try{c.drawImage(f,0,0,u.width,u.height),n(u)}catch(t){console.error("Invalid mask image"),console.error(t.toString())}var d=r.get("sizeRange"),v=r.get("rotationRange"),g=h.getDataExtent("value"),p=Math.PI/180,m=r.get("gridSize");o(u,{list:h.mapArray("value",function(t,e){var r=h.getItemModel(e);return[h.getName(e),r.get("textStyle.normal.textSize",!0)||i.number.linearMap(t,g,d),e]}).sort(function(t,e){return e[1]-t[1]}),fontFamily:r.get("textStyle.normal.fontFamily")||r.get("textStyle.emphasis.fontFamily")||t.get("textStyle.fontFamily"),fontWeight:r.get("textStyle.normal.fontWeight")||r.get("textStyle.emphasis.fontWeight")||t.get("textStyle.fontWeight"),gridSize:m,ellipticity:l.height/l.width,minRotation:v[0]*p,maxRotation:v[1]*p,clearCanvas:!f,rotateRatio:1,rotationStep:r.get("rotationStep")*p,drawOutOfBound:r.get("drawOutOfBound"),shuffle:!1,shape:r.get("shape")}),u.addEventListener("wordclouddrawn",s),r.layoutInstance&&r.layoutInstance.dispose(),r.layoutInstance={ondraw:null,dispose:function(){u.removeEventListener("wordclouddrawn",s),u.addEventListener("wordclouddrawn",function(t){t.preventDefault()})}}})}),i.registerPreprocessor(function(t){function e(t){t&&i.util.each(n,function(e){t.hasOwnProperty(e)&&(t["text"+i.format.capitalFirst(e)]=t[e])})}var r=(t||{}).series;!i.util.isArray(r)&&(r=r?[r]:[]);var n=["shadowColor","shadowBlur","shadowOffsetX","shadowOffsetY"];i.util.each(r,function(t){if(t&&"wordCloud"===t.type){var r=t.textStyle||{};e(r.normal),e(r.emphasis)}})})},function(t,e,r){function n(t,e,r,n,i){var a=0,o=0;null==n&&(n=1/0),null==i&&(i=1/0);var s=0;e.eachChild(function(l,h){var u,c,f=l.position,d=l.getBoundingRect(),v=e.childAt(h+1),g=v&&v.getBoundingRect();if("horizontal"===t){var p=d.width+(g?-g.x+d.x:0);u=a+p,u>n||l.newline?(a=0,u=p,o+=s+r,s=d.height):s=Math.max(s,d.height)}else{var m=d.height+(g?-g.y+d.y:0);c=o+m,c>i||l.newline?(a+=s+r,o=0,c=m,s=d.width):s=Math.max(s,d.width)}l.newline||(f[0]=a,f[1]=o,"horizontal"===t?a=u+r:o=c+r)})}function i(t,e,r){var n=e.width,i=e.height,a=v(t.x,n),o=v(t.y,i),s=v(t.x2,n),l=v(t.y2,i);return(isNaN(a)||isNaN(parseFloat(t.x)))&&(a=0),(isNaN(s)||isNaN(parseFloat(t.x2)))&&(s=n),(isNaN(o)||isNaN(parseFloat(t.y)))&&(o=0),(isNaN(l)||isNaN(parseFloat(t.y2)))&&(l=i),r=g.normalizeCssArray(r||0),{width:Math.max(s-a-r[1]-r[3],0),height:Math.max(l-o-r[0]-r[2],0)}}function a(t,e,r){r=g.normalizeCssArray(r||0);var n=e.width,i=e.height,a=v(t.left,n),o=v(t.top,i),s=v(t.right,n),l=v(t.bottom,i),h=v(t.width,n),u=v(t.height,i),c=r[2]+r[0],d=r[1]+r[3],p=t.aspect;switch(isNaN(h)&&(h=n-s-d-a),isNaN(u)&&(u=i-l-c-o),null!=p&&(isNaN(h)&&isNaN(u)&&(p>n/i?h=.8*n:u=.8*i),isNaN(h)&&(h=p*u),isNaN(u)&&(u=h/p)),isNaN(a)&&(a=n-s-h-d),isNaN(o)&&(o=i-l-u-c),t.left||t.right){case"center":a=n/2-h/2-r[3];break;case"right":a=n-h-d}switch(t.top||t.bottom){case"middle":case"center":o=i/2-u/2-r[0];break;case"bottom":o=i-u-c}a=a||0,o=o||0,isNaN(h)&&(h=n-d-a-(s||0)),isNaN(u)&&(u=i-c-o-(l||0));var m=new f(a+r[3],o+r[0],h,u);return m.margin=r,m}function o(t,e,r,n,i){var o=!i||!i.hv||i.hv[0],s=!i||!i.hv||i.hv[1],l=i&&i.boundingMode||"all";if(o||s){var h;if("raw"===l)h="group"===t.type?new f(0,0,+e.width||0,+e.height||0):t.getBoundingRect();else if(h=t.getBoundingRect(),t.needLocalTransform()){var u=t.getLocalTransform();h=h.clone(),h.applyTransform(u)}e=a(c.defaults({width:h.width,height:h.height},e),r,n);var d=t.position,v=o?e.x-h.x:0,g=s?e.y-h.y:0;t.attr("position","raw"===l?[v,g]:[d[0]+v,d[1]+g])}}function s(t,e){return null!=t[x[e][0]]||null!=t[x[e][1]]&&null!=t[x[e][2]]}function l(t,e,r){function n(r,n){var o={},l=0,h={},u=0;if(p(r,function(e){h[e]=t[e]}),p(r,function(t){i(e,t)&&(o[t]=h[t]=e[t]),a(o,t)&&l++,a(h,t)&&u++}),s[n])return a(e,r[1])?h[r[2]]=null:a(e,r[2])&&(h[r[1]]=null),h;if(2!==u&&l){if(l>=2)return o;for(var c=0;c=r.length&&r.push({option:t})}}),r}function u(t){var e=y.createHashMap();S(t,function(t,r){var n=t.exist;n&&e.set(n.id,t)}),S(t,function(t,r){var n=t.option;y.assert(!n||null==n.id||!e.get(n.id)||e.get(n.id)===t,"id duplicates: "+(n&&n.id)),n&&null!=n.id&&e.set(n.id,t),!t.keyInfo&&(t.keyInfo={})}),S(t,function(t,r){var n=t.exist,i=t.option,a=t.keyInfo;if(M(i)){if(a.name=null!=i.name?i.name+"":n?n.name:"\0-",n)a.id=n.id;else if(null!=i.id)a.id=i.id+"";else{var o=0;do{a.id="\0"+a.name+"\0"+o++}while(e.get(a.id))}e.set(a.id,t)}})}function c(t){return M(t)&&t.id&&0===(t.id+"").indexOf("\0_ec_\0")}function f(t,e){function r(t,e,r){for(var i=0,a=t.length;i=0)&&(n={textFill:null,textStroke:t.textStroke,textStrokeWidth:t.textStrokeWidth},t.textFill="#fff",null==t.textStroke&&(t.textStroke=r.autoColor,null==t.textStrokeWidth&&(t.textStrokeWidth=2))),n}function L(t){var e=t.insideRollback;e&&(t.textFill=e.textFill,t.textStroke=e.textStroke,t.textStrokeWidth=e.textStrokeWidth)}function F(t,e){var r=e||e.getModel("textStyle");return[t.fontStyle||r&&r.getShallow("fontStyle")||"",t.fontWeight||r&&r.getShallow("fontWeight")||"",(t.fontSize||r&&r.getShallow("fontSize")||12)+"px",t.fontFamily||r&&r.getShallow("fontFamily")||"sans-serif"].join(" ")}function R(t,e,r,n,i,a){if("function"==typeof i&&(a=i,i=null),n&&n.isAnimationEnabled()){var o=t?"Update":"",s=n.getShallow("animationDuration"+o),l=n.getShallow("animationEasing"+o),h=n.getShallow("animationDelay"+o);"function"==typeof h&&(h=h(i,n.getAnimationDelayParams?n.getAnimationDelayParams(e,i):null)),"function"==typeof s&&(s=s(i)),s>0?e.animateTo(r,s,h||0,l,a,!!a):(e.stopAnimation(),e.attr(r),a&&a())}else e.stopAnimation(),e.attr(r),a&&a()}function B(t,e,r,n,i){R(!0,t,e,r,n,i)}function z(t,e,r,n,i){R(!1,t,e,r,n,i)}function N(t,e){for(var r=U.identity([]);t&&t!==e;)U.mul(r,t.getLocalTransform(),r),t=t.parent;return r}function E(t,e,r){return e&&!Y.isArrayLike(e)&&(e=Q.getLocalTransform(e)),r&&(e=U.invert([],e)),$.applyTransform([],t,e)}function W(t,e,r){var n=0===e[4]||0===e[5]||0===e[0]?1:Math.abs(2*e[4]/e[0]),i=0===e[4]||0===e[5]||0===e[2]?1:Math.abs(2*e[4]/e[2]),a=["left"===t?-n:"right"===t?n:0,"top"===t?-i:"bottom"===t?i:0];return a=E(a,e,r),Math.abs(a[0])>Math.abs(a[1])?a[0]>0?"right":"left":a[1]>0?"bottom":"top"}function q(t,e,r,n){function i(t){var e={position:$.clone(t.position),rotation:t.rotation};return t.shape&&(e.shape=Y.extend({},t.shape)),e}if(t&&e){var a=function(t){var e={};return t.traverse(function(t){!t.isGroup&&t.anid&&(e[t.anid]=t)}),e}(t);e.traverse(function(t){if(!t.isGroup&&t.anid){var e=a[t.anid];if(e){var n=i(t);t.attr(i(e)),B(t,n,r,t.dataIndex)}}})}}function H(t,e){return Y.map(t,function(t){var r=t[0];r=gt(r,e.x),r=pt(r,e.x+e.width);var n=t[1];return n=gt(n,e.y),n=pt(n,e.y+e.height),[r,n]})}function j(t,e){var r=gt(t.x,e.x),n=pt(t.x+t.width,e.x+e.width),i=gt(t.y,e.y),a=pt(t.y+t.height,e.y+e.height);if(n>=r&&a>=i)return{x:r,y:i,width:n-r,height:a-i}}function X(t,e,r){e=Y.extend({rectHover:!0},e);var n=e.style={strokeNoScale:!0};if(r=r||{x:-1,y:-1,width:2,height:2},t)return 0===t.indexOf("image://")?(n.image=t.slice(8),Y.defaults(n,r),new K(e)):a(t.replace("path://",""),e,r,"center")}var Y=r(0),V=r(39),G=r(18),U=r(8),$=r(2),Z=r(1),Q=r(17),K=r(58);e.Image=K;var J=r(59);e.Group=J;var tt=r(60);e.Text=tt;var et=r(61);e.Circle=et;var rt=r(62);e.Sector=rt;var nt=r(64);e.Ring=nt;var it=r(65);e.Polygon=it;var at=r(68);e.Polyline=at;var ot=r(69);e.Rect=ot;var st=r(70);e.Line=st;var lt=r(71);e.BezierCurve=lt;var ht=r(72);e.Arc=ht;var ut=r(73);e.CompoundPath=ut;var ct=r(74);e.LinearGradient=ct;var ft=r(75);e.RadialGradient=ft;var dt=r(3);e.BoundingRect=dt;var vt=Math.round,gt=Math.max,pt=Math.min,mt={},xt=V.mergePath;e.extendShape=n,e.extendPath=i,e.makePath=a,e.makeImage=o,e.mergePath=xt,e.resizePath=l,e.subPixelOptimizeLine=h,e.subPixelOptimizeRect=u,e.subPixelOptimize=c,e.setHoverStyle=M,e.setLabelStyle=T,e.setTextStyle=k,e.setText=C,e.getFont=F,e.updateProps=B,e.initProps=z,e.getTransform=N,e.applyTransform=E,e.transformDirection=W,e.groupTransition=q,e.clipPointsByRect=H,e.clipRectByRect=j,e.createIcon=X},function(t,e,r){function n(t,e,r,n,i,a,o,s,l,h,u){var c=l*(p/180),f=g(c)*(t-r)/2+v(c)*(e-n)/2,m=-1*v(c)*(t-r)/2+g(c)*(e-n)/2,w=f*f/(o*o)+m*m/(s*s);w>1&&(o*=d(w),s*=d(w));var _=(i===a?-1:1)*d((o*o*(s*s)-o*o*(m*m)-s*s*(f*f))/(o*o*(m*m)+s*s*(f*f)))||0,b=_*o*m/s,S=_*-s*f/o,M=(t+r)/2+g(c)*b-v(c)*S,T=(e+n)/2+v(c)*b+g(c)*S,k=y([1,0],[(f-b)/o,(m-S)/s]),C=[(f-b)/o,(m-S)/s],P=[(-1*f-b)/o,(-1*m-S)/s],I=y(C,P);x(C,P)<=-1&&(I=p),x(C,P)>=1&&(I=0),0===a&&I>0&&(I-=2*p),1===a&&I<0&&(I+=2*p),u.addData(h,M,T,o,s,k,I,c,a)}function i(t){if(!t)return[];var e,r=t.replace(/-/g," -").replace(/ /g," ").replace(/ /g,",").replace(/,,/g,",");for(e=0;e0&&""===p[0]&&p.shift();for(var m=0;m0},extendFrom:function(t,e){if(t)for(var r in t)!t.hasOwnProperty(r)||!0!==e&&(!1===e?this.hasOwnProperty(r):null==t[r])||(this[r]=t[r])},set:function(t,e){"string"==typeof t?this[t]=e:this.extendFrom(t,!0)},clone:function(){var t=new this.constructor;return t.extendFrom(this,!0),t},getGradient:function(t,e,i){for(var a="radial"===e.type?n:r,o=a(t,e,i),s=e.colorStops,l=0;l3&&(e=r.call(e,1));for(var i=this._$handlers[t],a=i.length,o=0;o4&&(e=r.call(e,1,e.length-1));for(var i=e[e.length-1],a=this._$handlers[t],o=a.length,s=0;s0&&this.animate(t,!1).when(null==n?500:n,a).delay(i||0),this}};var f=c;t.exports=f},function(t,e,r){function n(t,e){return t[e]}function i(t,e,r){t[e]=r}function a(t,e,r){return(e-t)*r+t}function o(t,e,r){return r>.5?e:t}function s(t,e,r,n,i){var o=t.length;if(1==i)for(var s=0;si)t.length=i;else for(var a=n;a=0&&!(P[r]<=e);r--);r=Math.min(r,_-2)}else{for(r=H;r<_&&!(P[r]>e);r++);r=Math.min(r-1,_-2)}H=r,j=e;var n=P[r+1]-P[r];if(0!==n)if(z=(e-P[r])/n,w)if(E=I[r],N=I[0===r?r:r-1],W=I[r>_-2?_-1:r+1],q=I[r>_-3?_-1:r+2],M)u(N,E,W,q,z,z*z,z*z*z,g(t,i),C);else{var l;if(T)l=u(N,E,W,q,z,z*z,z*z*z,X,1),l=d(X);else{if(k)return o(E,W,z);l=c(N,E,W,q,z,z*z,z*z*z)}x(t,i,l)}else if(M)s(I[r],I[r+1],z,g(t,i),C);else{var l;if(T)s(I[r],I[r+1],z,X,1),l=d(X);else{if(k)return o(I[r],I[r+1],z);l=a(I[r],I[r+1],z)}x(t,i,l)}},V=new p({target:t._target,life:b,loop:t._loop,delay:t._delay,onframe:Y,ondestroy:r});return e&&"spline"!==e&&(V.easing=e),V}}}var p=r(45),m=r(18),x=r(0),y=x.isArrayLike,w=Array.prototype.slice,_=function(t,e,r,a){this._tracks={},this._target=t,this._loop=e||!1,this._getter=r||n,this._setter=a||i,this._clipCount=0,this._delay=0,this._doneList=[],this._onframeList=[],this._clipList=[]};_.prototype={when:function(t,e){var r=this._tracks;for(var n in e)if(e.hasOwnProperty(n)){if(!r[n]){r[n]=[];var i=this._getter(this._target,n);if(null==i)continue;0!==t&&r[n].push({time:0,value:f(i)})}r[n].push({time:t,value:e[n]})}return this},during:function(t){return this._onframeList.push(t),this},pause:function(){for(var t=0;t1&&(a=function(){for(var t in arguments)console.log(arguments[t])});var o=a;t.exports=o},function(t,e,r){var n=r(20),i=r(3),a=new i,o=function(){};o.prototype={constructor:o,drawRectText:function(t,e){var r=this.style;e=r.textRect||e,this.__dirty&&n.normalizeTextStyle(r,!0);var i=r.text;if(null!=i&&(i+=""),n.needDrawText(i,r)){t.save();var o=this.transform;r.transformText?this.setTransform(t):o&&(a.copy(e),a.applyTransform(o),e=a),n.renderText(this,t,i,r,e),t.restore()}}};var s=o;t.exports=s},function(t,e,r){function n(t,e,r){if(0!==t.length){var n,i=t[0],a=i[0],o=i[0],s=i[1],l=i[1];for(n=1;n1e-4)return s[0]=t-r,s[1]=e-n,h[0]=t+r,void(h[1]=e+n);if(g[0]=d(i)*r+t,g[1]=f(i)*n+e,p[0]=d(a)*r+t,p[1]=f(a)*n+e,u(s,g,p),c(h,g,p),i%=v,i<0&&(i+=v),a%=v,a<0&&(a+=v),i>a&&!o?a+=v:ii&&(m[0]=d(w)*r+t,m[1]=f(w)*n+e,u(s,m,s),c(h,m,h))}var l=r(2),h=r(4),u=Math.min,c=Math.max,f=Math.sin,d=Math.cos,v=2*Math.PI,g=l.create(),p=l.create(),m=l.create(),x=[],y=[];e.fromPoints=n,e.fromLine=i,e.fromCubic=a,e.fromQuadratic=o,e.fromArc=s},function(t,e,r){function n(t,e){return Math.abs(t-e)e&&u>n&&u>o&&u>l||u1&&i(),f=x.cubicAt(e,n,o,l,M[0]),g>1&&(d=x.cubicAt(e,n,o,l,M[1]))),2==g?me&&s>n&&s>a||s=0&&h<=1){for(var u=0,c=x.quadraticAt(e,n,a,h),f=0;fr||s<-r)return 0;var l=Math.sqrt(r*r-s*s);S[0]=-l,S[1]=l;var h=Math.abs(n-i);if(h<1e-4)return 0;if(h%_<1e-4){n=0,i=_;var u=a?1:-1;return o>=S[0]+t&&o<=S[1]+t?u:0}if(a){var l=n;n=m(i),i=m(l)}else n=m(n),i=m(i);n>i&&(i+=_);for(var c=0,f=0;f<2;f++){var d=S[f];if(d+t>o){var v=Math.atan2(s,d),u=a?1:-1;v<0&&(v=_+v),(v>=n&&v<=i||v+_>=n&&v+_<=i)&&(v>Math.PI/2&&v<1.5*Math.PI&&(u=-u),c+=u)}}return c}function l(t,e,r,i,l){for(var h=0,u=0,c=0,p=0,m=0,x=0;x1&&(r||(h+=y(u,c,p,m,i,l))),1==x&&(u=t[x],c=t[x+1],p=u,m=c),_){case w.M:p=t[x++],m=t[x++],u=p,c=m;break;case w.L:if(r){if(f.containStroke(u,c,t[x],t[x+1],e,i,l))return!0}else h+=y(u,c,t[x],t[x+1],i,l)||0;u=t[x++],c=t[x++];break;case w.C:if(r){if(d.containStroke(u,c,t[x++],t[x++],t[x++],t[x++],t[x],t[x+1],e,i,l))return!0}else h+=a(u,c,t[x++],t[x++],t[x++],t[x++],t[x],t[x+1],i,l)||0;u=t[x++],c=t[x++];break;case w.Q:if(r){if(v.containStroke(u,c,t[x++],t[x++],t[x],t[x+1],e,i,l))return!0}else h+=o(u,c,t[x++],t[x++],t[x],t[x+1],i,l)||0;u=t[x++],c=t[x++];break;case w.A:var b=t[x++],S=t[x++],M=t[x++],T=t[x++],k=t[x++],C=t[x++],P=(t[x++],1-t[x++]),I=Math.cos(k)*M+b,A=Math.sin(k)*T+S;x>1?h+=y(u,c,I,A,i,l):(p=I,m=A);var O=(i-b)*T/M+b;if(r){if(g.containStroke(b,S,T,k,k+C,P,e,O,l))return!0}else h+=s(b,S,T,k,k+C,P,O,l);u=Math.cos(k+C)*M+b,c=Math.sin(k+C)*T+S;break;case w.R:p=u=t[x++],m=c=t[x++];var D=t[x++],L=t[x++],I=p+D,A=m+L;if(r){if(f.containStroke(p,m,I,m,e,i,l)||f.containStroke(I,m,I,A,e,i,l)||f.containStroke(I,A,p,A,e,i,l)||f.containStroke(p,A,p,m,e,i,l))return!0}else h+=y(I,m,I,A,i,l),h+=y(p,A,p,m,i,l);break;case w.Z:if(r){if(f.containStroke(u,c,p,m,e,i,l))return!0}else h+=y(u,c,p,m,i,l);u=p,c=m}}return r||n(c,m)||(h+=y(u,c,p,m,i,l)||0),0!==h}function h(t,e,r){return l(t,0,!1,e,r)}function u(t,e,r,n){return l(t,e,!0,r,n)}var c=r(6),f=r(51),d=r(52),v=r(53),g=r(54),p=r(22),m=p.normalizeRadian,x=r(4),y=r(55),w=c.CMD,_=2*Math.PI,b=1e-4,S=[-1,-1,-1],M=[-1,-1];e.contain=h,e.containStroke=u},function(t,e){function r(t,e,r,n,i,a,o){if(0===i)return!1;var s=i,l=0,h=t;if(o>e+s&&o>n+s||ot+s&&a>r+s||ae+f&&c>n+f&&c>o+f&&c>l+f||ct+f&&u>r+f&&u>a+f&&u>s+f||ue+u&&h>n+u&&h>o+u||ht+u&&l>r+u&&l>i+u||lr||f+ci&&(i+=o);var v=Math.atan2(u,h);return v<0&&(v+=o),v>=n&&v<=i||v+o>=n&&v+o<=i}var i=r(22),a=i.normalizeRadian,o=2*Math.PI;e.containStroke=n},function(t,e){function r(t,e,r,n,i,a){if(a>e&&a>n||ai?o:0}t.exports=r},function(t,e){var r=function(t,e){this.image=t,this.repeat=e,this.type="pattern"};r.prototype.getCanvasPattern=function(t){return t.createPattern(this.image,this.repeat||"repeat")};var n=r;t.exports=n},function(t,e,r){function n(t,e){var r,n,i,a,c,f,d=t.data,v=s.M,g=s.C,p=s.L,m=s.R,x=s.A,y=s.Q;for(i=0,a=0;i=0&&(r.splice(n,0,t),this._doAdd(t))}return this},_doAdd:function(t){t.parent&&t.parent.remove(t),t.parent=this;var e=this.__storage,r=this.__zr;e&&e!==t.__storage&&(e.addToStorage(t),t instanceof o&&t.addChildrenToStorage(e)),r&&r.refresh()},remove:function(t){var e=this.__zr,r=this.__storage,i=this._children,a=n.indexOf(i,t);return a<0?this:(i.splice(a,1),t.parent=null,r&&(r.delFromStorage(t),t instanceof o&&t.delChildrenFromStorage(r)),e&&e.refresh(),this)},removeAll:function(){var t,e,r=this._children,n=this.__storage;for(e=0;e=11?function(){var e,r=this.__clipPaths,n=this.style;if(r)for(var i=0;ir-2?r-1:d+1],c=t[d>r-3?r-1:d+2]);var p=v*v,m=v*p;i.push([n(h[0],g[0],u[0],c[0],v,p,m),n(h[1],g[1],u[1],c[1],v,p,m)])}return i}var a=r(2),o=a.distance;t.exports=i},function(t,e,r){function n(t,e,r,n){var i,f,d,v,g=[],p=[],m=[],x=[];if(n){d=[1/0,1/0],v=[-1/0,-1/0];for(var y=0,w=t.length;y=0?1/(Math.cos(2*Math.PI/10-e)+3.07768*Math.sin(2*Math.PI/10-e)):1/(Math.cos(e)+3.07768*Math.sin(e))}}n.gridSize=Math.max(Math.floor(n.gridSize),4);var h,u,c,f,d,v,g,p=n.gridSize,m=p-n.maskGapWidth,x=Math.abs(n.maxRotation-n.minRotation),y=Math.min(n.maxRotation,n.minRotation),w=n.rotationStep;switch(n.color){case"random-dark":g=function(){return r(10,50)};break;case"random-light":g=function(){return r(50,90)};break;default:"function"==typeof n.color&&(g=n.color)}var _=null;"function"==typeof n.classes&&(_=n.classes);var b,S=!1,M=[],T=function(t){var e,r,n=t.currentTarget,i=n.getBoundingClientRect();t.touches?(e=t.touches[0].clientX,r=t.touches[0].clientY):(e=t.clientX,r=t.clientY);var a=e-i.left,o=r-i.top,s=Math.floor(a*(n.width/i.width||1)/p),l=Math.floor(o*(n.height/i.height||1)/p);return M[s][l]},k=function(t){var e=T(t);if(b!==e){if(b=e,!e)return void n.hover(void 0,void 0,t);n.hover(e.item,e.dimension,t)}},C=function(t){var e=T(t);e&&(n.click(e.item,e.dimension,t),t.preventDefault())},P=[],I=function(t){if(P[t])return P[t];var e=8*t,r=e,i=[];for(0===t&&i.push([f[0],f[1],0]);r--;){var a=1;"circle"!==n.shape&&(a=n.shape(r/e*2*Math.PI)),i.push([f[0]+t*a*Math.cos(-r/e*2*Math.PI),f[1]+t*a*Math.sin(-r/e*2*Math.PI)*n.ellipticity,r/e*2*Math.PI])}return P[t]=i,i},A=function(){return n.abortThreshold>0&&(new Date).getTime()-v>n.abortThreshold},O=function(){return 0===n.rotateRatio?0:Math.random()>n.rotateRatio?0:0===x?y:y+Math.round(Math.random()*x/w)*w},D=function(t,e,r){var i=n.weightFactor(e);if(i<=n.minSize)return!1;var a=1;iP[1]&&(P[1]=C),SP[2]&&(P[2]=S);break t}}return{mu:a,occupied:k,bounds:P,gw:y,gh:x,fillTextOffsetX:g,fillTextOffsetY:m,fillTextWidth:h,fillTextHeight:u,fontSize:i}},L=function(t,e,r,i,a){for(var o=a.length;o--;){var s=t+a[o][0],l=e+a[o][1];if(s>=u||l>=c||s<0||l<0){if(!n.drawOutOfBound)return!1}else if(!h[s][l])return!1}return!0},F=function(e,r,i,a,o,s,l,h,u){var c,f=i.fontSize;c=g?g(a,o,f,s,l):n.color;var d;d=_?_(a,o,f,s,l):n.classes;var v=i.bounds;v[3],v[0],v[1],v[3],v[2],v[0],t.forEach(function(t){if(t.getContext){var o=t.getContext("2d"),s=i.mu;o.save(),o.scale(1/s,1/s),o.font=n.fontWeight+" "+(f*s).toString(10)+"px "+n.fontFamily,o.fillStyle=c,o.translate((e+i.gw/2)*p*s,(r+i.gh/2)*p*s),0!==h&&o.rotate(-h),o.textBaseline="middle",o.fillText(a,i.fillTextOffsetX*s,(i.fillTextOffsetY+.5*f)*s),o.restore()}else{var l=document.createElement("span"),v="";v="rotate("+-h/Math.PI*180+"deg) ",1!==i.mu&&(v+="translateX(-"+i.fillTextWidth/4+"px) scale("+1/i.mu+")");var g={position:"absolute",display:"block",font:n.fontWeight+" "+f*i.mu+"px "+n.fontFamily,left:(e+i.gw/2)*p+i.fillTextOffsetX+"px",top:(r+i.gh/2)*p+i.fillTextOffsetY+"px",width:i.fillTextWidth+"px",height:i.fillTextHeight+"px",lineHeight:f+"px",whiteSpace:"nowrap",transform:v,webkitTransform:v,msTransform:v,transformOrigin:"50% 40%",webkitTransformOrigin:"50% 40%",msTransformOrigin:"50% 40%"};c&&(g.color=c),l.textContent=a;for(var m in g)l.style[m]=g[m];if(u)for(var x in u)l.setAttribute(x,u[x]);d&&(l.className+=d),t.appendChild(l)}})},R=function(e,r,n,i,a){if(!(e>=u||r>=c||e<0||r<0)){if(h[e][r]=!1,n){t[0].getContext("2d").fillRect(e*p,r*p,m,m)}S&&(M[e][r]={item:a,dimension:i})}},B=function(e,r,i,a,o,s){var l,h=o.occupied,f=n.drawMask;f&&(l=t[0].getContext("2d"),l.save(),l.fillStyle=n.maskColor);var d;if(S){var v=o.bounds;d={x:(e+v[3])*p,y:(r+v[0])*p,w:(v[1]-v[3]+1)*p,h:(v[2]-v[0]+1)*p}}for(var g=h.length;g--;){var m=e+h[g][0],x=r+h[g][1];m>=u||x>=c||m<0||x<0||R(m,x,f,d,s)}f&&l.restore()},z=function(t){var e,r,i;Array.isArray(t)?(e=t[0],r=t[1]):(e=t.word,r=t.weight,i=t.attributes);var a=O(),o=D(e,r,a);if(!o)return!1;if(A())return!1;if(!n.drawOutOfBound){var l=o.bounds;if(l[1]-l[3]+1>u||l[2]-l[0]+1>c)return!1}for(var h=d+1;h--;){var f=I(d-h);n.shuffle&&(f=[].concat(f),s(f));for(var v=0;v=n.list.length)return w(P),N("wordcloudstop",!1),void _("wordcloudstart",T);v=(new Date).getTime();var e=z(n.list[o]),r=!N("wordclouddrawn",!0,{item:n.list[o],drawn:e});if(A()||r)return w(P),n.abort(),N("wordcloudabort",!1),N("wordcloudstop",!1),void _("wordcloudstart",T);o++,P=y(t,n.wait)},n.wait)}}()}};l.isSupported=a,l.minFontSize=o,n=[],void 0!==(i=function(){return l}.apply(e,n))&&(t.exports=i)}()}])}); \ No newline at end of file diff --git a/static/assets/libs/echarts/echarts.min.js b/static/assets/libs/echarts/echarts.min.js new file mode 100644 index 0000000..58e3398 --- /dev/null +++ b/static/assets/libs/echarts/echarts.min.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e(t.echarts={})}(this,function(t){"use strict";var e=2311,n=function(){return e++},v="object"==typeof wx&&"function"==typeof wx.getSystemInfoSync?{browser:{},os:{},node:!1,wxa:!0,canvasSupported:!0,svgSupported:!1,touchEventsSupported:!0,domSupported:!1}:"undefined"==typeof document&&"undefined"!=typeof self?{browser:{},os:{},node:!1,worker:!0,canvasSupported:!0,domSupported:!1}:"undefined"==typeof navigator?{browser:{},os:{},node:!0,worker:!1,canvasSupported:!0,svgSupported:!0,domSupported:!1}:function(t){var e={},i=t.match(/Firefox\/([\d.]+)/),n=t.match(/MSIE\s([\d.]+)/)||t.match(/Trident\/.+?rv:(([\d.]+))/),o=t.match(/Edge\/([\d.]+)/),a=/micromessenger/i.test(t);i&&(e.firefox=!0,e.version=i[1]);n&&(e.ie=!0,e.version=n[1]);o&&(e.edge=!0,e.version=o[1]);a&&(e.weChat=!0);return{browser:e,os:{},node:!1,canvasSupported:!!document.createElement("canvas").getContext,svgSupported:"undefined"!=typeof SVGRect,touchEventsSupported:"ontouchstart"in window&&!e.ie&&!e.edge,pointerEventsSupported:"onpointerdown"in window&&(e.edge||e.ie&&11<=e.version),domSupported:"undefined"!=typeof document}}(navigator.userAgent);var s={"[object Function]":1,"[object RegExp]":1,"[object Date]":1,"[object Error]":1,"[object CanvasGradient]":1,"[object CanvasPattern]":1,"[object Image]":1,"[object Canvas]":1},l={"[object Int8Array]":1,"[object Uint8Array]":1,"[object Uint8ClampedArray]":1,"[object Int16Array]":1,"[object Uint16Array]":1,"[object Int32Array]":1,"[object Uint32Array]":1,"[object Float32Array]":1,"[object Float64Array]":1},u=Object.prototype.toString,i=Array.prototype,r=i.forEach,h=i.filter,o=i.slice,c=i.map,d=i.reduce,a={};function f(t,e){"createCanvas"===t&&(y=null),a[t]=e}function L(t){if(null==t||"object"!=typeof t)return t;var e=t,i=u.call(t);if("[object Array]"===i){if(!$(t)){e=[];for(var n=0,o=t.length;n>1)%2;s.cssText=["position: absolute","visibility: hidden","padding: 0","margin: 0","border-width: 0","user-select: none","width:0","height:0",n[l]+":0",o[u]+":0",n[1-l]+":auto",o[1-u]+":auto",""].join("!important;"),t.appendChild(r),i.push(r)}return i}(e,a),a,o);if(r)return r(t,i,n),!0}return!1}function zt(t){return"CANVAS"===t.nodeName.toUpperCase()}var Bt="undefined"!=typeof window&&!!window.addEventListener,Vt=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Gt=[];function Ft(t,e,i,n){return i=i||{},n||!v.canvasSupported?Wt(t,e,i):v.browser.firefox&&null!=e.layerX&&e.layerX!==e.offsetX?(i.zrX=e.layerX,i.zrY=e.layerY):null!=e.offsetX?(i.zrX=e.offsetX,i.zrY=e.offsetY):Wt(t,e,i),i}function Wt(t,e,i){if(v.domSupported&&t.getBoundingClientRect){var n=e.clientX,o=e.clientY;if(zt(t)){var a=t.getBoundingClientRect();return i.zrX=n-a.left,void(i.zrY=o-a.top)}if(Rt(Gt,t,n,o))return i.zrX=Gt[0],void(i.zrY=Gt[1])}i.zrX=i.zrY=0}function Ht(t){return t||window.event}function Zt(t,e,i){if(null!=(e=Ht(e)).zrX)return e;var n=e.type;if(n&&0<=n.indexOf("touch")){var o="touchend"!==n?e.targetTouches[0]:e.changedTouches[0];o&&Ft(t,o,e,i)}else Ft(t,e,e,i),e.zrDelta=e.wheelDelta?e.wheelDelta/120:-(e.detail||0)/3;var a=e.button;return null==e.which&&void 0!==a&&Vt.test(e.type)&&(e.which=1&a?1:2&a?3:4&a?2:0),e}function Ut(t,e,i,n){Bt?t.addEventListener(e,i,n):t.attachEvent("on"+e,i)}var Xt=Bt?function(t){t.preventDefault(),t.stopPropagation(),t.cancelBubble=!0}:function(t){t.returnValue=!1,t.cancelBubble=!0};function Yt(t){return 2===t.which||3===t.which}function jt(){this._track=[]}function qt(t){var e=t[1][0]-t[0][0],i=t[1][1]-t[0][1];return Math.sqrt(e*e+i*i)}jt.prototype={constructor:jt,recognize:function(t,e,i){return this._doTrack(t,e,i),this._recognize(t)},clear:function(){return this._track.length=0,this},_doTrack:function(t,e,i){var n=t.touches;if(n){for(var o={points:[],touches:[],target:e,event:t},a=0,r=n.length;an.getWidth()||i<0||i>n.getHeight()}te.prototype={constructor:te,setHandlerProxy:function(e){this.proxy&&this.proxy.dispose(),e&&(E(ee,function(t){e.on&&e.on(t,this[t],this)},this),e.handler=this),this.proxy=e},mousemove:function(t){var e=t.zrX,i=t.zrY,n=ne(this,e,i),o=this._hovered,a=o.target;a&&!a.__zr&&(a=(o=this.findHover(o.x,o.y)).target);var r=this._hovered=n?{x:e,y:i}:this.findHover(e,i),s=r.target,l=this.proxy;l.setCursor&&l.setCursor(s?s.cursor:"default"),a&&s!==a&&this.dispatchToElement(o,"mouseout",t),this.dispatchToElement(r,"mousemove",t),s&&s!==a&&this.dispatchToElement(r,"mouseover",t)},mouseout:function(t){var e=t.zrEventControl,i=t.zrIsToLocalDOM;"only_globalout"!==e&&this.dispatchToElement(this._hovered,"mouseout",t),"no_globalout"!==e&&(i||this.trigger("globalout",{type:"globalout",event:t}))},resize:function(t){this._hovered={}},dispatch:function(t,e){var i=this[t];i&&i.call(this,e)},dispose:function(){this.proxy.dispose(),this.storage=this.proxy=this.painter=null},setCursorStyle:function(t){var e=this.proxy;e.setCursor&&e.setCursor(t)},dispatchToElement:function(t,e,i){var n=(t=t||{}).target;if(!n||!n.silent){for(var o="on"+e,a=function(t,e,i){return{type:t,event:i,target:e.target,topTarget:e.topTarget,cancelBubble:!1,offsetX:i.zrX,offsetY:i.zrY,gestureEvent:i.gestureEvent,pinchX:i.pinchX,pinchY:i.pinchY,pinchScale:i.pinchScale,wheelDelta:i.zrDelta,zrByTouch:i.zrByTouch,which:i.which,stop:Jt}}(e,t,i);n&&(n[o]&&(a.cancelBubble=n[o].call(n,a)),n.trigger(e,a),n=n.parent,!a.cancelBubble););a.cancelBubble||(this.trigger(e,a),this.painter&&this.painter.eachOtherLayer(function(t){"function"==typeof t[o]&&t[o].call(t,a),t.trigger&&t.trigger(e,a)}))}},findHover:function(t,e,i){for(var n=this.storage.getDisplayList(),o={x:t,y:e},a=n.length-1;0<=a;a--){var r;if(n[a]!==i&&!n[a].ignore&&(r=ie(n[a],t,e))&&(o.topTarget||(o.topTarget=n[a]),r!==$t)){o.target=n[a];break}}return o},processGesture:function(t,e){this._gestureMgr||(this._gestureMgr=new jt);var i=this._gestureMgr;"start"===e&&i.clear();var n=i.recognize(t,this.findHover(t.zrX,t.zrY,null).target,this.proxy.dom);if("end"===e&&i.clear(),n){var o=n.type;t.gestureEvent=o,this.dispatchToElement({target:n.target},o,n.event)}}},E(["click","mousedown","mouseup","mousewheel","dblclick","contextmenu"],function(r){te.prototype[r]=function(t){var e,i,n=t.zrX,o=t.zrY,a=ne(this,n,o);if("mouseup"===r&&a||(i=(e=this.findHover(n,o)).target),"mousedown"===r)this._downEl=i,this._downPoint=[t.zrX,t.zrY],this._upEl=i;else if("mouseup"===r)this._upEl=i;else if("click"===r){if(this._downEl!==this._upEl||!this._downPoint||4=this._maxSize&&0>4|(3840&n)>>8,240&n|(240&n)>>4,15&n|(15&n)<<4,1),Ge(t,e),e):void Re(e,0,0,0,1):7===o.length?0<=(n=parseInt(o.substr(1),16))&&n<=16777215?(Re(e,(16711680&n)>>16,(65280&n)>>8,255&n,1),Ge(t,e),e):void Re(e,0,0,0,1):void 0;var a=o.indexOf("("),r=o.indexOf(")");if(-1!==a&&r+1===o.length){var s=o.substr(0,a),l=o.substr(a+1,r-(a+1)).split(","),u=1;switch(s){case"rgba":if(4!==l.length)return void Re(e,0,0,0,1);u=Ne(l.pop());case"rgb":return 3!==l.length?void Re(e,0,0,0,1):(Re(e,Pe(l[0]),Pe(l[1]),Pe(l[2]),u),Ge(t,e),e);case"hsla":return 4!==l.length?void Re(e,0,0,0,1):(l[3]=Ne(l[3]),We(l,e),Ge(t,e),e);case"hsl":return 3!==l.length?void Re(e,0,0,0,1):(We(l,e),Ge(t,e),e);default:return}}Re(e,0,0,0,1)}}function We(t,e){var i=(parseFloat(t[0])%360+360)%360/360,n=Ne(t[1]),o=Ne(t[2]),a=o<=.5?o*(n+1):o+n-o*n,r=2*o-a;return Re(e=e||[],Le(255*Oe(r,a,i+1/3)),Le(255*Oe(r,a,i)),Le(255*Oe(r,a,i-1/3)),1),4===t.length&&(e[3]=t[3]),e}function He(t,e){var i=Fe(t);if(i){for(var n=0;n<3;n++)i[n]=e<0?i[n]*(1-e)|0:(255-i[n])*e+i[n]|0,255e);i++);i=Math.min(i-1,u-2)}C=e;var n=g[(D=i)+1]-g[i];if(0!=n)if(S=(e-g[i])/n,l)if(I=m[i],M=m[0===i?i:i-1],T=m[u-2=i.x&&t<=i.x+i.width&&e>=i.y&&e<=i.y+i.height},clone:function(){return new Di(this.x,this.y,this.width,this.height)},copy:function(t){this.x=t.x,this.y=t.y,this.width=t.width,this.height=t.height},plain:function(){return{x:this.x,y:this.y,width:this.width,height:this.height}}},Di.create=function(t){return new Di(t.x,t.y,t.width,t.height)};var Ci=function(t){for(var e in t=t||{},_i.call(this,t),t)t.hasOwnProperty(e)&&(this[e]=t[e]);this._children=[],this.__storage=null,this.__dirty=!0};Ci.prototype={constructor:Ci,isGroup:!0,type:"group",silent:!1,children:function(){return this._children.slice()},childAt:function(t){return this._children[t]},childOfName:function(t){for(var e=this._children,i=0;i>>1])<0?l=a:s=1+a;var u=n-s;switch(u){case 3:t[s+3]=t[s+2];case 2:t[s+2]=t[s+1];case 1:t[s+1]=t[s];break;default:for(;0>>1);0>>1);a(t,e[i+h])<0?l=h:r=h+1}return l}function Ri(p,g){var r,s,m=ki,l=0,v=[];function e(t){var e=r[t],i=s[t],n=r[t+1],o=s[t+1];s[t]=i+o,t===l-3&&(r[t+1]=r[t+2],s[t+1]=s[t+2]),l--;var a=Ei(p[n],p,e,i,0,g);e+=a,0!==(i-=a)&&0!==(o=Oi(p[e+i-1],p,n,o,o-1,g))&&(i<=o?function(t,e,i,n){var o=0;for(o=0;os[t+1])break;e(t)}},this.forceMergeRuns=function(){for(;1>=1;return t+e}(o);do{if((a=Pi(t,i,n,e))=e.maxIterations){t+=e.ellipsis;break}var s=0===r?bn(t,o,e.ascCharWidth,e.cnCharWidth):0f)return{lines:[],width:0,height:0};C.textWidth=pn(C.text,w);var S=x.textWidth,M=null==S||"auto"===S;if("string"==typeof S&&"%"===S.charAt(S.length-1))C.percentWidth=S,u.push(C),S=0;else{if(M){S=C.textWidth;var I=x.textBackgroundColor,T=I&&I.image;T&&sn(T=on(T))&&(S=Math.max(S,T.width*b/T.height))}var A=_?_[1]+_[3]:0;S+=A;var D=null!=d?d-v:null;null!=D&&Dn[0]){for(r=0;rt);r++);a=i[n[r]]}if(n.splice(r+1,0,t),!(i[t]=e).virtual)if(a){var l=a.dom;l.nextSibling?s.insertBefore(e.dom,l.nextSibling):s.appendChild(e.dom)}else s.firstChild?s.insertBefore(e.dom,s.firstChild):s.appendChild(e.dom)}else vi("Layer of zlevel "+t+" is not valid")},eachLayer:function(t,e){var i,n,o=this._zlevelList;for(n=0;n=a.length&&a.push({option:t})}}),a}function Zo(t){var r=Q();Eo(t,function(t,e){var i=t.exist;i&&r.set(i.id,t)}),Eo(t,function(t,e){var i=t.option;Y(!i||null==i.id||!r.get(i.id)||r.get(i.id)===t,"id duplicates: "+(i&&i.id)),i&&null!=i.id&&r.set(i.id,t),t.keyInfo||(t.keyInfo={})}),Eo(t,function(t,e){var i=t.exist,n=t.option,o=t.keyInfo;if(Ro(n)){if(o.name=null!=n.name?n.name+"":i?i.name:Bo+e,i)o.id=i.id;else if(null!=n.id)o.id=n.id+"";else for(var a=0;o.id="\0"+o.name+"\0"+a++,r.get(o.id););r.set(o.id,t)}})}function Uo(t){var e=t.name;return!(!e||!e.indexOf(Bo))}function Xo(t){return Ro(t)&&t.id&&0===(t.id+"").indexOf("\0_ec_\0")}function Yo(e,t){return null!=t.dataIndexInside?t.dataIndexInside:null!=t.dataIndex?O(t.dataIndex)?N(t.dataIndex,function(t){return e.indexOfRawIndex(t)}):e.indexOfRawIndex(t.dataIndex):null!=t.name?O(t.name)?N(t.name,function(t){return e.indexOfName(t)}):e.indexOfName(t.name):void 0}function jo(){var e="__\0ec_inner_"+qo+++"_"+Math.random().toFixed(5);return function(t){return t[e]||(t[e]={})}}var qo=0;function Ko(s,l,u){if(R(l)){var t={};t[l+"Index"]=0,l=t}var e=u&&u.defaultMainType;!e||$o(l,e+"Index")||$o(l,e+"Id")||$o(l,e+"Name")||(l[e+"Index"]=0);var h={};return Eo(l,function(t,e){t=l[e];if("dataIndex"!==e&&"dataIndexInside"!==e){var i=e.match(/^(\w+)(Index|Id|Name)$/)||[],n=i[1],o=(i[2]||"").toLowerCase();if(!(!n||!o||null==t||"index"===o&&"none"===t||u&&u.includeMainTypes&&_(u.includeMainTypes,n)<0)){var a={mainType:n};"index"===o&&"all"===t||(a[o]=t);var r=s.queryComponents(a);h[n+"Models"]=r,h[n+"Model"]=r[0]}}else h[e]=t}),h}function $o(t,e){return t&&t.hasOwnProperty(e)}function Jo(t,e,i){t.setAttribute?t.setAttribute(e,i):t[e]=i}function Qo(t){return"auto"===t?v.domSupported?"html":"richText":t||"html"}function ta(t,i){var n=Q(),o=[];return E(t,function(t){var e=i(t);(n.get(e)||(o.push(e),n.set(e,[]))).push(t)}),{keys:o,buckets:n}}var ea=".",ia="___EC__COMPONENT__CONTAINER___";function na(t){var e={main:"",sub:""};return t&&(t=t.split(ea),e.main=t[0]||"",e.sub=t[1]||""),e}function oa(t){(t.$constructor=t).extend=function(t){function e(){t.$constructor?t.$constructor.apply(this,arguments):i.apply(this,arguments)}var i=this;return k(e.prototype,t),e.extend=this.extend,e.superCall=sa,e.superApply=la,w(e,this),e.superClass=i,e}}var aa=0;function ra(t){var e=["__\0is_clz",aa++,Math.random().toFixed(3)].join("_");t.prototype[e]=!0,t.isInstance=function(t){return!(!t||!t[e])}}function sa(t,e){var i=U(arguments,2);return this.superClass.prototype[e].apply(t,i)}function la(t,e,i){return this.superClass.prototype[e].apply(t,i)}function ua(i,t){t=t||{};var o={};if(i.registerClass=function(t,e){if(e)if(function(t){Y(/^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(t),'componentType "'+t+'" illegal')}(e),(e=na(e)).sub){if(e.sub!==ia){(function(t){var e=o[t.main];e&&e[ia]||((e=o[t.main]={})[ia]=!0);return e})(e)[e.sub]=t}}else o[e.main]=t;return t},i.getClass=function(t,e,i){var n=o[t];if(n&&n[ia]&&(n=e?n[e]:null),i&&!n)throw new Error(e?"Component "+t+"."+(e||"")+" not exists. Load it first.":t+".type should be specified.");return n},i.getClassesByMainType=function(t){t=na(t);var i=[],e=o[t.main];return e&&e[ia]?E(e,function(t,e){e!==ia&&i.push(t)}):i.push(e),i},i.hasClass=function(t){return t=na(t),!!o[t.main]},i.getAllClassMainTypes=function(){var i=[];return E(o,function(t,e){i.push(e)}),i},i.hasSubTypes=function(t){t=na(t);var e=o[t.main];return e&&e[ia]},i.parseClassType=na,t.registerWhenExtend){var n=i.extend;n&&(i.extend=function(t){var e=n.call(this,t);return i.registerClass(e,t.type)})}return i}function ha(s){for(var t=0;tthis._ux||or(e-this._yi)>this._uy||this._len<5;return this.addData(ja.L,t,e),this._ctx&&i&&(this._needsDash()?this._dashedLineTo(t,e):this._ctx.lineTo(t,e)),i&&(this._xi=t,this._yi=e),this},bezierCurveTo:function(t,e,i,n,o,a){return this.addData(ja.C,t,e,i,n,o,a),this._ctx&&(this._needsDash()?this._dashedBezierTo(t,e,i,n,o,a):this._ctx.bezierCurveTo(t,e,i,n,o,a)),this._xi=o,this._yi=a,this},quadraticCurveTo:function(t,e,i,n){return this.addData(ja.Q,t,e,i,n),this._ctx&&(this._needsDash()?this._dashedQuadraticTo(t,e,i,n):this._ctx.quadraticCurveTo(t,e,i,n)),this._xi=i,this._yi=n,this},arc:function(t,e,i,n,o,a){return this.addData(ja.A,t,e,i,i,n,o-n,0,a?0:1),this._ctx&&this._ctx.arc(t,e,i,n,o,a),this._xi=er(o)*i+t,this._yi=ir(o)*i+e,this},arcTo:function(t,e,i,n,o){return this._ctx&&this._ctx.arcTo(t,e,i,n,o),this},rect:function(t,e,i,n){return this._ctx&&this._ctx.rect(t,e,i,n),this.addData(ja.R,t,e,i,n),this},closePath:function(){this.addData(ja.Z);var t=this._ctx,e=this._x0,i=this._y0;return t&&(this._needsDash()&&this._dashedLineTo(e,i),t.closePath()),this._xi=e,this._yi=i,this},fill:function(t){t&&t.fill(),this.toStatic()},stroke:function(t){t&&t.stroke(),this.toStatic()},setLineDash:function(t){if(t instanceof Array){this._lineDash=t;for(var e=this._dashIdx=0,i=0;ie.length&&(this._expandData(),e=this.data);for(var i=0;il||or(r-o)>u||c===h-1)&&(t.lineTo(a,r),n=a,o=r);break;case ja.C:t.bezierCurveTo(s[c++],s[c++],s[c++],s[c++],s[c++],s[c++]),n=s[c-2],o=s[c-1];break;case ja.Q:t.quadraticCurveTo(s[c++],s[c++],s[c++],s[c++]),n=s[c-2],o=s[c-1];break;case ja.A:var f=s[c++],p=s[c++],g=s[c++],m=s[c++],v=s[c++],y=s[c++],x=s[c++],_=s[c++],w=m=yr[n=0]+t&&r<=yr[1]+t?h:0}if(a){l=n;n=cr(o),o=cr(l)}else n=cr(n),o=cr(o);oMath.PI/2&&p<1.5*Math.PI&&(h=-h),c+=h)}}return c}function Sr(t,e,i,n,o){for(var a=0,r=0,s=0,l=0,u=0,h=0;hMath.abs(a[1])?0=e[1])return i[1]}else{if(t>=e[0])return i[0];if(t<=e[1])return i[1]}else{if(t===e[0])return i[0];if(t===e[1])return i[1]}return(t-e[0])/o*a+i[0]}function Rl(t,e){switch(t){case"center":case"middle":t="50%";break;case"left":case"top":t="0%";break;case"right":case"bottom":t="100%"}return"string"==typeof t?function(t){return t.replace(/^\s+|\s+$/g,"")}(t).match(/%$/)?parseFloat(t)/100*e:parseFloat(t):null==t?NaN:+t}function zl(t,e,i){return null==e&&(e=10),e=Math.min(Math.max(0,e),20),t=(+t).toFixed(e),i?t:+t}function Bl(t){return t.sort(function(t,e){return t-e}),t}function Vl(t){if(t=+t,isNaN(t))return 0;for(var e=1,i=0;Math.round(t*e)/e!==t;)e*=10,i++;return i}function Gl(t){var e=t.toString(),i=e.indexOf("e");if(0h&&(h=u[d],c=d);++s[c],u[c]=0,++l}return s[e]/o}var Hl=9007199254740991;function Zl(t){var e=2*Math.PI;return(t%e+e)%e}function Ul(t){return-Ol"'])/g,ou={"&":"&","<":"<",">":">",'"':""","'":"'"};function au(t){return null==t?"":(t+"").replace(nu,function(t,e){return ou[e]})}function ru(t,e){return"{"+t+(null==e?"":e)+"}"}var su=["a","b","c","d","e","f","g"];function lu(t,e,i){O(e)||(e=[e]);var n=e.length;if(!n)return"";for(var o=e[0].$vars||[],a=0;a':'':{renderMode:o,content:"{marker"+a+"|} ",style:{color:i}}:""}function cu(t,e){return"0000".substr(0,e-(t+="").length)+t}function du(t,e,i){"week"!==t&&"month"!==t&&"quarter"!==t&&"half-year"!==t&&"year"!==t||(t="MM-dd\nyyyy");var n=Yl(e),o=i?"UTC":"",a=n["get"+o+"FullYear"](),r=n["get"+o+"Month"]()+1,s=n["get"+o+"Date"](),l=n["get"+o+"Hours"](),u=n["get"+o+"Minutes"](),h=n["get"+o+"Seconds"](),c=n["get"+o+"Milliseconds"]();return t=t.replace("MM",cu(r,2)).replace("M",r).replace("yyyy",a).replace("yy",a%100).replace("dd",cu(s,2)).replace("d",s).replace("hh",cu(l,2)).replace("h",l).replace("mm",cu(u,2)).replace("m",u).replace("ss",cu(h,2)).replace("s",h).replace("SSS",cu(c,3))}function fu(t){return t?t.charAt(0).toUpperCase()+t.substr(1):t}var pu=xn;var gu=(Object.freeze||Object)({addCommas:tu,toCamelCase:eu,normalizeCssArray:iu,encodeHTML:au,formatTpl:lu,formatTplSimple:uu,getTooltipMarker:hu,formatTime:du,capitalFirst:fu,truncateText:pu,getTextBoundingRect:function(t){return gn(t.text,t.font,t.textAlign,t.textVerticalAlign,t.textPadding,t.textLineHeight,t.rich,t.truncate)},getTextRect:function(t,e,i,n,o,a,r,s){return gn(t,e,i,n,o,s,a,r)}}),mu=E,vu=["left","right","top","bottom","width","height"],yu=[["width","left","right"],["height","top","bottom"]];function xu(h,c,d,f,p){var g=0,m=0;null==f&&(f=1/0),null==p&&(p=1/0);var v=0;c.eachChild(function(t,e){var i,n,o=t.position,a=t.getBoundingRect(),r=c.childAt(e+1),s=r&&r.getBoundingRect();if("horizontal"===h){var l=a.width+(s?-s.x+a.x:0);v=f<(i=g+l)||t.newline?(g=0,i=l,m+=v+d,a.height):Math.max(v,a.height)}else{var u=a.height+(s?-s.y+a.y:0);v=p<(n=m+u)||t.newline?(g+=v+d,m=0,n=u,a.width):Math.max(v,a.width)}t.newline||(o[0]=g,o[1]=m,"horizontal"===h?g=i+d:m=n+d)})}var _u=xu;T(xu,"vertical"),T(xu,"horizontal");function wu(t,e,i){i=iu(i||0);var n=e.width,o=e.height,a=Rl(t.left,n),r=Rl(t.top,o),s=Rl(t.right,n),l=Rl(t.bottom,o),u=Rl(t.width,n),h=Rl(t.height,o),c=i[2]+i[0],d=i[1]+i[3],f=t.aspect;switch(isNaN(u)&&(u=n-s-d-a),isNaN(h)&&(h=o-l-c-r),null!=f&&(isNaN(u)&&isNaN(h)&&(n/oe)return t[n];return t[i-1]}(s,i):r;if((l=l||r)&&l.length){var u=l[o];return t&&(a[t]=u),n.colorIdx=(o+1)%l.length,u}}},Ru="original",zu="arrayRows",Bu="objectRows",Vu="keyedColumns",Gu="unknown",Fu="typedArray",Wu="column",Hu="row";function Zu(t){this.fromDataset=t.fromDataset,this.data=t.data||(t.sourceFormat===Vu?{}:[]),this.sourceFormat=t.sourceFormat||Gu,this.seriesLayoutBy=t.seriesLayoutBy||Wu,this.dimensionsDefine=t.dimensionsDefine,this.encodeDefine=t.encodeDefine&&Q(t.encodeDefine),this.startIndex=t.startIndex||0,this.dimensionsDetectCount=t.dimensionsDetectCount}Zu.seriesDataToSource=function(t){return new Zu({data:t,sourceFormat:V(t)?Fu:Ru,fromDataset:!1})},ra(Zu);var Uu={Must:1,Might:2,Not:3},Xu=jo();function Yu(t){var e=t.option,i=e.data,n=V(i)?Fu:Ru,o=!1,a=e.seriesLayoutBy,r=e.sourceHeader,s=e.dimensions,l=Ju(t);if(l){var u=l.option;i=u.source,n=Xu(l).sourceFormat,o=!0,a=a||u.seriesLayoutBy,null==r&&(r=u.sourceHeader),s=s||u.dimensions}var h=function(t,e,i,n,o){if(!t)return{dimensionsDefine:ju(o)};var a,r;if(e===zu)"auto"===n||null==n?qu(function(t){null!=t&&"-"!==t&&(R(t)?null==r&&(r=1):r=0)},i,t,10):r=n?1:0,o||1!==r||(o=[],qu(function(t,e){o[e]=null!=t?t:""},i,t)),a=o?o.length:i===Hu?t.length:t[0]?t[0].length:null;else if(e===Bu)o=o||function(t){var e,i=0;for(;i":"\n",f="richText"===c,p={},g=0;function i(t){return{renderMode:c,content:au(tu(t)),style:p}}var m=this.getData(),a=m.mapDimension("defaultedTooltip",!0),n=a.length,r=this.getRawValue(o),s=O(r),v=m.getItemVisual(o,"color");z(v)&&v.colorStops&&(v=(v.colorStops[0]||{}).color),v=v||"transparent";var l=(1":"",n=i+u.join(i||", ");return{renderMode:c,content:n,style:p}}(r):i(n?Wh(m,o,a[0]):s?r[0]:r)).content,u=d.seriesIndex+"at"+g,y=hu({color:v,type:"item",renderMode:c,markerId:u});p[u]=v,++g;var x=m.getName(o),_=this.name;Uo(this)||(_=""),_=_?au(_)+(h?": ":e):"";var w="string"==typeof y?y:y.content;return{html:h?w+_+l:_+w+(x?au(x)+": "+l:l),markers:p}},isAnimationEnabled:function(){if(v.node)return!1;var t=this.getShallow("animation");return t&&this.getData().count()>this.getShallow("animationThreshold")&&(t=!1),t},restoreData:function(){this.dataTask.dirty()},getColorFromPalette:function(t,e,i){var n=this.ecModel,o=Eu.getColorFromPalette.call(this,t,e,i);return o=o||n.getColorFromPalette(t,e,i)},coordDimToDataDim:function(t){return this.getRawData().mapDimension(t,!0)},getProgressive:function(){return this.get("progressive")},getProgressiveThreshold:function(){return this.get("progressiveThreshold")},getAxisTooltipData:null,getTooltipPosition:null,pipeTask:null,preventIncremental:null,pipelineContext:null});function sc(t){var e=t.name;Uo(t)||(t.name=function(t){var i=t.getRawData(),e=i.mapDimension("seriesName",!0),n=[];return E(e,function(t){var e=i.getDimensionInfo(t);e.displayName&&n.push(e.displayName)}),n.join(" ")}(t)||e)}function lc(t){return t.model.getRawData().count()}function uc(t){var e=t.model;return e.setData(e.getRawData().cloneShallow()),hc}function hc(t,e){t.end>e.outputData.count()&&e.model.getRawData().cloneShallow(e.outputData)}function cc(e,i){E(e.CHANGABLE_METHODS,function(t){e.wrapMethod(t,T(dc,i))})}function dc(t){var e=fc(t);e&&e.setOutputEnd(this.count())}function fc(t){var e=(t.ecModel||{}).scheduler,i=e&&e.getPipeline(t.uid);if(i){var n=i.currentTask;if(n){var o=n.agentStubMap;o&&(n=o.get(t.uid))}return n}}b(rc,Uh),b(rc,Eu);var pc=function(){this.group=new Ci,this.uid=Nl("viewComponent")};pc.prototype={constructor:pc,init:function(t,e){},render:function(t,e,i,n){},dispose:function(){},filterForExposedEvent:null};var gc=pc.prototype;gc.updateView=gc.updateLayout=gc.updateVisual=function(t,e,i,n){},oa(pc),ua(pc,{registerWhenExtend:!0});function mc(){var s=jo();return function(t){var e=s(t),i=t.pipelineContext,n=e.large,o=e.progressiveRender,a=e.large=i&&i.large,r=e.progressiveRender=i&&i.progressiveRender;return!!(n^a||o^r)&&"reset"}}var vc=jo(),yc=mc();function xc(){this.group=new Ci,this.uid=Nl("viewChart"),this.renderTask=Xh({plan:Sc,reset:Mc}),this.renderTask.context={view:this}}var _c=xc.prototype={type:"chart",init:function(t,e){},render:function(t,e,i,n){},highlight:function(t,e,i,n){bc(t.getData(),n,"emphasis")},downplay:function(t,e,i,n){bc(t.getData(),n,"normal")},remove:function(t,e){this.group.removeAll()},dispose:function(){},incrementalPrepareRender:null,incrementalRender:null,updateTransform:null,filterForExposedEvent:null};function wc(t,e,i){if(t&&(t.trigger(e,i),t.isGroup&&!Qs(t)))for(var n=0,o=t.childCount();nc?i+=p(g("data.partialData"),{displayCnt:c}):i+=g("data.allData");for(var r=[],s=0;si.blockIndex?i.step:null,a=n&&n.modDataCount;return{step:o,modBy:null!=a?Math.ceil(a/o):null,modDataCount:a}}},zc.getPipeline=function(t){return this._pipelineMap.get(t)},zc.updateStreamModes=function(t,e){var i=this._pipelineMap.get(t.uid),n=t.getData().count(),o=i.progressiveEnabled&&e.incrementalPrepareRender&&n>=i.threshold,a=t.get("large")&&n>=t.get("largeThreshold"),r="mod"===t.get("progressiveChunkMode")?n:null;t.pipelineContext=i.context={progressiveRender:o,modDataCount:r,large:a}},zc.restorePipelines=function(t){var n=this,o=n._pipelineMap=Q();t.eachSeries(function(t){var e=t.getProgressive(),i=t.uid;o.set(i,{id:i,head:null,tail:null,threshold:t.getProgressiveThreshold(),progressiveEnabled:e&&!(t.preventIncremental&&t.preventIncremental()),blockIndex:-1,step:Math.round(e||700),count:0}),qc(n,t,t.dataTask)})},zc.prepareStageTasks=function(){var i=this._stageTaskMap,n=this.ecInstance.getModel(),o=this.api;E(this._allHandlers,function(t){var e=i.get(t.uid)||i.set(t.uid,[]);t.reset&&function(n,o,t,a,r){var s=t.seriesTaskMap||(t.seriesTaskMap=Q()),e=o.seriesType,i=o.getTargetSeries;o.createOnAllSeries?a.eachRawSeries(l):e?a.eachRawSeriesByType(e,l):i&&i(a,r).each(l);function l(t){var e=t.uid,i=s.get(e)||s.set(e,Xh({plan:Zc,reset:Uc,count:jc}));i.context={model:t,ecModel:a,api:r,useClearVisual:o.isVisual&&!o.isLayout,plan:o.plan,reset:o.reset,scheduler:n},qc(n,t,i)}var u=n._pipelineMap;s.each(function(t,e){u.get(e)||(t.dispose(),s.removeKey(e))})}(this,t,e,n,o),t.overallReset&&function(n,t,e,i,o){var a=e.overallTask=e.overallTask||Xh({reset:Gc});a.context={ecModel:i,api:o,overallReset:t.overallReset,scheduler:n};var r=a.agentStubMap=a.agentStubMap||Q(),s=t.seriesType,l=t.getTargetSeries,u=!0,h=t.modifyOutputEnd;s?i.eachRawSeriesByType(s,c):l?l(i,o).each(c):(u=!1,E(i.getSeries(),c));function c(t){var e=t.uid,i=r.get(e);i||(i=r.set(e,Xh({reset:Fc,onDirty:Hc})),a.dirty()),i.context={model:t,overallProgress:u,modifyOutputEnd:h},i.agent=a,i.__block=u,qc(n,t,i)}var d=n._pipelineMap;r.each(function(t,e){d.get(e)||(t.dispose(),a.dirty(),r.removeKey(e))})}(this,t,e,n,o)},this)},zc.prepareView=function(t,e,i,n){var o=t.renderTask,a=o.context;a.model=e,a.ecModel=i,a.api=n,o.__block=!t.incrementalPrepareRender,qc(this,e,o)},zc.performDataProcessorTasks=function(t,e){Bc(this,this._dataProcessorHandlers,t,e,{block:!0})},zc.performVisualTasks=function(t,e,i){Bc(this,this._visualHandlers,t,e,i)},zc.performSeriesTasks=function(t){var e;t.eachSeries(function(t){e|=t.dataTask.perform()}),this.unfinished|=e},zc.plan=function(){this._pipelineMap.each(function(t){var e=t.tail;do{if(e.__block){t.blockIndex=e.__idxInPipeline;break}e=e.getUpstream()}while(e)})};var Vc=zc.updatePayload=function(t,e){"remain"!==e&&(t.context.payload=e)};function Gc(t){t.overallReset(t.ecModel,t.api,t.payload)}function Fc(t,e){return t.overallProgress&&Wc}function Wc(){this.agent.dirty(),this.getDownstream().dirty()}function Hc(){this.agent&&this.agent.dirty()}function Zc(t){return t.plan&&t.plan(t.model,t.ecModel,t.api,t.payload)}function Uc(t){t.useClearVisual&&t.data.clearAllVisual();var e=t.resetDefines=Vo(t.reset(t.model,t.ecModel,t.api,t.payload));return 1t.get("hoverLayerThreshold")&&!v.node&&t.eachSeries(function(t){if(!t.preventUsingHoverLayer){var e=i._chartsMap[t.__viewId];e.__alive&&e.group.traverse(function(t){t.useHoverLayer=!0})}})}(n,t),Oc(n._zr.dom,t)}function qd(e,i){Id(of,function(t){t(e,i)})}Rd.resize=function(t){if(!this._disposed){this._zr.resize(t);var e=this._model;if(this._loadingFX&&this._loadingFX.resize(),e){var i=e.resetOption("media"),n=t&&t.silent;this[Ld]=!0,i&&Vd(this),Bd.update.call(this),this[Ld]=!1,Hd.call(this,n),Zd.call(this,n)}}},Rd.showLoading=function(t,e){if(!this._disposed&&(Ad(t)&&(e=t,t=""),t=t||"default",this.hideLoading(),sf[t])){var i=sf[t](this._api,e),n=this._zr;this._loadingFX=i,n.add(i)}},Rd.hideLoading=function(){this._disposed||(this._loadingFX&&this._zr.remove(this._loadingFX),this._loadingFX=null)},Rd.makeActionFromEvent=function(t){var e=k({},t);return e.type=tf[t.type],e},Rd.dispatchAction=function(t,e){this._disposed||(Ad(e)||(e={silent:!!e}),Qd[t.type]&&this._model&&(this[Ld]?this._pendingActions.push(t):(Wd.call(this,t,e.silent),e.flush?this._zr.flush(!0):!1!==e.flush&&v.browser.weChat&&this._throttledZrFlush(),Hd.call(this,e.silent),Zd.call(this,e.silent))))},Rd.appendData=function(t){if(!this._disposed){var e=t.seriesIndex;this.getModel().getSeriesByIndex(e).appendData(t),this._scheduler.unfinished=!0}},Rd.on=Nd("on",!1),Rd.off=Nd("off",!1),Rd.one=Nd("one",!1);var Kd=["click","dblclick","mouseover","mouseout","mousemove","mousedown","mouseup","globalout","contextmenu"];function $d(t,e){var i=t.get("z"),n=t.get("zlevel");e.group.traverse(function(t){"group"!==t.type&&(null!=i&&(t.z=i),null!=n&&(t.zlevel=n))})}function Jd(){this.eventInfo}Rd._initEvents=function(){Id(Kd,function(u){function t(t){var e,i=this.getModel(),n=t.target;if("globalout"===u)e={};else if(n&&null!=n.dataIndex){var o=n.dataModel||i.getSeriesByIndex(n.seriesIndex);e=o&&o.getDataParams(n.dataIndex,n.dataType,n)||{}}else n&&n.eventData&&(e=k({},n.eventData));if(e){var a=e.componentType,r=e.componentIndex;"markLine"!==a&&"markPoint"!==a&&"markArea"!==a||(a="series",r=e.seriesIndex);var s=a&&null!=r&&i.getComponent(a,r),l=s&&this["series"===s.mainType?"_chartsMap":"_componentsMap"][s.__viewId];e.event=t,e.type=u,this._ecEventProcessor.eventInfo={targetEl:n,packedEvent:e,model:s,view:l},this.trigger(u,e)}}t.zrEventfulCallAtLast=!0,this._zr.on(u,t,this)},this),Id(tf,function(t,e){this._messageCenter.on(e,function(t){this.trigger(e,t)},this)},this)},Rd.isDisposed=function(){return this._disposed},Rd.clear=function(){this._disposed||this.setOption({series:[]},!0)},Rd.dispose=function(){if(!this._disposed){this._disposed=!0,Jo(this.getDom(),df,"");var e=this._api,i=this._model;Id(this._componentsViews,function(t){t.dispose(i,e)}),Id(this._chartsViews,function(t){t.dispose(i,e)}),this._zr.dispose(),delete lf[this.id]}},b(Ed,Ct),Jd.prototype={constructor:Jd,normalizeQuery:function(t){var s={},l={},u={};if(R(t)){var e=Dd(t);s.mainType=e.main||null,s.subType=e.sub||null}else{var h=["Index","Name","Id"],c={name:1,dataIndex:1,dataType:1};E(t,function(t,e){for(var i=!1,n=0;nx[1]&&(x[1]=y)}e&&(this._nameList[d]=e[f])}this._rawCount=this._count=l,this._extent={},qf(this)},Yf._initDataFromProvider=function(t,e){if(!(e<=t)){for(var i,n=this._chunkSize,o=this._rawData,a=this._storage,r=this.dimensions,s=r.length,l=this._dimensionInfos,u=this._nameList,h=this._idList,c=this._rawExtent,d=this._nameRepeatCount={},f=this._chunkCount,p=0;pM[1]&&(M[1]=S)}if(!o.pure){var I=u[v];if(m&&null==I)if(null!=m.name)u[v]=I=m.name;else if(null!=i){var T=r[i],A=a[T][y];if(A){I=A[x];var D=l[T].ordinalMeta;D&&D.categories.length&&(I=D.categories[I])}}var C=null==m?null:m.id;null==C&&null!=I&&(d[I]=d[I]||0,0=this._rawCount||t<0)return-1;if(!this._indices)return t;var e=this._indices,i=e[t];if(null!=i&&it))return a;o=a-1}}return-1},Yf.indicesOfNearest=function(t,e,i){var n=[];if(!this._storage[t])return n;null==i&&(i=1/0);for(var o=1/0,a=-1,r=0,s=0,l=this.count();st[I][1])&&(M=!1)}M&&(a[r++]=this.getRawIndex(m))}return rw[1]&&(w[1]=_)}}}return o},Yf.downSample=function(t,e,i,n){for(var o=ep(this,[t]),a=o._storage,r=[],s=Math.floor(1/e),l=a[t],u=this.count(),h=this._chunkSize,c=o._rawExtent[t],d=new(Wf(this))(u),f=0,p=0;pc[1]&&(c[1]=x),d[f++]=_}return o._count=f,o._indices=d,o.getRawIndex=Jf,o},Yf.getItemModel=function(t){var e=this.hostModel;return new Cl(this.getRawDataItem(t),e,e&&e.ecModel)},Yf.diff=function(e){var i=this;return new Lf(e?e.getIndices():[],this.getIndices(),function(t){return Qf(e,t)},function(t){return Qf(i,t)})},Yf.getVisual=function(t){var e=this._visual;return e&&e[t]},Yf.setVisual=function(t,e){if(Rf(t))for(var i in t)t.hasOwnProperty(i)&&this.setVisual(i,t[i]);else this._visual=this._visual||{},this._visual[t]=e},Yf.setLayout=function(t,e){if(Rf(t))for(var i in t)t.hasOwnProperty(i)&&this.setLayout(i,t[i]);else this._layout[t]=e},Yf.getLayout=function(t){return this._layout[t]},Yf.getItemLayout=function(t){return this._itemLayouts[t]},Yf.setItemLayout=function(t,e,i){this._itemLayouts[t]=i?k(this._itemLayouts[t]||{},e):e},Yf.clearItemLayouts=function(){this._itemLayouts.length=0},Yf.getItemVisual=function(t,e,i){var n=this._itemVisuals[t],o=n&&n[e];return null!=o||i?o:this.getVisual(e)},Yf.setItemVisual=function(t,e,i){var n=this._itemVisuals[t]||{},o=this.hasItemVisual;if(this._itemVisuals[t]=n,Rf(e))for(var a in e)e.hasOwnProperty(a)&&(n[a]=e[a],o[a]=!0);else n[e]=i,o[e]=!0},Yf.clearAllVisual=function(){this._visual={},this._itemVisuals=[],this.hasItemVisual={}};function op(t){t.seriesIndex=this.seriesIndex,t.dataIndex=this.dataIndex,t.dataType=this.dataType}function ap(t,e,i){Zu.isInstance(e)||(e=Zu.seriesDataToSource(e)),i=i||{},t=(t||[]).slice();for(var n=(i.dimsDef||[]).slice(),o=Q(),a=Q(),l=[],r=function(t,e,i,n){var o=Math.max(t.dimensionsDetectCount||1,e.length,i.length,n||0);return E(e,function(t){var e=t.dimsDef;e&&(o=Math.max(o,e.length))}),o}(e,t,n,i.dimCount),s=0;s=e[0]&&t<=e[1]},gp.prototype.normalize=function(t){var e=this._extent;return e[1]===e[0]?.5:(t-e[0])/(e[1]-e[0])},gp.prototype.scale=function(t){var e=this._extent;return t*(e[1]-e[0])+e[0]},gp.prototype.unionExtent=function(t){var e=this._extent;t[0]e[1]&&(e[1]=t[1])},gp.prototype.unionExtentFromData=function(t,e){this.unionExtent(t.getApproximateExtent(e))},gp.prototype.getExtent=function(){return this._extent.slice()},gp.prototype.setExtent=function(t,e){var i=this._extent;isNaN(t)||(i[0]=t),isNaN(e)||(i[1]=e)},gp.prototype.isBlank=function(){return this._isBlank},gp.prototype.setBlank=function(t){this._isBlank=t},gp.prototype.getLabel=null,oa(gp),ua(gp,{registerWhenExtend:!0}),mp.createByAxisModel=function(t){var e=t.option,i=e.data,n=i&&N(i,xp);return new mp({categories:n,needCollect:!n,deduplication:!1!==e.dedplication})};var vp=mp.prototype;function yp(t){return t._map||(t._map=Q(t.categories))}function xp(t){return z(t)&&null!=t.value?t.value:t+""}vp.getOrdinal=function(t){return yp(this).get(t)},vp.parseAndCollect=function(t){var e,i=this._needCollect;if("string"!=typeof t&&!i)return t;if(i&&!this._deduplication)return e=this.categories.length,this.categories[e]=t,e;var n=yp(this);return null==(e=n.get(t))&&(i?(e=this.categories.length,this.categories[e]=t,n.set(t,e)):e=NaN),e};var _p=gp.prototype,wp=gp.extend({type:"ordinal",init:function(t,e){t&&!O(t)||(t=new mp({categories:t})),this._ordinalMeta=t,this._extent=e||[0,t.categories.length-1]},parse:function(t){return"string"==typeof t?this._ordinalMeta.getOrdinal(t):Math.round(t)},contain:function(t){return t=this.parse(t),_p.contain.call(this,t)&&null!=this._ordinalMeta.categories[t]},normalize:function(t){return _p.normalize.call(this,this.parse(t))},scale:function(t){return Math.round(_p.scale.call(this,t))},getTicks:function(){for(var t=[],e=this._extent,i=e[0];i<=e[1];)t.push(i),i++;return t},getLabel:function(t){if(!this.isBlank())return this._ordinalMeta.categories[t]},count:function(){return this._extent[1]-this._extent[0]+1},unionExtentFromData:function(t,e){this.unionExtent(t.getApproximateExtent(e))},getOrdinalMeta:function(){return this._ordinalMeta},niceTicks:et,niceExtent:et});wp.create=function(){return new wp};var bp=zl;function Sp(t){return Gl(t)+2}function Mp(t,e,i){t[e]=Math.max(Math.min(t[e],i[1]),i[0])}function Ip(t,e){isFinite(t[0])||(t[0]=e[0]),isFinite(t[1])||(t[1]=e[1]),Mp(t,0,e),Mp(t,1,e),t[0]>t[1]&&(t[0]=t[1])}var Tp=zl,Ap=gp.extend({type:"interval",_interval:0,_intervalPrecision:2,setExtent:function(t,e){var i=this._extent;isNaN(t)||(i[0]=parseFloat(t)),isNaN(e)||(i[1]=parseFloat(e))},unionExtent:function(t){var e=this._extent;t[0]e[1]&&(e[1]=t[1]),Ap.prototype.setExtent.call(this,e[0],e[1])},getInterval:function(){return this._interval},setInterval:function(t){this._interval=t,this._niceExtent=this._extent.slice(),this._intervalPrecision=Sp(t)},getTicks:function(t){var e=this._interval,i=this._extent,n=this._niceExtent,o=this._intervalPrecision,a=[];if(!e)return a;i[0]s&&(t?a.push(Tp(s+e,o)):a.push(i[1])),a},getMinorTicks:function(t){for(var e=this.getTicks(!0),i=[],n=this.getExtent(),o=1;on[0]&&h>>1;t[o][1]>1^-(1&s),l=l>>1^-(1&l),o=s+=o,a=l+=a,n.push([s/i,l/i])}return n}Tg.prototype={constructor:Tg,properties:null,getBoundingRect:function(){var t=this._rect;if(t)return t;for(var e=Number.MAX_VALUE,i=[e,e],n=[-e,-e],o=[],a=[],r=this.geometries,s=0;ss[1];d(e[0].coord,s[0])&&(n?e[0].coord=s[0]:e.shift());n&&d(s[0],e[0].coord)&&e.unshift({coord:s[0]});d(s[1],a.coord)&&(n?a.coord=s[1]:e.pop());n&&d(a.coord,s[1])&&e.push({coord:s[1]});function d(t,e){return t=zl(t),e=zl(e),c?en[0]&&(n[0]=a[0]),a[1]>n[1]&&(n[1]=a[1])}return{min:e?i:n,max:e?n:i}}var ym=Ar.extend({type:"ec-polyline",shape:{points:[],smooth:0,smoothConstraint:!0,smoothMonotone:null,connectNulls:!1},style:{fill:null,stroke:"#000"},brush:Xr(Ar.prototype.brush),buildPath:function(t,e){var i=e.points,n=0,o=i.length,a=vm(i,e.smoothConstraint);if(e.connectNulls){for(;0n)return!1;return!0}(a,e))){var r=e.mapDimension(a.dim),s={};return E(a.getViewLabels(),function(t){s[t.tickValue]=1}),function(t){return!s.hasOwnProperty(e.get(r,t))}}}}function Am(t,e,i){if("cartesian2d"!==t.type)return wm(t,e,i);var n=t.getBaseAxis().isHorizontal(),o=_m(t,e,i);if(!i.get("clip",!0)){var a=o.shape,r=Math.max(a.width,a.height);n?(a.y-=r,a.height+=2*r):(a.x-=r,a.width+=2*r)}return o}xc.extend({type:"line",init:function(){var t=new Ci,e=new em;this.group.add(e.group),this._symbolDraw=e,this._lineGroup=t},render:function(t,e,i){var n=t.coordinateSystem,o=this.group,a=t.getData(),r=t.getModel("lineStyle"),s=t.getModel("areaStyle"),l=a.mapArray(a.getItemLayout),u="polar"===n.type,h=this._coordSys,c=this._symbolDraw,d=this._polyline,f=this._polygon,p=this._lineGroup,g=t.get("animation"),m=!s.isEmpty(),v=s.get("origin"),y=function(t,e,i){if(!i.valueDim)return[];for(var n=[],o=0,a=e.count();oh[c-1].coord&&(h.reverse(),d.reverse());var f=h[0].coord-10,p=h[c-1].coord+10,g=p-f;if(g<.001)return"transparent";E(h,function(t){t.offset=(t.coord-f)/g}),h.push({offset:c?h[c-1].offset:.5,color:d[1]||"transparent"}),h.unshift({offset:c?h[0].offset:.5,color:d[0]||"transparent"});var m=new gs(0,0,0,0,h,!0);return m[n]=f,m[n+"2"]=p,m}}}(a,n)||a.getVisual("color");d.useStyle(D(r.getLineStyle(),{fill:"none",stroke:M,lineJoin:"bevel"}));var I=t.get("smooth");if(I=Mm(t.get("smooth")),d.setShape({smooth:I,smoothMonotone:t.get("smoothMonotone"),connectNulls:t.get("connectNulls")}),f){var T=a.getCalculationInfo("stackedOnSeries"),A=0;f.useStyle(D(s.getAreaStyle(),{fill:M,opacity:.7,lineJoin:"bevel"})),T&&(A=Mm(T.get("smooth"))),f.setShape({smooth:I,stackedOnSmooth:A,smoothMonotone:t.get("smoothMonotone"),connectNulls:t.get("connectNulls")})}this._data=a,this._coordSys=n,this._stackedOnPoints=y,this._points=l,this._step=S,this._valueOrigin=v},dispose:function(){},highlight:function(t,e,i,n){var o=t.getData(),a=Yo(o,n);if(!(a instanceof Array)&&null!=a&&0<=a){var r=o.getItemGraphicEl(a);if(!r){var s=o.getItemLayout(a);if(!s)return;if(this._clipShapeForSymbol&&!this._clipShapeForSymbol.contain(s[0],s[1]))return;(r=new Ug(o,a)).position=s,r.setZ(t.get("zlevel"),t.get("z")),r.ignore=isNaN(s[0])||isNaN(s[1]),r.__temp=!0,o.setItemGraphicEl(a,r),r.stopSymbolAnimation(!0),this.group.add(r)}r.highlight()}else xc.prototype.highlight.call(this,t,e,i,n)},downplay:function(t,e,i,n){var o=t.getData(),a=Yo(o,n);if(null!=a&&0<=a){var r=o.getItemGraphicEl(a);r&&(r.__temp?(o.setItemGraphicEl(a,null),this.group.remove(r)):r.downplay())}else xc.prototype.downplay.call(this,t,e,i,n)},_newPolyline:function(t){var e=this._polyline;return e&&this._lineGroup.remove(e),e=new ym({shape:{points:t},silent:!0,z2:10}),this._lineGroup.add(e),this._polyline=e},_newPolygon:function(t,e){var i=this._polygon;return i&&this._lineGroup.remove(i),i=new xm({shape:{points:t,stackedOnPoints:e},silent:!0}),this._lineGroup.add(i),this._polygon=i},_updateAnimation:function(t,e,i,n,o,a){var r=this._polyline,s=this._polygon,l=t.hostModel,u=function(t,e,i,n,o,a,r,s){for(var l=function(t,e){var i=[];return e.diff(t).add(function(t){i.push({cmd:"+",idx:t})}).update(function(t,e){i.push({cmd:"=",idx:e,idx1:t})}).remove(function(t){i.push({cmd:"-",idx:t})}).execute(),i}(t,e),u=[],h=[],c=[],d=[],f=[],p=[],g=[],m=rm(o,e,r),v=rm(a,t,s),y=0;ye&&(e=t[i]);return isFinite(e)?e:NaN},min:function(t){for(var e=1/0,i=0;ie[1]&&e.reverse(),e},getOtherAxis:function(){this.grid.getOtherAxis()},pointToData:function(t,e){return this.coordToData(this.toLocalCoord(t["x"===this.dim?0:1]),e)},toLocalCoord:null,toGlobalCoord:null},w(Em,Vg);var Rm={show:!0,zlevel:0,z:0,inverse:!1,name:"",nameLocation:"end",nameRotate:null,nameTruncate:{maxWidth:null,ellipsis:"...",placeholder:"."},nameTextStyle:{},nameGap:15,silent:!1,triggerEvent:!1,tooltip:{show:!1},axisPointer:{},axisLine:{show:!0,onZero:!0,onZeroAxisIndex:null,lineStyle:{color:"#333",width:1,type:"solid"},symbol:["none","none"],symbolSize:[10,15]},axisTick:{show:!0,inside:!1,length:5,lineStyle:{width:1}},axisLabel:{show:!0,inside:!1,rotate:0,showMinLabel:null,showMaxLabel:null,margin:8,fontSize:12},splitLine:{show:!0,lineStyle:{color:["#ccc"],width:1,type:"solid"}},splitArea:{show:!1,areaStyle:{color:["rgba(250,250,250,0.3)","rgba(200,200,200,0.3)"]}}},zm={};zm.categoryAxis=m({boundaryGap:!0,deduplication:null,splitLine:{show:!1},axisTick:{alignWithLabel:!1,interval:"auto"},axisLabel:{interval:"auto"}},Rm),zm.valueAxis=m({boundaryGap:[0,0],splitNumber:5,minorTick:{show:!1,splitNumber:5,length:3,lineStyle:{}},minorSplitLine:{show:!1,lineStyle:{color:"#eee",width:1}}},Rm),zm.timeAxis=D({scale:!0,min:"dataMin",max:"dataMax"},zm.valueAxis),zm.logAxis=D({scale:!0,logBase:10},zm.valueAxis);function Bm(a,t,r,e){E(Vm,function(o){t.extend({type:a+"Axis."+o,mergeDefaultAndTheme:function(t,e){var i=this.layoutMode,n=i?Mu(t):{};m(t,e.getTheme().get(o+"Axis")),m(t,this.getDefaultOption()),t.type=r(a,t),i&&Su(t,n,i)},optionUpdated:function(){"category"===this.option.type&&(this.__ordinalMeta=mp.createByAxisModel(this))},getCategories:function(t){var e=this.option;if("category"===e.type)return t?e.data:this.__ordinalMeta.categories},getOrdinalMeta:function(){return this.__ordinalMeta},defaultOption:p([{},zm[o+"Axis"],e],!0)})}),Lu.registerSubTypeDefaulter(a+"Axis",T(r,a))}var Vm=["value","category","time","log"],Gm=Lu.extend({type:"cartesian2dAxis",axis:null,init:function(){Gm.superApply(this,"init",arguments),this.resetRange()},mergeOption:function(){Gm.superApply(this,"mergeOption",arguments),this.resetRange()},restoreData:function(){Gm.superApply(this,"restoreData",arguments),this.resetRange()},getCoordSysModel:function(){return this.ecModel.queryComponents({mainType:"grid",index:this.option.gridIndex,id:this.option.gridId})[0]}});function Fm(t,e){return e.type||(e.data?"category":"value")}m(Gm.prototype,cg);var Wm={offset:0};function Hm(t,e){return t.getCoordSysModel()===e}function Zm(t,e,i){this._coordsMap={},this._coordsList=[],this._axesMap={},this._axesList=[],this._initCartesian(t,e,i),this.model=t}Bm("x",Gm,Fm,Wm),Bm("y",Gm,Fm,Wm),Lu.extend({type:"grid",dependencies:["xAxis","yAxis"],layoutMode:"box",coordinateSystem:null,defaultOption:{show:!1,zlevel:0,z:0,left:"10%",top:60,right:"10%",bottom:60,containLabel:!1,backgroundColor:"rgba(0,0,0,0)",borderWidth:1,borderColor:"#ccc"}});var Um=Zm.prototype;function Xm(t,e,i,n){i.getAxesOnZeroOf=function(){return o?[o]:[]};var o,a=t[e],r=i.model,s=r.get("axisLine.onZero"),l=r.get("axisLine.onZeroAxisIndex");if(s){if(null!=l)Ym(a[l])&&(o=a[l]);else for(var u in a)if(a.hasOwnProperty(u)&&Ym(a[u])&&!n[h(a[u])]){o=a[u];break}o&&(n[h(o)]=!0)}function h(t){return t.dim+"_"+t.index}}function Ym(t){return t&&"category"!==t.type&&"time"!==t.type&&function(t){var e=t.scale.getExtent(),i=e[0],n=e[1];return!(0u[1]?-1:1,c=["start"===a?u[0]-h*l:"end"===a?u[1]+h*l:(u[0]+u[1])/2,av(a)?t.labelOffset+r*l:0],d=e.get("nameRotate");null!=d&&(d=d*Jm/180),av(a)?n=ev(t.rotation,null!=d?d:t.rotation,r):(n=function(t,e,i,n){var o,a,r=Zl(i-t.rotation),s=n[0]>n[1],l="start"===e&&!s||"start"!==e&&s;o=Ul(r-Jm/2)?(a=l?"bottom":"top","center"):Ul(r-1.5*Jm)?(a=l?"top":"bottom","center"):(a="middle",r<1.5*Jm&&Jm/2l[1]&&l.reverse(),(null==r||r>l[1])&&(r=l[1]),r=i.r0}}});var ey=Math.PI/180;function iy(o,t,e,i,n,a,r,s,l,u){function h(t,e,i){for(var n=t;nl+r);n++)if(o[n].y+=i,to[n].y+o[n].height)return void c(n,i/2);c(e-1,i/2)}function c(t,e){for(var i=t;0<=i&&!(o[i].y-eo[i-1].y+o[i-1].height));i--);}function d(t,e,i,n,o,a){for(var r=e?Number.MAX_VALUE:0,s=0,l=t.length;s=e?v.push(o[y]):m.push(o[y]);d(m,!1,t,e,i,n),d(v,!0,t,e,i,n)}function ny(t){return"center"===t.position}function oy(L,k,P,t,N,e){var O,E,R=L.getData(),z=[],B=!1,V=(L.get("minShowLabelAngle")||0)*ey;R.each(function(t){var e=R.getItemLayout(t),i=R.getItemModel(t),n=i.getModel("label"),o=n.get("position")||i.get("emphasis.label.position"),a=n.get("distanceToLabelLine"),r=n.get("alignTo"),s=Rl(n.get("margin"),P),l=n.get("bleedMargin"),u=n.getFont(),h=i.getModel("labelLine"),c=h.get("length");c=Rl(c,P);var d=h.get("length2");if(d=Rl(d,P),!(e.anglei[0]&&isFinite(h)&&isFinite(i[0]););else{var l=o.getTicks().length-1;c"+N(t,function(t,e){var i=o.get(o.mapDimension(t.dim),n);return au(t.name+" : "+i)}).join("
")},getTooltipPosition:function(t){if(null!=t)for(var e=this.getData(),i=this.coordinateSystem,n=e.getValues(N(i.dimensions,function(t){return e.mapDimension(t)}),t,!0),o=0,a=n.length;o"+au(n+" : "+i)},getTooltipPosition:function(t){if(null!=t){var e=this.getData().getName(t),i=this.coordinateSystem,n=i.getRegion(e);return n&&i.dataToPoint(n.center)}},setZoom:function(t){this.option.zoom=t},setCenter:function(t){this.option.center=t},defaultOption:{zlevel:0,z:2,coordinateSystem:"geo",map:"",left:"center",top:"center",aspectScale:.75,showLegendSymbol:!0,dataRangeHoverLink:!0,boundingCoords:null,center:null,zoom:1,scaleLimit:null,label:{show:!1,color:"#000"},itemStyle:{borderWidth:.5,borderColor:"#444",areaColor:"#eee"},emphasis:{label:{show:!0,color:"rgb(100,0,0)"},itemStyle:{areaColor:"rgba(255,215,0,0.8)"}}}}),Xv);var Oy="\0_ec_interaction_mutex";function Ey(t,e){return!!Ry(t)[e]}function Ry(t){return t[Oy]||(t[Oy]={})}function zy(i){this.pointerChecker,this._zr=i,this._opt={};var t=A,n=t(By,this),o=t(Vy,this),a=t(Gy,this),r=t(Fy,this),s=t(Wy,this);Ct.call(this),this.setPointerChecker=function(t){this.pointerChecker=t},this.enable=function(t,e){this.disable(),this._opt=D(L(e)||{},{zoomOnMouseWheel:!0,moveOnMouseMove:!0,moveOnMouseWheel:!1,preventDefaultMouseMove:!0}),null==t&&(t=!0),!0!==t&&"move"!==t&&"pan"!==t||(i.on("mousedown",n),i.on("mousemove",o),i.on("mouseup",a)),!0!==t&&"scale"!==t&&"zoom"!==t||(i.on("mousewheel",r),i.on("pinch",s))},this.disable=function(){i.off("mousedown",n),i.off("mousemove",o),i.off("mouseup",a),i.off("mousewheel",r),i.off("pinch",s)},this.dispose=this.disable,this.isDragging=function(){return this._dragging},this.isPinching=function(){return this._pinching}}function By(t){if(!(Yt(t)||t.target&&t.target.draggable)){var e=t.offsetX,i=t.offsetY;this.pointerChecker&&this.pointerChecker(t,e,i)&&(this._x=e,this._y=i,this._dragging=!0)}}function Vy(t){if(this._dragging&&Uy("moveOnMouseMove",t,this._opt)&&"pinch"!==t.gestureEvent&&!Ey(this._zr,"globalPan")){var e=t.offsetX,i=t.offsetY,n=this._x,o=this._y,a=e-n,r=i-o;this._x=e,this._y=i,this._opt.preventDefaultMouseMove&&Xt(t.event),Zy(this,"pan","moveOnMouseMove",t,{dx:a,dy:r,oldX:n,oldY:o,newX:e,newY:i})}}function Gy(t){Yt(t)||(this._dragging=!1)}function Fy(t){var e=Uy("zoomOnMouseWheel",t,this._opt),i=Uy("moveOnMouseWheel",t,this._opt),n=t.wheelDelta,o=Math.abs(n),a=t.offsetX,r=t.offsetY;if(0!==n&&(e||i)){if(e){var s=3e&&(e=n.height)}this.height=e+1},getNodeById:function(t){if(this.getId()===t)return this;for(var e=0,i=this.children,n=i.length;ea&&(a=t.depth)});var r=t.expandAndCollapse&&0<=t.initialTreeDepth?t.initialTreeDepth:a;return o.root.eachNode("preorder",function(t){var e=t.hostTree.data.getRawDataItem(t.dataIndex);t.isExpand=e&&null!=e.collapsed?!e.collapsed:t.depth<=r}),o.data},getOrient:function(){var t=this.get("orient");return"horizontal"===t?t="LR":"vertical"===t&&(t="TB"),t},setZoom:function(t){this.option.zoom=t},setCenter:function(t){this.option.center=t},formatTooltip:function(t){for(var e=this.getData().tree,i=e.root.children[0],n=e.getNodeByDataIndex(t),o=n.getValue(),a=n.name;n&&n!==i;)a=n.parentNode.name+"."+a,n=n.parentNode;return au(a+(isNaN(o)||null==o?"":" : "+o))},defaultOption:{zlevel:0,z:2,coordinateSystem:"view",left:"12%",top:"12%",right:"12%",bottom:"12%",layout:"orthogonal",edgeShape:"curve",edgeForkPosition:"50%",roam:!1,nodeScaleRatio:.4,center:null,zoom:1,orient:"LR",symbol:"emptyCircle",symbolSize:7,expandAndCollapse:!0,initialTreeDepth:2,lineStyle:{color:"#ccc",width:1.5,curveness:.5},itemStyle:{color:"lightsteelblue",borderColor:"#c23531",borderWidth:1.5},label:{show:!0,color:"#555"},leaves:{label:{show:!0}},animationEasing:"linear",animationDuration:700,animationDurationUpdate:1e3}});var Ex=Cs({shape:{parentPoint:[],childPoints:[],orient:"",forkPosition:""},style:{stroke:"#000",fill:null},buildPath:function(t,e){var i=e.childPoints,n=i.length,o=e.parentPoint,a=i[0],r=i[n-1];if(1===n)return t.moveTo(o[0],o[1]),void t.lineTo(a[0],a[1]);var s=e.orient,l="TB"===s||"BT"===s?0:1,u=1-l,h=Rl(e.forkPosition,1),c=[];c[l]=o[l],c[u]=o[u]+(r[u]-o[u])*h,t.moveTo(o[0],o[1]),t.lineTo(c[0],c[1]),t.moveTo(a[0],a[1]),c[l]=a[l],t.lineTo(c[0],c[1]),c[l]=r[l],t.lineTo(c[0],c[1]),t.lineTo(r[0],r[1]);for(var d=1;dx.x)||(m-=Math.PI);var b=v?"left":"right",S=a.labelModel.get("rotate"),M=S*(Math.PI/180);g.setStyle({textPosition:a.labelModel.get("position")||b,textRotation:null==S?-m:M,textOrigin:"center",verticalAlign:"middle"})}!function(t,e,i,n,o,a,r,s,l){var u=l.edgeShape,h=n.__edge;if("curve"===u)e.parentNode&&e.parentNode!==i&&cl(h=h||(n.__edge=new ds({shape:Gx(l,o,o),style:D({opacity:0,strokeNoScale:!0},l.lineStyle)})),{shape:Gx(l,a,r),style:{opacity:1}},t);else if("polyline"===u&&"orthogonal"===l.layout&&e!==i&&e.children&&0!==e.children.length&&!0===e.isExpand){for(var c=e.children,d=[],f=0;fh.getLayout().x&&(h=t),t.depth>c.depth&&(c=t)});var d=u===h?1:r(u,h)/2,f=d-u.getLayout().x,p=0,g=0,m=0,v=0;if("radial"===n)p=o/(h.getLayout().x+d+f),g=a/(c.depth-1||1),Fx(l,function(t){m=(t.getLayout().x+f)*p,v=(t.depth-1)*g;var e=Lx(m,v);t.setLayout({x:e.x,y:e.y,rawX:m,rawY:v},!0)});else{var y=t.getOrient();"RL"===y||"LR"===y?(g=a/(h.getLayout().x+d+f),p=o/(c.depth-1||1),Fx(l,function(t){v=(t.getLayout().x+f)*g,m="LR"===y?(t.depth-1)*p:o-(t.depth-1)*p,t.setLayout({x:m,y:v},!0)})):"TB"!==y&&"BT"!==y||(p=o/(h.getLayout().x+d+f),g=a/(c.depth-1||1),Fx(l,function(t){m=(t.getLayout().x+f)*p,v="TB"===y?(t.depth-1)*g:a-(t.depth-1)*g,t.setLayout({x:m,y:v},!0)}))}}}(t,e)})}),rc.extend({type:"series.treemap",layoutMode:"box",dependencies:["grid","polar"],preventUsingHoverLayer:!0,_viewRoot:null,defaultOption:{progressive:0,left:"center",top:"middle",right:null,bottom:null,width:"80%",height:"80%",sort:!0,clipWindow:"origin",squareRatio:.5*(1+Math.sqrt(5)),leafDepth:null,drillDownIcon:"▶",zoomToNodeRatio:.1024,roam:!0,nodeClick:"zoomToNode",animation:!0,animationDurationUpdate:900,animationEasing:"quinticInOut",breadcrumb:{show:!0,height:22,left:"center",top:"bottom",emptyItemWidth:25,itemStyle:{color:"rgba(0,0,0,0.7)",borderColor:"rgba(255,255,255,0.7)",borderWidth:1,shadowColor:"rgba(150,150,150,1)",shadowBlur:3,shadowOffsetX:0,shadowOffsetY:0,textStyle:{color:"#fff"}},emphasis:{textStyle:{}}},label:{show:!0,distance:0,padding:5,position:"inside",color:"#fff",ellipsis:!0},upperLabel:{show:!1,position:[0,"50%"],height:20,color:"#fff",ellipsis:!0,verticalAlign:"middle"},itemStyle:{color:null,colorAlpha:null,colorSaturation:null,borderWidth:0,gapWidth:0,borderColor:"#fff",borderColorSaturation:null},emphasis:{upperLabel:{show:!0,position:[0,"50%"],color:"#fff",ellipsis:!0,verticalAlign:"middle"}},visualDimension:0,visualMin:null,visualMax:null,color:[],colorAlpha:null,colorSaturation:null,colorMappingBy:"index",visibleMin:10,childrenVisibleMin:null,levels:[]},getInitialData:function(t,e){var i={name:t.name,children:t.data};!function i(t){var n=0;E(t.children,function(t){i(t);var e=t.value;O(e)&&(e=e[0]),n+=e});var e=t.value;O(e)&&(e=e[0]);null!=e&&!isNaN(e)||(e=n);e<0&&(e=0);O(t.value)?t.value[0]=e:t.value=e}(i);var n=t.levels||[];n=t.levels=function(t,e){var n,i=e.get("color");if(!i)return;if(E(t=t||[],function(t){var e=new Cl(t),i=e.get("color");(e.get("itemStyle.color")||i&&"none"!==i)&&(n=!0)}),!n){(t[0]||(t[0]={})).color=i.slice()}return t}(n,e);var o={};return o.levels=n,Ix.createTree(i,this,o).data},optionUpdated:function(){this.resetViewRoot()},formatTooltip:function(t){var e=this.getData(),i=this.getRawValue(t),n=O(i)?tu(i[0]):tu(i);return au(e.getName(t)+": "+n)},getDataParams:function(t){var e=rc.prototype.getDataParams.apply(this,arguments),i=this.getData().tree.getNodeByDataIndex(t);return e.treePathInfo=Ux(i,this),e},setLayoutInfo:function(t){this.layoutInfo=this.layoutInfo||{},k(this.layoutInfo,t)},mapIdToIndex:function(t){var e=this._idIndexMap;e||(e=this._idIndexMap=Q(),this._idIndexMapCount=0);var i=e.get(t);return null==i&&e.set(t,i=this._idIndexMapCount++),i},getViewRoot:function(){return this._viewRoot},resetViewRoot:function(t){t?this._viewRoot=t:t=this._viewRoot;var e=this.getRawData().tree.root;t&&(t===e||e.contains(t))||(this._viewRoot=e)}});var Xx=5;function Yx(t){this.group=new Ci,t.add(this.group)}function jx(t,e,i,n,o,a){var r=[[o?t:t-Xx,e],[t+i,e],[t+i,e+n],[o?t:t-Xx,e+n]];return a||r.splice(2,0,[t+i+Xx,e+n/2]),o||r.push([t,e+n/2]),r}Yx.prototype={constructor:Yx,render:function(t,e,i,n){var o=t.getModel("breadcrumb"),a=this.group;if(a.removeAll(),o.get("show")&&i){var r=o.getModel("itemStyle"),s=r.getModel("textStyle"),l={pos:{left:o.get("left"),right:o.get("right"),top:o.get("top"),bottom:o.get("bottom")},box:{width:e.getWidth(),height:e.getHeight()},emptyItemWidth:o.get("emptyItemWidth"),totalWidth:0,renderList:[]};this._prepare(i,l,s),this._renderContent(t,l,r,s,n),bu(a,l.pos,l.box)}},_prepare:function(t,e,i){for(var n=t;n;n=n.parentNode){var o=n.getModel().get("name"),a=i.getTextRect(o),r=Math.max(a.width+16,e.emptyItemWidth);e.totalWidth+=r+8,e.renderList.push({node:n,text:o,width:r})}},_renderContent:function(t,e,i,n,o){for(var a,r,s=0,l=e.emptyItemWidth,u=t.get("breadcrumb.height"),h=function(t,e,i){var n=e.width,o=e.height,a=Rl(t.x,n),r=Rl(t.y,o),s=Rl(t.x2,n),l=Rl(t.y2,o);return(isNaN(a)||isNaN(parseFloat(t.x)))&&(a=0),(isNaN(s)||isNaN(parseFloat(t.x2)))&&(s=n),(isNaN(r)||isNaN(parseFloat(t.y)))&&(r=0),(isNaN(l)||isNaN(parseFloat(t.y2)))&&(l=o),i=iu(i||0),{width:Math.max(s-a-i[1]-i[3],0),height:Math.max(l-r-i[0]-i[2],0)}}(e.pos,e.box),c=e.totalWidth,d=e.renderList,f=d.length-1;0<=f;f--){var p=d[f],g=p.node,m=p.width,v=p.text;c>h.width&&(c-=m-l,m=l,v=null);var y=new Qr({shape:{points:jx(s,0,m,u,f===d.length-1,0===f)},style:D(i.getItemStyle(),{lineJoin:"bevel",text:v,textFill:n.getTextColor(),textFont:n.getFont()}),z:10,onclick:T(o,g)});this.group.add(y),a=t,r=g,y.eventData={componentType:"series",componentSubType:"treemap",componentIndex:a.componentIndex,seriesIndex:a.componentIndex,seriesName:a.name,seriesType:"treemap",selfType:"breadcrumb",nodeData:{dataIndex:r&&r.dataIndex,name:r&&r.name},treePathInfo:r&&Ux(r,a)},s+=m+8}},remove:function(){this.group.removeAll()}};function qx(t){var e=a_(t);return e.stroke=e.fill=e.lineWidth=null,e}var Kx=A,$x=Ci,Jx=rs,Qx=E,t_=["label"],e_=["emphasis","label"],i_=["upperLabel"],n_=["emphasis","upperLabel"],o_=10,a_=ha([["fill","color"],["stroke","strokeColor"],["lineWidth","strokeWidth"],["shadowBlur"],["shadowOffsetX"],["shadowOffsetY"],["shadowColor"]]);function r_(c,r,s,l,u,e,d,t,i,n){if(d){var f=d.getLayout(),o=c.getData();if(o.setItemGraphicEl(d.dataIndex,null),f&&f.isInView){var h=f.width,p=f.height,g=f.borderWidth,m=f.invisible,v=d.getRawIndex(),y=t&&t.getRawIndex(),a=d.viewChildren,x=f.upperHeight,_=a&&a.length,w=d.getModel("itemStyle"),b=d.getModel("emphasis.itemStyle"),S=C("nodeGroup",$x);if(S){if(i.add(S),S.attr("position",[f.x||0,f.y||0]),S.__tmNodeWidth=h,S.__tmNodeHeight=p,f.isAboveViewRoot)return S;var M=d.getModel(),I=C("background",Jx,n,1);if(I&&function(t,e,i){if(e.dataIndex=d.dataIndex,e.seriesIndex=c.seriesIndex,e.setShape({x:0,y:0,width:h,height:p}),m)A(e);else{e.invisible=!1;var n=d.getVisual("borderColor",!0),o=b.get("borderColor"),a=qx(w);a.fill=n;var r=a_(b);if(r.fill=o,i){var s=h-2*g;D(a,r,n,s,x,{x:g,y:0,width:s,height:x})}else a.text=r.text=null;e.setStyle(a),Us(e,r)}t.add(e)}(S,I,_&&f.upperHeight),_)Qs(S)&&Js(S,!1),I&&(Js(I,!0),o.setItemGraphicEl(d.dataIndex,I));else{var T=C("content",Jx,n,2);T&&function(t,e){e.dataIndex=d.dataIndex,e.seriesIndex=c.seriesIndex;var i=Math.max(h-2*g,0),n=Math.max(p-2*g,0);if(e.culling=!0,e.setShape({x:g,y:g,width:i,height:n}),m)A(e);else{e.invisible=!1;var o=d.getVisual("color",!0),a=qx(w);a.fill=o;var r=a_(b);D(a,r,o,i,n),e.setStyle(a),Us(e,r)}t.add(e)}(S,T),I&&Qs(I)&&Js(I,!1),Js(S,!0),o.setItemGraphicEl(d.dataIndex,S)}return S}}}function A(t){t.invisible||e.push(t)}function D(t,e,i,n,o,a){var r=W(c.getFormattedLabel(d.dataIndex,"normal",null,null,a?"upperLabel":"label"),M.get("name"));if(!a&&f.isLeafRoot){var s=c.get("drillDownIcon",!0);r=s?s+" "+r:r}var l=M.getModel(a?i_:t_),u=M.getModel(a?n_:e_),h=l.getShallow("show");el(t,e,l,u,{defaultText:h?r:null,autoColor:i,isRectText:!0}),a&&(t.textRect=L(a)),t.truncate=h&&l.get("ellipsis")?{outerWidth:n,outerHeight:o,minChar:2}:null}function C(t,e,i,n){var o=null!=y&&s[t][y],a=u[t];return o?(s[t][y]=null,function(t,e,i){(t[v]={}).old="nodeGroup"===i?e.position.slice():k({},e.shape)}(a,o,t)):m||((o=new e({z:function(t,e){var i=t*o_+e;return(i-1)/i}(i,n)})).__tmDepth=i,function(t,e,i){var n=t[v]={},o=d.parentNode;if(o&&(!l||"drillDown"===l.direction)){var a=0,r=0,s=u.background[o.getRawIndex()];!l&&s&&s.old&&(a=s.old.width,r=s.old.height),n.old="nodeGroup"===i?[0,r]:{x:a,y:r,width:0,height:0}}n.fadein="nodeGroup"!==i}(a,0,o.__tmStorageName=t)),r[t][v]=o}}Df({type:"treemap",init:function(t,e){this._containerGroup,this._storage={nodeGroup:[],background:[],content:[]},this._oldTree,this._breadcrumb,this._controller,this._state="ready"},render:function(t,e,i,n){if(!(_(e.findComponents({mainType:"series",subType:"treemap",query:n}),t)<0)){this.seriesModel=t,this.api=i,this.ecModel=e;var o=Wx(n,["treemapZoomToNode","treemapRootToNode"],t),a=n&&n.type,r=t.layoutInfo,s=!this._oldTree,l=this._storage,u="treemapRootToNode"===a&&o&&l?{rootNodeGroup:l.nodeGroup[o.node.getRawIndex()],direction:n.direction}:null,h=this._giveContainerGroup(r),c=this._doRender(h,t,u);s||a&&"treemapZoomToNode"!==a&&"treemapRootToNode"!==a?c.renderFinally():this._doAnimation(h,c,t,u),this._resetController(i),this._renderBreadcrumb(t,i,o)}},_giveContainerGroup:function(t){var e=this._containerGroup;return e||(e=this._containerGroup=new $x,this._initEvents(e),this.group.add(e)),e.attr("position",[t.x,t.y]),e},_doRender:function(t,e,i){var n=e.getData().tree,o=this._oldTree,a={nodeGroup:[],background:[],content:[]},r={nodeGroup:[],background:[],content:[]},s=this._storage,l=[],c=T(r_,e,r,s,i,a,l);!function a(r,s,l,u,h){u?Qx(s=r,function(t,e){t.isRemoved()||i(e,e)}):new Lf(s,r,t,t).add(i).update(i).remove(T(i,null)).execute();function t(t){return t.getId()}function i(t,e){var i=null!=t?r[t]:null,n=null!=e?s[e]:null,o=c(i,n,l,h);o&&a(i&&i.viewChildren||[],n&&n.viewChildren||[],o,u,h+1)}}(n.root?[n.root]:[],o&&o.root?[o.root]:[],t,n===o||!o,0);var u,h,d=(h={nodeGroup:[],background:[],content:[]},(u=s)&&Qx(u,function(t,e){var i=h[e];Qx(t,function(t){t&&(i.push(t),t.__tmWillDelete=1)})}),h);return this._oldTree=n,this._storage=r,{lastsForAnimation:a,willDeleteEls:d,renderFinally:function(){Qx(d,function(t){Qx(t,function(t){t.parent&&t.parent.remove(t)})}),Qx(l,function(t){t.invisible=!0,t.dirty()})}}},_doAnimation:function(t,a,e,s){if(e.get("animation")){var l=e.get("animationDurationUpdate"),u=e.get("animationEasing"),h=function(){var a,r=[],s={};return{add:function(t,e,i,n,o){return R(n)&&(o=n,n=0),!s[t.id]&&(s[t.id]=1,r.push({el:t,target:e,time:i,delay:n,easing:o}),!0)},done:function(t){return a=t,this},start:function(){for(var t=r.length,e=0,i=r.length;e=r.length||t===r[t.depth]){var i=O_(l,c,t,e,y,s);n(t,i,o,a,r,s)}})}else p=L_(c),t.setVisual("color",p)}(a,{},N(o.levelModels,function(t){return t?t.get(A_):null}),r,t.getViewRoot().getAncestors(),t)}};function C_(i,n,o,a){var r=k({},n);return E(["color","colorAlpha","colorSaturation"],function(t){var e=i.get(t,!0);null==e&&o&&(e=o[t]),null==e&&(e=n[t]),null==e&&(e=a.get(t)),null!=e&&(r[t]=e)}),r}function L_(t){var e=k_(t,"color");if(e){var i=k_(t,"colorAlpha"),n=k_(t,"colorSaturation");return n&&(e=qe(e,null,null,n)),i&&(e=Ke(e,i)),e}}function k_(t,e){var i=t[e];if(null!=i&&"none"!==i)return i}function P_(t,e,i,n,o,a){if(a&&a.length){var r=N_(e,"color")||null!=o.color&&"none"!==o.color&&(N_(e,"colorAlpha")||N_(e,"colorSaturation"));if(r){var s=e.get("visualMin"),l=e.get("visualMax"),u=i.dataExtent.slice();null!=s&&su[1]&&(u[1]=l);var h=e.get("colorMappingBy"),c={type:r.name,dataExtent:u,visual:r.range};"color"!==c.type||"index"!==h&&"id"!==h?c.mappingMethod="linear":(c.mappingMethod="category",c.loop=!0);var d=new f_(c);return d.__drColorMappingBy=h,d}}}function N_(t,e){var i=t.get(e);return T_(i)&&i.length?{name:e,range:i}:null}function O_(t,e,i,n,o,a){var r=k({},e);if(o){var s=o.type,l="color"===s&&o.__drColorMappingBy,u="index"===l?n:"id"===l?a.mapIdToIndex(i.getId()):i.getValue(t.get("visualDimension"));r[s]=o.mapValueToVisual(u)}return r}var E_=Math.max,R_=Math.min,z_=W,B_=E,V_=["itemStyle","borderWidth"],G_=["itemStyle","gapWidth"],F_=["upperLabel","show"],W_=["upperLabel","height"],H_={seriesType:"treemap",reset:function(t,e,i,n){var o=i.getWidth(),a=i.getHeight(),r=t.option,s=wu(t.getBoxLayoutParams(),{width:i.getWidth(),height:i.getHeight()}),l=r.size||[],u=Rl(z_(s.width,l[0]),o),h=Rl(z_(s.height,l[1]),a),c=n&&n.type,d=Wx(n,["treemapZoomToNode","treemapRootToNode"],t),f="treemapRender"===c||"treemapMove"===c?n.rootRect:null,p=t.getViewRoot(),g=Hx(p);if("treemapMove"!==c){var m="treemapZoomToNode"===c?function(t,e,i,n,o){var a,r=(e||{}).node,s=[n,o];if(!r||r===i)return s;var l=n*o,u=l*t.option.zoomToNodeRatio;for(;a=r.parentNode;){for(var h=0,c=a.children,d=0,f=c.length;ds[1]&&(s[1]=e)})}else s=[NaN,NaN];return{sum:n,dataExtent:s}}(e,r,s);if(0===u.sum)return t.viewChildren=[];if(u.sum=function(t,e,i,n,o){if(!n)return i;for(var a=t.get("visibleMin"),r=o.length,s=r,l=r-1;0<=l;l--){var u=o["asc"===n?r-l-1:l].getValue();u/i*ei[l[r]])&&(h=i[l[r]]);for(var c=0,d=t.length;c "+d)),u++)}var f,p=i.get("coordinateSystem");if("cartesian2d"===p||"polar"===p)f=pp(t,i);else{var g=sh.get(p),m=g&&"view"!==g.type&&g.dimensions||[];_(m,"value")<0&&m.concat(["value"]);var v=sp(t,{coordDimensions:m});(f=new Xf(v,i)).initData(t)}var y=new Xf(["value"],i);return y.initData(l,s),o&&o(f,y),mx({mainData:f,struct:a,structAttr:"graph",datas:{node:f,edge:y},datasAttr:{node:"data",edge:"edgeData"}}),a.update(),a}var ew=Af({type:"series.graph",init:function(t){ew.superApply(this,"init",arguments);var e=this;function i(){return e._categoriesData}this.legendVisualProvider=new Yv(i,i),this.fillDataTextStyle(t.edges||t.links),this._updateCategoriesData()},mergeOption:function(t){ew.superApply(this,"mergeOption",arguments),this.fillDataTextStyle(t.edges||t.links),this._updateCategoriesData()},mergeDefaultAndTheme:function(t){ew.superApply(this,"mergeDefaultAndTheme",arguments),Go(t,["edgeLabel"],["show"])},getInitialData:function(t,s){var e=t.edges||t.links||[],i=t.data||t.nodes||[],l=this;if(i&&e)return tw(i,e,this,!0,function(t,e){t.wrapMethod("getItemModel",function(t){var e=l._categoriesModels[t.getShallow("category")];return e&&(e.parentModel=t.parentModel,t.parentModel=e),t});var i=l.getModel("edgeLabel"),n=new Cl({label:i.option},i.parentModel,s),o=l.getModel("emphasis.edgeLabel"),a=new Cl({emphasis:{label:o.option}},o.parentModel,s);function r(t){return(t=this.parsePath(t))&&"label"===t[0]?n:t&&"emphasis"===t[0]&&"label"===t[1]?a:this.parentModel}e.wrapMethod("getItemModel",function(t){return t.customizeGetParent(r),t})}).data},getGraph:function(){return this.getData().graph},getEdgeData:function(){return this.getGraph().edgeData},getCategoriesData:function(){return this._categoriesData},formatTooltip:function(t,e,i){if("edge"!==i)return ew.superApply(this,"formatTooltip",arguments);var n=this.getData(),o=this.getDataParams(t,i),a=n.graph.getEdgeByIndex(t),r=n.getName(a.node1.dataIndex),s=n.getName(a.node2.dataIndex),l=[];return null!=r&&l.push(r),null!=s&&l.push(s),l=au(l.join(" > ")),o.value&&(l+=" : "+au(o.value)),l},_updateCategoriesData:function(){var t=N(this.option.categories||[],function(t){return null!=t.value?t:k({value:0},t)}),e=new Xf(["value"],this);e.initData(t),this._categoriesData=e,this._categoriesModels=e.mapArray(function(t){return e.getItemModel(t,!0)})},setZoom:function(t){this.option.zoom=t},setCenter:function(t){this.option.center=t},isAnimationEnabled:function(){return ew.superCall(this,"isAnimationEnabled")&&!("force"===this.get("layout")&&this.get("force.layoutAnimation"))},defaultOption:{zlevel:0,z:2,coordinateSystem:"view",legendHoverLink:!0,hoverAnimation:!0,layout:null,focusNodeAdjacency:!1,circular:{rotateLabel:!1},force:{initLayout:null,repulsion:[0,50],gravity:.1,friction:.6,edgeLength:30,layoutAnimation:!0},left:"center",top:"center",symbol:"circle",symbolSize:10,edgeSymbol:["none","none"],edgeSymbolSize:10,edgeLabel:{position:"middle",distance:5},draggable:!1,roam:!1,center:null,zoom:1,nodeScaleRatio:.6,label:{show:!1,formatter:"{b}"},itemStyle:{},lineStyle:{color:"#aaa",width:1,curveness:0,opacity:.5},emphasis:{label:{show:!0}}}}),iw=ls.prototype,nw=ds.prototype;function ow(t){return isNaN(+t.cpx1)||isNaN(+t.cpy1)}var aw=Cs({type:"ec-line",style:{stroke:"#000",fill:null},shape:{x1:0,y1:0,x2:0,y2:0,percent:1,cpx1:null,cpy1:null},buildPath:function(t,e){this[ow(e)?"_buildPathLine":"_buildPathCurve"](t,e)},_buildPathLine:iw.buildPath,_buildPathCurve:nw.buildPath,pointAt:function(t){return this[ow(this.shape)?"_pointAtLine":"_pointAtCurve"](t)},_pointAtLine:iw.pointAt,_pointAtCurve:nw.pointAt,tangentAt:function(t){var e=this.shape,i=ow(e)?[e.x2-e.x1,e.y2-e.y1]:this._tangentAtCurve(t);return mt(i,i)},_tangentAtCurve:nw.tangentAt}),rw=["fromSymbol","toSymbol"];function sw(t){return"_"+t+"Type"}function lw(t,e,i){var n=e.getItemVisual(i,"color"),o=e.getItemVisual(i,t),a=e.getItemVisual(i,t+"Size");if(o&&"none"!==o){O(a)||(a=[a,a]);var r=_g(o,-a[0]/2,-a[1]/2,a[0],a[1],n);return r.name=t,r}}function uw(t,e){t.x1=e[0][0],t.y1=e[0][1],t.x2=e[1][0],t.y2=e[1][1],t.percent=1;var i=e[2];i?(t.cpx1=i[0],t.cpy1=i[1]):(t.cpx1=NaN,t.cpy1=NaN)}function hw(t,e,i){Ci.call(this),this._createLine(t,e,i)}var cw=hw.prototype;function dw(t){this._ctor=t||hw,this.group=new Ci}cw.beforeUpdate=function(){var t=this.childOfName("fromSymbol"),e=this.childOfName("toSymbol"),i=this.childOfName("label");if(t||e||!i.ignore){for(var n=1,o=this.parent;o;)o.scale&&(n/=o.scale[0]),o=o.parent;var a=this.childOfName("line");if(this.__dirty||a.__dirty){var r=a.shape.percent,s=a.pointAt(0),l=a.pointAt(r),u=ht([],l,s);if(mt(u,u),t){t.attr("position",s);var h=a.tangentAt(0);t.attr("rotation",Math.PI/2-Math.atan2(h[1],h[0])),t.attr("scale",[n*r,n*r])}if(e){e.attr("position",l);h=a.tangentAt(1);e.attr("rotation",-Math.PI/2-Math.atan2(h[1],h[0])),e.attr("scale",[n*r,n*r])}if(!i.ignore){var c,d,f,p;i.attr("position",l);var g=i.__labelDistance,m=g[0]*n,v=g[1]*n,y=r/2,x=[(h=a.tangentAt(y))[1],-h[0]],_=a.pointAt(y);0=t&&(0===e?0:n[e-1][0])a&&(e[1-n]=e[n]+c.sign*a),e}function $w(t,e){var i=t[e]-t[1-e];return{span:Math.abs(i),sign:0o*(1-h[0])?(l="jump",r=s-o*(1-h[2])):0<=(r=s-o*h[1])&&(r=s-o*(1-h[1]))<=0&&(r=0),(r*=e.axisExpandWidth/u)?Kw(r,n,a,"all"):l="none";else{o=n[1]-n[0];(n=[eb(0,a[1]*s/o-o/2)])[1]=tb(a[1],n[0]+o),n[0]=n[1]-o}return{axisExpandWindow:n,behavior:l}}},sh.register("parallel",{create:function(n,o){var a=[];return n.eachComponent("parallel",function(t,e){var i=new rb(t,n,o);i.name="parallel_"+e,i.resize(t,o),(t.coordinateSystem=i).model=t,a.push(i)}),n.eachSeries(function(t){if("parallel"===t.get("coordinateSystem")){var e=n.queryComponents({mainType:"parallel",index:t.get("parallelIndex"),id:t.get("parallelId")})[0];t.coordinateSystem=e.coordinateSystem}}),a}});var lb=Lu.extend({type:"baseParallelAxis",axis:null,activeIntervals:[],getAreaSelectStyle:function(){return ha([["fill","color"],["lineWidth","borderWidth"],["stroke","borderColor"],["width","width"],["opacity","opacity"]])(this.getModel("areaSelectStyle"))},setActiveIntervals:function(t){var e=this.activeIntervals=L(t);if(e)for(var i=e.length-1;0<=i;i--)Bl(e[i])},getActiveState:function(t){var e=this.activeIntervals;if(!e.length)return"normal";if(null==t||isNaN(t))return"inactive";if(1===e.length){var i=e[0];if(i[0]<=t&&t<=i[1])return"active"}else for(var n=0,o=e.length;nn.getWidth()||i<0||i>n.getHeight()}(t,e)){var n=t._zr,o=t._covers,a=Lb(t,e,i);if(!t._dragging)for(var r=0;rf&&(f=m.depth),g.setLayout({depth:v?m.depth:c},!0),"vertical"===a?g.setLayout({dy:i},!0):g.setLayout({dx:i},!0);for(var y=0;y "))},preventIncremental:function(){return!!this.get("effect.show")},getProgressive:function(){var t=this.option.progressive;return null==t?this.option.large?1e4:this.get("progressive"):t},getProgressiveThreshold:function(){var t=this.option.progressiveThreshold;return null==t?this.option.large?2e4:this.get("progressiveThreshold"):t},defaultOption:{coordinateSystem:"geo",zlevel:0,z:2,legendHoverLink:!0,hoverAnimation:!0,xAxisIndex:0,yAxisIndex:0,symbol:["none","none"],symbolSize:[10,10],geoIndex:0,effect:{show:!1,period:4,constantSpeed:0,symbol:"circle",symbolSize:3,loop:!0,trailLength:.2},large:!1,largeThreshold:2e3,polyline:!1,clip:!0,label:{show:!1,position:"end"},lineStyle:{opacity:.5}}});function uM(t,e,i){Ci.call(this),this.add(this.createLine(t,e,i)),this._updateEffectSymbol(t,e)}var hM=uM.prototype;function cM(t,e,i){Ci.call(this),this._createPolyline(t,e,i)}hM.createLine=function(t,e,i){return new hw(t,e,i)},hM._updateEffectSymbol=function(t,e){var i=t.getItemModel(e).getModel("effect"),n=i.get("symbolSize"),o=i.get("symbol");O(n)||(n=[n,n]);var a=i.get("color")||t.getItemVisual(e,"color"),r=this.childAt(1);this._symbolType!==o&&(this.remove(r),(r=_g(o,-.5,-.5,1,1,a)).z2=100,r.culling=!0,this.add(r)),r&&(r.setStyle("shadowColor",a),r.setStyle(i.getItemStyle(["color"])),r.attr("scale",n),r.setColor(a),r.attr("scale",n),this._symbolType=o,this._symbolScale=n,this._updateEffectAnimation(t,i,e))},hM._updateEffectAnimation=function(e,t,i){var n=this.childAt(1);if(n){var o=this,a=e.getItemLayout(i),r=1e3*t.get("period"),s=t.get("loop"),l=t.get("constantSpeed"),u=W(t.get("delay"),function(t){return t/e.count()*r/3}),h="function"==typeof u;if(n.ignore=!0,this.updateAnimationPoints(n,a),0e);r++);r=Math.min(r-1,o-2)}wt(t.position,i[r],i[r+1],(e-n[r])/(n[r+1]-n[r]));var s=i[r+1][0]-i[r][0],l=i[r+1][1]-i[r][1];t.rotation=-Math.atan2(l,s)-Math.PI/2,this._lastFrame=r,this._lastFramePercent=e,t.ignore=!1}},w(fM,uM);var gM=Cs({shape:{polyline:!1,curveness:0,segs:[]},buildPath:function(t,e){var i=e.segs,n=e.curveness;if(e.polyline)for(var o=0;o=e[0]&&t<=e[1]}}(y,e.option.range):function(e,n,o){var i=e[1]-e[0],a=(n=N(n,function(t){return{interval:[(t.interval[0]-e[0])/i,(t.interval[1]-e[0])/i]}})).length,r=0;return function(t){for(var e=r;e=e.y&&t[1]<=e.y+e.height:i.contain(i.toLocalCoord(t[1]))&&t[0]>=e.y&&t[0]<=e.y+e.height},pointToData:function(t){var e=this.getAxis();return[e.coordToData(e.toLocalCoord(t["horizontal"===e.orient?0:1]))]},dataToPoint:function(t){var e=this.getAxis(),i=this.getRect(),n=[],o="horizontal"===e.orient?0:1;return t instanceof Array&&(t=t[0]),n[o]=e.toGlobalCoord(e.dataToCoord(+t)),n[1-o]=0==o?i.y+i.height/2:i.x+i.width/2,n}}).dimensions});var jM=["axisLine","axisTickLabel","axisName"],qM=["splitArea","splitLine"],KM=pv.extend({type:"singleAxis",axisPointerClass:"SingleAxisPointer",render:function(e,t,i,n){var o=this.group;o.removeAll();var a=this._axisGroup;this._axisGroup=new Ci;var r=YM(e),s=new $m(e,r);E(jM,s.add,s),o.add(this._axisGroup),o.add(s.getGroup()),E(qM,function(t){e.get(t+".show")&&this["_"+t](e)},this),ml(a,this._axisGroup,e),KM.superCall(this,"render",e,t,i,n)},remove:function(){_v(this)},_splitLine:function(t){var e=t.axis;if(!e.scale.isBlank()){var i=t.getModel("splitLine"),n=i.getModel("lineStyle"),o=n.get("width"),a=n.get("color");a=a instanceof Array?a:[a];for(var r=t.coordinateSystem.getRect(),s=e.isHorizontal(),l=[],u=0,h=e.getTicksCoords({tickModel:i}),c=[],d=[],f=0;fr)return!0;if(a){var s=cv(t).seriesDataCount,l=n.getExtent();return Math.abs(l[0]-l[1])/s>r}return!1},makeElOption:function(t,e,i,n,o){},createPointerEl:function(t,e,i,n){var o=e.pointer;if(o){var a=pI(t).pointerEl=new bl[o.type](gI(e.pointer));t.add(a)}},createLabelEl:function(t,e,i,n){if(e.label){var o=pI(t).labelEl=new rs(gI(e.label));t.add(o),xI(o,n)}},updatePointerEl:function(t,e,i){var n=pI(t).pointerEl;n&&e.pointer&&(n.setStyle(e.pointer.style),i(n,{shape:e.pointer.shape}))},updateLabelEl:function(t,e,i,n){var o=pI(t).labelEl;o&&(o.setStyle(e.label.style),i(o,{shape:e.label.shape,position:e.label.position}),xI(o,n))},_renderHandle:function(t){if(!this._dragging&&this.updateHandleTransform){var e,i=this._axisPointerModel,n=this._api.getZr(),o=this._handle,a=i.getModel("handle"),r=i.get("status");if(!a.get("show")||!r||"hide"===r)return o&&n.remove(o),void(this._handle=null);this._handle||(e=!0,o=this._handle=yl(a.get("icon"),{cursor:"move",draggable:!0,onmousemove:function(t){Xt(t.event)},onmousedown:mI(this._onHandleDragMove,this,0,0),drift:mI(this._onHandleDragMove,this),ondragend:mI(this._onHandleDragEnd,this)}),n.add(o)),wI(o,i,!1);o.setStyle(a.getItemStyle(null,["color","borderColor","borderWidth","opacity","shadowColor","shadowBlur","shadowOffsetX","shadowOffsetY"]));var s=a.get("size");O(s)||(s=[s,s]),o.attr("scale",[s[0]/2,s[1]/2]),Lc(this,"_doDispatchAxisPointer",a.get("throttle")||0,"fixRate"),this._moveHandleToValue(t,e)}},_moveHandleToValue:function(t,e){yI(this._axisPointerModel,!e&&this._moveAnimation,this._handle,_I(this.getHandleTransform(t,this._axisModel,this._axisPointerModel)))},_onHandleDragMove:function(t,e){var i=this._handle;if(i){this._dragging=!0;var n=this.updateHandleTransform(_I(i),[t,e],this._axisModel,this._axisPointerModel);this._payloadInfo=n,i.stopAnimation(),i.attr(_I(n)),pI(i).lastProp=null,this._doDispatchAxisPointer()}},_doDispatchAxisPointer:function(){if(this._handle){var t=this._payloadInfo,e=this._axisModel;this._api.dispatchAction({type:"updateAxisPointer",x:t.cursorPoint[0],y:t.cursorPoint[1],tooltipOption:t.tooltipOption,axesInfo:[{axisDim:e.axis.dim,axisIndex:e.componentIndex}]})}},_onHandleDragEnd:function(t){if(this._dragging=!1,this._handle){var e=this._axisPointerModel.get("value");this._moveHandleToValue(e),this._api.dispatchAction({type:"hideTip"})}},getHandleTransform:null,updateHandleTransform:null,clear:function(t){this._lastValue=null,this._lastStatus=null;var e=t.getZr(),i=this._group,n=this._handle;e&&i&&(this._lastGraphicKey=null,i&&e.remove(i),n&&e.remove(n),this._group=null,this._handle=null,this._payloadInfo=null)},doClear:function(){},buildLabel:function(t,e,i){return{x:t[i=i||0],y:t[1-i],width:e[i],height:e[1-i]}}}).constructor=vI);var LI=vI.extend({makeElOption:function(t,e,i,n,o){var a=i.axis,r=a.grid,s=n.get("type"),l=kI(r,a).getOtherAxis(a).getGlobalExtent(),u=a.toGlobalCoord(a.dataToCoord(e,!0));if(s&&"none"!==s){var h=bI(n),c=PI[s](a,u,l);c.style=h,t.graphicKey=c.type,t.pointer=c}TI(e,t,yv(r.model,i),i,n,o)},getHandleTransform:function(t,e,i){var n=yv(e.axis.grid.model,e,{labelInside:!1});return n.labelMargin=i.get("handle.margin"),{position:II(e.axis,t,n),rotation:n.rotation+(n.labelDirection<0?Math.PI:0)}},updateHandleTransform:function(t,e,i,n){var o=i.axis,a=o.grid,r=o.getGlobalExtent(!0),s=kI(a,o).getOtherAxis(o).getGlobalExtent(),l="x"===o.dim?0:1,u=t.position;u[l]+=e[l],u[l]=Math.min(r[1],u[l]),u[l]=Math.max(r[0],u[l]);var h=(s[1]+s[0])/2,c=[h,h];c[l]=u[l];return{position:u,rotation:t.rotation,cursorPoint:c,tooltipOption:[{verticalAlign:"middle"},{align:"center"}][l]}}});function kI(t,e){var i={};return i[e.dim+"AxisIndex"]=e.index,t.getCartesian(i)}var PI={line:function(t,e,i){return{type:"Line",subPixelOptimize:!0,shape:AI([e,i[0]],[e,i[1]],NI(t))}},shadow:function(t,e,i){var n=Math.max(1,t.getBandWidth()),o=i[1]-i[0];return{type:"Rect",shape:DI([e-n/2,i[0]],[n,o],NI(t))}}};function NI(t){return"x"===t.dim?0:1}pv.registerAxisPointerClass("CartesianAxisPointer",LI),vf(function(t){if(t){t.axisPointer&&0!==t.axisPointer.length||(t.axisPointer={});var e=t.axisPointer.link;e&&!O(e)&&(t.axisPointer.link=[e])}}),yf(Cd.PROCESSOR.STATISTIC,function(t,e){t.getComponent("axisPointer").coordSysAxesInfo=uv(t,e)}),xf({type:"updateAxisPointer",event:"updateAxisPointer",update:":updateAxisPointer"},function(t,e,i){var n=t.currTrigger,r=[t.x,t.y],o=t,a=t.dispatchAction||A(i.dispatchAction,i),s=e.getComponent("axisPointer").coordSysAxesInfo;if(s){rI(r)&&(r=JM({seriesIndex:o.seriesIndex,dataIndex:o.dataIndex},e).point);var l=rI(r),u=o.axesInfo,h=s.axesInfo,c="leave"===n||rI(r),d={},f={},p={list:[],map:{}},g={showPointer:tI(nI,f),showTooltip:tI(oI,p)};QM(s.coordSysMap,function(t,e){var a=l||t.containPoint(r);QM(s.coordSysAxesInfo[e],function(t,e){var i=t.axis,n=function(t,e){for(var i=0;i<(t||[]).length;i++){var n=t[i];if(e.axis.dim===n.axisDim&&e.axis.model.componentIndex===n.axisIndex)return n}}(u,t);if(!c&&a&&(!u||n)){var o=n&&n.value;null!=o||l||(o=i.pointToData(r)),null!=o&&iI(t,o,g,!1,d)}})});var m={};return QM(h,function(o,t){var a=o.linkGroup;a&&!f[t]&&QM(a.axesInfo,function(t,e){var i=f[e];if(t!==o&&i){var n=i.value;a.mapper&&(n=o.axis.scale.parse(a.mapper(n,aI(t),aI(o)))),m[o.key]=n}})}),QM(m,function(t,e){iI(h[e],t,g,!0,d)}),function(o,t,e){var a=e.axesInfo=[];QM(t,function(t,e){var i=t.axisPointerModel.option,n=o[e];n?(t.useHandle||(i.status="show"),i.value=n.value,i.seriesDataIndices=(n.payloadBatch||[]).slice()):t.useHandle||(i.status="hide"),"show"===i.status&&a.push({axisDim:t.axis.dim,axisIndex:t.axis.model.componentIndex,value:i.value})})}(f,h,d),function(t,e,i,n){if(rI(e)||!t.list.length)return n({type:"hideTip"});var o=((t.list[0].dataByAxis[0]||{}).seriesDataIndices||[])[0]||{};n({type:"showTip",escapeConnect:!0,x:e[0],y:e[1],tooltipOption:i.tooltipOption,position:i.position,dataIndexInside:o.dataIndexInside,dataIndex:o.dataIndex,seriesIndex:o.seriesIndex,dataByCoordSys:t.list})}(p,r,t,a),function(t,e,i){var n=i.getZr(),o="axisPointerLastHighlights",a=eI(n)[o]||{},r=eI(n)[o]={};QM(t,function(t,e){var i=t.axisPointerModel.option;"show"===i.status&&QM(i.seriesDataIndices,function(t){var e=t.seriesIndex+" | "+t.dataIndex;r[e]=t})});var s=[],l=[];E(a,function(t,e){r[e]||l.push(t)}),E(r,function(t,e){a[e]||s.push(t)}),l.length&&i.dispatchAction({type:"downplay",escapeConnect:!0,batch:l}),s.length&&i.dispatchAction({type:"highlight",escapeConnect:!0,batch:s})}(h,0,i),d}});var OI=["x","y"],EI=["width","height"],RI=vI.extend({makeElOption:function(t,e,i,n,o){var a=i.axis,r=a.coordinateSystem,s=VI(r,1-BI(a)),l=r.dataToPoint(e)[0],u=n.get("type");if(u&&"none"!==u){var h=bI(n),c=zI[u](a,l,s);c.style=h,t.graphicKey=c.type,t.pointer=c}TI(e,t,YM(i),i,n,o)},getHandleTransform:function(t,e,i){var n=YM(e,{labelInside:!1});return n.labelMargin=i.get("handle.margin"),{position:II(e.axis,t,n),rotation:n.rotation+(n.labelDirection<0?Math.PI:0)}},updateHandleTransform:function(t,e,i,n){var o=i.axis,a=o.coordinateSystem,r=BI(o),s=VI(a,r),l=t.position;l[r]+=e[r],l[r]=Math.min(s[1],l[r]),l[r]=Math.max(s[0],l[r]);var u=VI(a,1-r),h=(u[1]+u[0])/2,c=[h,h];return c[r]=l[r],{position:l,rotation:t.rotation,cursorPoint:c,tooltipOption:{verticalAlign:"middle"}}}}),zI={line:function(t,e,i){return{type:"Line",subPixelOptimize:!0,shape:AI([e,i[0]],[e,i[1]],BI(t))}},shadow:function(t,e,i){var n=t.getBandWidth(),o=i[1]-i[0];return{type:"Rect",shape:DI([e-n/2,i[0]],[n,o],BI(t))}}};function BI(t){return t.isHorizontal()?0:1}function VI(t,e){var i=t.getRect();return[i[OI[e]],i[OI[e]]+i[EI[e]]]}pv.registerAxisPointerClass("SingleAxisPointer",RI),Tf({type:"single"});var GI=rc.extend({type:"series.themeRiver",dependencies:["singleAxis"],nameMap:null,init:function(t){GI.superApply(this,"init",arguments),this.legendVisualProvider=new Yv(A(this.getData,this),A(this.getRawData,this))},fixData:function(t){var e=t.length,i=ta(t,function(t){return t[2]}),n=[];i.buckets.each(function(t,e){n.push({name:e,dataList:t})});for(var o=n.length,a=-1,r=-1,s=0;sMath.PI/2?"right":"left"):y&&"center"!==y?"left"===y?(d=l.r0+v,f>Math.PI/2&&(y="right")):"right"===y&&(d=l.r-v,f>Math.PI/2&&(y="left")):(d=(l.r+l.r0)/2,y="center"),c.attr("style",{text:s,textAlign:y,textVerticalAlign:S("verticalAlign")||"middle",opacity:S("opacity")});var x=d*p+l.cx,_=d*g+l.cy;c.attr("position",[x,_]);var w=S("rotate"),b=0;function S(t){var e=a.get(t);return null==e?o.get(t):e}"radial"===w?(b=-f)<-Math.PI/2&&(b+=Math.PI):"tangential"===w?(b=Math.PI/2-f)>Math.PI/2?b-=Math.PI:b<-Math.PI/2&&(b+=Math.PI):"number"==typeof w&&(b=w*Math.PI/180),c.attr("rotation",b)},jI._initEvents=function(t,e,i,n){t.off("mouseover").off("mouseout").off("emphasis").off("normal");function o(){r.onEmphasis(n)}function a(){r.onNormal()}var r=this;i.isAnimationEnabled()&&t.on("mouseover",o).on("mouseout",a).on("emphasis",o).on("normal",a).on("downplay",function(){r.onDownplay()}).on("highlight",function(){r.onHighlight()})},w(YI,Ci);xc.extend({type:"sunburst",init:function(){},render:function(o,a,t,e){var n=this;this.seriesModel=o,this.api=t,this.ecModel=a;var r=o.getData(),s=r.tree.root,i=o.getViewRoot(),l=this.group,u=o.get("renderLabelForZeroData"),h=[];i.eachNode(function(t){h.push(t)});var c=this._oldChildren||[];if(function(i,n){if(0===i.length&&0===n.length)return;function t(t){return t.getId()}function e(t,e){!function(t,e){u||!t||t.getValue()||(t=null);if(t!==s&&e!==s)if(e&&e.piece)t?(e.piece.updateData(!1,t,"normal",o,a),r.setItemGraphicEl(t.dataIndex,e.piece)):function(t){if(!t)return;t.piece&&(l.remove(t.piece),t.piece=null)}(e);else if(t){var i=new YI(t,o,a);l.add(i),r.setItemGraphicEl(t.dataIndex,i)}}(null==t?null:i[t],null==e?null:n[e])}new Lf(n,i,t,t).add(e).update(e).remove(T(e,null)).execute()}(h,c),function(t,e){if(0=i.r0}}});var qI="sunburstRootToNode";xf({type:qI,update:"updateView"},function(o,t){t.eachComponent({mainType:"series",subType:"sunburst",query:o},function(t,e){var i=Wx(o,[qI],t);if(i){var n=t.getViewRoot();n&&(o.direction=Zx(n,i.node)?"rollUp":"drillDown"),t.resetViewRoot(i.node)}})});var KI="sunburstHighlight";xf({type:KI,update:"updateView"},function(n,t){t.eachComponent({mainType:"series",subType:"sunburst",query:n},function(t,e){var i=Wx(n,[KI],t);i&&(n.highlight=i.node)})});xf({type:"sunburstUnhighlight",update:"updateView"},function(i,t){t.eachComponent({mainType:"series",subType:"sunburst",query:i},function(t,e){i.unhighlight=!0})});var $I=Math.PI/180;function JI(t,e){if("function"==typeof e)return t.sort(e);var n="asc"===e;return t.sort(function(t,e){var i=(t.getValue()-e.getValue())*(n?1:-1);return 0==i?(t.dataIndex-e.dataIndex)*(n?-1:1):i})}function QI(a,r){return r=r||[0,0],N(["x","y"],function(t,e){var i=this.getAxis(t),n=r[e],o=a[e]/2;return"category"===i.type?i.getBandWidth():Math.abs(i.dataToCoord(n-o)-i.dataToCoord(n+o))},this)}bf(T(ty,"sunburst")),wf(T(function(t,e,C,i){e.eachSeriesByType(t,function(t){var e=t.get("center"),i=t.get("radius");O(i)||(i=[0,i]),O(e)||(e=[e,e]);var n=C.getWidth(),o=C.getHeight(),h=Math.min(n,o),c=Rl(e[0],n),d=Rl(e[1],o),f=Rl(i[0],h/2),a=Rl(i[1],h/2),r=-t.get("startAngle")*$I,p=t.get("minAngle")*$I,g=t.getData().tree.root,s=t.getViewRoot(),m=s.depth,l=t.get("sort");null!=l&&!function e(t,i){var n=t.children||[];t.children=JI(n,i);n.length&&E(t.children,function(t){e(t,i)})}(s,l);var u=0;E(s.children,function(t){isNaN(t.getValue())||u++});var v=s.getValue(),y=Math.PI/(v||u)*2,x=0t[1]&&t.reverse(),{coordSys:{type:"polar",cx:o.cx,cy:o.cy,r:t[1],r0:t[0]},api:{coord:A(function(t){var e=a.dataToRadius(t[0]),i=r.dataToAngle(t[1]),n=o.coordToPoint([e,i]);return n.push(e,i*Math.PI/180),n}),size:A(iT,o)}}},calendar:function(i){var t=i.getRect(),e=i.getRangeInfo();return{coordSys:{type:"calendar",x:t.x,y:t.y,width:t.width,height:t.height,cellWidth:i.getCellWidth(),cellHeight:i.getCellHeight(),rangeInfo:{start:e.start,end:e.end,weeks:e.weeks,dayCount:e.allDay}},api:{coord:function(t,e){return i.dataToPoint(t,e)}}}}};function hT(t,e,i,n,o){null==i[t]||o||(e[t]=i[t],i[t]=n[t])}function cT(a,r,e,t){var i=a.get("renderItem"),n=a.coordinateSystem,o={};n&&(o=n.prepareCustoms?n.prepareCustoms():uT[n.type](n));var s,l,u,h,c,d=D({getWidth:t.getWidth,getHeight:t.getHeight,getZr:t.getZr,getDevicePixelRatio:t.getDevicePixelRatio,value:function(t,e){return null==e&&(e=s),r.get(r.getDimension(t||0),e)},style:function(t,e){null==e&&(e=s),g(e);var i=l.getModel(oT).getItemStyle();null!=c&&(i.fill=c);var n=r.getItemVisual(e,"opacity");null!=n&&(i.opacity=n);var o=t?mT(t,u):u;return nl(i,o,null,{autoColor:c,isRectText:!0}),i.text=o.getShallow("show")?H(a.getFormattedLabel(e,"normal"),Zg(r,e)):null,t&&vT(i,t),i},styleEmphasis:function(t,e){null==e&&(e=s),g(e);var i=l.getModel(aT).getItemStyle(),n=t?mT(t,h):h;return nl(i,n,null,{isRectText:!0},!0),i.text=n.getShallow("show")?Z(a.getFormattedLabel(e,"emphasis"),a.getFormattedLabel(e,"normal"),Zg(r,e)):null,t&&vT(i,t),i},visual:function(t,e){return null==e&&(e=s),r.getItemVisual(e,t)},barLayout:function(t){if(n.getBaseAxis){return function(t){var e=[],i=t.axis;if("category"===i.type){for(var n=i.getBandWidth(),o=0;oe[1]&&e.reverse();var i=t.getExtent(),n=Math.PI/180;return{cx:this.cx,cy:this.cy,r0:e[0],r:e[1],startAngle:-i[0]*n,endAngle:-i[1]*n,clockwise:t.inverse,contain:function(t,e){var i=t-this.cx,n=e-this.cy,o=i*i+n*n,a=this.r,r=this.r0;return o<=a*a&&r*r<=o}}}};var AT=Lu.extend({type:"polarAxis",axis:null,getCoordSysModel:function(){return this.ecModel.queryComponents({mainType:"polar",index:this.option.polarIndex,id:this.option.polarId})[0]}});m(AT.prototype,cg);var DT={splitNumber:5};function CT(t,e){return e.type||(e.data?"category":"value")}function LT(t,e){var i=this,n=i.getAngleAxis(),o=i.getRadiusAxis();if(n.scale.setExtent(1/0,-1/0),o.scale.setExtent(1/0,-1/0),t.eachSeries(function(t){if(t.coordinateSystem===i){var e=t.getData();E(e.mapDimension("radius",!0),function(t){o.scale.unionExtentFromData(e,fp(e,t))}),E(e.mapDimension("angle",!0),function(t){n.scale.unionExtentFromData(e,fp(e,t))})}}),ag(n.scale,n.model),ag(o.scale,o.model),"category"===n.type&&!n.onBand){var a=n.getExtent(),r=360/n.scale.count();n.inverse?a[1]+=r:a[1]-=r,n.setExtent(a[0],a[1])}}function kT(t,e){if(t.type=e.get("type"),t.scale=rg(e),t.onBand=e.get("boundaryGap")&&"category"===t.type,t.inverse=e.get("inverse"),"angleAxis"===e.mainType){t.inverse^=e.get("clockwise");var i=e.get("startAngle");t.setExtent(i,i+(t.inverse?-360:360))}(e.axis=t).model=e}Bm("angle",AT,CT,{startAngle:90,clockwise:!0,splitNumber:12,axisLabel:{rotate:!1}}),Bm("radius",AT,CT,DT),If({type:"polar",dependencies:["polarAxis","angleAxis"],coordinateSystem:null,findAxisModel:function(t){var e;return this.ecModel.eachComponent(t,function(t){t.getCoordSysModel()===this&&(e=t)},this),e},defaultOption:{zlevel:0,z:0,center:["50%","50%"],radius:"80%"}}),sh.register("polar",{dimensions:TT.prototype.dimensions,create:function(i,s){var l=[];return i.eachComponent("polar",function(t,e){var i=new TT(e);i.update=LT;var n=i.getRadiusAxis(),o=i.getAngleAxis(),a=t.findAxisModel("radiusAxis"),r=t.findAxisModel("angleAxis");kT(n,a),kT(o,r),function(t,e,i){var n=e.get("center"),o=i.getWidth(),a=i.getHeight();t.cx=Rl(n[0],o),t.cy=Rl(n[1],a);var r=t.getRadiusAxis(),s=Math.min(o,a)/2,l=e.get("radius");null==l?l=[0,"100%"]:O(l)||(l=[0,l]),l=[Rl(l[0],s),Rl(l[1],s)],r.inverse?r.setExtent(l[1],l[0]):r.setExtent(l[0],l[1])}(i,t,s),l.push(i),(t.coordinateSystem=i).model=t}),i.eachSeries(function(t){if("polar"===t.get("coordinateSystem")){var e=i.queryComponents({mainType:"polar",index:t.get("polarIndex"),id:t.get("polarId")})[0];t.coordinateSystem=e.coordinateSystem}}),l}});var PT=["axisLine","axisLabel","axisTick","minorTick","splitLine","minorSplitLine","splitArea"];function NT(t,e,i){e[1]>e[0]&&(e=e.slice().reverse());var n=t.coordToPoint([e[0],i]),o=t.coordToPoint([e[1],i]);return{x1:n[0],y1:n[1],x2:o[0],y2:o[1]}}function OT(t){return t.getRadiusAxis().inverse?0:1}function ET(t){var e=t[0],i=t[t.length-1];e&&i&&Math.abs(Math.abs(e.coord-i.coord)-360)<1e-4&&t.pop()}pv.extend({type:"angleAxis",axisPointerClass:"PolarAxisPointer",render:function(e,t){if(this.group.removeAll(),e.get("show")){var i=e.axis,n=i.polar,o=n.getRadiusAxis().getExtent(),a=i.getTicksCoords(),r=i.getMinorTicksCoords(),s=N(i.getViewLabels(),function(t){return(t=L(t)).coord=i.dataToCoord(t.tickValue),t});ET(s),ET(a),E(PT,function(t){!e.get(t+".show")||i.scale.isBlank()&&"axisLine"!==t||this["_"+t](e,n,a,r,o,s)},this)}},_axisLine:function(t,e,i,n,o){var a,r=t.getModel("axisLine.lineStyle"),s=OT(e),l=s?0:1;(a=0===o[l]?new Yr({shape:{cx:e.cx,cy:e.cy,r:o[s]},style:r.getLineStyle(),z2:1,silent:!0}):new Kr({shape:{cx:e.cx,cy:e.cy,r:o[s],r0:o[l]},style:r.getLineStyle(),z2:1,silent:!0})).style.fill=null,this.group.add(a)},_axisTick:function(t,e,i,n,o){var a=t.getModel("axisTick"),r=(a.get("inside")?-1:1)*a.get("length"),s=o[OT(e)],l=N(i,function(t){return new ls({shape:NT(e,[s,s+r],t.coord)})});this.group.add(Es(l,{style:D(a.getModel("lineStyle").getLineStyle(),{stroke:t.get("axisLine.lineStyle.color")})}))},_minorTick:function(t,e,i,n,o){if(n.length){for(var a=t.getModel("axisTick"),r=t.getModel("minorTick"),s=(a.get("inside")?-1:1)*r.get("length"),l=o[OT(e)],u=[],h=0;hr?"left":"right",u=Math.abs(a[1]-s)/o<.3?"middle":a[1]>s?"top":"bottom";p&&p[n]&&p[n].textStyle&&(i=new Cl(p[n].textStyle,g,g.ecModel));var h=new Ur({silent:$m.isLabelSilent(c)});this.group.add(h),nl(h.style,i,{x:a[0],y:a[1],textFill:i.getTextColor()||c.get("axisLine.lineStyle.color"),text:t.formattedLabel,textAlign:l,textVerticalAlign:u}),v&&(h.eventData=$m.makeAxisEventDataBase(c),h.eventData.targetType="axisLabel",h.eventData.value=t.rawLabel)},this)},_splitLine:function(t,e,i,n,o){var a=t.getModel("splitLine").getModel("lineStyle"),r=a.get("color"),s=0;r=r instanceof Array?r:[r];for(var l=[],u=0;um?"left":"right",h=Math.abs(l[1]-v)/g<.3?"middle":l[1]>v?"top":"bottom"}return{position:l,align:u,verticalAlign:h}}(e,i,0,s,d))}});var VT={line:function(t,e,i,n,o){return"angle"===t.dim?{type:"Line",shape:AI(e.coordToPoint([n[0],i]),e.coordToPoint([n[1],i]))}:{type:"Circle",shape:{cx:e.cx,cy:e.cy,r:i}}},shadow:function(t,e,i,n,o){var a=Math.max(1,t.getBandWidth()),r=Math.PI/180;return"angle"===t.dim?{type:"Sector",shape:CI(e.cx,e.cy,n[0],n[1],(-i-a/2)*r,(a/2-i)*r)}:{type:"Sector",shape:CI(e.cx,e.cy,i-a/2,i+a/2,0,2*Math.PI)}}};function GT(n,t){t.update="updateView",xf(t,function(t,e){var i={};return e.eachComponent({mainType:"geo",query:t},function(e){e[n](t.name),E(e.coordinateSystem.regions,function(t){i[t.name]=e.isSelected(t.name)||!1})}),{selected:i,name:t.name}})}pv.registerAxisPointerClass("PolarAxisPointer",BT),wf(T(function(t,e,i){var N={},O=function(t){var g={};E(t,function(t,e){var i=t.getData(),n=t.coordinateSystem,o=n.getBaseAxis(),a=bT(n,o),r=o.getExtent(),s="category"===o.type?o.getBandWidth():Math.abs(r[1]-r[0])/i.count(),l=g[a]||{bandWidth:s,remainedWidth:s,autoWidthCount:0,categoryGap:"20%",gap:"30%",stacks:{}},u=l.stacks;g[a]=l;var h=wT(t);u[h]||l.autoWidthCount++,u[h]=u[h]||{width:0,maxWidth:0};var c=Rl(t.get("barWidth"),s),d=Rl(t.get("barMaxWidth"),s),f=t.get("barGap"),p=t.get("barCategoryGap");c&&!u[h].width&&(c=Math.min(l.remainedWidth,c),u[h].width=c,l.remainedWidth-=c),d&&(u[h].maxWidth=d),null!=f&&(l.gap=f),null!=p&&(l.categoryGap=p)});var d={};return E(g,function(t,i){d[i]={};var e=t.stacks,n=t.bandWidth,o=Rl(t.categoryGap,n),a=Rl(t.gap,1),r=t.remainedWidth,s=t.autoWidthCount,l=(r-o)/(s+(s-1)*a);l=Math.max(l,0),E(e,function(t,e){var i=t.maxWidth;i&&i=n.start.time&&i.timea.end.time&&t.reverse(),t},_getRangeInfo:function(t){var e;(t=[this.getDateInfo(t[0]),this.getDateInfo(t[1])])[0].time>t[1].time&&(e=!0,t.reverse());var i=Math.floor(t[1].time/864e5)-Math.floor(t[0].time/864e5)+1,n=new Date(t[0].time),o=n.getDate(),a=t[1].date.getDate();if(n.setDate(o+i-1),n.getDate()!==a)for(var r=0n.weeks||0===t&&en.lweek)return!1;var o=7*(t-1)-n.fweek+e,a=new Date(n.start.time);return a.setDate(n.start.d+o),this.getDateInfo(a)}},FT.dimensions=FT.prototype.dimensions,FT.getDimensionsInfo=FT.prototype.getDimensionsInfo,FT.create=function(i,n){var o=[];return i.eachComponent("calendar",function(t){var e=new FT(t,i,n);o.push(e),t.coordinateSystem=e}),i.eachSeries(function(t){"calendar"===t.get("coordinateSystem")&&(t.coordinateSystem=o[t.get("calendarIndex")||0])}),o},sh.register("calendar",FT);var HT=Lu.extend({type:"calendar",coordinateSystem:null,defaultOption:{zlevel:0,z:2,left:80,top:60,cellSize:20,orient:"horizontal",splitLine:{show:!0,lineStyle:{color:"#000",width:1,type:"solid"}},itemStyle:{color:"#fff",borderWidth:1,borderColor:"#ccc"},dayLabel:{show:!0,firstDay:0,position:"start",margin:"50%",nameMap:"en",color:"#000"},monthLabel:{show:!0,position:"start",margin:5,align:"center",nameMap:"en",formatter:null,color:"#000"},yearLabel:{show:!0,position:null,margin:30,formatter:null,color:"#ccc",fontFamily:"sans-serif",fontWeight:"bolder",fontSize:20}},init:function(t,e,i,n){var o=Mu(t);HT.superApply(this,"init",arguments),ZT(t,o)},mergeOption:function(t,e){HT.superApply(this,"mergeOption",arguments),ZT(this.option,t)}});function ZT(t,e){var i=t.cellSize;O(i)?1===i.length&&(i[1]=i[0]):i=t.cellSize=[i,i];var n=N([0,1],function(t){return function(t,e){return null!=t[yu[e][0]]||null!=t[yu[e][1]]&&null!=t[yu[e][2]]}(e,t)&&(i[t]="auto"),null!=i[t]&&"auto"!==i[t]});Su(t,e,{type:"box",ignoreSize:n})}var UT={EN:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],CN:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"]},XT={EN:["S","M","T","W","T","F","S"],CN:["日","一","二","三","四","五","六"]};Tf({type:"calendar",_tlpoints:null,_blpoints:null,_firstDayOfMonth:null,_firstDayPoints:null,render:function(t,e,i){var n=this.group;n.removeAll();var o=t.coordinateSystem,a=o.getRangeInfo(),r=o.getOrient();this._renderDayRect(t,a,n),this._renderLines(t,a,r,n),this._renderYearText(t,a,r,n),this._renderMonthText(t,r,n),this._renderWeekText(t,a,r,n)},_renderDayRect:function(t,e,i){for(var n=t.coordinateSystem,o=t.getModel("itemStyle").getItemStyle(),a=n.getCellWidth(),r=n.getCellHeight(),s=e.start.time;s<=e.end.time;s=n.getNextNDay(s,1).time){var l=n.dataToRect([s],!1).tl,u=new rs({shape:{x:l[0],y:l[1],width:a,height:r},cursor:"default",style:o});i.add(u)}},_renderLines:function(i,t,n,o){var a=this,r=i.coordinateSystem,s=i.getModel("splitLine.lineStyle").getLineStyle(),l=i.get("splitLine.show"),e=s.lineWidth;this._tlpoints=[],this._blpoints=[],this._firstDayOfMonth=[],this._firstDayPoints=[];for(var u=t.start,h=0;u.time<=t.end.time;h++){d(u.formatedDate),0===h&&(u=r.getDateInfo(t.start.y+"-"+t.start.m));var c=u.date;c.setMonth(c.getMonth()+1),u=r.getDateInfo(c)}function d(t){a._firstDayOfMonth.push(r.getDateInfo(t)),a._firstDayPoints.push(r.dataToRect([t],!1).tl);var e=a._getLinePointsOfOneWeek(i,t,n);a._tlpoints.push(e[0]),a._blpoints.push(e[e.length-1]),l&&a._drawSplitline(e,s,o)}d(r.getNextNDay(t.end.time,1).formatedDate),l&&this._drawSplitline(a._getEdgesPoints(a._tlpoints,e,n),s,o),l&&this._drawSplitline(a._getEdgesPoints(a._blpoints,e,n),s,o)},_getEdgesPoints:function(t,e,i){var n=[t[0].slice(),t[t.length-1].slice()],o="horizontal"===i?0:1;return n[0][o]=n[0][o]-e/2,n[1][o]=n[1][o]+e/2,n},_drawSplitline:function(t,e,i){var n=new ts({z2:20,shape:{points:t},style:e});i.add(n)},_getLinePointsOfOneWeek:function(t,e,i){var n=t.coordinateSystem;e=n.getDateInfo(e);for(var o=[],a=0;a<7;a++){var r=n.getNextNDay(e.time,a),s=n.dataToRect([r.time],!1);o[2*r.day]=s.tl,o[2*r.day+1]=s["horizontal"===i?"bl":"tr"]}return o},_formatterLabel:function(t,e){return"string"==typeof t&&t?uu(t,e):"function"==typeof t?t(e):e.nameMap},_yearTextPositionControl:function(t,e,i,n,o){e=e.slice();var a=["center","bottom"];"bottom"===n?(e[1]+=o,a=["center","top"]):"left"===n?e[0]-=o:"right"===n?(e[0]+=o,a=["center","top"]):e[1]-=o;var r=0;return"left"!==n&&"right"!==n||(r=Math.PI/2),{rotation:r,position:e,style:{textAlign:a[0],textVerticalAlign:a[1]}}},_renderYearText:function(t,e,i,n){var o=t.getModel("yearLabel");if(o.get("show")){var a=o.get("margin"),r=o.get("position");r=r||("horizontal"!==i?"top":"left");var s=[this._tlpoints[this._tlpoints.length-1],this._blpoints[0]],l=(s[0][0]+s[1][0])/2,u=(s[0][1]+s[1][1])/2,h="horizontal"===i?0:1,c={top:[l,s[h][1]],bottom:[l,s[1-h][1]],left:[s[1-h][0],u],right:[s[h][0],u]},d=e.start.y;+e.end.y>+e.start.y&&(d=d+"-"+e.end.y);var f=o.get("formatter"),p={start:e.start.y,end:e.end.y,nameMap:d},g=this._formatterLabel(f,p),m=new Ur({z2:30});nl(m.style,o,{text:g}),m.attr(this._yearTextPositionControl(m,c[r],i,r,a)),n.add(m)}},_monthTextPositionControl:function(t,e,i,n,o){var a="left",r="top",s=t[0],l=t[1];return"horizontal"===i?(l+=o,e&&(a="center"),"start"===n&&(r="bottom")):(s+=o,e&&(r="middle"),"start"===n&&(a="right")),{x:s,y:l,textAlign:a,textVerticalAlign:r}},_renderMonthText:function(t,e,i){var n=t.getModel("monthLabel");if(n.get("show")){var o=n.get("nameMap"),a=n.get("margin"),r=n.get("position"),s=n.get("align"),l=[this._tlpoints,this._blpoints];R(o)&&(o=UT[o.toUpperCase()]||[]);var u="start"===r?0:1,h="horizontal"===e?0:1;a="start"===r?-a:a;for(var c="center"===s,d=0;dd.getHeight()&&(i.textPosition="top",a=!0);var r=a?-5-n.height:p+8;o+n.width/2>d.getWidth()?(i.textPosition=["100%",r],i.textAlign="right"):o-n.width/2<0&&(i.textPosition=[0,r],i.textAlign="left")}})}function t(t,e){var i,n=m[t],o=m[e],a=u[n],r=new Cl(a,h,h.ecModel);if(l&&null!=l.newTitle&&(a.title=l.newTitle),n&&!o){if(function(t){return 0===t.indexOf("my")}(n))i={model:r,onclick:r.option.onclick,featureName:n};else{var s=tA(n);if(!s)return;i=new s(r,c,d)}g[n]=i}else{if(!(i=g[o]))return;i.model=r,i.ecModel=c,i.api=d}n||!o?r.get("show")&&!i.unusable?(function(o,a,t){var r=o.getModel("iconStyle"),s=o.getModel("emphasis.iconStyle"),e=a.getIcons?a.getIcons():o.get("icon"),l=o.get("title")||{};if("string"==typeof e){var i=e,n=l;l={},(e={})[t]=i,l[t]=n}var u=o.iconPaths={};E(e,function(t,e){var i=yl(t,{},{x:-p/2,y:-p/2,width:p,height:p});i.setStyle(r.getItemStyle()),i.hoverStyle=s.getItemStyle(),i.setStyle({text:l[e],textAlign:s.get("textAlign"),textBorderRadius:s.get("textBorderRadius"),textPadding:s.get("textPadding"),textFill:null});var n=h.getModel("tooltip");n&&n.get("show")&&i.attr("tooltip",k({content:l[e],formatter:n.get("formatter",!0)||function(){return l[e]},formatterParams:{componentType:"toolbox",name:e,title:l[e],$vars:["name","title"]},position:n.get("position",!0)||"bottom"},n.option)),$s(i),h.get("showTitle")&&(i.__title=l[e],i.on("mouseover",function(){var t=s.getItemStyle(),e="vertical"===h.get("orient")?null==h.get("right")?"right":"left":null==h.get("bottom")?"bottom":"top";i.setStyle({textFill:s.get("textFill")||t.fill||t.stroke||"#000",textBackgroundColor:s.get("textBackgroundColor"),textPosition:s.get("textPosition")||e})}).on("mouseout",function(){i.setStyle({textFill:null,textBackgroundColor:null})})),i.trigger(o.get("iconStatus."+e)||"normal"),f.add(i),i.on("click",A(a.onclick,a,c,d,e)),u[e]=i})}(r,i,n),r.setIconStatus=function(t,e){var i=this.option,n=this.iconPaths;i.iconStatus=i.iconStatus||{},i.iconStatus[t]=e,n[t]&&n[t].trigger(e)},i.render&&i.render(r,c,d,l)):i.remove&&i.remove(c,d):i.dispose&&i.dispose(c,d)}},updateView:function(t,e,i,n){E(this._features,function(t){t.updateView&&t.updateView(t.model,e,i,n)})},remove:function(e,i){E(this._features,function(t){t.remove&&t.remove(e,i)}),this.group.removeAll()},dispose:function(e,i){E(this._features,function(t){t.dispose&&t.dispose(e,i)})}});var nA=Nc.toolbox.saveAsImage;function oA(t){this.model=t}oA.defaultOption={show:!0,icon:"M4.7,22.9L29.3,45.5L54.7,23.4M4.6,43.6L4.6,58L53.8,58L53.8,43.6M29.2,45.1L29.2,0",title:nA.title,type:"png",connectedBackgroundColor:"#fff",name:"",excludeComponents:["toolbox"],pixelRatio:1,lang:nA.lang.slice()},oA.prototype.unusable=!v.canvasSupported,oA.prototype.onclick=function(t,e){var i=this.model,n=i.get("name")||t.get("title.0.text")||"echarts",o=i.get("type",!0)||"png",a=e.getConnectedDataURL({type:o,backgroundColor:i.get("backgroundColor",!0)||t.get("backgroundColor")||"#fff",connectedBackgroundColor:i.get("connectedBackgroundColor"),excludeComponents:i.get("excludeComponents"),pixelRatio:i.get("pixelRatio")});if("function"!=typeof MouseEvent||v.browser.ie||v.browser.edge)if(window.navigator.msSaveOrOpenBlob){for(var r=atob(a.split(",")[1]),s=r.length,l=new Uint8Array(s);s--;)l[s]=r.charCodeAt(s);var u=new Blob([l]);window.navigator.msSaveOrOpenBlob(u,n+"."+o)}else{var h=i.get("lang"),c='';window.open().document.write(c)}else{var d=document.createElement("a");d.download=n+"."+o,d.target="_blank",d.href=a;var f=new MouseEvent("click",{view:window,bubbles:!0,cancelable:!1});d.dispatchEvent(f)}},QT("saveAsImage",oA);var aA=Nc.toolbox.magicType,rA="__ec_magicType_stack__";function sA(t){this.model=t}sA.defaultOption={show:!0,type:[],icon:{line:"M4.1,28.9h7.1l9.3-22l7.4,38l9.7-19.7l3,12.8h14.9M4.1,58h51.4",bar:"M6.7,22.9h10V48h-10V22.9zM24.9,13h10v35h-10V13zM43.2,2h10v46h-10V2zM3.1,58h53.7",stack:"M8.2,38.4l-8.4,4.1l30.6,15.3L60,42.5l-8.1-4.1l-21.5,11L8.2,38.4z M51.9,30l-8.1,4.2l-13.4,6.9l-13.9-6.9L8.2,30l-8.4,4.2l8.4,4.2l22.2,11l21.5-11l8.1-4.2L51.9,30z M51.9,21.7l-8.1,4.2L35.7,30l-5.3,2.8L24.9,30l-8.4-4.1l-8.3-4.2l-8.4,4.2L8.2,30l8.3,4.2l13.9,6.9l13.4-6.9l8.1-4.2l8.1-4.1L51.9,21.7zM30.4,2.2L-0.2,17.5l8.4,4.1l8.3,4.2l8.4,4.2l5.5,2.7l5.3-2.7l8.1-4.2l8.1-4.2l8.1-4.1L30.4,2.2z"},title:L(aA.title),option:{},seriesIndex:{}};var lA=sA.prototype;lA.getIcons=function(){var t=this.model,e=t.get("icon"),i={};return E(t.get("type"),function(t){e[t]&&(i[t]=e[t])}),i};var uA={line:function(t,e,i,n){if("bar"===t)return m({id:e,type:"line",data:i.get("data"),stack:i.get("stack"),markPoint:i.get("markPoint"),markLine:i.get("markLine")},n.get("option.line")||{},!0)},bar:function(t,e,i,n){if("line"===t)return m({id:e,type:"bar",data:i.get("data"),stack:i.get("stack"),markPoint:i.get("markPoint"),markLine:i.get("markLine")},n.get("option.bar")||{},!0)},stack:function(t,e,i,n){var o=i.get("stack")===rA;if("line"===t||"bar"===t)return n.setIconStatus("stack",o?"normal":"emphasis"),m({id:e,stack:o?"":rA},n.get("option.stack")||{},!0)}},hA=[["line","bar"],["stack"]];lA.onclick=function(u,t,h){var c=this.model,e=c.get("seriesIndex."+h);if(uA[h]){var i,d={series:[]};if(E(hA,function(t){0<=_(t,h)&&E(t,function(t){c.setIconStatus(t,"normal")})}),c.setIconStatus(h,"emphasis"),u.eachComponent({mainType:"series",query:null==e?null:{seriesIndex:e}},function(t){var e=t.subType,i=t.id,n=uA[h](e,i,t,c);n&&(D(n,t.option),d.series.push(n));var o=t.coordinateSystem;if(o&&"cartesian2d"===o.type&&("line"===h||"bar"===h)){var a=o.getAxesByScale("ordinal")[0];if(a){var r=a.dim+"Axis",s=u.queryComponents({mainType:r,index:t.get(name+"Index"),id:t.get(name+"Id")})[0].componentIndex;d[r]=d[r]||[];for(var l=0;l<=s;l++)d[r][s]=d[r][s]||{};d[r][s].boundaryGap="bar"===h}}}),"stack"===h)i=d.series&&d.series[0]&&d.series[0].stack===rA?m({stack:aA.title.tiled},aA.title):L(aA.title);t.dispatchAction({type:"changeMagicType",currentType:h,newOption:d,newTitle:i})}},xf({type:"changeMagicType",event:"magicTypeChanged",update:"prepareAndUpdate"},function(t,e){e.mergeOption(t.newOption)}),QT("magicType",sA);var cA=Nc.toolbox.dataView,dA=new Array(60).join("-"),fA="\t";function pA(t){var e=function(t){var o={},a=[],r=[];return t.eachRawSeries(function(t){var e=t.coordinateSystem;if(!e||"cartesian2d"!==e.type&&"polar"!==e.type)a.push(t);else{var i=e.getBaseAxis();if("category"===i.type){var n=i.dim+"_"+i.index;o[n]||(o[n]={categoryAxis:i,valueAxis:e.getOtherAxis(i),series:[]},r.push({axisDim:i.dim,axisIndex:i.index})),o[n].series.push(t)}else a.push(t)}}),{seriesGroupByCategoryAxis:o,other:a,meta:r}}(t);return{value:M([function(t){var h=[];return E(t,function(t,e){var i=t.categoryAxis,n=t.valueAxis.dim,o=[" "].concat(N(t.series,function(t){return t.name})),a=[i.model.getCategories()];E(t.series,function(t){a.push(t.getRawData().mapArray(n,function(t){return t}))});for(var r=[o.join(fA)],s=0;st[1]&&t.reverse(),t}function AA(t,e){return Ko(t,e,{includeMainTypes:SA})}IA.setOutputRanges=function(t,e){this.matchOutputRanges(t,e,function(t,e,i){if((t.coordRanges||(t.coordRanges=[])).push(e),!t.coordRange){t.coordRange=e;var n=kA[t.brushType](0,i,e);t.__rangeOffset={offset:NA[t.brushType](n.values,t.range,[1,1]),xyMinMax:n.xyMinMax}}})},IA.matchOutputRanges=function(t,n,o){xA(t,function(i){var t=this.findTargetInfo(i,n);t&&!0!==t&&E(t.coordSyses,function(t){var e=kA[i.brushType](1,t,i.range);o(i,e.values,t,n)})},this)},IA.setInputRanges=function(t,o){xA(t,function(t){var e=this.findTargetInfo(t,o);if(t.range=t.range||[],e&&!0!==e){t.panelId=e.panelId;var i=kA[t.brushType](0,e.coordSys,t.coordRange),n=t.__rangeOffset;t.range=n?NA[t.brushType](i.values,n.offset,function(t,e){var i=EA(t),n=EA(e),o=[i[0]/n[0],i[1]/n[1]];return isNaN(o[0])&&(o[0]=1),isNaN(o[1])&&(o[1]=1),o}(i.xyMinMax,n.xyMinMax)):i.values}},this)},IA.makePanelOpts=function(i,n){return N(this._targetInfoList,function(t){var e=t.getPanelRect();return{panelId:t.panelId,defaultBrushType:n&&n(t),clipPath:Qb(e),isTargetByCursor:eS(e,i,t.coordSysModel),getLinearBrushOtherExtent:tS(e)}})},IA.controlSeries=function(t,e,i){var n=this.findTargetInfo(t,i);return!0===n||n&&0<=_A(n.coordSyses,e.coordinateSystem)},IA.findTargetInfo=function(t,e){for(var i=this._targetInfoList,n=AA(e,t),o=0;on[1]&&(n[1]=e[1])})}),n[1]c[1];if(r&&!s&&!l)return!0;r&&(n=!0),s&&(e=!0),l&&(i=!0)}return n&&e&&i}):YA(h,function(t){if("empty"===o)i.setData(u=u.map(t,function(t){return function(t){return t>=c[0]&&t<=c[1]}(t)?t:NaN}));else{var e={};e[t]=c,u.selectRange(e)}}),YA(h,function(t){u.setApproximateExtent(c,t)}))})}}};var KA=E,$A=ZA,JA=If({type:"dataZoom",dependencies:["xAxis","yAxis","zAxis","radiusAxis","angleAxis","singleAxis","series"],defaultOption:{zlevel:0,z:4,orient:null,xAxisIndex:null,yAxisIndex:null,filterMode:"filter",throttle:null,start:0,end:100,startValue:null,endValue:null,minSpan:null,maxSpan:null,minValueSpan:null,maxValueSpan:null,rangeMode:null},init:function(t,e,i){this._dataIntervalByAxis={},this._dataInfo={},this._axisProxies={},this.textStyleModel,this._autoThrottle=!0,this._rangePropMode=["percent","percent"];var n=QA(t);this.settledOption=n,this.mergeDefaultAndTheme(t,i),this.doInit(n)},mergeOption:function(t){var e=QA(t);m(this.option,t,!0),m(this.settledOption,e,!0),this.doInit(e)},doInit:function(t){var i=this.option;v.canvasSupported||(i.realtime=!1),this._setDefaultThrottle(t),tD(this,t);var n=this.settledOption;KA([["start","startValue"],["end","endValue"]],function(t,e){"value"===this._rangePropMode[e]&&(i[t[0]]=n[t[0]]=null)},this),this.textStyleModel=this.getModel("textStyle"),this._resetTarget(),this._giveAxisProxies()},_giveAxisProxies:function(){var r=this._axisProxies;this.eachTargetAxis(function(t,e,i,n){var o=this.dependentModels[t.axis][e],a=o.__dzAxisProxy||(o.__dzAxisProxy=new XA(t.name,e,this,n));r[t.name+"_"+e]=a},this)},_resetTarget:function(){var i=this.option,t=this._judgeAutoMode();$A(function(t){var e=t.axisIndex;i[e]=Vo(i[e])},this),"axisIndex"===t?this._autoSetAxisIndex():"orient"===t&&this._autoSetOrient()},_judgeAutoMode:function(){var e=this.option,i=!1;$A(function(t){null!=e[t.axisIndex]&&(i=!0)},this);var t=e.orient;return null==t&&i?"orient":i?void 0:(null==t&&(e.orient="horizontal"),"axisIndex")},_autoSetAxisIndex:function(){var a=!0,e=this.get("orient",!0),r=this.option,t=this.dependentModels;if(a){var i="vertical"===e?"y":"x";t[i+"Axis"].length?(r[i+"AxisIndex"]=[0],a=!1):KA(t.singleAxis,function(t){a&&t.get("orient",!0)===e&&(r.singleAxisIndex=[t.componentIndex],a=!1)})}a&&$A(function(t){if(a){var e=[],i=this.dependentModels[t.axis];if(i.length&&!e.length)for(var n=0,o=i.length;ne[0][1]&&(e[0][1]=a[0]),a[1]e[1][1]&&(e[1][1]=a[1])}return e&&FD(e)}};function FD(t){return new Di(t[0][0],t[1][0],t[0][1]-t[0][0],t[1][1]-t[1][0])}var WD=["#ddd"];If({type:"brush",dependencies:["geo","grid","xAxis","yAxis","parallel","series"],defaultOption:{toolbox:null,brushLink:null,seriesIndex:"all",geoIndex:null,xAxisIndex:null,yAxisIndex:null,brushType:"rect",brushMode:"single",transformable:!0,brushStyle:{borderWidth:1,color:"rgba(120,140,180,0.3)",borderColor:"rgba(120,140,180,0.8)"},throttleType:"fixRate",throttleDelay:0,removeOnClick:!0,z:1e4},areas:[],brushType:null,brushOption:{},coordInfoList:[],optionUpdated:function(t,e){var i=this.option;e||DD(i,t,["inBrush","outOfBrush"]);var n=i.inBrush=i.inBrush||{};i.outOfBrush=i.outOfBrush||{color:WD},n.hasOwnProperty("liftZ")||(n.liftZ=5)},setAreas:function(t){t&&(this.areas=N(t,function(t){return HD(this.option,t)},this))},setBrushOption:function(t){this.brushOption=HD(this.option,t),this.brushType=this.brushOption.brushType}});function HD(t,e){return m({brushType:t.brushType,brushMode:t.brushMode,transformable:t.transformable,brushStyle:new Cl(t.brushStyle).getItemStyle(),removeOnClick:t.removeOnClick,z:t.z},e,!0)}function ZD(t,e,i,n){n&&n.$from===t.id||this._brushController.setPanels(t.brushTargetManager.makePanelOpts(i)).enableBrush(t.brushOption).updateCovers(t.areas.slice())}Tf({type:"brush",init:function(t,e){this.ecModel=t,this.api=e,this.model,(this._brushController=new Sb(e.getZr())).on("brush",A(this._onBrush,this)).mount()},render:function(t){return this.model=t,ZD.apply(this,arguments)},updateTransform:function(t,e){return RD(e),ZD.apply(this,arguments)},updateView:ZD,dispose:function(){this._brushController.dispose()},_onBrush:function(t,e){var i=this.model.id;this.model.brushTargetManager.setOutputRanges(t,this.ecModel),e.isEnd&&!e.removeOnClick||this.api.dispatchAction({type:"brush",brushId:i,areas:L(t),$from:i}),e.isEnd&&this.api.dispatchAction({type:"brushEnd",brushId:i,areas:L(t),$from:i})}}),xf({type:"brush",event:"brush"},function(e,t){t.eachComponent({mainType:"brush",query:e},function(t){t.setAreas(e.areas)})}),xf({type:"brushSelect",event:"brushSelected",update:"none"},function(){}),xf({type:"brushEnd",event:"brushEnd",update:"none"},function(){});var UD=Nc.toolbox.brush;function XD(t,e,i){this.model=t,this.ecModel=e,this.api=i,this._brushType,this._brushMode}XD.defaultOption={show:!0,type:["rect","polygon","lineX","lineY","keep","clear"],icon:{rect:"M7.3,34.7 M0.4,10V-0.2h9.8 M89.6,10V-0.2h-9.8 M0.4,60v10.2h9.8 M89.6,60v10.2h-9.8 M12.3,22.4V10.5h13.1 M33.6,10.5h7.8 M49.1,10.5h7.8 M77.5,22.4V10.5h-13 M12.3,31.1v8.2 M77.7,31.1v8.2 M12.3,47.6v11.9h13.1 M33.6,59.5h7.6 M49.1,59.5 h7.7 M77.5,47.6v11.9h-13",polygon:"M55.2,34.9c1.7,0,3.1,1.4,3.1,3.1s-1.4,3.1-3.1,3.1 s-3.1-1.4-3.1-3.1S53.5,34.9,55.2,34.9z M50.4,51c1.7,0,3.1,1.4,3.1,3.1c0,1.7-1.4,3.1-3.1,3.1c-1.7,0-3.1-1.4-3.1-3.1 C47.3,52.4,48.7,51,50.4,51z M55.6,37.1l1.5-7.8 M60.1,13.5l1.6-8.7l-7.8,4 M59,19l-1,5.3 M24,16.1l6.4,4.9l6.4-3.3 M48.5,11.6 l-5.9,3.1 M19.1,12.8L9.7,5.1l1.1,7.7 M13.4,29.8l1,7.3l6.6,1.6 M11.6,18.4l1,6.1 M32.8,41.9 M26.6,40.4 M27.3,40.2l6.1,1.6 M49.9,52.1l-5.6-7.6l-4.9-1.2",lineX:"M15.2,30 M19.7,15.6V1.9H29 M34.8,1.9H40.4 M55.3,15.6V1.9H45.9 M19.7,44.4V58.1H29 M34.8,58.1H40.4 M55.3,44.4 V58.1H45.9 M12.5,20.3l-9.4,9.6l9.6,9.8 M3.1,29.9h16.5 M62.5,20.3l9.4,9.6L62.3,39.7 M71.9,29.9H55.4",lineY:"M38.8,7.7 M52.7,12h13.2v9 M65.9,26.6V32 M52.7,46.3h13.2v-9 M24.9,12H11.8v9 M11.8,26.6V32 M24.9,46.3H11.8v-9 M48.2,5.1l-9.3-9l-9.4,9.2 M38.9-3.9V12 M48.2,53.3l-9.3,9l-9.4-9.2 M38.9,62.3V46.4",keep:"M4,10.5V1h10.3 M20.7,1h6.1 M33,1h6.1 M55.4,10.5V1H45.2 M4,17.3v6.6 M55.6,17.3v6.6 M4,30.5V40h10.3 M20.7,40 h6.1 M33,40h6.1 M55.4,30.5V40H45.2 M21,18.9h62.9v48.6H21V18.9z",clear:"M22,14.7l30.9,31 M52.9,14.7L22,45.7 M4.7,16.8V4.2h13.1 M26,4.2h7.8 M41.6,4.2h7.8 M70.3,16.8V4.2H57.2 M4.7,25.9v8.6 M70.3,25.9v8.6 M4.7,43.2v12.6h13.1 M26,55.8h7.8 M41.6,55.8h7.8 M70.3,43.2v12.6H57.2"},title:L(UD.title)};var YD=XD.prototype;YD.render=YD.updateView=function(e,t,i){var n,o,a;t.eachComponent({mainType:"brush"},function(t){n=t.brushType,o=t.brushOption.brushMode||"single",a|=t.areas.length}),this._brushType=n,this._brushMode=o,E(e.get("type",!0),function(t){e.setIconStatus(t,("keep"===t?"multiple"===o:"clear"===t?a:t===n)?"emphasis":"normal")})},YD.getIcons=function(){var t=this.model,e=t.get("icon",!0),i={};return E(t.get("type",!0),function(t){e[t]&&(i[t]=e[t])}),i},YD.onclick=function(t,e,i){var n=this._brushType,o=this._brushMode;"clear"===i?(e.dispatchAction({type:"axisAreaSelect",intervals:[]}),e.dispatchAction({type:"brush",command:"clear",areas:[]})):e.dispatchAction({type:"takeGlobalCursor",key:"brush",brushOption:{brushType:"keep"===i?n:n!==i&&i,brushMode:"keep"===i?"multiple"===o?"single":"multiple":o}})},QT("brush",XD),vf(function(t,e){var i=t&&t.brush;if(O(i)||(i=i?[i]:[]),i.length){var n=[];E(i,function(t){var e=t.hasOwnProperty("toolbox")?t.toolbox:[];e instanceof Array&&(n=n.concat(e))});var o=t&&t.toolbox;O(o)&&(o=o[0]),o||(o={feature:{}},t.toolbox=[o]);var a=o.feature||(o.feature={}),r=a.brush||(a.brush={}),s=r.type||(r.type=[]);s.push.apply(s,n),function(i){var e={};E(i,function(t){e[t]=1}),i.length=0,E(e,function(t,e){i.push(e)})}(s),e&&!s.length&&s.push.apply(s,MD)}}),If({type:"title",layoutMode:{type:"box",ignoreSize:!0},defaultOption:{zlevel:0,z:6,show:!0,text:"",target:"blank",subtext:"",subtarget:"blank",left:0,top:0,backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderWidth:0,padding:5,itemGap:10,textStyle:{fontSize:18,fontWeight:"bolder",color:"#333"},subtextStyle:{color:"#aaa"}}}),Tf({type:"title",render:function(t,e,i){if(this.group.removeAll(),t.get("show")){var n=this.group,o=t.getModel("textStyle"),a=t.getModel("subtextStyle"),r=t.get("textAlign"),s=H(t.get("textBaseline"),t.get("textVerticalAlign")),l=new Ur({style:nl({},o,{text:t.get("text"),textFill:o.getTextColor()},{disableBox:!0}),z2:10}),u=l.getBoundingRect(),h=t.get("subtext"),c=new Ur({style:nl({},a,{text:h,textFill:a.getTextColor(),y:u.height+t.get("itemGap"),textVerticalAlign:"top"},{disableBox:!0}),z2:10}),d=t.get("link"),f=t.get("sublink"),p=t.get("triggerEvent",!0);l.silent=!d&&!p,c.silent=!f&&!p,d&&l.on("click",function(){window.open(d,"_"+t.get("target"))}),f&&c.on("click",function(){window.open(f,"_"+t.get("subtarget"))}),l.eventData=c.eventData=p?{componentType:"title",componentIndex:t.componentIndex}:null,n.add(l),h&&n.add(c);var g=n.getBoundingRect(),m=t.getBoxLayoutParams();m.width=g.width,m.height=g.height;var v=wu(m,{width:i.getWidth(),height:i.getHeight()},t.get("padding"));r||("middle"===(r=t.get("left")||t.get("right"))&&(r="center"),"right"===r?v.x+=v.width:"center"===r&&(v.x+=v.width/2)),s||("center"===(s=t.get("top")||t.get("bottom"))&&(s="middle"),"bottom"===s?v.y+=v.height:"middle"===s&&(v.y+=v.height/2),s=s||"top"),n.attr("position",[v.x,v.y]);var y={textAlign:r,textVerticalAlign:s};l.setStyle(y),c.setStyle(y),g=n.getBoundingRect();var x=v.margin,_=t.getItemStyle(["color","opacity"]);_.fill=t.get("backgroundColor");var w=new rs({shape:{x:g.x-x[3],y:g.y-x[0],width:g.width+x[1]+x[3],height:g.height+x[0]+x[2],r:t.get("borderRadius")},style:_,subPixelOptimize:!0,silent:!0});n.add(w)}}});function jD(t){var e=t.itemStyle||(t.itemStyle={}),i=e.emphasis||(e.emphasis={}),n=t.label||t.label||{},o=n.normal||(n.normal={}),a={normal:1,emphasis:1};E(n,function(t,e){a[e]||qD(o,e)||(o[e]=t)}),i.label&&!qD(n,"emphasis")&&(n.emphasis=i.label,delete i.label)}function qD(t,e){return t.hasOwnProperty(e)}Lu.registerSubTypeDefaulter("timeline",function(){return"slider"}),xf({type:"timelineChange",event:"timelineChanged",update:"prepareAndUpdate"},function(t,e){var i=e.getComponent("timeline");return i&&null!=t.currentIndex&&(i.setCurrentIndex(t.currentIndex),!i.get("loop",!0)&&i.isIndexMax()&&i.setPlayState(!1)),e.resetOption("timeline"),D({currentIndex:i.option.currentIndex},t)}),xf({type:"timelinePlayChange",event:"timelinePlayChanged",update:"update"},function(t,e){var i=e.getComponent("timeline");i&&null!=t.playState&&i.setPlayState(t.playState)});var KD=Lu.extend({type:"timeline",layoutMode:"box",defaultOption:{zlevel:0,z:4,show:!0,axisType:"time",realtime:!0,left:"20%",top:null,right:"20%",bottom:0,width:null,height:40,padding:5,controlPosition:"left",autoPlay:!1,rewind:!1,loop:!0,playInterval:2e3,currentIndex:0,itemStyle:{},label:{color:"#000"},data:[]},init:function(t,e,i){this._data,this._names,this.mergeDefaultAndTheme(t,i),this._initData()},mergeOption:function(t){KD.superApply(this,"mergeOption",arguments),this._initData()},setCurrentIndex:function(t){null==t&&(t=this.option.currentIndex);var e=this._data.count();this.option.loop?t=(t%e+e)%e:(e<=t&&(t=e-1),t<0&&(t=0)),this.option.currentIndex=t},getCurrentIndex:function(){return this.option.currentIndex},isIndexMax:function(){return this.getCurrentIndex()>=this._data.count()-1},setPlayState:function(t){this.option.autoPlay=!!t},getPlayState:function(){return!!this.option.autoPlay},_initData:function(){var t=this.option,e=t.data||[],i=t.axisType,o=this._names=[];if("category"===i){var a=[];E(e,function(t,e){var i,n=Wo(t);z(t)?(i=L(t)).value=e:i=e,a.push(i),R(n)||null!=n&&!isNaN(n)||(n=""),o.push(n+"")}),e=a}var n={category:"ordinal",time:"time"}[i]||"number";(this._data=new Xf([{name:"value",type:n}],this)).initData(e,o)},getData:function(){return this._data},getCategories:function(){if("category"===this.get("axisType"))return this._names.slice()}});b(KD.extend({type:"timeline.slider",defaultOption:{backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderWidth:0,orient:"horizontal",inverse:!1,tooltip:{trigger:"item"},symbol:"emptyCircle",symbolSize:10,lineStyle:{show:!0,width:2,color:"#304654"},label:{position:"auto",show:!0,interval:"auto",rotate:0,color:"#304654"},itemStyle:{color:"#304654",borderWidth:1},checkpointStyle:{symbol:"circle",symbolSize:13,color:"#c23531",borderWidth:5,borderColor:"rgba(194,53,49, 0.5)",animation:!0,animationDuration:300,animationEasing:"quinticInOut"},controlStyle:{show:!0,showPlayBtn:!0,showPrevBtn:!0,showNextBtn:!0,itemSize:22,itemGap:12,position:"left",playIcon:"path://M31.6,53C17.5,53,6,41.5,6,27.4S17.5,1.8,31.6,1.8C45.7,1.8,57.2,13.3,57.2,27.4S45.7,53,31.6,53z M31.6,3.3 C18.4,3.3,7.5,14.1,7.5,27.4c0,13.3,10.8,24.1,24.1,24.1C44.9,51.5,55.7,40.7,55.7,27.4C55.7,14.1,44.9,3.3,31.6,3.3z M24.9,21.3 c0-2.2,1.6-3.1,3.5-2l10.5,6.1c1.899,1.1,1.899,2.9,0,4l-10.5,6.1c-1.9,1.1-3.5,0.2-3.5-2V21.3z",stopIcon:"path://M30.9,53.2C16.8,53.2,5.3,41.7,5.3,27.6S16.8,2,30.9,2C45,2,56.4,13.5,56.4,27.6S45,53.2,30.9,53.2z M30.9,3.5C17.6,3.5,6.8,14.4,6.8,27.6c0,13.3,10.8,24.1,24.101,24.1C44.2,51.7,55,40.9,55,27.6C54.9,14.4,44.1,3.5,30.9,3.5z M36.9,35.8c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H36c0.5,0,0.9,0.4,0.9,1V35.8z M27.8,35.8 c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H27c0.5,0,0.9,0.4,0.9,1L27.8,35.8L27.8,35.8z",nextIcon:"path://M18.6,50.8l22.5-22.5c0.2-0.2,0.3-0.4,0.3-0.7c0-0.3-0.1-0.5-0.3-0.7L18.7,4.4c-0.1-0.1-0.2-0.3-0.2-0.5 c0-0.4,0.3-0.8,0.8-0.8c0.2,0,0.5,0.1,0.6,0.3l23.5,23.5l0,0c0.2,0.2,0.3,0.4,0.3,0.7c0,0.3-0.1,0.5-0.3,0.7l-0.1,0.1L19.7,52 c-0.1,0.1-0.3,0.2-0.5,0.2c-0.4,0-0.8-0.3-0.8-0.8C18.4,51.2,18.5,51,18.6,50.8z",prevIcon:"path://M43,52.8L20.4,30.3c-0.2-0.2-0.3-0.4-0.3-0.7c0-0.3,0.1-0.5,0.3-0.7L42.9,6.4c0.1-0.1,0.2-0.3,0.2-0.5 c0-0.4-0.3-0.8-0.8-0.8c-0.2,0-0.5,0.1-0.6,0.3L18.3,28.8l0,0c-0.2,0.2-0.3,0.4-0.3,0.7c0,0.3,0.1,0.5,0.3,0.7l0.1,0.1L41.9,54 c0.1,0.1,0.3,0.2,0.5,0.2c0.4,0,0.8-0.3,0.8-0.8C43.2,53.2,43.1,53,43,52.8z",color:"#304654",borderColor:"#304654",borderWidth:1},emphasis:{label:{show:!0,color:"#c23531"},itemStyle:{color:"#c23531"},controlStyle:{color:"#c23531",borderColor:"#c23531",borderWidth:2}},data:[]}}),Uh);function $D(t,e,i,n){Vg.call(this,t,e,i),this.type=n||"value",this.model=null}var JD=pc.extend({type:"timeline"});$D.prototype={constructor:$D,getLabelModel:function(){return this.model.getModel("label")},isHorizontal:function(){return"horizontal"===this.model.get("orient")}},w($D,Vg);var QD=A,tC=E,eC=Math.PI;function iC(t,e,i,n,o,a){var r=e.get("color");o?(o.setColor(r),i.add(o),a&&a.onUpdate(o)):((o=_g(t.get("symbol"),-1,-1,2,2,r)).setStyle("strokeNoScale",!0),i.add(o),a&&a.onCreate(o));var s=e.getItemStyle(["color","symbol","symbolSize"]);o.setStyle(s),n=m({rectHover:!0,z2:100},n,!0);var l=t.get("symbolSize");(l=l instanceof Array?l.slice():[+l,+l])[0]/=2,l[1]/=2,n.scale=l;var u=t.get("symbolOffset");if(u){var h=n.position=n.position||[0,0];h[0]+=Rl(u[0],l[0]),h[1]+=Rl(u[1],l[1])}var c=t.get("symbolRotate");return n.rotation=(c||0)*Math.PI/180||0,o.attr(n),o.updateTransform(),o}function nC(t,e,i,n,o){if(!t.dragging){var a=n.getModel("checkpointStyle"),r=i.dataToCoord(n.getData().get(["value"],e));o||!a.get("animation",!0)?t.attr({position:[r,0]}):(t.stopAnimation(!0),t.animateTo({position:[r,0]},a.get("animationDuration",!0),a.get("animationEasing",!0)))}}JD.extend({type:"timeline.slider",init:function(t,e){this.api=e,this._axis,this._viewRect,this._timer,this._currentPointer,this._mainGroup,this._labelGroup},render:function(e,t,i,n){if(this.model=e,this.api=i,this.ecModel=t,this.group.removeAll(),e.get("show",!0)){var o=this._layout(e,i),a=this._createGroup("mainGroup"),r=this._createGroup("labelGroup"),s=this._axis=this._createAxis(o,e);e.formatTooltip=function(t){return au(s.scale.getLabel(t))},tC(["AxisLine","AxisTick","Control","CurrentPointer"],function(t){this["_render"+t](o,a,s,e)},this),this._renderAxisLabel(o,r,s,e),this._position(o,e)}this._doPlayStop()},remove:function(){this._clearTimer(),this.group.removeAll()},dispose:function(){this._clearTimer()},_layout:function(t,e){var i=t.get("label.position"),n=t.get("orient"),o=function(t,e){return wu(t.getBoxLayoutParams(),{width:e.getWidth(),height:e.getHeight()},t.get("padding"))}(t,e);null==i||"auto"===i?i="horizontal"===n?o.y+o.height/2n[1]&&(i=n[1]),i"),o&&(a+=aC(o),null!=i&&(a+=" : ")),null!=i&&(a+=aC(n)),a},getData:function(){return this._data},setData:function(t){this._data=t}});b(sC,Uh),sC.extend({type:"markPoint",defaultOption:{zlevel:0,z:5,symbol:"pin",symbolSize:50,tooltip:{trigger:"item"},label:{show:!0,position:"inside"},itemStyle:{borderWidth:2},emphasis:{label:{show:!0}}}});var lC=_;function uC(t,e,i,n,o,a){var r=[],s=dp(e,n)?e.getCalculationInfo("stackResultDimension"):n,l=mC(e,s,t),u=e.indicesOfNearest(s,l)[0];r[o]=e.get(i,u),r[a]=e.get(s,u);var h=e.get(n,u),c=Vl(e.get(n,u));return 0<=(c=Math.min(c,20))&&(r[a]=+r[a].toFixed(c)),[r,h]}var hC=T,cC={min:hC(uC,"min"),max:hC(uC,"max"),average:hC(uC,"average")};function dC(t,e){var i=t.getData(),n=t.coordinateSystem;if(e&&!function(t){return!isNaN(parseFloat(t.x))&&!isNaN(parseFloat(t.y))}(e)&&!O(e.coord)&&n){var o=n.dimensions,a=fC(e,i,n,t);if((e=L(e)).type&&cC[e.type]&&a.baseAxis&&a.valueAxis){var r=lC(o,a.baseAxis.dim),s=lC(o,a.valueAxis.dim),l=cC[e.type](i,a.baseDataDim,a.valueDataDim,r,s);e.coord=l[0],e.value=l[1]}else{for(var u=[null!=e.xAxis?e.xAxis:e.radiusAxis,null!=e.yAxis?e.yAxis:e.angleAxis],h=0;h<2;h++)cC[u[h]]&&(u[h]=mC(i,i.mapDimension(o[h]),u[h]));e.coord=u}}return e}function fC(t,e,i,n){var o={};return null!=t.valueIndex||null!=t.valueDim?(o.valueDataDim=null!=t.valueIndex?e.getDimension(t.valueIndex):t.valueDim,o.valueAxis=i.getAxis(function(t,e){var i=t.getData(),n=i.dimensions;e=i.getDimension(e);for(var o=0;oi[o],f=[-h.x,-h.y];e||(f[n]=s.position[n]);var p=[0,0],g=[-c.x,-c.y],m=H(t.get("pageButtonGap",!0),t.get("itemGap",!0));d&&("end"===t.get("pageButtonPosition",!0)?g[n]+=i[o]-c[o]:p[n]+=c[o]+m);g[1-n]+=h[a]/2-c[a]/2,s.attr("position",f),l.attr("position",p),u.attr("position",g);var v={x:0,y:0};if(v[o]=d?i[o]:h[o],v[a]=Math.max(h[a],c[a]),v[r]=Math.min(0,c[r]+g[1-n]),l.__rectSize=i[o],d){var y={x:0,y:0};y[o]=Math.max(i[o]-c[o]-m,0),y[a]=v[a],l.setClipPath(new rs({shape:y})),l.__rectSize=y[o]}else u.eachChild(function(t){t.attr({invisible:!0,silent:!0})});var x=this._getPageInfo(t);return null!=x.pageIndex&&cl(s,{position:x.contentPosition},d&&t),this._updatePageInfoView(t,x),v},_pageGo:function(t,e,i){var n=this._getPageInfo(e)[t];null!=n&&i.dispatchAction({type:"legendScroll",scrollDataIndex:n,legendId:e.id})},_updatePageInfoView:function(n,o){var a=this._controllerGroup;E(["pagePrev","pageNext"],function(t){var e=null!=o[t+"DataIndex"],i=a.childOfName(t);i&&(i.setStyle("fill",e?n.get("pageIconColor",!0):n.get("pageIconInactiveColor",!0)),i.cursor=e?"pointer":"default")});var t=a.childOfName("pageText"),e=n.get("pageFormatter"),i=o.pageIndex,r=null!=i?i+1:0,s=o.pageCount;t&&e&&t.setStyle("text",R(e)?e.replace("{current}",r).replace("{total}",s):e({current:r,total:s}))},_getPageInfo:function(t){var e=t.get("scrollDataIndex",!0),i=this.getContentGroup(),n=this._containerGroup.__rectSize,o=t.getOrient().index,a=UC[o],r=XC[o],s=this._findTargetItemIndex(e),l=i.children(),u=l[s],h=l.length,c=h?1:0,d={contentPosition:i.position.slice(),pageCount:c,pageIndex:c-1,pagePrevDataIndex:null,pageNextDataIndex:null};if(!u)return d;var f=y(u);d.contentPosition[o]=-f.s;for(var p=s+1,g=f,m=f,v=null;p<=h;++p)(!(v=y(l[p]))&&m.e>g.s+n||v&&!x(v,g.s))&&(g=m.i>g.i?m:v)&&(null==d.pageNextDataIndex&&(d.pageNextDataIndex=g.i),++d.pageCount),m=v;for(p=s-1,g=f,m=f,v=null;-1<=p;--p)(v=y(l[p]))&&x(m,v.s)||!(g.i=e&&t.s<=e+n}},_findTargetItemIndex:function(n){return this._showController?(this.getContentGroup().eachChild(function(t,e){var i=t.__legendDataIndex;null==a&&null!=i&&(a=e),i===n&&(o=e)}),null!=o?o:a):0;var o,a}});xf("legendScroll","legendscroll",function(t,e){var i=t.scrollDataIndex;null!=i&&e.eachComponent({mainType:"legend",subType:"scroll",query:t},function(t){t.setScrollDataIndex(i)})});JA.extend({type:"dataZoom.slider",layoutMode:"box",defaultOption:{show:!0,right:"ph",top:"ph",width:"ph",height:"ph",left:null,bottom:null,backgroundColor:"rgba(47,69,84,0)",dataBackground:{lineStyle:{color:"#2f4554",width:.5,opacity:.3},areaStyle:{color:"rgba(47,69,84,0.3)",opacity:.3}},borderColor:"#ddd",fillerColor:"rgba(167,183,204,0.4)",handleIcon:"M8.2,13.6V3.9H6.3v9.7H3.1v14.9h3.3v9.7h1.8v-9.7h3.3V13.6H8.2z M9.7,24.4H4.8v-1.4h4.9V24.4z M9.7,19.1H4.8v-1.4h4.9V19.1z",handleSize:"100%",handleStyle:{color:"#a7b7cc"},labelPrecision:null,labelFormatter:null,showDetail:!0,showDataShadow:"auto",realtime:!0,zoomLock:!1,textStyle:{color:"#333"}}});var jC=rs,qC=El,KC=Bl,$C=A,JC=E,QC="horizontal",tL="vertical",eL=["line","bar","candlestick","scatter"],iL=eD.extend({type:"dataZoom.slider",init:function(t,e){this._displayables={},this._orient,this._range,this._handleEnds,this._size,this._handleWidth,this._handleHeight,this._location,this._dragging,this._dataShadowInfo,this.api=e},render:function(t,e,i,n){iL.superApply(this,"render",arguments),Lc(this,"_dispatchZoomAction",this.dataZoomModel.get("throttle"),"fixRate"),this._orient=t.get("orient"),!1!==this.dataZoomModel.get("show")?(n&&"dataZoom"===n.type&&n.from===this.uid||this._buildView(),this._updateView()):this.group.removeAll()},remove:function(){iL.superApply(this,"remove",arguments),kc(this,"_dispatchZoomAction")},dispose:function(){iL.superApply(this,"dispose",arguments),kc(this,"_dispatchZoomAction")},_buildView:function(){var t=this.group;t.removeAll(),this._resetLocation(),this._resetInterval();var e=this._displayables.barGroup=new Ci;this._renderBackground(),this._renderHandle(),this._renderDataShadow(),t.add(e),this._positionGroup()},_resetLocation:function(){var t=this.dataZoomModel,e=this.api,i=this._findCoordRect(),n={width:e.getWidth(),height:e.getHeight()},o=this._orient===QC?{right:n.width-i.x-i.width,top:n.height-30-7,width:i.width,height:30}:{right:7,top:i.y,width:30,height:i.height},a=Mu(t.option);E(["right","top","width","height"],function(t){"ph"===a[t]&&(a[t]=o[t])});var r=wu(a,n,t.padding);this._location={x:r.x,y:r.y},this._size=[r.width,r.height],this._orient===tL&&this._size.reverse()},_positionGroup:function(){var t=this.group,e=this._location,i=this._orient,n=this.dataZoomModel.getFirstTargetAxisModel(),o=n&&n.get("inverse"),a=this._displayables.barGroup,r=(this._dataShadowInfo||{}).otherAxisInverse;a.attr(i!==QC||o?i===QC&&o?{scale:r?[-1,1]:[-1,-1]}:i!==tL||o?{scale:r?[-1,-1]:[-1,1],rotation:Math.PI/2}:{scale:r?[1,-1]:[1,1],rotation:Math.PI/2}:{scale:r?[1,1]:[1,-1]});var s=t.getBoundingRect([a]);t.attr("position",[e.x-s.x,e.y-s.y])},_getViewExtent:function(){return[0,this._size[0]]},_renderBackground:function(){var t=this.dataZoomModel,e=this._size,i=this._displayables.barGroup;i.add(new jC({silent:!0,shape:{x:0,y:0,width:e[0],height:e[1]},style:{fill:t.get("backgroundColor")},z2:-40})),i.add(new jC({shape:{x:0,y:0,width:e[0],height:e[1]},style:{fill:"transparent"},z2:0,onclick:A(this._onClickPanelClick,this)}))},_renderDataShadow:function(){var t=this._dataShadowInfo=this._prepareDataShadowInfo();if(t){var e=this._size,i=t.series,n=i.getRawData(),o=i.getShadowDim?i.getShadowDim():t.otherDim;if(null!=o){var a=n.getDataExtent(o),r=.3*(a[1]-a[0]);a=[a[0]-r,a[1]+r];var s,l=[0,e[1]],u=[0,e[0]],h=[[e[0],0],[0,0]],c=[],d=u[1]/(n.count()-1),f=0,p=Math.round(n.count()/e[0]);n.each([o],function(t,e){if(0e[0]||i[1]<0||i[1]>e[1])){var n=this._handleEnds,o=(n[0]+n[1])/2,a=this._updateInterval("all",i[0]-o);this._updateView(),a&&this._dispatchZoomAction()}},_dispatchZoomAction:function(){var t=this._range;this.api.dispatchAction({type:"dataZoom",from:this.uid,dataZoomId:this.dataZoomModel.id,start:t[0],end:t[1]})},_findCoordRect:function(){var i;if(JC(this.getTargetCoordInfo(),function(t){if(!i&&t.length){var e=t[0].model.coordinateSystem;i=e.getRect&&e.getRect()}}),!i){var t=this.api.getWidth(),e=this.api.getHeight();i={x:.2*t,y:.2*e,width:.6*t,height:.6*e}}return i}});function nL(t){return"vertical"===t?"ns-resize":"ew-resize"}JA.extend({type:"dataZoom.inside",defaultOption:{disabled:!1,zoomLock:!1,zoomOnMouseWheel:!0,moveOnMouseMove:!0,moveOnMouseWheel:!1,preventDefaultMouseMove:!0}});var oL="\0_ec_dataZoom_roams";function aL(t,n){var e=sL(t),o=n.dataZoomId,a=n.coordId;E(e,function(t,e){var i=t.dataZoomInfos;i[o]&&_(n.allCoordIds,a)<0&&(delete i[o],t.count--)}),lL(e);var i=e[a];i||((i=e[a]={coordId:a,dataZoomInfos:{},count:0}).controller=function(t,r){var e=new zy(t.getZr());return E(["pan","zoom","scrollMove"],function(a){e.on(a,function(n){var o=[];E(r.dataZoomInfos,function(t){if(n.isAvailableBehavior(t.dataZoomModel.option)){var e=(t.getRange||{})[a],i=e&&e(r.controller,n);!t.dataZoomModel.get("disabled",!0)&&i&&o.push({dataZoomId:t.dataZoomId,start:i[0],end:i[1]})}}),o.length&&r.dispatchAction(o)})}),e}(t,i),i.dispatchAction=T(uL,t)),i.dataZoomInfos[o]||i.count++,i.dataZoomInfos[o]=n;var r=function(t){var n,o={type_true:2,type_move:1,type_false:0,type_undefined:-1},a=!0;return E(t,function(t){var e=t.dataZoomModel,i=!e.get("disabled",!0)&&(!e.get("zoomLock",!0)||"move");o["type_"+n]"],O(t)&&(t=t.slice(),n=!0),o=e?t:n?[u(t[0]),u(t[1])]:u(t),R(l)?l.replace("{value}",n?o[0]:o).replace("{value2}",n?o[1]:o):C(l)?n?l(t[0],t[1]):l(t):n?t[0]===s[0]?i[0]+" "+o[1]:t[1]===s[1]?i[1]+" "+o[0]:o[0]+" - "+o[1]:o;function u(t){return t===s[0]?"min":t===s[1]?"max":(+t).toFixed(Math.min(r,20))}},resetExtent:function(){var t=this.option,e=TL([t.min,t.max]);this._dataExtent=e},getDataDimension:function(t){var e=this.option.dimension,i=t.dimensions;if(null!=e||i.length){if(null!=e)return t.getDimension(e);for(var n=t.dimensions,o=n.length-1;0<=o;o--){var a=n[o];if(!t.getDimensionInfo(a).isCalculationCoord)return a}}},getExtent:function(){return this._dataExtent.slice()},completeVisualOption:function(){var t=this.ecModel,e=this.option,i={inRange:e.inRange,outOfRange:e.outOfRange},n=e.target||(e.target={}),o=e.controller||(e.controller={});m(n,i),m(o,i);var u=this.isCategory();function a(n){ML(e.color)&&!n.inRange&&(n.inRange={color:e.color.slice().reverse()}),n.inRange=n.inRange||{color:t.get("gradientColor")},IL(this.stateList,function(t){var e=n[t];if(R(e)){var i=_L(e,"active",u);i?(n[t]={},n[t][e]=i):delete n[t]}},this)}a.call(this,n),a.call(this,o),function(t,e,i){var n=t[e],o=t[i];n&&!o&&(o=t[i]={},IL(n,function(t,e){if(f_.isValidType(e)){var i=_L(e,"inactive",u);null!=i&&(o[e]=i,"color"!==e||o.hasOwnProperty("opacity")||o.hasOwnProperty("colorAlpha")||(o.opacity=[0,0]))}}))}.call(this,n,"inRange","outOfRange"),function(a){var r=(a.inRange||{}).symbol||(a.outOfRange||{}).symbol,s=(a.inRange||{}).symbolSize||(a.outOfRange||{}).symbolSize,l=this.get("inactiveColor");IL(this.stateList,function(t){var e=this.itemSize,i=a[t];null==(i=i||(a[t]={color:u?l:[l]})).symbol&&(i.symbol=r&&L(r)||(u?"roundRect":["roundRect"])),null==i.symbolSize&&(i.symbolSize=s&&L(s)||(u?e[0]:[e[0],e[0]])),i.symbol=bL(i.symbol,function(t){return"none"===t||"square"===t?"roundRect":t});var n=i.symbolSize;if(null!=n){var o=-1/0;SL(n,function(t){oe[1]&&e.reverse(),e[0]=Math.max(e[0],t[0]),e[1]=Math.min(e[1],t[1]))},completeVisualOption:function(){DL.prototype.completeVisualOption.apply(this,arguments),E(this.stateList,function(t){var e=this.option.controller[t].symbolSize;e&&e[0]!==e[1]&&(e[0]=0)},this)},setSelected:function(t){this.option.range=t.slice(),this._resetRange()},getSelected:function(){var t=this.getExtent(),e=Bl((this.get("range")||[]).slice());return e[0]>t[1]&&(e[0]=t[1]),e[1]>t[1]&&(e[1]=t[1]),e[0]=i[1]||t<=e[1])?"inRange":"outOfRange"},findTargetDataIndices:function(n){var o=[];return this.eachTargetSeries(function(t){var i=[],e=t.getData();e.each(this.getDataDimension(e),function(t,e){n[0]<=t&&t<=n[1]&&i.push(e)},this),o.push({seriesId:t.id,dataIndex:i})},this),o},getVisualMeta:function(i){var t=kL(this,"outOfRange",this.getExtent()),e=kL(this,"inRange",this.option.range.slice()),n=[];function o(t,e){n.push({value:t,color:i(t,e)})}for(var a=0,r=0,s=e.length,l=t.length;rt[1])break;i.push({color:this.getControllerVisual(a,"color",e),offset:o/100})}return i.push({color:this.getControllerVisual(t[1],"color",e),offset:1}),i},_createBarPoints:function(t,e){var i=this.visualMapModel.itemSize;return[[i[0]-e[0],t[0]],[i[0],t[0]],[i[0],t[1]],[i[0]-e[1],t[1]]]},_createBarGroup:function(t){var e=this._orient,i=this.visualMapModel.get("inverse");return new Ci("horizontal"!==e||i?"horizontal"===e&&i?{scale:"bottom"===t?[-1,1]:[1,1],rotation:-Math.PI/2}:"vertical"!==e||i?{scale:"left"===t?[1,1]:[-1,1]}:{scale:"left"===t?[1,-1]:[-1,-1]}:{scale:"bottom"===t?[1,1]:[-1,1],rotation:Math.PI/2})},_updateHandle:function(n,o){if(this._useHandle){var a=this._shapes,r=this.visualMapModel,s=a.handleThumbs,l=a.handleLabels;RL([0,1],function(t){var e=s[t];e.setStyle("fill",o.handlesColor[t]),e.position[1]=n[t];var i=pl(a.handleLabelPoints[t],fl(e,this.group));l[t].setStyle({x:i[0],y:i[1],text:r.formatValueText(this._dataInterval[t]),textVerticalAlign:"middle",textAlign:this._applyTransform("horizontal"===this._orient?0===t?"bottom":"top":"left",a.barGroup)})},this)}},_showIndicator:function(t,e,i,n){var o=this.visualMapModel,a=o.getExtent(),r=o.itemSize,s=[0,r[1]],l=EL(t,a,s,!0),u=this._shapes,h=u.indicator;if(h){h.position[1]=l,h.attr("invisible",!1),h.setShape("points",function(t,e,i,n){return t?[[0,-zL(e,BL(i,0))],[6,0],[0,zL(e,BL(n-i,0))]]:[[0,0],[5,-5],[5,5]]}(!!i,n,l,r[1]));var c=this.getControllerVisual(t,"color",{convertOpacityToAlpha:!0});h.setStyle("fill",c);var d=pl(u.indicatorLabelPoint,fl(h,this.group)),f=u.indicatorLabel;f.attr("invisible",!1);var p=this._applyTransform("left",u.barGroup),g=this._orient;f.setStyle({text:(i||"")+o.formatValueText(e),textVerticalAlign:"horizontal"===g?p:"middle",textAlign:"horizontal"===g?"center":p,x:d[0],y:d[1]})}},_enableHoverLinkToSeries:function(){var n=this;this._shapes.barGroup.on("mousemove",function(t){if(n._hovering=!0,!n._dragging){var e=n.visualMapModel.itemSize,i=n._applyTransform([t.offsetX,t.offsetY],n._shapes.barGroup,!0,!0);i[1]=zL(BL(0,i[1]),e[1]),n._doHoverLinkToSeries(i[1],0<=i[0]&&i[0]<=e[0])}}).on("mouseout",function(){n._hovering=!1,n._dragging||n._clearHoverLinkToSeries()})},_enableHoverLinkFromSeries:function(){var t=this.api.getZr();this.visualMapModel.option.hoverLink?(t.on("mouseover",this._hoverLinkFromSeriesMouseOver,this),t.on("mouseout",this._hideIndicator,this)):this._clearHoverLinkFromSeries()},_doHoverLinkToSeries:function(t,e){var i=this.visualMapModel,n=i.itemSize;if(i.option.hoverLink){var o=[0,n[1]],a=i.getExtent();t=zL(BL(o[0],t),o[1]);var r=function(t,e,i){var n=6,o=t.get("hoverLinkDataSize");o&&(n=EL(o,e,i,!0)/2);return n}(i,a,o),s=[t-r,t+r],l=EL(t,o,a,!0),u=[EL(s[0],o,a,!0),EL(s[1],o,a,!0)];s[0] ",r):this._showIndicator(l,l,"≈ ",r));var h=this._hoverLinkDataIndices,c=[];(e||FL(i))&&(c=this._hoverLinkDataIndices=i.findTargetDataIndices(u));var d=function(t,e){var i={},n={};return o(t||[],i),o(e||[],n,i),[a(i),a(n)];function o(t,e,i){for(var n=0,o=t.length;ni&&n([i,e[0]],"outOfRange"),n(e.slice()),i=e[1])},this),{stops:a,outerColors:r}}function n(t,e){var i=s.getRepresentValue({interval:t});e=e||s.getValueState(i);var n=o(i,e);t[0]===-1/0?r[0]=n:t[1]===1/0?r[1]=n:a.push({value:t[0],color:n},{value:t[1],color:n})}}}),ZL={splitNumber:function(){var t=this.option,e=this._pieceList,i=Math.min(t.precision,20),n=this.getExtent(),o=t.splitNumber;o=Math.max(parseInt(o,10),1),t.splitNumber=o;for(var a=(n[1]-n[0])/o;+a.toFixed(i)!==a&&i<5;)i++;t.precision=i,a=+a.toFixed(i);var r=0;t.minOpen&&e.push({index:r++,interval:[-1/0,n[0]],close:[0,0]});for(var s=n[0],l=r+o;r","≥"][e[0]]];t.text=t.text||this.formatValueText(null!=t.value?t.value:t.interval,!1,i)},this)}};function UL(t,e){var i=t.inverse;("vertical"===t.orient?!i:i)&&e.reverse()}PL.extend({type:"visualMap.piecewise",doRender:function(){var a=this.group;a.removeAll();var r=this.visualMapModel,s=r.get("textGap"),t=r.textStyleModel,l=t.getFont(),u=t.getTextColor(),h=this._getItemAlign(),c=r.itemSize,e=this._getViewData(),i=e.endsText,d=W(r.get("showLabel",!0),!i);i&&this._renderEndsText(a,i[0],c,d,h),E(e.viewPieceList,function(t){var e=t.piece,i=new Ci;i.onclick=A(this._onItemClick,this,e),this._enableHoverLink(i,t.indexInModelPieceList);var n=r.getRepresentValue(e);if(this._createItemSymbol(i,n,[0,0,c[0],c[1]]),d){var o=this.visualMapModel.getValueState(n);i.add(new Ur({style:{x:"right"===h?-s:c[0]+s,y:c[1]/2,text:e.text,textVerticalAlign:"middle",textAlign:h,textFont:l,textFill:u,opacity:"outOfRange"===o?.5:1}}))}a.add(i)},this),i&&this._renderEndsText(a,i[1],c,d,h),_u(r.get("orient"),a,r.get("itemGap")),this.renderBackground(a),this.positionGroup(a)},_enableHoverLink:function(t,i){function e(t){var e=this.visualMapModel;e.option.hoverLink&&this.api.dispatchAction({type:t,batch:OL(e.findTargetDataIndices(i),e)})}t.on("mouseover",A(e,this,"highlight")).on("mouseout",A(e,this,"downplay"))},_getItemAlign:function(){var t=this.visualMapModel,e=t.option;if("vertical"===e.orient)return NL(t,this.api,t.itemSize);var i=e.align;return i&&"auto"!==i||(i="left"),i},_renderEndsText:function(t,e,i,n,o){if(e){var a=new Ci,r=this.visualMapModel.textStyleModel;a.add(new Ur({style:{x:n?"right"===o?i[0]:0:i[0]/2,y:i[1]/2,textVerticalAlign:"middle",textAlign:n?o:"center",text:e,textFont:r.getFont(),textFill:r.getTextColor()}})),t.add(a)}},_getViewData:function(){var t=this.visualMapModel,e=N(t.getPieceList(),function(t,e){return{piece:t,indexInModelPieceList:e}}),i=t.get("text"),n=t.get("orient"),o=t.get("inverse");return("horizontal"===n?o:!o)?e.reverse():i=i&&i.slice().reverse(),{viewPieceList:e,endsText:i}},_createItemSymbol:function(t,e,i){t.add(_g(this.getControllerVisual(e,"symbol"),i[0],i[1],i[2],i[3],this.getControllerVisual(e,"color")))},_onItemClick:function(t){var e=this.visualMapModel,i=e.option,n=L(i.selected),o=e.getSelectedMapKey(t);"single"===i.selectedMode?(n[o]=!0,E(n,function(t,e){n[e]=e===o})):n[o]=!n[o],this.api.dispatchAction({type:"selectDataRange",from:this.uid,visualMapId:this.visualMapModel.id,selected:n})}});vf(pL);var XL,YL="urn:schemas-microsoft-com:vml",jL="undefined"==typeof window?null:window,qL=!1,KL=jL&&jL.document;function $L(t){return XL(t)}if(KL&&!v.canvasSupported)try{KL.namespaces.zrvml||KL.namespaces.add("zrvml",YL),XL=function(t){return KL.createElement("')}}catch(t){XL=function(t){return KL.createElement("<"+t+' xmlns="'+YL+'" class="zrvml">')}}var JL,QL=rr.CMD,tk=Math.round,ek=Math.sqrt,ik=Math.abs,nk=Math.cos,ok=Math.sin,ak=Math.max;if(!v.canvasSupported){var rk=",",sk="progid:DXImageTransform.Microsoft",lk=21600,uk=lk/2,hk=function(t){t.style.cssText="position:absolute;left:0;top:0;width:1px;height:1px;",t.coordsize=lk+","+lk,t.coordorigin="0,0"},ck=function(t,e,i){return"rgb("+[t,e,i].join(",")+")"},dk=function(t,e){e&&t&&e.parentNode!==t&&t.appendChild(e)},fk=function(t,e){e&&t&&e.parentNode===t&&t.removeChild(e)},pk=function(t,e,i){return 1e5*(parseFloat(t)||0)+1e3*(parseFloat(e)||0)+i},gk=Yn,mk=function(t,e,i){var n=Fe(e);i=+i,isNaN(i)&&(i=1),n&&(t.color=ck(n[0],n[1],n[2]),t.opacity=i*n[3])},vk=function(t,e,i,n){var o="fill"===e,a=t.getElementsByTagName(e)[0];null!=i[e]&&"none"!==i[e]&&(o||!o&&i.lineWidth)?(t[o?"filled":"stroked"]="true",i[e]instanceof cs&&fk(t,a),a=a||$L(e),o?function(t,e,i){var n,o,a=e.fill;if(null!=a)if(a instanceof cs){var r,s=0,l=[0,0],u=0,h=1,c=i.getBoundingRect(),d=c.width,f=c.height;if("linear"===a.type){r="gradient";var p=i.transform,g=[a.x*d,a.y*f],m=[a.x2*d,a.y2*f];p&&(bt(g,g,p),bt(m,m,p));var v=m[0]-g[0],y=m[1]-g[1];(s=180*Math.atan2(v,y)/Math.PI)<0&&(s+=360),s<1e-6&&(s=0)}else{r="gradientradial";g=[a.x*d,a.y*f],p=i.transform;var x=i.scale,_=d,w=f;l=[(g[0]-c.x)/_,(g[1]-c.y)/w],p&&bt(g,g,p),_/=x[0]*lk,w/=x[1]*lk;var b=ak(_,w);u=0/b,h=2*a.r/b-u}var S=a.colorStops.slice();S.sort(function(t,e){return t.offset-e.offset});for(var M=S.length,I=[],T=[],A=0;A=c&&d<=i+1){for(var n=[],o=0;o=c&&d<=o+1)return rP(h,e.components,u,l);p[t]=e}else p[t]=void 0}var s;f++}for(;f<=e;){var r=a();if(r)return r}},pushComponent:function(t,e,i){var n=t[t.length-1];n&&n.added===e&&n.removed===i?t[t.length-1]={count:n.count+1,added:e,removed:i}:t.push({count:1,added:e,removed:i})},extractCommon:function(t,e,i,n){for(var o=e.length,a=i.length,r=t.newPos,s=r-n,l=0;r+1el día", + eventLimitText: "más", + noEventsMessage: "No hay eventos para mostrar" + }; + + var _m19 = { + code: "es", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Ant", + next: "Sig", + today: "Hoy", + month: "Mes", + week: "Semana", + day: "Día", + list: "Agenda" + }, + weekLabel: "Sm", + allDayHtml: "Todo
el día", + eventLimitText: "más", + noEventsMessage: "No hay eventos para mostrar" + }; + + var _m20 = { + code: "et", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Eelnev", + next: "Järgnev", + today: "Täna", + month: "Kuu", + week: "Nädal", + day: "Päev", + list: "Päevakord" + }, + weekLabel: "näd", + allDayText: "Kogu päev", + eventLimitText: function (n) { + return "+ veel " + n; + }, + noEventsMessage: "Kuvamiseks puuduvad sündmused" + }; + + var _m21 = { + code: "eu", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Aur", + next: "Hur", + today: "Gaur", + month: "Hilabetea", + week: "Astea", + day: "Eguna", + list: "Agenda" + }, + weekLabel: "As", + allDayHtml: "Egun
osoa", + eventLimitText: "gehiago", + noEventsMessage: "Ez dago ekitaldirik erakusteko" + }; + + var _m22 = { + code: "fa", + week: { + dow: 6, + doy: 12 // The week that contains Jan 1st is the first week of the year. + }, + dir: 'rtl', + buttonText: { + prev: "قبلی", + next: "بعدی", + today: "امروز", + month: "ماه", + week: "هفته", + day: "روز", + list: "برنامه" + }, + weekLabel: "هف", + allDayText: "تمام روز", + eventLimitText: function (n) { + return "بیش از " + n; + }, + noEventsMessage: "هیچ رویدادی به نمایش" + }; + + var _m23 = { + code: "fi", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Edellinen", + next: "Seuraava", + today: "Tänään", + month: "Kuukausi", + week: "Viikko", + day: "Päivä", + list: "Tapahtumat" + }, + weekLabel: "Vk", + allDayText: "Koko päivä", + eventLimitText: "lisää", + noEventsMessage: "Ei näytettäviä tapahtumia" + }; + + var _m24 = { + code: "fr", + buttonText: { + prev: "Précédent", + next: "Suivant", + today: "Aujourd'hui", + year: "Année", + month: "Mois", + week: "Semaine", + day: "Jour", + list: "Mon planning" + }, + weekLabel: "Sem.", + allDayHtml: "Toute la
journée", + eventLimitText: "en plus", + noEventsMessage: "Aucun événement à afficher" + }; + + var _m25 = { + code: "fr-ch", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Précédent", + next: "Suivant", + today: "Courant", + year: "Année", + month: "Mois", + week: "Semaine", + day: "Jour", + list: "Mon planning" + }, + weekLabel: "Sm", + allDayHtml: "Toute la
journée", + eventLimitText: "en plus", + noEventsMessage: "Aucun événement à afficher" + }; + + var _m26 = { + code: "fr", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Précédent", + next: "Suivant", + today: "Aujourd'hui", + year: "Année", + month: "Mois", + week: "Semaine", + day: "Jour", + list: "Mon planning" + }, + weekLabel: "Sem.", + allDayHtml: "Toute la
journée", + eventLimitText: "en plus", + noEventsMessage: "Aucun événement à afficher" + }; + + var _m27 = { + code: "gl", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Ant", + next: "Seg", + today: "Hoxe", + month: "Mes", + week: "Semana", + day: "Día", + list: "Axenda" + }, + weekLabel: "Sm", + allDayHtml: "Todo
o día", + eventLimitText: "máis", + noEventsMessage: "Non hai eventos para amosar" + }; + + var _m28 = { + code: "he", + dir: 'rtl', + buttonText: { + prev: "הקודם", + next: "הבא", + today: "היום", + month: "חודש", + week: "שבוע", + day: "יום", + list: "סדר יום" + }, + allDayText: "כל היום", + eventLimitText: "אחר", + noEventsMessage: "אין אירועים להצגה", + weekLabel: "שבוע" + }; + + var _m29 = { + code: "hi", + week: { + dow: 0, + doy: 6 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "पिछला", + next: "अगला", + today: "आज", + month: "महीना", + week: "सप्ताह", + day: "दिन", + list: "कार्यसूची" + }, + weekLabel: "हफ्ता", + allDayText: "सभी दिन", + eventLimitText: function (n) { + return "+अधिक " + n; + }, + noEventsMessage: "कोई घटनाओं को प्रदर्शित करने के लिए" + }; + + var _m30 = { + code: "hr", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Prijašnji", + next: "Sljedeći", + today: "Danas", + month: "Mjesec", + week: "Tjedan", + day: "Dan", + list: "Raspored" + }, + weekLabel: "Tje", + allDayText: "Cijeli dan", + eventLimitText: function (n) { + return "+ još " + n; + }, + noEventsMessage: "Nema događaja za prikaz" + }; + + var _m31 = { + code: "hu", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "vissza", + next: "előre", + today: "ma", + month: "Hónap", + week: "Hét", + day: "Nap", + list: "Napló" + }, + weekLabel: "Hét", + allDayText: "Egész nap", + eventLimitText: "további", + noEventsMessage: "Nincs megjeleníthető esemény" + }; + + var _m32 = { + code: "id", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "mundur", + next: "maju", + today: "hari ini", + month: "Bulan", + week: "Minggu", + day: "Hari", + list: "Agenda" + }, + weekLabel: "Mg", + allDayHtml: "Sehari
penuh", + eventLimitText: "lebih", + noEventsMessage: "Tidak ada acara untuk ditampilkan" + }; + + var _m33 = { + code: "is", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Fyrri", + next: "Næsti", + today: "Í dag", + month: "Mánuður", + week: "Vika", + day: "Dagur", + list: "Dagskrá" + }, + weekLabel: "Vika", + allDayHtml: "Allan
daginn", + eventLimitText: "meira", + noEventsMessage: "Engir viðburðir til að sýna" + }; + + var _m34 = { + code: "it", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Prec", + next: "Succ", + today: "Oggi", + month: "Mese", + week: "Settimana", + day: "Giorno", + list: "Agenda" + }, + weekLabel: "Sm", + allDayHtml: "Tutto il
giorno", + eventLimitText: function (n) { + return "+altri " + n; + }, + noEventsMessage: "Non ci sono eventi da visualizzare" + }; + + var _m35 = { + code: "ja", + buttonText: { + prev: "前", + next: "次", + today: "今日", + month: "月", + week: "週", + day: "日", + list: "予定リスト" + }, + weekLabel: "週", + allDayText: "終日", + eventLimitText: function (n) { + return "他 " + n + " 件"; + }, + noEventsMessage: "表示する予定はありません" + }; + + var _m36 = { + code: "ka", + week: { + dow: 1, + doy: 7 + }, + buttonText: { + prev: "წინა", + next: "შემდეგი", + today: "დღეს", + month: "თვე", + week: "კვირა", + day: "დღე", + list: "დღის წესრიგი" + }, + weekLabel: "კვ", + allDayText: "მთელი დღე", + eventLimitText: function (n) { + return "+ კიდევ " + n; + }, + noEventsMessage: "ღონისძიებები არ არის" + }; + + var _m37 = { + code: "kk", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Алдыңғы", + next: "Келесі", + today: "Бүгін", + month: "Ай", + week: "Апта", + day: "Күн", + list: "Күн тәртібі" + }, + weekLabel: "Не", + allDayText: "Күні бойы", + eventLimitText: function (n) { + return "+ тағы " + n; + }, + noEventsMessage: "Көрсету үшін оқиғалар жоқ" + }; + + var _m38 = { + code: "ko", + buttonText: { + prev: "이전달", + next: "다음달", + today: "오늘", + month: "월", + week: "주", + day: "일", + list: "일정목록" + }, + weekLabel: "주", + allDayText: "종일", + eventLimitText: "개", + noEventsMessage: "일정이 없습니다" + }; + + var _m39 = { + code: "lb", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Zréck", + next: "Weider", + today: "Haut", + month: "Mount", + week: "Woch", + day: "Dag", + list: "Terminiwwersiicht" + }, + weekLabel: "W", + allDayText: "Ganzen Dag", + eventLimitText: "méi", + noEventsMessage: "Nee Evenementer ze affichéieren" + }; + + var _m40 = { + code: "lt", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Atgal", + next: "Pirmyn", + today: "Šiandien", + month: "Mėnuo", + week: "Savaitė", + day: "Diena", + list: "Darbotvarkė" + }, + weekLabel: "SAV", + allDayText: "Visą dieną", + eventLimitText: "daugiau", + noEventsMessage: "Nėra įvykių rodyti" + }; + + var _m41 = { + code: "lv", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Iepr.", + next: "Nāk.", + today: "Šodien", + month: "Mēnesis", + week: "Nedēļa", + day: "Diena", + list: "Dienas kārtība" + }, + weekLabel: "Ned.", + allDayText: "Visu dienu", + eventLimitText: function (n) { + return "+vēl " + n; + }, + noEventsMessage: "Nav notikumu" + }; + + var _m42 = { + code: "mk", + buttonText: { + prev: "претходно", + next: "следно", + today: "Денес", + month: "Месец", + week: "Недела", + day: "Ден", + list: "График" + }, + weekLabel: "Сед", + allDayText: "Цел ден", + eventLimitText: function (n) { + return "+повеќе " + n; + }, + noEventsMessage: "Нема настани за прикажување" + }; + + var _m43 = { + code: "ms", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Sebelum", + next: "Selepas", + today: "hari ini", + month: "Bulan", + week: "Minggu", + day: "Hari", + list: "Agenda" + }, + weekLabel: "Mg", + allDayText: "Sepanjang hari", + eventLimitText: function (n) { + return "masih ada " + n + " acara"; + }, + noEventsMessage: "Tiada peristiwa untuk dipaparkan" + }; + + var _m44 = { + code: "nb", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Forrige", + next: "Neste", + today: "I dag", + month: "Måned", + week: "Uke", + day: "Dag", + list: "Agenda" + }, + weekLabel: "Uke", + allDayText: "Hele dagen", + eventLimitText: "til", + noEventsMessage: "Ingen hendelser å vise" + }; + + var _m45 = { + code: "nl", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Voorgaand", + next: "Volgende", + today: "Vandaag", + year: "Jaar", + month: "Maand", + week: "Week", + day: "Dag", + list: "Agenda" + }, + allDayText: "Hele dag", + eventLimitText: "extra", + noEventsMessage: "Geen evenementen om te laten zien" + }; + + var _m46 = { + code: "nn", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Førre", + next: "Neste", + today: "I dag", + month: "Månad", + week: "Veke", + day: "Dag", + list: "Agenda" + }, + weekLabel: "Veke", + allDayText: "Heile dagen", + eventLimitText: "til", + noEventsMessage: "Ingen hendelser å vise" + }; + + var _m47 = { + code: "pl", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Poprzedni", + next: "Następny", + today: "Dziś", + month: "Miesiąc", + week: "Tydzień", + day: "Dzień", + list: "Plan dnia" + }, + weekLabel: "Tydz", + allDayText: "Cały dzień", + eventLimitText: "więcej", + noEventsMessage: "Brak wydarzeń do wyświetlenia" + }; + + var _m48 = { + code: "pt-br", + buttonText: { + prev: "Anterior", + next: "Próximo", + today: "Hoje", + month: "Mês", + week: "Semana", + day: "Dia", + list: "Compromissos" + }, + weekLabel: "Sm", + allDayText: "dia inteiro", + eventLimitText: function (n) { + return "mais +" + n; + }, + noEventsMessage: "Não há eventos para mostrar" + }; + + var _m49 = { + code: "pt", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Anterior", + next: "Seguinte", + today: "Hoje", + month: "Mês", + week: "Semana", + day: "Dia", + list: "Agenda" + }, + weekLabel: "Sem", + allDayText: "Todo o dia", + eventLimitText: "mais", + noEventsMessage: "Não há eventos para mostrar" + }; + + var _m50 = { + code: "ro", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "precedentă", + next: "următoare", + today: "Azi", + month: "Lună", + week: "Săptămână", + day: "Zi", + list: "Agendă" + }, + weekLabel: "Săpt", + allDayText: "Toată ziua", + eventLimitText: function (n) { + return "+alte " + n; + }, + noEventsMessage: "Nu există evenimente de afișat" + }; + + var _m51 = { + code: "ru", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Пред", + next: "След", + today: "Сегодня", + month: "Месяц", + week: "Неделя", + day: "День", + list: "Повестка дня" + }, + weekLabel: "Нед", + allDayText: "Весь день", + eventLimitText: function (n) { + return "+ ещё " + n; + }, + noEventsMessage: "Нет событий для отображения" + }; + + var _m52 = { + code: "sk", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Predchádzajúci", + next: "Nasledujúci", + today: "Dnes", + month: "Mesiac", + week: "Týždeň", + day: "Deň", + list: "Rozvrh" + }, + weekLabel: "Ty", + allDayText: "Celý deň", + eventLimitText: function (n) { + return "+ďalšie: " + n; + }, + noEventsMessage: "Žiadne akcie na zobrazenie" + }; + + var _m53 = { + code: "sl", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Prejšnji", + next: "Naslednji", + today: "Trenutni", + month: "Mesec", + week: "Teden", + day: "Dan", + list: "Dnevni red" + }, + weekLabel: "Teden", + allDayText: "Ves dan", + eventLimitText: "več", + noEventsMessage: "Ni dogodkov za prikaz" + }; + + var _m54 = { + code: "sq", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "mbrapa", + next: "Përpara", + today: "sot", + month: "Muaj", + week: "Javë", + day: "Ditë", + list: "Listë" + }, + weekLabel: "Ja", + allDayHtml: "Gjithë
ditën", + eventLimitText: function (n) { + return "+më tepër " + n; + }, + noEventsMessage: "Nuk ka evente për të shfaqur" + }; + + var _m55 = { + code: "sr-cyrl", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Претходна", + next: "следећи", + today: "Данас", + month: "Месец", + week: "Недеља", + day: "Дан", + list: "Планер" + }, + weekLabel: "Сед", + allDayText: "Цео дан", + eventLimitText: function (n) { + return "+ још " + n; + }, + noEventsMessage: "Нема догађаја за приказ" + }; + + var _m56 = { + code: "sr", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Prethodna", + next: "Sledeći", + today: "Danas", + month: "Mеsеc", + week: "Nеdеlja", + day: "Dan", + list: "Planеr" + }, + weekLabel: "Sed", + allDayText: "Cеo dan", + eventLimitText: function (n) { + return "+ još " + n; + }, + noEventsMessage: "Nеma događaja za prikaz" + }; + + var _m57 = { + code: "sv", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Förra", + next: "Nästa", + today: "Idag", + month: "Månad", + week: "Vecka", + day: "Dag", + list: "Program" + }, + weekLabel: "v.", + allDayText: "Heldag", + eventLimitText: "till", + noEventsMessage: "Inga händelser att visa" + }; + + var _m58 = { + code: "th", + buttonText: { + prev: "ย้อน", + next: "ถัดไป", + today: "วันนี้", + month: "เดือน", + week: "สัปดาห์", + day: "วัน", + list: "แผนงาน" + }, + allDayText: "ตลอดวัน", + eventLimitText: "เพิ่มเติม", + noEventsMessage: "ไม่มีกิจกรรมที่จะแสดง" + }; + + var _m59 = { + code: "tr", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "geri", + next: "ileri", + today: "bugün", + month: "Ay", + week: "Hafta", + day: "Gün", + list: "Ajanda" + }, + weekLabel: "Hf", + allDayText: "Tüm gün", + eventLimitText: "daha fazla", + noEventsMessage: "Gösterilecek etkinlik yok" + }; + + var _m60 = { + code: "uk", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Попередній", + next: "далі", + today: "Сьогодні", + month: "Місяць", + week: "Тиждень", + day: "День", + list: "Порядок денний" + }, + weekLabel: "Тиж", + allDayText: "Увесь день", + eventLimitText: function (n) { + return "+ще " + n + "..."; + }, + noEventsMessage: "Немає подій для відображення" + }; + + var _m61 = { + code: "vi", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Trước", + next: "Tiếp", + today: "Hôm nay", + month: "Tháng", + week: "Tuần", + day: "Ngày", + list: "Lịch biểu" + }, + weekLabel: "Tu", + allDayText: "Cả ngày", + eventLimitText: function (n) { + return "+ thêm " + n; + }, + noEventsMessage: "Không có sự kiện để hiển thị" + }; + + var _m62 = { + code: "zh-cn", + week: { + // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效 + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "上月", + next: "下月", + today: "今天", + month: "月", + week: "周", + day: "日", + list: "日程" + }, + weekLabel: "周", + allDayText: "全天", + eventLimitText: function (n) { + return "另外 " + n + " 个"; + }, + noEventsMessage: "没有事件显示" + }; + + var _m63 = { + code: "zh-tw", + buttonText: { + prev: "上月", + next: "下月", + today: "今天", + month: "月", + week: "週", + day: "天", + list: "活動列表" + }, + weekLabel: "周", + allDayText: "整天", + eventLimitText: '顯示更多', + noEventsMessage: "没有任何活動" + }; + + var _rollupPluginMultiEntry_entryPoint = [ + _m0, _m1, _m2, _m3, _m4, _m5, _m6, _m7, _m8, _m9, _m10, _m11, _m12, _m13, _m14, _m15, _m16, _m17, _m18, _m19, _m20, _m21, _m22, _m23, _m24, _m25, _m26, _m27, _m28, _m29, _m30, _m31, _m32, _m33, _m34, _m35, _m36, _m37, _m38, _m39, _m40, _m41, _m42, _m43, _m44, _m45, _m46, _m47, _m48, _m49, _m50, _m51, _m52, _m53, _m54, _m55, _m56, _m57, _m58, _m59, _m60, _m61, _m62, _m63 + ]; + + return _rollupPluginMultiEntry_entryPoint; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales-all.min.js b/static/assets/libs/fullcalendar/core/locales-all.min.js new file mode 100644 index 0000000..810d6de --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales-all.min.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).FullCalendarLocalesAll=t()}(this,function(){"use strict";return[{code:"af",week:{dow:1,doy:4},buttonText:{prev:"Vorige",next:"Volgende",today:"Vandag",year:"Jaar",month:"Maand",week:"Week",day:"Dag",list:"Agenda"},allDayHtml:"Heeldag",eventLimitText:"Addisionele",noEventsMessage:"Daar is geen gebeurtenisse nie"},{code:"ar-dz",week:{dow:0,doy:4},dir:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekLabel:"أسبوع",allDayText:"اليوم كله",eventLimitText:"أخرى",noEventsMessage:"أي أحداث لعرض"},{code:"ar-kw",week:{dow:0,doy:12},dir:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekLabel:"أسبوع",allDayText:"اليوم كله",eventLimitText:"أخرى",noEventsMessage:"أي أحداث لعرض"},{code:"ar-ly",week:{dow:6,doy:12},dir:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekLabel:"أسبوع",allDayText:"اليوم كله",eventLimitText:"أخرى",noEventsMessage:"أي أحداث لعرض"},{code:"ar-ma",week:{dow:6,doy:12},dir:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekLabel:"أسبوع",allDayText:"اليوم كله",eventLimitText:"أخرى",noEventsMessage:"أي أحداث لعرض"},{code:"ar-sa",week:{dow:0,doy:6},dir:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekLabel:"أسبوع",allDayText:"اليوم كله",eventLimitText:"أخرى",noEventsMessage:"أي أحداث لعرض"},{code:"ar-tn",week:{dow:1,doy:4},dir:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekLabel:"أسبوع",allDayText:"اليوم كله",eventLimitText:"أخرى",noEventsMessage:"أي أحداث لعرض"},{code:"ar",week:{dow:6,doy:12},dir:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekLabel:"أسبوع",allDayText:"اليوم كله",eventLimitText:"أخرى",noEventsMessage:"أي أحداث لعرض"},{code:"bg",week:{dow:1,doy:7},buttonText:{prev:"назад",next:"напред",today:"днес",month:"Месец",week:"Седмица",day:"Ден",list:"График"},allDayText:"Цял ден",eventLimitText:function(e){return"+още "+e},noEventsMessage:"Няма събития за показване"},{code:"bs",week:{dow:1,doy:7},buttonText:{prev:"Prošli",next:"Sljedeći",today:"Danas",month:"Mjesec",week:"Sedmica",day:"Dan",list:"Raspored"},weekLabel:"Sed",allDayText:"Cijeli dan",eventLimitText:function(e){return"+ još "+e},noEventsMessage:"Nema događaja za prikazivanje"},{code:"ca",week:{dow:1,doy:4},buttonText:{prev:"Anterior",next:"Següent",today:"Avui",month:"Mes",week:"Setmana",day:"Dia",list:"Agenda"},weekLabel:"Set",allDayText:"Tot el dia",eventLimitText:"més",noEventsMessage:"No hi ha esdeveniments per mostrar"},{code:"cs",week:{dow:1,doy:4},buttonText:{prev:"Dříve",next:"Později",today:"Nyní",month:"Měsíc",week:"Týden",day:"Den",list:"Agenda"},weekLabel:"Týd",allDayText:"Celý den",eventLimitText:function(e){return"+další: "+e},noEventsMessage:"Žádné akce k zobrazení"},{code:"da",week:{dow:1,doy:4},buttonText:{prev:"Forrige",next:"Næste",today:"I dag",month:"Måned",week:"Uge",day:"Dag",list:"Agenda"},weekLabel:"Uge",allDayText:"Hele dagen",eventLimitText:"flere",noEventsMessage:"Ingen arrangementer at vise"},{code:"de",week:{dow:1,doy:4},buttonText:{prev:"Zurück",next:"Vor",today:"Heute",year:"Jahr",month:"Monat",week:"Woche",day:"Tag",list:"Terminübersicht"},weekLabel:"KW",allDayText:"Ganztägig",eventLimitText:function(e){return"+ weitere "+e},noEventsMessage:"Keine Ereignisse anzuzeigen"},{code:"el",week:{dow:1,doy:4},buttonText:{prev:"Προηγούμενος",next:"Επόμενος",today:"Σήμερα",month:"Μήνας",week:"Εβδομάδα",day:"Ημέρα",list:"Ατζέντα"},weekLabel:"Εβδ",allDayText:"Ολοήμερο",eventLimitText:"περισσότερα",noEventsMessage:"Δεν υπάρχουν γεγονότα για να εμφανιστεί"},{code:"en-au",week:{dow:1,doy:4}},{code:"en-gb",week:{dow:1,doy:4}},{code:"en-nz",week:{dow:1,doy:4}},{code:"es",week:{dow:0,doy:6},buttonText:{prev:"Ant",next:"Sig",today:"Hoy",month:"Mes",week:"Semana",day:"Día",list:"Agenda"},weekLabel:"Sm",allDayHtml:"Todo
el día",eventLimitText:"más",noEventsMessage:"No hay eventos para mostrar"},{code:"es",week:{dow:1,doy:4},buttonText:{prev:"Ant",next:"Sig",today:"Hoy",month:"Mes",week:"Semana",day:"Día",list:"Agenda"},weekLabel:"Sm",allDayHtml:"Todo
el día",eventLimitText:"más",noEventsMessage:"No hay eventos para mostrar"},{code:"et",week:{dow:1,doy:4},buttonText:{prev:"Eelnev",next:"Järgnev",today:"Täna",month:"Kuu",week:"Nädal",day:"Päev",list:"Päevakord"},weekLabel:"näd",allDayText:"Kogu päev",eventLimitText:function(e){return"+ veel "+e},noEventsMessage:"Kuvamiseks puuduvad sündmused"},{code:"eu",week:{dow:1,doy:7},buttonText:{prev:"Aur",next:"Hur",today:"Gaur",month:"Hilabetea",week:"Astea",day:"Eguna",list:"Agenda"},weekLabel:"As",allDayHtml:"Egun
osoa",eventLimitText:"gehiago",noEventsMessage:"Ez dago ekitaldirik erakusteko"},{code:"fa",week:{dow:6,doy:12},dir:"rtl",buttonText:{prev:"قبلی",next:"بعدی",today:"امروز",month:"ماه",week:"هفته",day:"روز",list:"برنامه"},weekLabel:"هف",allDayText:"تمام روز",eventLimitText:function(e){return"بیش از "+e},noEventsMessage:"هیچ رویدادی به نمایش"},{code:"fi",week:{dow:1,doy:4},buttonText:{prev:"Edellinen",next:"Seuraava",today:"Tänään",month:"Kuukausi",week:"Viikko",day:"Päivä",list:"Tapahtumat"},weekLabel:"Vk",allDayText:"Koko päivä",eventLimitText:"lisää",noEventsMessage:"Ei näytettäviä tapahtumia"},{code:"fr",buttonText:{prev:"Précédent",next:"Suivant",today:"Aujourd'hui",year:"Année",month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},weekLabel:"Sem.",allDayHtml:"Toute la
journée",eventLimitText:"en plus",noEventsMessage:"Aucun événement à afficher"},{code:"fr-ch",week:{dow:1,doy:4},buttonText:{prev:"Précédent",next:"Suivant",today:"Courant",year:"Année",month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},weekLabel:"Sm",allDayHtml:"Toute la
journée",eventLimitText:"en plus",noEventsMessage:"Aucun événement à afficher"},{code:"fr",week:{dow:1,doy:4},buttonText:{prev:"Précédent",next:"Suivant",today:"Aujourd'hui",year:"Année",month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},weekLabel:"Sem.",allDayHtml:"Toute la
journée",eventLimitText:"en plus",noEventsMessage:"Aucun événement à afficher"},{code:"gl",week:{dow:1,doy:4},buttonText:{prev:"Ant",next:"Seg",today:"Hoxe",month:"Mes",week:"Semana",day:"Día",list:"Axenda"},weekLabel:"Sm",allDayHtml:"Todo
o día",eventLimitText:"máis",noEventsMessage:"Non hai eventos para amosar"},{code:"he",dir:"rtl",buttonText:{prev:"הקודם",next:"הבא",today:"היום",month:"חודש",week:"שבוע",day:"יום",list:"סדר יום"},allDayText:"כל היום",eventLimitText:"אחר",noEventsMessage:"אין אירועים להצגה",weekLabel:"שבוע"},{code:"hi",week:{dow:0,doy:6},buttonText:{prev:"पिछला",next:"अगला",today:"आज",month:"महीना",week:"सप्ताह",day:"दिन",list:"कार्यसूची"},weekLabel:"हफ्ता",allDayText:"सभी दिन",eventLimitText:function(e){return"+अधिक "+e},noEventsMessage:"कोई घटनाओं को प्रदर्शित करने के लिए"},{code:"hr",week:{dow:1,doy:7},buttonText:{prev:"Prijašnji",next:"Sljedeći",today:"Danas",month:"Mjesec",week:"Tjedan",day:"Dan",list:"Raspored"},weekLabel:"Tje",allDayText:"Cijeli dan",eventLimitText:function(e){return"+ još "+e},noEventsMessage:"Nema događaja za prikaz"},{code:"hu",week:{dow:1,doy:4},buttonText:{prev:"vissza",next:"előre",today:"ma",month:"Hónap",week:"Hét",day:"Nap",list:"Napló"},weekLabel:"Hét",allDayText:"Egész nap",eventLimitText:"további",noEventsMessage:"Nincs megjeleníthető esemény"},{code:"id",week:{dow:1,doy:7},buttonText:{prev:"mundur",next:"maju",today:"hari ini",month:"Bulan",week:"Minggu",day:"Hari",list:"Agenda"},weekLabel:"Mg",allDayHtml:"Sehari
penuh",eventLimitText:"lebih",noEventsMessage:"Tidak ada acara untuk ditampilkan"},{code:"is",week:{dow:1,doy:4},buttonText:{prev:"Fyrri",next:"Næsti",today:"Í dag",month:"Mánuður",week:"Vika",day:"Dagur",list:"Dagskrá"},weekLabel:"Vika",allDayHtml:"Allan
daginn",eventLimitText:"meira",noEventsMessage:"Engir viðburðir til að sýna"},{code:"it",week:{dow:1,doy:4},buttonText:{prev:"Prec",next:"Succ",today:"Oggi",month:"Mese",week:"Settimana",day:"Giorno",list:"Agenda"},weekLabel:"Sm",allDayHtml:"Tutto il
giorno",eventLimitText:function(e){return"+altri "+e},noEventsMessage:"Non ci sono eventi da visualizzare"},{code:"ja",buttonText:{prev:"前",next:"次",today:"今日",month:"月",week:"週",day:"日",list:"予定リスト"},weekLabel:"週",allDayText:"終日",eventLimitText:function(e){return"他 "+e+" 件"},noEventsMessage:"表示する予定はありません"},{code:"ka",week:{dow:1,doy:7},buttonText:{prev:"წინა",next:"შემდეგი",today:"დღეს",month:"თვე",week:"კვირა",day:"დღე",list:"დღის წესრიგი"},weekLabel:"კვ",allDayText:"მთელი დღე",eventLimitText:function(e){return"+ კიდევ "+e},noEventsMessage:"ღონისძიებები არ არის"},{code:"kk",week:{dow:1,doy:7},buttonText:{prev:"Алдыңғы",next:"Келесі",today:"Бүгін",month:"Ай",week:"Апта",day:"Күн",list:"Күн тәртібі"},weekLabel:"Не",allDayText:"Күні бойы",eventLimitText:function(e){return"+ тағы "+e},noEventsMessage:"Көрсету үшін оқиғалар жоқ"},{code:"ko",buttonText:{prev:"이전달",next:"다음달",today:"오늘",month:"월",week:"주",day:"일",list:"일정목록"},weekLabel:"주",allDayText:"종일",eventLimitText:"개",noEventsMessage:"일정이 없습니다"},{code:"lb",week:{dow:1,doy:4},buttonText:{prev:"Zréck",next:"Weider",today:"Haut",month:"Mount",week:"Woch",day:"Dag",list:"Terminiwwersiicht"},weekLabel:"W",allDayText:"Ganzen Dag",eventLimitText:"méi",noEventsMessage:"Nee Evenementer ze affichéieren"},{code:"lt",week:{dow:1,doy:4},buttonText:{prev:"Atgal",next:"Pirmyn",today:"Šiandien",month:"Mėnuo",week:"Savaitė",day:"Diena",list:"Darbotvarkė"},weekLabel:"SAV",allDayText:"Visą dieną",eventLimitText:"daugiau",noEventsMessage:"Nėra įvykių rodyti"},{code:"lv",week:{dow:1,doy:4},buttonText:{prev:"Iepr.",next:"Nāk.",today:"Šodien",month:"Mēnesis",week:"Nedēļa",day:"Diena",list:"Dienas kārtība"},weekLabel:"Ned.",allDayText:"Visu dienu",eventLimitText:function(e){return"+vēl "+e},noEventsMessage:"Nav notikumu"},{code:"mk",buttonText:{prev:"претходно",next:"следно",today:"Денес",month:"Месец",week:"Недела",day:"Ден",list:"График"},weekLabel:"Сед",allDayText:"Цел ден",eventLimitText:function(e){return"+повеќе "+e},noEventsMessage:"Нема настани за прикажување"},{code:"ms",week:{dow:1,doy:7},buttonText:{prev:"Sebelum",next:"Selepas",today:"hari ini",month:"Bulan",week:"Minggu",day:"Hari",list:"Agenda"},weekLabel:"Mg",allDayText:"Sepanjang hari",eventLimitText:function(e){return"masih ada "+e+" acara"},noEventsMessage:"Tiada peristiwa untuk dipaparkan"},{code:"nb",week:{dow:1,doy:4},buttonText:{prev:"Forrige",next:"Neste",today:"I dag",month:"Måned",week:"Uke",day:"Dag",list:"Agenda"},weekLabel:"Uke",allDayText:"Hele dagen",eventLimitText:"til",noEventsMessage:"Ingen hendelser å vise"},{code:"nl",week:{dow:1,doy:4},buttonText:{prev:"Voorgaand",next:"Volgende",today:"Vandaag",year:"Jaar",month:"Maand",week:"Week",day:"Dag",list:"Agenda"},allDayText:"Hele dag",eventLimitText:"extra",noEventsMessage:"Geen evenementen om te laten zien"},{code:"nn",week:{dow:1,doy:4},buttonText:{prev:"Førre",next:"Neste",today:"I dag",month:"Månad",week:"Veke",day:"Dag",list:"Agenda"},weekLabel:"Veke",allDayText:"Heile dagen",eventLimitText:"til",noEventsMessage:"Ingen hendelser å vise"},{code:"pl",week:{dow:1,doy:4},buttonText:{prev:"Poprzedni",next:"Następny",today:"Dziś",month:"Miesiąc",week:"Tydzień",day:"Dzień",list:"Plan dnia"},weekLabel:"Tydz",allDayText:"Cały dzień",eventLimitText:"więcej",noEventsMessage:"Brak wydarzeń do wyświetlenia"},{code:"pt-br",buttonText:{prev:"Anterior",next:"Próximo",today:"Hoje",month:"Mês",week:"Semana",day:"Dia",list:"Compromissos"},weekLabel:"Sm",allDayText:"dia inteiro",eventLimitText:function(e){return"mais +"+e},noEventsMessage:"Não há eventos para mostrar"},{code:"pt",week:{dow:1,doy:4},buttonText:{prev:"Anterior",next:"Seguinte",today:"Hoje",month:"Mês",week:"Semana",day:"Dia",list:"Agenda"},weekLabel:"Sem",allDayText:"Todo o dia",eventLimitText:"mais",noEventsMessage:"Não há eventos para mostrar"},{code:"ro",week:{dow:1,doy:7},buttonText:{prev:"precedentă",next:"următoare",today:"Azi",month:"Lună",week:"Săptămână",day:"Zi",list:"Agendă"},weekLabel:"Săpt",allDayText:"Toată ziua",eventLimitText:function(e){return"+alte "+e},noEventsMessage:"Nu există evenimente de afișat"},{code:"ru",week:{dow:1,doy:4},buttonText:{prev:"Пред",next:"След",today:"Сегодня",month:"Месяц",week:"Неделя",day:"День",list:"Повестка дня"},weekLabel:"Нед",allDayText:"Весь день",eventLimitText:function(e){return"+ ещё "+e},noEventsMessage:"Нет событий для отображения"},{code:"sk",week:{dow:1,doy:4},buttonText:{prev:"Predchádzajúci",next:"Nasledujúci",today:"Dnes",month:"Mesiac",week:"Týždeň",day:"Deň",list:"Rozvrh"},weekLabel:"Ty",allDayText:"Celý deň",eventLimitText:function(e){return"+ďalšie: "+e},noEventsMessage:"Žiadne akcie na zobrazenie"},{code:"sl",week:{dow:1,doy:7},buttonText:{prev:"Prejšnji",next:"Naslednji",today:"Trenutni",month:"Mesec",week:"Teden",day:"Dan",list:"Dnevni red"},weekLabel:"Teden",allDayText:"Ves dan",eventLimitText:"več",noEventsMessage:"Ni dogodkov za prikaz"},{code:"sq",week:{dow:1,doy:4},buttonText:{prev:"mbrapa",next:"Përpara",today:"sot",month:"Muaj",week:"Javë",day:"Ditë",list:"Listë"},weekLabel:"Ja",allDayHtml:"Gjithë
ditën",eventLimitText:function(e){return"+më tepër "+e},noEventsMessage:"Nuk ka evente për të shfaqur"},{code:"sr-cyrl",week:{dow:1,doy:7},buttonText:{prev:"Претходна",next:"следећи",today:"Данас",month:"Месец",week:"Недеља",day:"Дан",list:"Планер"},weekLabel:"Сед",allDayText:"Цео дан",eventLimitText:function(e){return"+ још "+e},noEventsMessage:"Нема догађаја за приказ"},{code:"sr",week:{dow:1,doy:7},buttonText:{prev:"Prethodna",next:"Sledeći",today:"Danas",month:"Mеsеc",week:"Nеdеlja",day:"Dan",list:"Planеr"},weekLabel:"Sed",allDayText:"Cеo dan",eventLimitText:function(e){return"+ još "+e},noEventsMessage:"Nеma događaja za prikaz"},{code:"sv",week:{dow:1,doy:4},buttonText:{prev:"Förra",next:"Nästa",today:"Idag",month:"Månad",week:"Vecka",day:"Dag",list:"Program"},weekLabel:"v.",allDayText:"Heldag",eventLimitText:"till",noEventsMessage:"Inga händelser att visa"},{code:"th",buttonText:{prev:"ย้อน",next:"ถัดไป",today:"วันนี้",month:"เดือน",week:"สัปดาห์",day:"วัน",list:"แผนงาน"},allDayText:"ตลอดวัน",eventLimitText:"เพิ่มเติม",noEventsMessage:"ไม่มีกิจกรรมที่จะแสดง"},{code:"tr",week:{dow:1,doy:7},buttonText:{prev:"geri",next:"ileri",today:"bugün",month:"Ay",week:"Hafta",day:"Gün",list:"Ajanda"},weekLabel:"Hf",allDayText:"Tüm gün",eventLimitText:"daha fazla",noEventsMessage:"Gösterilecek etkinlik yok"},{code:"uk",week:{dow:1,doy:7},buttonText:{prev:"Попередній",next:"далі",today:"Сьогодні",month:"Місяць",week:"Тиждень",day:"День",list:"Порядок денний"},weekLabel:"Тиж",allDayText:"Увесь день",eventLimitText:function(e){return"+ще "+e+"..."},noEventsMessage:"Немає подій для відображення"},{code:"vi",week:{dow:1,doy:4},buttonText:{prev:"Trước",next:"Tiếp",today:"Hôm nay",month:"Tháng",week:"Tuần",day:"Ngày",list:"Lịch biểu"},weekLabel:"Tu",allDayText:"Cả ngày",eventLimitText:function(e){return"+ thêm "+e},noEventsMessage:"Không có sự kiện để hiển thị"},{code:"zh-cn",week:{dow:1,doy:4},buttonText:{prev:"上月",next:"下月",today:"今天",month:"月",week:"周",day:"日",list:"日程"},weekLabel:"周",allDayText:"全天",eventLimitText:function(e){return"另外 "+e+" 个"},noEventsMessage:"没有事件显示"},{code:"zh-tw",buttonText:{prev:"上月",next:"下月",today:"今天",month:"月",week:"週",day:"天",list:"活動列表"},weekLabel:"周",allDayText:"整天",eventLimitText:"顯示更多",noEventsMessage:"没有任何活動"}]}); \ No newline at end of file diff --git a/static/assets/libs/fullcalendar/core/locales/af.js b/static/assets/libs/fullcalendar/core/locales/af.js new file mode 100644 index 0000000..ee9f9f7 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/af.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.af = factory())); +}(this, function () { 'use strict'; + + var af = { + code: "af", + week: { + dow: 1, + doy: 4 // Die week wat die 4de Januarie bevat is die eerste week van die jaar. + }, + buttonText: { + prev: "Vorige", + next: "Volgende", + today: "Vandag", + year: "Jaar", + month: "Maand", + week: "Week", + day: "Dag", + list: "Agenda" + }, + allDayHtml: "Heeldag", + eventLimitText: "Addisionele", + noEventsMessage: "Daar is geen gebeurtenisse nie" + }; + + return af; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/ar-dz.js b/static/assets/libs/fullcalendar/core/locales/ar-dz.js new file mode 100644 index 0000000..201eb17 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/ar-dz.js @@ -0,0 +1,31 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['ar-dz'] = factory())); +}(this, function () { 'use strict'; + + var arDz = { + code: "ar-dz", + week: { + dow: 0, + doy: 4 // The week that contains Jan 1st is the first week of the year. + }, + dir: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekLabel: "أسبوع", + allDayText: "اليوم كله", + eventLimitText: "أخرى", + noEventsMessage: "أي أحداث لعرض" + }; + + return arDz; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/ar-kw.js b/static/assets/libs/fullcalendar/core/locales/ar-kw.js new file mode 100644 index 0000000..94c6900 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/ar-kw.js @@ -0,0 +1,31 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['ar-kw'] = factory())); +}(this, function () { 'use strict'; + + var arKw = { + code: "ar-kw", + week: { + dow: 0, + doy: 12 // The week that contains Jan 1st is the first week of the year. + }, + dir: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekLabel: "أسبوع", + allDayText: "اليوم كله", + eventLimitText: "أخرى", + noEventsMessage: "أي أحداث لعرض" + }; + + return arKw; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/ar-ly.js b/static/assets/libs/fullcalendar/core/locales/ar-ly.js new file mode 100644 index 0000000..e1c8aeb --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/ar-ly.js @@ -0,0 +1,31 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['ar-ly'] = factory())); +}(this, function () { 'use strict'; + + var arLy = { + code: "ar-ly", + week: { + dow: 6, + doy: 12 // The week that contains Jan 1st is the first week of the year. + }, + dir: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekLabel: "أسبوع", + allDayText: "اليوم كله", + eventLimitText: "أخرى", + noEventsMessage: "أي أحداث لعرض" + }; + + return arLy; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/ar-ma.js b/static/assets/libs/fullcalendar/core/locales/ar-ma.js new file mode 100644 index 0000000..00cc7c6 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/ar-ma.js @@ -0,0 +1,31 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['ar-ma'] = factory())); +}(this, function () { 'use strict'; + + var arMa = { + code: "ar-ma", + week: { + dow: 6, + doy: 12 // The week that contains Jan 1st is the first week of the year. + }, + dir: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekLabel: "أسبوع", + allDayText: "اليوم كله", + eventLimitText: "أخرى", + noEventsMessage: "أي أحداث لعرض" + }; + + return arMa; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/ar-sa.js b/static/assets/libs/fullcalendar/core/locales/ar-sa.js new file mode 100644 index 0000000..0361f6d --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/ar-sa.js @@ -0,0 +1,31 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['ar-sa'] = factory())); +}(this, function () { 'use strict'; + + var arSa = { + code: "ar-sa", + week: { + dow: 0, + doy: 6 // The week that contains Jan 1st is the first week of the year. + }, + dir: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekLabel: "أسبوع", + allDayText: "اليوم كله", + eventLimitText: "أخرى", + noEventsMessage: "أي أحداث لعرض" + }; + + return arSa; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/ar-tn.js b/static/assets/libs/fullcalendar/core/locales/ar-tn.js new file mode 100644 index 0000000..57a07f8 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/ar-tn.js @@ -0,0 +1,31 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['ar-tn'] = factory())); +}(this, function () { 'use strict'; + + var arTn = { + code: "ar-tn", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + dir: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekLabel: "أسبوع", + allDayText: "اليوم كله", + eventLimitText: "أخرى", + noEventsMessage: "أي أحداث لعرض" + }; + + return arTn; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/ar.js b/static/assets/libs/fullcalendar/core/locales/ar.js new file mode 100644 index 0000000..f789afd --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/ar.js @@ -0,0 +1,31 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.ar = factory())); +}(this, function () { 'use strict'; + + var ar = { + code: "ar", + week: { + dow: 6, + doy: 12 // The week that contains Jan 1st is the first week of the year. + }, + dir: 'rtl', + buttonText: { + prev: "السابق", + next: "التالي", + today: "اليوم", + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + weekLabel: "أسبوع", + allDayText: "اليوم كله", + eventLimitText: "أخرى", + noEventsMessage: "أي أحداث لعرض" + }; + + return ar; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/bg.js b/static/assets/libs/fullcalendar/core/locales/bg.js new file mode 100644 index 0000000..e7343a6 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/bg.js @@ -0,0 +1,31 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.bg = factory())); +}(this, function () { 'use strict'; + + var bg = { + code: "bg", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "назад", + next: "напред", + today: "днес", + month: "Месец", + week: "Седмица", + day: "Ден", + list: "График" + }, + allDayText: "Цял ден", + eventLimitText: function (n) { + return "+още " + n; + }, + noEventsMessage: "Няма събития за показване" + }; + + return bg; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/bs.js b/static/assets/libs/fullcalendar/core/locales/bs.js new file mode 100644 index 0000000..d96b8ad --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/bs.js @@ -0,0 +1,32 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.bs = factory())); +}(this, function () { 'use strict'; + + var bs = { + code: "bs", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Prošli", + next: "Sljedeći", + today: "Danas", + month: "Mjesec", + week: "Sedmica", + day: "Dan", + list: "Raspored" + }, + weekLabel: "Sed", + allDayText: "Cijeli dan", + eventLimitText: function (n) { + return "+ još " + n; + }, + noEventsMessage: "Nema događaja za prikazivanje" + }; + + return bs; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/ca.js b/static/assets/libs/fullcalendar/core/locales/ca.js new file mode 100644 index 0000000..d2d3e2a --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/ca.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.ca = factory())); +}(this, function () { 'use strict'; + + var ca = { + code: "ca", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Anterior", + next: "Següent", + today: "Avui", + month: "Mes", + week: "Setmana", + day: "Dia", + list: "Agenda" + }, + weekLabel: "Set", + allDayText: "Tot el dia", + eventLimitText: "més", + noEventsMessage: "No hi ha esdeveniments per mostrar" + }; + + return ca; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/cs.js b/static/assets/libs/fullcalendar/core/locales/cs.js new file mode 100644 index 0000000..2624e36 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/cs.js @@ -0,0 +1,32 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.cs = factory())); +}(this, function () { 'use strict'; + + var cs = { + code: "cs", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Dříve", + next: "Později", + today: "Nyní", + month: "Měsíc", + week: "Týden", + day: "Den", + list: "Agenda" + }, + weekLabel: "Týd", + allDayText: "Celý den", + eventLimitText: function (n) { + return "+další: " + n; + }, + noEventsMessage: "Žádné akce k zobrazení" + }; + + return cs; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/da.js b/static/assets/libs/fullcalendar/core/locales/da.js new file mode 100644 index 0000000..73d1559 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/da.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.da = factory())); +}(this, function () { 'use strict'; + + var da = { + code: "da", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Forrige", + next: "Næste", + today: "I dag", + month: "Måned", + week: "Uge", + day: "Dag", + list: "Agenda" + }, + weekLabel: "Uge", + allDayText: "Hele dagen", + eventLimitText: "flere", + noEventsMessage: "Ingen arrangementer at vise" + }; + + return da; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/de.js b/static/assets/libs/fullcalendar/core/locales/de.js new file mode 100644 index 0000000..ab5a815 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/de.js @@ -0,0 +1,33 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.de = factory())); +}(this, function () { 'use strict'; + + var de = { + code: "de", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Zurück", + next: "Vor", + today: "Heute", + year: "Jahr", + month: "Monat", + week: "Woche", + day: "Tag", + list: "Terminübersicht" + }, + weekLabel: "KW", + allDayText: "Ganztägig", + eventLimitText: function (n) { + return "+ weitere " + n; + }, + noEventsMessage: "Keine Ereignisse anzuzeigen" + }; + + return de; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/el.js b/static/assets/libs/fullcalendar/core/locales/el.js new file mode 100644 index 0000000..9f59e36 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/el.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.el = factory())); +}(this, function () { 'use strict'; + + var el = { + code: "el", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4st is the first week of the year. + }, + buttonText: { + prev: "Προηγούμενος", + next: "Επόμενος", + today: "Σήμερα", + month: "Μήνας", + week: "Εβδομάδα", + day: "Ημέρα", + list: "Ατζέντα" + }, + weekLabel: "Εβδ", + allDayText: "Ολοήμερο", + eventLimitText: "περισσότερα", + noEventsMessage: "Δεν υπάρχουν γεγονότα για να εμφανιστεί" + }; + + return el; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/en-au.js b/static/assets/libs/fullcalendar/core/locales/en-au.js new file mode 100644 index 0000000..be10bfb --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/en-au.js @@ -0,0 +1,17 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['en-au'] = factory())); +}(this, function () { 'use strict'; + + var enAu = { + code: "en-au", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + } + }; + + return enAu; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/en-gb.js b/static/assets/libs/fullcalendar/core/locales/en-gb.js new file mode 100644 index 0000000..8a4a84e --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/en-gb.js @@ -0,0 +1,17 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['en-gb'] = factory())); +}(this, function () { 'use strict'; + + var enGb = { + code: "en-gb", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + } + }; + + return enGb; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/en-nz.js b/static/assets/libs/fullcalendar/core/locales/en-nz.js new file mode 100644 index 0000000..df56c14 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/en-nz.js @@ -0,0 +1,17 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['en-nz'] = factory())); +}(this, function () { 'use strict'; + + var enNz = { + code: "en-nz", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + } + }; + + return enNz; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/es-us.js b/static/assets/libs/fullcalendar/core/locales/es-us.js new file mode 100644 index 0000000..1efa89a --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/es-us.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['es-us'] = factory())); +}(this, function () { 'use strict'; + + var esUs = { + code: "es", + week: { + dow: 0, + doy: 6 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Ant", + next: "Sig", + today: "Hoy", + month: "Mes", + week: "Semana", + day: "Día", + list: "Agenda" + }, + weekLabel: "Sm", + allDayHtml: "Todo
el día", + eventLimitText: "más", + noEventsMessage: "No hay eventos para mostrar" + }; + + return esUs; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/es.js b/static/assets/libs/fullcalendar/core/locales/es.js new file mode 100644 index 0000000..bfd9af4 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/es.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.es = factory())); +}(this, function () { 'use strict'; + + var es = { + code: "es", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Ant", + next: "Sig", + today: "Hoy", + month: "Mes", + week: "Semana", + day: "Día", + list: "Agenda" + }, + weekLabel: "Sm", + allDayHtml: "Todo
el día", + eventLimitText: "más", + noEventsMessage: "No hay eventos para mostrar" + }; + + return es; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/et.js b/static/assets/libs/fullcalendar/core/locales/et.js new file mode 100644 index 0000000..c44fcae --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/et.js @@ -0,0 +1,32 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.et = factory())); +}(this, function () { 'use strict'; + + var et = { + code: "et", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Eelnev", + next: "Järgnev", + today: "Täna", + month: "Kuu", + week: "Nädal", + day: "Päev", + list: "Päevakord" + }, + weekLabel: "näd", + allDayText: "Kogu päev", + eventLimitText: function (n) { + return "+ veel " + n; + }, + noEventsMessage: "Kuvamiseks puuduvad sündmused" + }; + + return et; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/eu.js b/static/assets/libs/fullcalendar/core/locales/eu.js new file mode 100644 index 0000000..91903aa --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/eu.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.eu = factory())); +}(this, function () { 'use strict'; + + var eu = { + code: "eu", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Aur", + next: "Hur", + today: "Gaur", + month: "Hilabetea", + week: "Astea", + day: "Eguna", + list: "Agenda" + }, + weekLabel: "As", + allDayHtml: "Egun
osoa", + eventLimitText: "gehiago", + noEventsMessage: "Ez dago ekitaldirik erakusteko" + }; + + return eu; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/fa.js b/static/assets/libs/fullcalendar/core/locales/fa.js new file mode 100644 index 0000000..031fc7b --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/fa.js @@ -0,0 +1,33 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.fa = factory())); +}(this, function () { 'use strict'; + + var fa = { + code: "fa", + week: { + dow: 6, + doy: 12 // The week that contains Jan 1st is the first week of the year. + }, + dir: 'rtl', + buttonText: { + prev: "قبلی", + next: "بعدی", + today: "امروز", + month: "ماه", + week: "هفته", + day: "روز", + list: "برنامه" + }, + weekLabel: "هف", + allDayText: "تمام روز", + eventLimitText: function (n) { + return "بیش از " + n; + }, + noEventsMessage: "هیچ رویدادی به نمایش" + }; + + return fa; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/fi.js b/static/assets/libs/fullcalendar/core/locales/fi.js new file mode 100644 index 0000000..3912845 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/fi.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.fi = factory())); +}(this, function () { 'use strict'; + + var fi = { + code: "fi", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Edellinen", + next: "Seuraava", + today: "Tänään", + month: "Kuukausi", + week: "Viikko", + day: "Päivä", + list: "Tapahtumat" + }, + weekLabel: "Vk", + allDayText: "Koko päivä", + eventLimitText: "lisää", + noEventsMessage: "Ei näytettäviä tapahtumia" + }; + + return fi; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/fr-ca.js b/static/assets/libs/fullcalendar/core/locales/fr-ca.js new file mode 100644 index 0000000..d554c14 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/fr-ca.js @@ -0,0 +1,27 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['fr-ca'] = factory())); +}(this, function () { 'use strict'; + + var frCa = { + code: "fr", + buttonText: { + prev: "Précédent", + next: "Suivant", + today: "Aujourd'hui", + year: "Année", + month: "Mois", + week: "Semaine", + day: "Jour", + list: "Mon planning" + }, + weekLabel: "Sem.", + allDayHtml: "Toute la
journée", + eventLimitText: "en plus", + noEventsMessage: "Aucun événement à afficher" + }; + + return frCa; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/fr-ch.js b/static/assets/libs/fullcalendar/core/locales/fr-ch.js new file mode 100644 index 0000000..358b8bf --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/fr-ch.js @@ -0,0 +1,31 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['fr-ch'] = factory())); +}(this, function () { 'use strict'; + + var frCh = { + code: "fr-ch", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Précédent", + next: "Suivant", + today: "Courant", + year: "Année", + month: "Mois", + week: "Semaine", + day: "Jour", + list: "Mon planning" + }, + weekLabel: "Sm", + allDayHtml: "Toute la
journée", + eventLimitText: "en plus", + noEventsMessage: "Aucun événement à afficher" + }; + + return frCh; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/fr.js b/static/assets/libs/fullcalendar/core/locales/fr.js new file mode 100644 index 0000000..b679cef --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/fr.js @@ -0,0 +1,31 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.fr = factory())); +}(this, function () { 'use strict'; + + var fr = { + code: "fr", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Précédent", + next: "Suivant", + today: "Aujourd'hui", + year: "Année", + month: "Mois", + week: "Semaine", + day: "Jour", + list: "Mon planning" + }, + weekLabel: "Sem.", + allDayHtml: "Toute la
journée", + eventLimitText: "en plus", + noEventsMessage: "Aucun événement à afficher" + }; + + return fr; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/gl.js b/static/assets/libs/fullcalendar/core/locales/gl.js new file mode 100644 index 0000000..721a6a8 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/gl.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.gl = factory())); +}(this, function () { 'use strict'; + + var gl = { + code: "gl", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Ant", + next: "Seg", + today: "Hoxe", + month: "Mes", + week: "Semana", + day: "Día", + list: "Axenda" + }, + weekLabel: "Sm", + allDayHtml: "Todo
o día", + eventLimitText: "máis", + noEventsMessage: "Non hai eventos para amosar" + }; + + return gl; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/he.js b/static/assets/libs/fullcalendar/core/locales/he.js new file mode 100644 index 0000000..3521d9e --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/he.js @@ -0,0 +1,27 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.he = factory())); +}(this, function () { 'use strict'; + + var he = { + code: "he", + dir: 'rtl', + buttonText: { + prev: "הקודם", + next: "הבא", + today: "היום", + month: "חודש", + week: "שבוע", + day: "יום", + list: "סדר יום" + }, + allDayText: "כל היום", + eventLimitText: "אחר", + noEventsMessage: "אין אירועים להצגה", + weekLabel: "שבוע" + }; + + return he; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/hi.js b/static/assets/libs/fullcalendar/core/locales/hi.js new file mode 100644 index 0000000..15348e6 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/hi.js @@ -0,0 +1,32 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.hi = factory())); +}(this, function () { 'use strict'; + + var hi = { + code: "hi", + week: { + dow: 0, + doy: 6 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "पिछला", + next: "अगला", + today: "आज", + month: "महीना", + week: "सप्ताह", + day: "दिन", + list: "कार्यसूची" + }, + weekLabel: "हफ्ता", + allDayText: "सभी दिन", + eventLimitText: function (n) { + return "+अधिक " + n; + }, + noEventsMessage: "कोई घटनाओं को प्रदर्शित करने के लिए" + }; + + return hi; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/hr.js b/static/assets/libs/fullcalendar/core/locales/hr.js new file mode 100644 index 0000000..295b485 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/hr.js @@ -0,0 +1,32 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.hr = factory())); +}(this, function () { 'use strict'; + + var hr = { + code: "hr", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Prijašnji", + next: "Sljedeći", + today: "Danas", + month: "Mjesec", + week: "Tjedan", + day: "Dan", + list: "Raspored" + }, + weekLabel: "Tje", + allDayText: "Cijeli dan", + eventLimitText: function (n) { + return "+ još " + n; + }, + noEventsMessage: "Nema događaja za prikaz" + }; + + return hr; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/hu.js b/static/assets/libs/fullcalendar/core/locales/hu.js new file mode 100644 index 0000000..2f0fe8a --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/hu.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.hu = factory())); +}(this, function () { 'use strict'; + + var hu = { + code: "hu", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "vissza", + next: "előre", + today: "ma", + month: "Hónap", + week: "Hét", + day: "Nap", + list: "Napló" + }, + weekLabel: "Hét", + allDayText: "Egész nap", + eventLimitText: "további", + noEventsMessage: "Nincs megjeleníthető esemény" + }; + + return hu; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/id.js b/static/assets/libs/fullcalendar/core/locales/id.js new file mode 100644 index 0000000..b742e80 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/id.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.id = factory())); +}(this, function () { 'use strict'; + + var id = { + code: "id", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "mundur", + next: "maju", + today: "hari ini", + month: "Bulan", + week: "Minggu", + day: "Hari", + list: "Agenda" + }, + weekLabel: "Mg", + allDayHtml: "Sehari
penuh", + eventLimitText: "lebih", + noEventsMessage: "Tidak ada acara untuk ditampilkan" + }; + + return id; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/is.js b/static/assets/libs/fullcalendar/core/locales/is.js new file mode 100644 index 0000000..dd569bc --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/is.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.is = factory())); +}(this, function () { 'use strict'; + + var is = { + code: "is", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Fyrri", + next: "Næsti", + today: "Í dag", + month: "Mánuður", + week: "Vika", + day: "Dagur", + list: "Dagskrá" + }, + weekLabel: "Vika", + allDayHtml: "Allan
daginn", + eventLimitText: "meira", + noEventsMessage: "Engir viðburðir til að sýna" + }; + + return is; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/it.js b/static/assets/libs/fullcalendar/core/locales/it.js new file mode 100644 index 0000000..39a2829 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/it.js @@ -0,0 +1,32 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.it = factory())); +}(this, function () { 'use strict'; + + var it = { + code: "it", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Prec", + next: "Succ", + today: "Oggi", + month: "Mese", + week: "Settimana", + day: "Giorno", + list: "Agenda" + }, + weekLabel: "Sm", + allDayHtml: "Tutto il
giorno", + eventLimitText: function (n) { + return "+altri " + n; + }, + noEventsMessage: "Non ci sono eventi da visualizzare" + }; + + return it; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/ja.js b/static/assets/libs/fullcalendar/core/locales/ja.js new file mode 100644 index 0000000..eb4245b --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/ja.js @@ -0,0 +1,28 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.ja = factory())); +}(this, function () { 'use strict'; + + var ja = { + code: "ja", + buttonText: { + prev: "前", + next: "次", + today: "今日", + month: "月", + week: "週", + day: "日", + list: "予定リスト" + }, + weekLabel: "週", + allDayText: "終日", + eventLimitText: function (n) { + return "他 " + n + " 件"; + }, + noEventsMessage: "表示する予定はありません" + }; + + return ja; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/ka.js b/static/assets/libs/fullcalendar/core/locales/ka.js new file mode 100644 index 0000000..b971c03 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/ka.js @@ -0,0 +1,32 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.ka = factory())); +}(this, function () { 'use strict'; + + var ka = { + code: "ka", + week: { + dow: 1, + doy: 7 + }, + buttonText: { + prev: "წინა", + next: "შემდეგი", + today: "დღეს", + month: "თვე", + week: "კვირა", + day: "დღე", + list: "დღის წესრიგი" + }, + weekLabel: "კვ", + allDayText: "მთელი დღე", + eventLimitText: function (n) { + return "+ კიდევ " + n; + }, + noEventsMessage: "ღონისძიებები არ არის" + }; + + return ka; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/kk.js b/static/assets/libs/fullcalendar/core/locales/kk.js new file mode 100644 index 0000000..5b19b99 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/kk.js @@ -0,0 +1,32 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.kk = factory())); +}(this, function () { 'use strict'; + + var kk = { + code: "kk", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Алдыңғы", + next: "Келесі", + today: "Бүгін", + month: "Ай", + week: "Апта", + day: "Күн", + list: "Күн тәртібі" + }, + weekLabel: "Не", + allDayText: "Күні бойы", + eventLimitText: function (n) { + return "+ тағы " + n; + }, + noEventsMessage: "Көрсету үшін оқиғалар жоқ" + }; + + return kk; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/ko.js b/static/assets/libs/fullcalendar/core/locales/ko.js new file mode 100644 index 0000000..ffe985d --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/ko.js @@ -0,0 +1,26 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.ko = factory())); +}(this, function () { 'use strict'; + + var ko = { + code: "ko", + buttonText: { + prev: "이전달", + next: "다음달", + today: "오늘", + month: "월", + week: "주", + day: "일", + list: "일정목록" + }, + weekLabel: "주", + allDayText: "종일", + eventLimitText: "개", + noEventsMessage: "일정이 없습니다" + }; + + return ko; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/lb.js b/static/assets/libs/fullcalendar/core/locales/lb.js new file mode 100644 index 0000000..b9b17e3 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/lb.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.lb = factory())); +}(this, function () { 'use strict'; + + var lb = { + code: "lb", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Zréck", + next: "Weider", + today: "Haut", + month: "Mount", + week: "Woch", + day: "Dag", + list: "Terminiwwersiicht" + }, + weekLabel: "W", + allDayText: "Ganzen Dag", + eventLimitText: "méi", + noEventsMessage: "Nee Evenementer ze affichéieren" + }; + + return lb; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/lt.js b/static/assets/libs/fullcalendar/core/locales/lt.js new file mode 100644 index 0000000..ec641b7 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/lt.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.lt = factory())); +}(this, function () { 'use strict'; + + var lt = { + code: "lt", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Atgal", + next: "Pirmyn", + today: "Šiandien", + month: "Mėnuo", + week: "Savaitė", + day: "Diena", + list: "Darbotvarkė" + }, + weekLabel: "SAV", + allDayText: "Visą dieną", + eventLimitText: "daugiau", + noEventsMessage: "Nėra įvykių rodyti" + }; + + return lt; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/lv.js b/static/assets/libs/fullcalendar/core/locales/lv.js new file mode 100644 index 0000000..5453630 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/lv.js @@ -0,0 +1,32 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.lv = factory())); +}(this, function () { 'use strict'; + + var lv = { + code: "lv", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Iepr.", + next: "Nāk.", + today: "Šodien", + month: "Mēnesis", + week: "Nedēļa", + day: "Diena", + list: "Dienas kārtība" + }, + weekLabel: "Ned.", + allDayText: "Visu dienu", + eventLimitText: function (n) { + return "+vēl " + n; + }, + noEventsMessage: "Nav notikumu" + }; + + return lv; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/mk.js b/static/assets/libs/fullcalendar/core/locales/mk.js new file mode 100644 index 0000000..6729fa6 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/mk.js @@ -0,0 +1,28 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.mk = factory())); +}(this, function () { 'use strict'; + + var mk = { + code: "mk", + buttonText: { + prev: "претходно", + next: "следно", + today: "Денес", + month: "Месец", + week: "Недела", + day: "Ден", + list: "График" + }, + weekLabel: "Сед", + allDayText: "Цел ден", + eventLimitText: function (n) { + return "+повеќе " + n; + }, + noEventsMessage: "Нема настани за прикажување" + }; + + return mk; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/ms.js b/static/assets/libs/fullcalendar/core/locales/ms.js new file mode 100644 index 0000000..7205ecc --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/ms.js @@ -0,0 +1,32 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.ms = factory())); +}(this, function () { 'use strict'; + + var ms = { + code: "ms", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Sebelum", + next: "Selepas", + today: "hari ini", + month: "Bulan", + week: "Minggu", + day: "Hari", + list: "Agenda" + }, + weekLabel: "Mg", + allDayText: "Sepanjang hari", + eventLimitText: function (n) { + return "masih ada " + n + " acara"; + }, + noEventsMessage: "Tiada peristiwa untuk dipaparkan" + }; + + return ms; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/nb.js b/static/assets/libs/fullcalendar/core/locales/nb.js new file mode 100644 index 0000000..6464461 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/nb.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.nb = factory())); +}(this, function () { 'use strict'; + + var nb = { + code: "nb", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Forrige", + next: "Neste", + today: "I dag", + month: "Måned", + week: "Uke", + day: "Dag", + list: "Agenda" + }, + weekLabel: "Uke", + allDayText: "Hele dagen", + eventLimitText: "til", + noEventsMessage: "Ingen hendelser å vise" + }; + + return nb; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/nl.js b/static/assets/libs/fullcalendar/core/locales/nl.js new file mode 100644 index 0000000..c91b5e5 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/nl.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.nl = factory())); +}(this, function () { 'use strict'; + + var nl = { + code: "nl", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Voorgaand", + next: "Volgende", + today: "Vandaag", + year: "Jaar", + month: "Maand", + week: "Week", + day: "Dag", + list: "Agenda" + }, + allDayText: "Hele dag", + eventLimitText: "extra", + noEventsMessage: "Geen evenementen om te laten zien" + }; + + return nl; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/nn.js b/static/assets/libs/fullcalendar/core/locales/nn.js new file mode 100644 index 0000000..a5cdd16 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/nn.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.nn = factory())); +}(this, function () { 'use strict'; + + var nn = { + code: "nn", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Førre", + next: "Neste", + today: "I dag", + month: "Månad", + week: "Veke", + day: "Dag", + list: "Agenda" + }, + weekLabel: "Veke", + allDayText: "Heile dagen", + eventLimitText: "til", + noEventsMessage: "Ingen hendelser å vise" + }; + + return nn; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/pl.js b/static/assets/libs/fullcalendar/core/locales/pl.js new file mode 100644 index 0000000..0a22e69 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/pl.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.pl = factory())); +}(this, function () { 'use strict'; + + var pl = { + code: "pl", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Poprzedni", + next: "Następny", + today: "Dziś", + month: "Miesiąc", + week: "Tydzień", + day: "Dzień", + list: "Plan dnia" + }, + weekLabel: "Tydz", + allDayText: "Cały dzień", + eventLimitText: "więcej", + noEventsMessage: "Brak wydarzeń do wyświetlenia" + }; + + return pl; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/pt-br.js b/static/assets/libs/fullcalendar/core/locales/pt-br.js new file mode 100644 index 0000000..0133cd6 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/pt-br.js @@ -0,0 +1,28 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['pt-br'] = factory())); +}(this, function () { 'use strict'; + + var ptBr = { + code: "pt-br", + buttonText: { + prev: "Anterior", + next: "Próximo", + today: "Hoje", + month: "Mês", + week: "Semana", + day: "Dia", + list: "Compromissos" + }, + weekLabel: "Sm", + allDayText: "dia inteiro", + eventLimitText: function (n) { + return "mais +" + n; + }, + noEventsMessage: "Não há eventos para mostrar" + }; + + return ptBr; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/pt.js b/static/assets/libs/fullcalendar/core/locales/pt.js new file mode 100644 index 0000000..5c54d8d --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/pt.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.pt = factory())); +}(this, function () { 'use strict'; + + var pt = { + code: "pt", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Anterior", + next: "Seguinte", + today: "Hoje", + month: "Mês", + week: "Semana", + day: "Dia", + list: "Agenda" + }, + weekLabel: "Sem", + allDayText: "Todo o dia", + eventLimitText: "mais", + noEventsMessage: "Não há eventos para mostrar" + }; + + return pt; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/ro.js b/static/assets/libs/fullcalendar/core/locales/ro.js new file mode 100644 index 0000000..e8992f2 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/ro.js @@ -0,0 +1,32 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.ro = factory())); +}(this, function () { 'use strict'; + + var ro = { + code: "ro", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "precedentă", + next: "următoare", + today: "Azi", + month: "Lună", + week: "Săptămână", + day: "Zi", + list: "Agendă" + }, + weekLabel: "Săpt", + allDayText: "Toată ziua", + eventLimitText: function (n) { + return "+alte " + n; + }, + noEventsMessage: "Nu există evenimente de afișat" + }; + + return ro; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/ru.js b/static/assets/libs/fullcalendar/core/locales/ru.js new file mode 100644 index 0000000..77e0308 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/ru.js @@ -0,0 +1,32 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.ru = factory())); +}(this, function () { 'use strict'; + + var ru = { + code: "ru", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Пред", + next: "След", + today: "Сегодня", + month: "Месяц", + week: "Неделя", + day: "День", + list: "Повестка дня" + }, + weekLabel: "Нед", + allDayText: "Весь день", + eventLimitText: function (n) { + return "+ ещё " + n; + }, + noEventsMessage: "Нет событий для отображения" + }; + + return ru; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/sk.js b/static/assets/libs/fullcalendar/core/locales/sk.js new file mode 100644 index 0000000..3513a64 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/sk.js @@ -0,0 +1,32 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.sk = factory())); +}(this, function () { 'use strict'; + + var sk = { + code: "sk", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Predchádzajúci", + next: "Nasledujúci", + today: "Dnes", + month: "Mesiac", + week: "Týždeň", + day: "Deň", + list: "Rozvrh" + }, + weekLabel: "Ty", + allDayText: "Celý deň", + eventLimitText: function (n) { + return "+ďalšie: " + n; + }, + noEventsMessage: "Žiadne akcie na zobrazenie" + }; + + return sk; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/sl.js b/static/assets/libs/fullcalendar/core/locales/sl.js new file mode 100644 index 0000000..3233553 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/sl.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.sl = factory())); +}(this, function () { 'use strict'; + + var sl = { + code: "sl", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Prejšnji", + next: "Naslednji", + today: "Trenutni", + month: "Mesec", + week: "Teden", + day: "Dan", + list: "Dnevni red" + }, + weekLabel: "Teden", + allDayText: "Ves dan", + eventLimitText: "več", + noEventsMessage: "Ni dogodkov za prikaz" + }; + + return sl; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/sq.js b/static/assets/libs/fullcalendar/core/locales/sq.js new file mode 100644 index 0000000..0d43a52 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/sq.js @@ -0,0 +1,32 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.sq = factory())); +}(this, function () { 'use strict'; + + var sq = { + code: "sq", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "mbrapa", + next: "Përpara", + today: "sot", + month: "Muaj", + week: "Javë", + day: "Ditë", + list: "Listë" + }, + weekLabel: "Ja", + allDayHtml: "Gjithë
ditën", + eventLimitText: function (n) { + return "+më tepër " + n; + }, + noEventsMessage: "Nuk ka evente për të shfaqur" + }; + + return sq; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/sr-cyrl.js b/static/assets/libs/fullcalendar/core/locales/sr-cyrl.js new file mode 100644 index 0000000..ba0d0df --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/sr-cyrl.js @@ -0,0 +1,32 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['sr-cyrl'] = factory())); +}(this, function () { 'use strict'; + + var srCyrl = { + code: "sr-cyrl", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Претходна", + next: "следећи", + today: "Данас", + month: "Месец", + week: "Недеља", + day: "Дан", + list: "Планер" + }, + weekLabel: "Сед", + allDayText: "Цео дан", + eventLimitText: function (n) { + return "+ још " + n; + }, + noEventsMessage: "Нема догађаја за приказ" + }; + + return srCyrl; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/sr.js b/static/assets/libs/fullcalendar/core/locales/sr.js new file mode 100644 index 0000000..23e5c9b --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/sr.js @@ -0,0 +1,32 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.sr = factory())); +}(this, function () { 'use strict'; + + var sr = { + code: "sr", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Prethodna", + next: "Sledeći", + today: "Danas", + month: "Mеsеc", + week: "Nеdеlja", + day: "Dan", + list: "Planеr" + }, + weekLabel: "Sed", + allDayText: "Cеo dan", + eventLimitText: function (n) { + return "+ još " + n; + }, + noEventsMessage: "Nеma događaja za prikaz" + }; + + return sr; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/sv.js b/static/assets/libs/fullcalendar/core/locales/sv.js new file mode 100644 index 0000000..a887060 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/sv.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.sv = factory())); +}(this, function () { 'use strict'; + + var sv = { + code: "sv", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Förra", + next: "Nästa", + today: "Idag", + month: "Månad", + week: "Vecka", + day: "Dag", + list: "Program" + }, + weekLabel: "v.", + allDayText: "Heldag", + eventLimitText: "till", + noEventsMessage: "Inga händelser att visa" + }; + + return sv; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/th.js b/static/assets/libs/fullcalendar/core/locales/th.js new file mode 100644 index 0000000..caa3fe9 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/th.js @@ -0,0 +1,25 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.th = factory())); +}(this, function () { 'use strict'; + + var th = { + code: "th", + buttonText: { + prev: "ย้อน", + next: "ถัดไป", + today: "วันนี้", + month: "เดือน", + week: "สัปดาห์", + day: "วัน", + list: "แผนงาน" + }, + allDayText: "ตลอดวัน", + eventLimitText: "เพิ่มเติม", + noEventsMessage: "ไม่มีกิจกรรมที่จะแสดง" + }; + + return th; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/tr.js b/static/assets/libs/fullcalendar/core/locales/tr.js new file mode 100644 index 0000000..4845898 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/tr.js @@ -0,0 +1,30 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.tr = factory())); +}(this, function () { 'use strict'; + + var tr = { + code: "tr", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "geri", + next: "ileri", + today: "bugün", + month: "Ay", + week: "Hafta", + day: "Gün", + list: "Ajanda" + }, + weekLabel: "Hf", + allDayText: "Tüm gün", + eventLimitText: "daha fazla", + noEventsMessage: "Gösterilecek etkinlik yok" + }; + + return tr; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/uk.js b/static/assets/libs/fullcalendar/core/locales/uk.js new file mode 100644 index 0000000..de33f25 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/uk.js @@ -0,0 +1,32 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.uk = factory())); +}(this, function () { 'use strict'; + + var uk = { + code: "uk", + week: { + dow: 1, + doy: 7 // The week that contains Jan 1st is the first week of the year. + }, + buttonText: { + prev: "Попередній", + next: "далі", + today: "Сьогодні", + month: "Місяць", + week: "Тиждень", + day: "День", + list: "Порядок денний" + }, + weekLabel: "Тиж", + allDayText: "Увесь день", + eventLimitText: function (n) { + return "+ще " + n + "..."; + }, + noEventsMessage: "Немає подій для відображення" + }; + + return uk; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/vi.js b/static/assets/libs/fullcalendar/core/locales/vi.js new file mode 100644 index 0000000..167ce11 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/vi.js @@ -0,0 +1,32 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.vi = factory())); +}(this, function () { 'use strict'; + + var vi = { + code: "vi", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Trước", + next: "Tiếp", + today: "Hôm nay", + month: "Tháng", + week: "Tuần", + day: "Ngày", + list: "Lịch biểu" + }, + weekLabel: "Tu", + allDayText: "Cả ngày", + eventLimitText: function (n) { + return "+ thêm " + n; + }, + noEventsMessage: "Không có sự kiện để hiển thị" + }; + + return vi; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/zh-cn.js b/static/assets/libs/fullcalendar/core/locales/zh-cn.js new file mode 100644 index 0000000..4debbb9 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/zh-cn.js @@ -0,0 +1,33 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['zh-cn'] = factory())); +}(this, function () { 'use strict'; + + var zhCn = { + code: "zh-cn", + week: { + // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效 + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "上月", + next: "下月", + today: "今天", + month: "月", + week: "周", + day: "日", + list: "日程" + }, + weekLabel: "周", + allDayText: "全天", + eventLimitText: function (n) { + return "另外 " + n + " 个"; + }, + noEventsMessage: "没有事件显示" + }; + + return zhCn; + +})); diff --git a/static/assets/libs/fullcalendar/core/locales/zh-tw.js b/static/assets/libs/fullcalendar/core/locales/zh-tw.js new file mode 100644 index 0000000..bc14dcd --- /dev/null +++ b/static/assets/libs/fullcalendar/core/locales/zh-tw.js @@ -0,0 +1,26 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['zh-tw'] = factory())); +}(this, function () { 'use strict'; + + var zhTw = { + code: "zh-tw", + buttonText: { + prev: "上月", + next: "下月", + today: "今天", + month: "月", + week: "週", + day: "天", + list: "活動列表" + }, + weekLabel: "周", + allDayText: "整天", + eventLimitText: '顯示更多', + noEventsMessage: "没有任何活動" + }; + + return zhTw; + +})); diff --git a/static/assets/libs/fullcalendar/core/main.css b/static/assets/libs/fullcalendar/core/main.css new file mode 100644 index 0000000..4412a18 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/main.css @@ -0,0 +1,1052 @@ +@charset "UTF-8"; +.fc { + direction: ltr; + text-align: left; +} + +.fc-rtl { + text-align: right; +} + +body .fc { + /* extra precedence to overcome jqui */ + font-size: 1em; +} + +/* Colors +--------------------------------------------------------------------------------------------------*/ +.fc-highlight { + /* when user is selecting cells */ + background: #bce8f1; + opacity: 0.3; +} + +.fc-bgevent { + /* default look for background events */ + background: #8fdf82; + opacity: 0.3; +} + +.fc-nonbusiness { + /* default look for non-business-hours areas */ + /* will inherit .fc-bgevent's styles */ + background: #d7d7d7; +} + +/* Popover +--------------------------------------------------------------------------------------------------*/ +.fc-popover { + position: absolute; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); +} + +.fc-popover .fc-header { + /* TODO: be more consistent with fc-head/fc-body */ + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 2px 4px; +} + +.fc-rtl .fc-popover .fc-header { + flex-direction: row-reverse; +} + +.fc-popover .fc-header .fc-title { + margin: 0 2px; +} + +.fc-popover .fc-header .fc-close { + cursor: pointer; + opacity: 0.65; + font-size: 1.1em; +} + +/* Misc Reusable Components +--------------------------------------------------------------------------------------------------*/ +.fc-divider { + border-style: solid; + border-width: 1px; +} + +hr.fc-divider { + height: 0; + margin: 0; + padding: 0 0 2px; + /* height is unreliable across browsers, so use padding */ + border-width: 1px 0; +} + +.fc-bg, +.fc-bgevent-skeleton, +.fc-highlight-skeleton, +.fc-mirror-skeleton { + /* these element should always cling to top-left/right corners */ + position: absolute; + top: 0; + left: 0; + right: 0; +} + +.fc-bg { + bottom: 0; + /* strech bg to bottom edge */ +} + +.fc-bg table { + height: 100%; + /* strech bg to bottom edge */ +} + +/* Tables +--------------------------------------------------------------------------------------------------*/ +.fc table { + width: 100%; + box-sizing: border-box; + /* fix scrollbar issue in firefox */ + table-layout: fixed; + border-collapse: collapse; + border-spacing: 0; + font-size: 1em; + /* normalize cross-browser */ +} + +.fc th { + text-align: center; +} + +.fc th, +.fc td { + border-style: solid; + border-width: 1px; + padding: 0; + vertical-align: top; +} + +.fc td.fc-today { + border-style: double; + /* overcome neighboring borders */ +} + +/* Internal Nav Links +--------------------------------------------------------------------------------------------------*/ +a[data-goto] { + cursor: pointer; +} + +a[data-goto]:hover { + text-decoration: underline; +} + +/* Fake Table Rows +--------------------------------------------------------------------------------------------------*/ +.fc .fc-row { + /* extra precedence to overcome themes forcing a 1px border */ + /* no visible border by default. but make available if need be (scrollbar width compensation) */ + border-style: solid; + border-width: 0; +} + +.fc-row table { + /* don't put left/right border on anything within a fake row. + the outer tbody will worry about this */ + border-left: 0 hidden transparent; + border-right: 0 hidden transparent; + /* no bottom borders on rows */ + border-bottom: 0 hidden transparent; +} + +.fc-row:first-child table { + border-top: 0 hidden transparent; + /* no top border on first row */ +} + +/* Day Row (used within the header and the DayGrid) +--------------------------------------------------------------------------------------------------*/ +.fc-row { + position: relative; +} + +.fc-row .fc-bg { + z-index: 1; +} + +/* highlighting cells & background event skeleton */ +.fc-row .fc-bgevent-skeleton, +.fc-row .fc-highlight-skeleton { + bottom: 0; + /* stretch skeleton to bottom of row */ +} + +.fc-row .fc-bgevent-skeleton table, +.fc-row .fc-highlight-skeleton table { + height: 100%; + /* stretch skeleton to bottom of row */ +} + +.fc-row .fc-highlight-skeleton td, +.fc-row .fc-bgevent-skeleton td { + border-color: transparent; +} + +.fc-row .fc-bgevent-skeleton { + z-index: 2; +} + +.fc-row .fc-highlight-skeleton { + z-index: 3; +} + +/* +row content (which contains day/week numbers and events) as well as "mirror" (which contains +temporary rendered events). +*/ +.fc-row .fc-content-skeleton { + position: relative; + z-index: 4; + padding-bottom: 2px; + /* matches the space above the events */ +} + +.fc-row .fc-mirror-skeleton { + z-index: 5; +} + +.fc .fc-row .fc-content-skeleton table, +.fc .fc-row .fc-content-skeleton td, +.fc .fc-row .fc-mirror-skeleton td { + /* see-through to the background below */ + /* extra precedence to prevent theme-provided backgrounds */ + background: none; + /* in case s are globally styled */ + border-color: transparent; +} + +.fc-row .fc-content-skeleton td, +.fc-row .fc-mirror-skeleton td { + /* don't put a border between events and/or the day number */ + border-bottom: 0; +} + +.fc-row .fc-content-skeleton tbody td, +.fc-row .fc-mirror-skeleton tbody td { + /* don't put a border between event cells */ + border-top: 0; +} + +/* Scrolling Container +--------------------------------------------------------------------------------------------------*/ +.fc-scroller { + -webkit-overflow-scrolling: touch; +} + +/* TODO: move to timegrid/daygrid */ +.fc-scroller > .fc-day-grid, +.fc-scroller > .fc-time-grid { + position: relative; + /* re-scope all positions */ + width: 100%; + /* hack to force re-sizing this inner element when scrollbars appear/disappear */ +} + +/* Global Event Styles +--------------------------------------------------------------------------------------------------*/ +.fc-event { + position: relative; + /* for resize handle and other inner positioning */ + display: block; + /* make the tag block */ + font-size: 0.85em; + line-height: 1.4; + border-radius: 3px; + border: 1px solid #3788d8; +} + +.fc-event, +.fc-event-dot { + background-color: #3788d8; + /* default BACKGROUND color */ +} + +.fc-event, +.fc-event:hover { + color: #fff; + /* default TEXT color */ + text-decoration: none; + /* if has an href */ +} + +.fc-event[href], +.fc-event.fc-draggable { + cursor: pointer; + /* give events with links and draggable events a hand mouse pointer */ +} + +.fc-not-allowed, +.fc-not-allowed .fc-event { + /* to override an event's custom cursor */ + cursor: not-allowed; +} + +.fc-event .fc-content { + position: relative; + z-index: 2; +} + +/* resizer (cursor AND touch devices) */ +.fc-event .fc-resizer { + position: absolute; + z-index: 4; +} + +/* resizer (touch devices) */ +.fc-event .fc-resizer { + display: none; +} + +.fc-event.fc-allow-mouse-resize .fc-resizer, +.fc-event.fc-selected .fc-resizer { + /* only show when hovering or selected (with touch) */ + display: block; +} + +/* hit area */ +.fc-event.fc-selected .fc-resizer:before { + /* 40x40 touch area */ + content: ""; + position: absolute; + z-index: 9999; + /* user of this util can scope within a lower z-index */ + top: 50%; + left: 50%; + width: 40px; + height: 40px; + margin-left: -20px; + margin-top: -20px; +} + +/* Event Selection (only for touch devices) +--------------------------------------------------------------------------------------------------*/ +.fc-event.fc-selected { + z-index: 9999 !important; + /* overcomes inline z-index */ + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); +} + +.fc-event.fc-selected:after { + content: ""; + position: absolute; + z-index: 1; + /* same z-index as fc-bg, behind text */ + /* overcome the borders */ + top: -1px; + right: -1px; + bottom: -1px; + left: -1px; + /* darkening effect */ + background: #000; + opacity: 0.25; +} + +/* Event Dragging +--------------------------------------------------------------------------------------------------*/ +.fc-event.fc-dragging.fc-selected { + box-shadow: 0 2px 7px rgba(0, 0, 0, 0.3); +} + +.fc-event.fc-dragging:not(.fc-selected) { + opacity: 0.75; +} + +/* Horizontal Events +--------------------------------------------------------------------------------------------------*/ +/* bigger touch area when selected */ +.fc-h-event.fc-selected:before { + content: ""; + position: absolute; + z-index: 3; + /* below resizers */ + top: -10px; + bottom: -10px; + left: 0; + right: 0; +} + +/* events that are continuing to/from another week. kill rounded corners and butt up against edge */ +.fc-ltr .fc-h-event.fc-not-start, +.fc-rtl .fc-h-event.fc-not-end { + margin-left: 0; + border-left-width: 0; + padding-left: 1px; + /* replace the border with padding */ + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.fc-ltr .fc-h-event.fc-not-end, +.fc-rtl .fc-h-event.fc-not-start { + margin-right: 0; + border-right-width: 0; + padding-right: 1px; + /* replace the border with padding */ + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +/* resizer (cursor AND touch devices) */ +/* left resizer */ +.fc-ltr .fc-h-event .fc-start-resizer, +.fc-rtl .fc-h-event .fc-end-resizer { + cursor: w-resize; + left: -1px; + /* overcome border */ +} + +/* right resizer */ +.fc-ltr .fc-h-event .fc-end-resizer, +.fc-rtl .fc-h-event .fc-start-resizer { + cursor: e-resize; + right: -1px; + /* overcome border */ +} + +/* resizer (mouse devices) */ +.fc-h-event.fc-allow-mouse-resize .fc-resizer { + width: 7px; + top: -1px; + /* overcome top border */ + bottom: -1px; + /* overcome bottom border */ +} + +/* resizer (touch devices) */ +.fc-h-event.fc-selected .fc-resizer { + /* 8x8 little dot */ + border-radius: 4px; + border-width: 1px; + width: 6px; + height: 6px; + border-style: solid; + border-color: inherit; + background: #fff; + /* vertically center */ + top: 50%; + margin-top: -4px; +} + +/* left resizer */ +.fc-ltr .fc-h-event.fc-selected .fc-start-resizer, +.fc-rtl .fc-h-event.fc-selected .fc-end-resizer { + margin-left: -4px; + /* centers the 8x8 dot on the left edge */ +} + +/* right resizer */ +.fc-ltr .fc-h-event.fc-selected .fc-end-resizer, +.fc-rtl .fc-h-event.fc-selected .fc-start-resizer { + margin-right: -4px; + /* centers the 8x8 dot on the right edge */ +} + +/* DayGrid events +---------------------------------------------------------------------------------------------------- +We use the full "fc-day-grid-event" class instead of using descendants because the event won't +be a descendant of the grid when it is being dragged. +*/ +.fc-day-grid-event { + margin: 1px 2px 0; + /* spacing between events and edges */ + padding: 0 1px; +} + +tr:first-child > td > .fc-day-grid-event { + margin-top: 2px; + /* a little bit more space before the first event */ +} + +.fc-mirror-skeleton tr:first-child > td > .fc-day-grid-event { + margin-top: 0; + /* except for mirror skeleton */ +} + +.fc-day-grid-event .fc-content { + /* force events to be one-line tall */ + white-space: nowrap; + overflow: hidden; +} + +.fc-day-grid-event .fc-time { + font-weight: bold; +} + +/* resizer (cursor devices) */ +/* left resizer */ +.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer, +.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer { + margin-left: -2px; + /* to the day cell's edge */ +} + +/* right resizer */ +.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer, +.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer { + margin-right: -2px; + /* to the day cell's edge */ +} + +/* Event Limiting +--------------------------------------------------------------------------------------------------*/ +/* "more" link that represents hidden events */ +a.fc-more { + margin: 1px 3px; + font-size: 0.85em; + cursor: pointer; + text-decoration: none; +} + +a.fc-more:hover { + text-decoration: underline; +} + +.fc-limited { + /* rows and cells that are hidden because of a "more" link */ + display: none; +} + +/* popover that appears when "more" link is clicked */ +.fc-day-grid .fc-row { + z-index: 1; + /* make the "more" popover one higher than this */ +} + +.fc-more-popover { + z-index: 2; + width: 220px; +} + +.fc-more-popover .fc-event-container { + padding: 10px; +} + +/* Now Indicator +--------------------------------------------------------------------------------------------------*/ +.fc-now-indicator { + position: absolute; + border: 0 solid red; +} + +/* Utilities +--------------------------------------------------------------------------------------------------*/ +.fc-unselectable { + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-touch-callout: none; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +/* +TODO: more distinction between this file and common.css +*/ +/* Colors +--------------------------------------------------------------------------------------------------*/ +.fc-unthemed th, +.fc-unthemed td, +.fc-unthemed thead, +.fc-unthemed tbody, +.fc-unthemed .fc-divider, +.fc-unthemed .fc-row, +.fc-unthemed .fc-content, +.fc-unthemed .fc-popover, +.fc-unthemed .fc-list-view, +.fc-unthemed .fc-list-heading td { + border-color: #ddd; +} + +.fc-unthemed .fc-popover { + background-color: #fff; +} + +.fc-unthemed .fc-divider, +.fc-unthemed .fc-popover .fc-header, +.fc-unthemed .fc-list-heading td { + background: #eee; +} + +.fc-unthemed td.fc-today { + background: #fcf8e3; +} + +.fc-unthemed .fc-disabled-day { + background: #d7d7d7; + opacity: 0.3; +} + +/* Icons +-------------------------------------------------------------------------------------------------- +from https://feathericons.com/ and built with IcoMoon +*/ +@font-face { + font-family: "fcicons"; + src: url("data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SBfAAAAC8AAAAYGNtYXAXVtKNAAABHAAAAFRnYXNwAAAAEAAAAXAAAAAIZ2x5ZgYydxIAAAF4AAAFNGhlYWQUJ7cIAAAGrAAAADZoaGVhB20DzAAABuQAAAAkaG10eCIABhQAAAcIAAAALGxvY2ED4AU6AAAHNAAAABhtYXhwAA8AjAAAB0wAAAAgbmFtZXsr690AAAdsAAABhnBvc3QAAwAAAAAI9AAAACAAAwPAAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADpBgPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAOAAAAAoACAACAAIAAQAg6Qb//f//AAAAAAAg6QD//f//AAH/4xcEAAMAAQAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAABAWIAjQKeAskAEwAAJSc3NjQnJiIHAQYUFwEWMjc2NCcCnuLiDQ0MJAz/AA0NAQAMJAwNDcni4gwjDQwM/wANIwz/AA0NDCMNAAAAAQFiAI0CngLJABMAACUBNjQnASYiBwYUHwEHBhQXFjI3AZ4BAA0N/wAMJAwNDeLiDQ0MJAyNAQAMIw0BAAwMDSMM4uINIwwNDQAAAAIA4gC3Ax4CngATACcAACUnNzY0JyYiDwEGFB8BFjI3NjQnISc3NjQnJiIPAQYUHwEWMjc2NCcB87e3DQ0MIw3VDQ3VDSMMDQ0BK7e3DQ0MJAzVDQ3VDCQMDQ3zuLcMJAwNDdUNIwzWDAwNIwy4twwkDA0N1Q0jDNYMDA0jDAAAAgDiALcDHgKeABMAJwAAJTc2NC8BJiIHBhQfAQcGFBcWMjchNzY0LwEmIgcGFB8BBwYUFxYyNwJJ1Q0N1Q0jDA0Nt7cNDQwjDf7V1Q0N1QwkDA0Nt7cNDQwkDLfWDCMN1Q0NDCQMt7gMIw0MDNYMIw3VDQ0MJAy3uAwjDQwMAAADAFUAAAOrA1UAMwBoAHcAABMiBgcOAQcOAQcOARURFBYXHgEXHgEXHgEzITI2Nz4BNz4BNz4BNRE0JicuAScuAScuASMFITIWFx4BFx4BFx4BFREUBgcOAQcOAQcOASMhIiYnLgEnLgEnLgE1ETQ2Nz4BNz4BNz4BMxMhMjY1NCYjISIGFRQWM9UNGAwLFQkJDgUFBQUFBQ4JCRULDBgNAlYNGAwLFQkJDgUFBQUFBQ4JCRULDBgN/aoCVgQIBAQHAwMFAQIBAQIBBQMDBwQECAT9qgQIBAQHAwMFAQIBAQIBBQMDBwQECASAAVYRGRkR/qoRGRkRA1UFBAUOCQkVDAsZDf2rDRkLDBUJCA4FBQUFBQUOCQgVDAsZDQJVDRkLDBUJCQ4FBAVVAgECBQMCBwQECAX9qwQJAwQHAwMFAQICAgIBBQMDBwQDCQQCVQUIBAQHAgMFAgEC/oAZEhEZGRESGQAAAAADAFUAAAOrA1UAMwBoAIkAABMiBgcOAQcOAQcOARURFBYXHgEXHgEXHgEzITI2Nz4BNz4BNz4BNRE0JicuAScuAScuASMFITIWFx4BFx4BFx4BFREUBgcOAQcOAQcOASMhIiYnLgEnLgEnLgE1ETQ2Nz4BNz4BNz4BMxMzFRQWMzI2PQEzMjY1NCYrATU0JiMiBh0BIyIGFRQWM9UNGAwLFQkJDgUFBQUFBQ4JCRULDBgNAlYNGAwLFQkJDgUFBQUFBQ4JCRULDBgN/aoCVgQIBAQHAwMFAQIBAQIBBQMDBwQECAT9qgQIBAQHAwMFAQIBAQIBBQMDBwQECASAgBkSEhmAERkZEYAZEhIZgBEZGREDVQUEBQ4JCRUMCxkN/asNGQsMFQkIDgUFBQUFBQ4JCBUMCxkNAlUNGQsMFQkJDgUEBVUCAQIFAwIHBAQIBf2rBAkDBAcDAwUBAgICAgEFAwMHBAMJBAJVBQgEBAcCAwUCAQL+gIASGRkSgBkSERmAEhkZEoAZERIZAAABAOIAjQMeAskAIAAAExcHBhQXFjI/ARcWMjc2NC8BNzY0JyYiDwEnJiIHBhQX4uLiDQ0MJAzi4gwkDA0N4uINDQwkDOLiDCQMDQ0CjeLiDSMMDQ3h4Q0NDCMN4uIMIw0MDOLiDAwNIwwAAAABAAAAAQAAa5n0y18PPPUACwQAAAAAANivOVsAAAAA2K85WwAAAAADqwNVAAAACAACAAAAAAAAAAEAAAPA/8AAAAQAAAAAAAOrAAEAAAAAAAAAAAAAAAAAAAALBAAAAAAAAAAAAAAAAgAAAAQAAWIEAAFiBAAA4gQAAOIEAABVBAAAVQQAAOIAAAAAAAoAFAAeAEQAagCqAOoBngJkApoAAQAAAAsAigADAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAA4ArgABAAAAAAABAAcAAAABAAAAAAACAAcAYAABAAAAAAADAAcANgABAAAAAAAEAAcAdQABAAAAAAAFAAsAFQABAAAAAAAGAAcASwABAAAAAAAKABoAigADAAEECQABAA4ABwADAAEECQACAA4AZwADAAEECQADAA4APQADAAEECQAEAA4AfAADAAEECQAFABYAIAADAAEECQAGAA4AUgADAAEECQAKADQApGZjaWNvbnMAZgBjAGkAYwBvAG4Ac1ZlcnNpb24gMS4wAFYAZQByAHMAaQBvAG4AIAAxAC4AMGZjaWNvbnMAZgBjAGkAYwBvAG4Ac2ZjaWNvbnMAZgBjAGkAYwBvAG4Ac1JlZ3VsYXIAUgBlAGcAdQBsAGEAcmZjaWNvbnMAZgBjAGkAYwBvAG4Ac0ZvbnQgZ2VuZXJhdGVkIGJ5IEljb01vb24uAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") format("truetype"); + font-weight: normal; + font-style: normal; +} +.fc-icon { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: "fcicons" !important; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.fc-icon-chevron-left:before { + content: ""; +} + +.fc-icon-chevron-right:before { + content: ""; +} + +.fc-icon-chevrons-left:before { + content: ""; +} + +.fc-icon-chevrons-right:before { + content: ""; +} + +.fc-icon-minus-square:before { + content: ""; +} + +.fc-icon-plus-square:before { + content: ""; +} + +.fc-icon-x:before { + content: ""; +} + +.fc-icon { + display: inline-block; + width: 1em; + height: 1em; + text-align: center; +} + +/* Buttons +-------------------------------------------------------------------------------------------------- +Lots taken from Flatly (MIT): https://bootswatch.com/4/flatly/bootstrap.css +*/ +/* reset */ +.fc-button { + border-radius: 0; + overflow: visible; + text-transform: none; + margin: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +.fc-button:focus { + outline: 1px dotted; + outline: 5px auto -webkit-focus-ring-color; +} + +.fc-button { + -webkit-appearance: button; +} + +.fc-button:not(:disabled) { + cursor: pointer; +} + +.fc-button::-moz-focus-inner { + padding: 0; + border-style: none; +} + +/* theme */ +.fc-button { + display: inline-block; + font-weight: 400; + color: #212529; + text-align: center; + vertical-align: middle; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-color: transparent; + border: 1px solid transparent; + padding: 0.4em 0.65em; + font-size: 1em; + line-height: 1.5; + border-radius: 0.25em; +} + +.fc-button:hover { + color: #212529; + text-decoration: none; +} + +.fc-button:focus { + outline: 0; + -webkit-box-shadow: 0 0 0 0.2rem rgba(44, 62, 80, 0.25); + box-shadow: 0 0 0 0.2rem rgba(44, 62, 80, 0.25); +} + +.fc-button:disabled { + opacity: 0.65; +} + +/* "primary" coloring */ +.fc-button-primary { + color: #fff; + background-color: #2C3E50; + border-color: #2C3E50; +} + +.fc-button-primary:hover { + color: #fff; + background-color: #1e2b37; + border-color: #1a252f; +} + +.fc-button-primary:focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(76, 91, 106, 0.5); + box-shadow: 0 0 0 0.2rem rgba(76, 91, 106, 0.5); +} + +.fc-button-primary:disabled { + color: #fff; + background-color: #2C3E50; + border-color: #2C3E50; +} + +.fc-button-primary:not(:disabled):active, +.fc-button-primary:not(:disabled).fc-button-active { + color: #fff; + background-color: #1a252f; + border-color: #151e27; +} + +.fc-button-primary:not(:disabled):active:focus, +.fc-button-primary:not(:disabled).fc-button-active:focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(76, 91, 106, 0.5); + box-shadow: 0 0 0 0.2rem rgba(76, 91, 106, 0.5); +} + +/* icons within buttons */ +.fc-button .fc-icon { + vertical-align: middle; + font-size: 1.5em; +} + +/* Buttons Groups +--------------------------------------------------------------------------------------------------*/ +.fc-button-group { + position: relative; + display: -webkit-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; + vertical-align: middle; +} + +.fc-button-group > .fc-button { + position: relative; + -webkit-box-flex: 1; + -ms-flex: 1 1 auto; + flex: 1 1 auto; +} + +.fc-button-group > .fc-button:hover { + z-index: 1; +} + +.fc-button-group > .fc-button:focus, +.fc-button-group > .fc-button:active, +.fc-button-group > .fc-button.fc-button-active { + z-index: 1; +} + +.fc-button-group > .fc-button:not(:first-child) { + margin-left: -1px; +} + +.fc-button-group > .fc-button:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.fc-button-group > .fc-button:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +/* Popover +--------------------------------------------------------------------------------------------------*/ +.fc-unthemed .fc-popover { + border-width: 1px; + border-style: solid; +} + +/* List View +--------------------------------------------------------------------------------------------------*/ +.fc-unthemed .fc-list-item:hover td { + background-color: #f5f5f5; +} + +/* Toolbar +--------------------------------------------------------------------------------------------------*/ +.fc-toolbar { + display: flex; + justify-content: space-between; + align-items: center; +} + +.fc-toolbar.fc-header-toolbar { + margin-bottom: 1.5em; +} + +.fc-toolbar.fc-footer-toolbar { + margin-top: 1.5em; +} + +/* inner content */ +.fc-toolbar > * > :not(:first-child) { + margin-left: 0.75em; +} + +.fc-toolbar h2 { + font-size: 1.75em; + margin: 0; +} + +/* View Structure +--------------------------------------------------------------------------------------------------*/ +.fc-view-container { + position: relative; +} + +/* undo twitter bootstrap's box-sizing rules. normalizes positioning techniques */ +/* don't do this for the toolbar because we'll want bootstrap to style those buttons as some pt */ +.fc-view-container *, +.fc-view-container *:before, +.fc-view-container *:after { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +.fc-view, +.fc-view > table { + /* so dragged elements can be above the view's main element */ + position: relative; + z-index: 1; +} + +@media print { + .fc { + max-width: 100% !important; + } + + /* Global Event Restyling + --------------------------------------------------------------------------------------------------*/ + .fc-event { + background: #fff !important; + color: #000 !important; + page-break-inside: avoid; + } + + .fc-event .fc-resizer { + display: none; + } + + /* Table & Day-Row Restyling + --------------------------------------------------------------------------------------------------*/ + .fc th, +.fc td, +.fc hr, +.fc thead, +.fc tbody, +.fc-row { + border-color: #ccc !important; + background: #fff !important; + } + + /* kill the overlaid, absolutely-positioned components */ + /* common... */ + .fc-bg, +.fc-bgevent-skeleton, +.fc-highlight-skeleton, +.fc-mirror-skeleton, +.fc-bgevent-container, +.fc-business-container, +.fc-highlight-container, +.fc-mirror-container { + display: none; + } + + /* don't force a min-height on rows (for DayGrid) */ + .fc tbody .fc-row { + height: auto !important; + /* undo height that JS set in distributeHeight */ + min-height: 0 !important; + /* undo the min-height from each view's specific stylesheet */ + } + + .fc tbody .fc-row .fc-content-skeleton { + position: static; + /* undo .fc-rigid */ + padding-bottom: 0 !important; + /* use a more border-friendly method for this... */ + } + + .fc tbody .fc-row .fc-content-skeleton tbody tr:last-child td { + /* only works in newer browsers */ + padding-bottom: 1em; + /* ...gives space within the skeleton. also ensures min height in a way */ + } + + .fc tbody .fc-row .fc-content-skeleton table { + /* provides a min-height for the row, but only effective for IE, which exaggerates this value, + making it look more like 3em. for other browers, it will already be this tall */ + height: 1em; + } + + /* Undo month-view event limiting. Display all events and hide the "more" links + --------------------------------------------------------------------------------------------------*/ + .fc-more-cell, +.fc-more { + display: none !important; + } + + .fc tr.fc-limited { + display: table-row !important; + } + + .fc td.fc-limited { + display: table-cell !important; + } + + .fc-popover { + display: none; + /* never display the "more.." popover in print mode */ + } + + /* TimeGrid Restyling + --------------------------------------------------------------------------------------------------*/ + /* undo the min-height 100% trick used to fill the container's height */ + .fc-time-grid { + min-height: 0 !important; + } + + /* don't display the side axis at all ("all-day" and time cells) */ + .fc-timeGrid-view .fc-axis { + display: none; + } + + /* don't display the horizontal lines */ + .fc-slats, +.fc-time-grid hr { + /* this hr is used when height is underused and needs to be filled */ + display: none !important; + /* important overrides inline declaration */ + } + + /* let the container that holds the events be naturally positioned and create real height */ + .fc-time-grid .fc-content-skeleton { + position: static; + } + + /* in case there are no events, we still want some height */ + .fc-time-grid .fc-content-skeleton table { + height: 4em; + } + + /* kill the horizontal spacing made by the event container. event margins will be done below */ + .fc-time-grid .fc-event-container { + margin: 0 !important; + } + + /* TimeGrid *Event* Restyling + --------------------------------------------------------------------------------------------------*/ + /* naturally position events, vertically stacking them */ + .fc-time-grid .fc-event { + position: static !important; + margin: 3px 2px !important; + } + + /* for events that continue to a future day, give the bottom border back */ + .fc-time-grid .fc-event.fc-not-end { + border-bottom-width: 1px !important; + } + + /* indicate the event continues via "..." text */ + .fc-time-grid .fc-event.fc-not-end:after { + content: "..."; + } + + /* for events that are continuations from previous days, give the top border back */ + .fc-time-grid .fc-event.fc-not-start { + border-top-width: 1px !important; + } + + /* indicate the event is a continuation via "..." text */ + .fc-time-grid .fc-event.fc-not-start:before { + content: "..."; + } + + /* time */ + /* undo a previous declaration and let the time text span to a second line */ + .fc-time-grid .fc-event .fc-time { + white-space: normal !important; + } + + /* hide the the time that is normally displayed... */ + .fc-time-grid .fc-event .fc-time span { + display: none; + } + + /* ...replace it with a more verbose version (includes AM/PM) stored in an html attribute */ + .fc-time-grid .fc-event .fc-time:after { + content: attr(data-full); + } + + /* Vertical Scroller & Containers + --------------------------------------------------------------------------------------------------*/ + /* kill the scrollbars and allow natural height */ + .fc-scroller, +.fc-day-grid-container, +.fc-time-grid-container { + /* */ + overflow: visible !important; + height: auto !important; + } + + /* kill the horizontal border/padding used to compensate for scrollbars */ + .fc-row { + border: 0 !important; + margin: 0 !important; + } + + /* Button Controls + --------------------------------------------------------------------------------------------------*/ + .fc-button-group, +.fc button { + display: none; + /* don't display any button-related controls */ + } +} diff --git a/static/assets/libs/fullcalendar/core/main.js b/static/assets/libs/fullcalendar/core/main.js new file mode 100644 index 0000000..ec0808d --- /dev/null +++ b/static/assets/libs/fullcalendar/core/main.js @@ -0,0 +1,8717 @@ +/*! +FullCalendar Core Package v4.3.1 +Docs & License: https://fullcalendar.io/ +(c) 2019 Adam Shaw +*/ + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = global || self, factory(global.FullCalendar = {})); +}(this, function (exports) { 'use strict'; + + // Creating + // ---------------------------------------------------------------------------------------------------------------- + var elementPropHash = { + className: true, + colSpan: true, + rowSpan: true + }; + var containerTagHash = { + '= rect.left && + point.left < rect.right && + point.top >= rect.top && + point.top < rect.bottom; + } + // Returns a new rectangle that is the intersection of the two rectangles. If they don't intersect, returns false + function intersectRects(rect1, rect2) { + var res = { + left: Math.max(rect1.left, rect2.left), + right: Math.min(rect1.right, rect2.right), + top: Math.max(rect1.top, rect2.top), + bottom: Math.min(rect1.bottom, rect2.bottom) + }; + if (res.left < res.right && res.top < res.bottom) { + return res; + } + return false; + } + function translateRect(rect, deltaX, deltaY) { + return { + left: rect.left + deltaX, + right: rect.right + deltaX, + top: rect.top + deltaY, + bottom: rect.bottom + deltaY + }; + } + // Returns a new point that will have been moved to reside within the given rectangle + function constrainPoint(point, rect) { + return { + left: Math.min(Math.max(point.left, rect.left), rect.right), + top: Math.min(Math.max(point.top, rect.top), rect.bottom) + }; + } + // Returns a point that is the center of the given rectangle + function getRectCenter(rect) { + return { + left: (rect.left + rect.right) / 2, + top: (rect.top + rect.bottom) / 2 + }; + } + // Subtracts point2's coordinates from point1's coordinates, returning a delta + function diffPoints(point1, point2) { + return { + left: point1.left - point2.left, + top: point1.top - point2.top + }; + } + + // Logic for determining if, when the element is right-to-left, the scrollbar appears on the left side + var isRtlScrollbarOnLeft = null; + function getIsRtlScrollbarOnLeft() { + if (isRtlScrollbarOnLeft === null) { + isRtlScrollbarOnLeft = computeIsRtlScrollbarOnLeft(); + } + return isRtlScrollbarOnLeft; + } + function computeIsRtlScrollbarOnLeft() { + var outerEl = createElement('div', { + style: { + position: 'absolute', + top: -1000, + left: 0, + border: 0, + padding: 0, + overflow: 'scroll', + direction: 'rtl' + } + }, '
'); + document.body.appendChild(outerEl); + var innerEl = outerEl.firstChild; + var res = innerEl.getBoundingClientRect().left > outerEl.getBoundingClientRect().left; + removeElement(outerEl); + return res; + } + // The scrollbar width computations in computeEdges are sometimes flawed when it comes to + // retina displays, rounding, and IE11. Massage them into a usable value. + function sanitizeScrollbarWidth(width) { + width = Math.max(0, width); // no negatives + width = Math.round(width); + return width; + } + + function computeEdges(el, getPadding) { + if (getPadding === void 0) { getPadding = false; } + var computedStyle = window.getComputedStyle(el); + var borderLeft = parseInt(computedStyle.borderLeftWidth, 10) || 0; + var borderRight = parseInt(computedStyle.borderRightWidth, 10) || 0; + var borderTop = parseInt(computedStyle.borderTopWidth, 10) || 0; + var borderBottom = parseInt(computedStyle.borderBottomWidth, 10) || 0; + // must use offset(Width|Height) because compatible with client(Width|Height) + var scrollbarLeftRight = sanitizeScrollbarWidth(el.offsetWidth - el.clientWidth - borderLeft - borderRight); + var scrollbarBottom = sanitizeScrollbarWidth(el.offsetHeight - el.clientHeight - borderTop - borderBottom); + var res = { + borderLeft: borderLeft, + borderRight: borderRight, + borderTop: borderTop, + borderBottom: borderBottom, + scrollbarBottom: scrollbarBottom, + scrollbarLeft: 0, + scrollbarRight: 0 + }; + if (getIsRtlScrollbarOnLeft() && computedStyle.direction === 'rtl') { // is the scrollbar on the left side? + res.scrollbarLeft = scrollbarLeftRight; + } + else { + res.scrollbarRight = scrollbarLeftRight; + } + if (getPadding) { + res.paddingLeft = parseInt(computedStyle.paddingLeft, 10) || 0; + res.paddingRight = parseInt(computedStyle.paddingRight, 10) || 0; + res.paddingTop = parseInt(computedStyle.paddingTop, 10) || 0; + res.paddingBottom = parseInt(computedStyle.paddingBottom, 10) || 0; + } + return res; + } + function computeInnerRect(el, goWithinPadding) { + if (goWithinPadding === void 0) { goWithinPadding = false; } + var outerRect = computeRect(el); + var edges = computeEdges(el, goWithinPadding); + var res = { + left: outerRect.left + edges.borderLeft + edges.scrollbarLeft, + right: outerRect.right - edges.borderRight - edges.scrollbarRight, + top: outerRect.top + edges.borderTop, + bottom: outerRect.bottom - edges.borderBottom - edges.scrollbarBottom + }; + if (goWithinPadding) { + res.left += edges.paddingLeft; + res.right -= edges.paddingRight; + res.top += edges.paddingTop; + res.bottom -= edges.paddingBottom; + } + return res; + } + function computeRect(el) { + var rect = el.getBoundingClientRect(); + return { + left: rect.left + window.pageXOffset, + top: rect.top + window.pageYOffset, + right: rect.right + window.pageXOffset, + bottom: rect.bottom + window.pageYOffset + }; + } + function computeViewportRect() { + return { + left: window.pageXOffset, + right: window.pageXOffset + document.documentElement.clientWidth, + top: window.pageYOffset, + bottom: window.pageYOffset + document.documentElement.clientHeight + }; + } + function computeHeightAndMargins(el) { + return el.getBoundingClientRect().height + computeVMargins(el); + } + function computeVMargins(el) { + var computed = window.getComputedStyle(el); + return parseInt(computed.marginTop, 10) + + parseInt(computed.marginBottom, 10); + } + // does not return window + function getClippingParents(el) { + var parents = []; + while (el instanceof HTMLElement) { // will stop when gets to document or null + var computedStyle = window.getComputedStyle(el); + if (computedStyle.position === 'fixed') { + break; + } + if ((/(auto|scroll)/).test(computedStyle.overflow + computedStyle.overflowY + computedStyle.overflowX)) { + parents.push(el); + } + el = el.parentNode; + } + return parents; + } + function computeClippingRect(el) { + return getClippingParents(el) + .map(function (el) { + return computeInnerRect(el); + }) + .concat(computeViewportRect()) + .reduce(function (rect0, rect1) { + return intersectRects(rect0, rect1) || rect1; // should always intersect + }); + } + + // Stops a mouse/touch event from doing it's native browser action + function preventDefault(ev) { + ev.preventDefault(); + } + // Event Delegation + // ---------------------------------------------------------------------------------------------------------------- + function listenBySelector(container, eventType, selector, handler) { + function realHandler(ev) { + var matchedChild = elementClosest(ev.target, selector); + if (matchedChild) { + handler.call(matchedChild, ev, matchedChild); + } + } + container.addEventListener(eventType, realHandler); + return function () { + container.removeEventListener(eventType, realHandler); + }; + } + function listenToHoverBySelector(container, selector, onMouseEnter, onMouseLeave) { + var currentMatchedChild; + return listenBySelector(container, 'mouseover', selector, function (ev, matchedChild) { + if (matchedChild !== currentMatchedChild) { + currentMatchedChild = matchedChild; + onMouseEnter(ev, matchedChild); + var realOnMouseLeave_1 = function (ev) { + currentMatchedChild = null; + onMouseLeave(ev, matchedChild); + matchedChild.removeEventListener('mouseleave', realOnMouseLeave_1); + }; + // listen to the next mouseleave, and then unattach + matchedChild.addEventListener('mouseleave', realOnMouseLeave_1); + } + }); + } + // Animation + // ---------------------------------------------------------------------------------------------------------------- + var transitionEventNames = [ + 'webkitTransitionEnd', + 'otransitionend', + 'oTransitionEnd', + 'msTransitionEnd', + 'transitionend' + ]; + // triggered only when the next single subsequent transition finishes + function whenTransitionDone(el, callback) { + var realCallback = function (ev) { + callback(ev); + transitionEventNames.forEach(function (eventName) { + el.removeEventListener(eventName, realCallback); + }); + }; + transitionEventNames.forEach(function (eventName) { + el.addEventListener(eventName, realCallback); // cross-browser way to determine when the transition finishes + }); + } + + var DAY_IDS = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; + // Adding + function addWeeks(m, n) { + var a = dateToUtcArray(m); + a[2] += n * 7; + return arrayToUtcDate(a); + } + function addDays(m, n) { + var a = dateToUtcArray(m); + a[2] += n; + return arrayToUtcDate(a); + } + function addMs(m, n) { + var a = dateToUtcArray(m); + a[6] += n; + return arrayToUtcDate(a); + } + // Diffing (all return floats) + function diffWeeks(m0, m1) { + return diffDays(m0, m1) / 7; + } + function diffDays(m0, m1) { + return (m1.valueOf() - m0.valueOf()) / (1000 * 60 * 60 * 24); + } + function diffHours(m0, m1) { + return (m1.valueOf() - m0.valueOf()) / (1000 * 60 * 60); + } + function diffMinutes(m0, m1) { + return (m1.valueOf() - m0.valueOf()) / (1000 * 60); + } + function diffSeconds(m0, m1) { + return (m1.valueOf() - m0.valueOf()) / 1000; + } + function diffDayAndTime(m0, m1) { + var m0day = startOfDay(m0); + var m1day = startOfDay(m1); + return { + years: 0, + months: 0, + days: Math.round(diffDays(m0day, m1day)), + milliseconds: (m1.valueOf() - m1day.valueOf()) - (m0.valueOf() - m0day.valueOf()) + }; + } + // Diffing Whole Units + function diffWholeWeeks(m0, m1) { + var d = diffWholeDays(m0, m1); + if (d !== null && d % 7 === 0) { + return d / 7; + } + return null; + } + function diffWholeDays(m0, m1) { + if (timeAsMs(m0) === timeAsMs(m1)) { + return Math.round(diffDays(m0, m1)); + } + return null; + } + // Start-Of + function startOfDay(m) { + return arrayToUtcDate([ + m.getUTCFullYear(), + m.getUTCMonth(), + m.getUTCDate() + ]); + } + function startOfHour(m) { + return arrayToUtcDate([ + m.getUTCFullYear(), + m.getUTCMonth(), + m.getUTCDate(), + m.getUTCHours() + ]); + } + function startOfMinute(m) { + return arrayToUtcDate([ + m.getUTCFullYear(), + m.getUTCMonth(), + m.getUTCDate(), + m.getUTCHours(), + m.getUTCMinutes() + ]); + } + function startOfSecond(m) { + return arrayToUtcDate([ + m.getUTCFullYear(), + m.getUTCMonth(), + m.getUTCDate(), + m.getUTCHours(), + m.getUTCMinutes(), + m.getUTCSeconds() + ]); + } + // Week Computation + function weekOfYear(marker, dow, doy) { + var y = marker.getUTCFullYear(); + var w = weekOfGivenYear(marker, y, dow, doy); + if (w < 1) { + return weekOfGivenYear(marker, y - 1, dow, doy); + } + var nextW = weekOfGivenYear(marker, y + 1, dow, doy); + if (nextW >= 1) { + return Math.min(w, nextW); + } + return w; + } + function weekOfGivenYear(marker, year, dow, doy) { + var firstWeekStart = arrayToUtcDate([year, 0, 1 + firstWeekOffset(year, dow, doy)]); + var dayStart = startOfDay(marker); + var days = Math.round(diffDays(firstWeekStart, dayStart)); + return Math.floor(days / 7) + 1; // zero-indexed + } + // start-of-first-week - start-of-year + function firstWeekOffset(year, dow, doy) { + // first-week day -- which january is always in the first week (4 for iso, 1 for other) + var fwd = 7 + dow - doy; + // first-week day local weekday -- which local weekday is fwd + var fwdlw = (7 + arrayToUtcDate([year, 0, fwd]).getUTCDay() - dow) % 7; + return -fwdlw + fwd - 1; + } + // Array Conversion + function dateToLocalArray(date) { + return [ + date.getFullYear(), + date.getMonth(), + date.getDate(), + date.getHours(), + date.getMinutes(), + date.getSeconds(), + date.getMilliseconds() + ]; + } + function arrayToLocalDate(a) { + return new Date(a[0], a[1] || 0, a[2] == null ? 1 : a[2], // day of month + a[3] || 0, a[4] || 0, a[5] || 0); + } + function dateToUtcArray(date) { + return [ + date.getUTCFullYear(), + date.getUTCMonth(), + date.getUTCDate(), + date.getUTCHours(), + date.getUTCMinutes(), + date.getUTCSeconds(), + date.getUTCMilliseconds() + ]; + } + function arrayToUtcDate(a) { + // according to web standards (and Safari), a month index is required. + // massage if only given a year. + if (a.length === 1) { + a = a.concat([0]); + } + return new Date(Date.UTC.apply(Date, a)); + } + // Other Utils + function isValidDate(m) { + return !isNaN(m.valueOf()); + } + function timeAsMs(m) { + return m.getUTCHours() * 1000 * 60 * 60 + + m.getUTCMinutes() * 1000 * 60 + + m.getUTCSeconds() * 1000 + + m.getUTCMilliseconds(); + } + + var INTERNAL_UNITS = ['years', 'months', 'days', 'milliseconds']; + var PARSE_RE = /^(-?)(?:(\d+)\.)?(\d+):(\d\d)(?::(\d\d)(?:\.(\d\d\d))?)?/; + // Parsing and Creation + function createDuration(input, unit) { + var _a; + if (typeof input === 'string') { + return parseString(input); + } + else if (typeof input === 'object' && input) { // non-null object + return normalizeObject(input); + } + else if (typeof input === 'number') { + return normalizeObject((_a = {}, _a[unit || 'milliseconds'] = input, _a)); + } + else { + return null; + } + } + function parseString(s) { + var m = PARSE_RE.exec(s); + if (m) { + var sign = m[1] ? -1 : 1; + return { + years: 0, + months: 0, + days: sign * (m[2] ? parseInt(m[2], 10) : 0), + milliseconds: sign * ((m[3] ? parseInt(m[3], 10) : 0) * 60 * 60 * 1000 + // hours + (m[4] ? parseInt(m[4], 10) : 0) * 60 * 1000 + // minutes + (m[5] ? parseInt(m[5], 10) : 0) * 1000 + // seconds + (m[6] ? parseInt(m[6], 10) : 0) // ms + ) + }; + } + return null; + } + function normalizeObject(obj) { + return { + years: obj.years || obj.year || 0, + months: obj.months || obj.month || 0, + days: (obj.days || obj.day || 0) + + getWeeksFromInput(obj) * 7, + milliseconds: (obj.hours || obj.hour || 0) * 60 * 60 * 1000 + // hours + (obj.minutes || obj.minute || 0) * 60 * 1000 + // minutes + (obj.seconds || obj.second || 0) * 1000 + // seconds + (obj.milliseconds || obj.millisecond || obj.ms || 0) // ms + }; + } + function getWeeksFromInput(obj) { + return obj.weeks || obj.week || 0; + } + // Equality + function durationsEqual(d0, d1) { + return d0.years === d1.years && + d0.months === d1.months && + d0.days === d1.days && + d0.milliseconds === d1.milliseconds; + } + function isSingleDay(dur) { + return dur.years === 0 && dur.months === 0 && dur.days === 1 && dur.milliseconds === 0; + } + // Simple Math + function addDurations(d0, d1) { + return { + years: d0.years + d1.years, + months: d0.months + d1.months, + days: d0.days + d1.days, + milliseconds: d0.milliseconds + d1.milliseconds + }; + } + function subtractDurations(d1, d0) { + return { + years: d1.years - d0.years, + months: d1.months - d0.months, + days: d1.days - d0.days, + milliseconds: d1.milliseconds - d0.milliseconds + }; + } + function multiplyDuration(d, n) { + return { + years: d.years * n, + months: d.months * n, + days: d.days * n, + milliseconds: d.milliseconds * n + }; + } + // Conversions + // "Rough" because they are based on average-case Gregorian months/years + function asRoughYears(dur) { + return asRoughDays(dur) / 365; + } + function asRoughMonths(dur) { + return asRoughDays(dur) / 30; + } + function asRoughDays(dur) { + return asRoughMs(dur) / 864e5; + } + function asRoughMinutes(dur) { + return asRoughMs(dur) / (1000 * 60); + } + function asRoughSeconds(dur) { + return asRoughMs(dur) / 1000; + } + function asRoughMs(dur) { + return dur.years * (365 * 864e5) + + dur.months * (30 * 864e5) + + dur.days * 864e5 + + dur.milliseconds; + } + // Advanced Math + function wholeDivideDurations(numerator, denominator) { + var res = null; + for (var i = 0; i < INTERNAL_UNITS.length; i++) { + var unit = INTERNAL_UNITS[i]; + if (denominator[unit]) { + var localRes = numerator[unit] / denominator[unit]; + if (!isInt(localRes) || (res !== null && res !== localRes)) { + return null; + } + res = localRes; + } + else if (numerator[unit]) { + // needs to divide by something but can't! + return null; + } + } + return res; + } + function greatestDurationDenominator(dur, dontReturnWeeks) { + var ms = dur.milliseconds; + if (ms) { + if (ms % 1000 !== 0) { + return { unit: 'millisecond', value: ms }; + } + if (ms % (1000 * 60) !== 0) { + return { unit: 'second', value: ms / 1000 }; + } + if (ms % (1000 * 60 * 60) !== 0) { + return { unit: 'minute', value: ms / (1000 * 60) }; + } + if (ms) { + return { unit: 'hour', value: ms / (1000 * 60 * 60) }; + } + } + if (dur.days) { + if (!dontReturnWeeks && dur.days % 7 === 0) { + return { unit: 'week', value: dur.days / 7 }; + } + return { unit: 'day', value: dur.days }; + } + if (dur.months) { + return { unit: 'month', value: dur.months }; + } + if (dur.years) { + return { unit: 'year', value: dur.years }; + } + return { unit: 'millisecond', value: 0 }; + } + + /* FullCalendar-specific DOM Utilities + ----------------------------------------------------------------------------------------------------------------------*/ + // Given the scrollbar widths of some other container, create borders/margins on rowEls in order to match the left + // and right space that was offset by the scrollbars. A 1-pixel border first, then margin beyond that. + function compensateScroll(rowEl, scrollbarWidths) { + if (scrollbarWidths.left) { + applyStyle(rowEl, { + borderLeftWidth: 1, + marginLeft: scrollbarWidths.left - 1 + }); + } + if (scrollbarWidths.right) { + applyStyle(rowEl, { + borderRightWidth: 1, + marginRight: scrollbarWidths.right - 1 + }); + } + } + // Undoes compensateScroll and restores all borders/margins + function uncompensateScroll(rowEl) { + applyStyle(rowEl, { + marginLeft: '', + marginRight: '', + borderLeftWidth: '', + borderRightWidth: '' + }); + } + // Make the mouse cursor express that an event is not allowed in the current area + function disableCursor() { + document.body.classList.add('fc-not-allowed'); + } + // Returns the mouse cursor to its original look + function enableCursor() { + document.body.classList.remove('fc-not-allowed'); + } + // Given a total available height to fill, have `els` (essentially child rows) expand to accomodate. + // By default, all elements that are shorter than the recommended height are expanded uniformly, not considering + // any other els that are already too tall. if `shouldRedistribute` is on, it considers these tall rows and + // reduces the available height. + function distributeHeight(els, availableHeight, shouldRedistribute) { + // *FLOORING NOTE*: we floor in certain places because zoom can give inaccurate floating-point dimensions, + // and it is better to be shorter than taller, to avoid creating unnecessary scrollbars. + var minOffset1 = Math.floor(availableHeight / els.length); // for non-last element + var minOffset2 = Math.floor(availableHeight - minOffset1 * (els.length - 1)); // for last element *FLOORING NOTE* + var flexEls = []; // elements that are allowed to expand. array of DOM nodes + var flexOffsets = []; // amount of vertical space it takes up + var flexHeights = []; // actual css height + var usedHeight = 0; + undistributeHeight(els); // give all elements their natural height + // find elements that are below the recommended height (expandable). + // important to query for heights in a single first pass (to avoid reflow oscillation). + els.forEach(function (el, i) { + var minOffset = i === els.length - 1 ? minOffset2 : minOffset1; + var naturalHeight = el.getBoundingClientRect().height; + var naturalOffset = naturalHeight + computeVMargins(el); + if (naturalOffset < minOffset) { + flexEls.push(el); + flexOffsets.push(naturalOffset); + flexHeights.push(naturalHeight); + } + else { + // this element stretches past recommended height (non-expandable). mark the space as occupied. + usedHeight += naturalOffset; + } + }); + // readjust the recommended height to only consider the height available to non-maxed-out rows. + if (shouldRedistribute) { + availableHeight -= usedHeight; + minOffset1 = Math.floor(availableHeight / flexEls.length); + minOffset2 = Math.floor(availableHeight - minOffset1 * (flexEls.length - 1)); // *FLOORING NOTE* + } + // assign heights to all expandable elements + flexEls.forEach(function (el, i) { + var minOffset = i === flexEls.length - 1 ? minOffset2 : minOffset1; + var naturalOffset = flexOffsets[i]; + var naturalHeight = flexHeights[i]; + var newHeight = minOffset - (naturalOffset - naturalHeight); // subtract the margin/padding + if (naturalOffset < minOffset) { // we check this again because redistribution might have changed things + el.style.height = newHeight + 'px'; + } + }); + } + // Undoes distrubuteHeight, restoring all els to their natural height + function undistributeHeight(els) { + els.forEach(function (el) { + el.style.height = ''; + }); + } + // Given `els`, a set of cells, find the cell with the largest natural width and set the widths of all the + // cells to be that width. + // PREREQUISITE: if you want a cell to take up width, it needs to have a single inner element w/ display:inline + function matchCellWidths(els) { + var maxInnerWidth = 0; + els.forEach(function (el) { + var innerEl = el.firstChild; // hopefully an element + if (innerEl instanceof HTMLElement) { + var innerWidth_1 = innerEl.getBoundingClientRect().width; + if (innerWidth_1 > maxInnerWidth) { + maxInnerWidth = innerWidth_1; + } + } + }); + maxInnerWidth++; // sometimes not accurate of width the text needs to stay on one line. insurance + els.forEach(function (el) { + el.style.width = maxInnerWidth + 'px'; + }); + return maxInnerWidth; + } + // Given one element that resides inside another, + // Subtracts the height of the inner element from the outer element. + function subtractInnerElHeight(outerEl, innerEl) { + // effin' IE8/9/10/11 sometimes returns 0 for dimensions. this weird hack was the only thing that worked + var reflowStyleProps = { + position: 'relative', + left: -1 // ensure reflow in case the el was already relative. negative is less likely to cause new scroll + }; + applyStyle(outerEl, reflowStyleProps); + applyStyle(innerEl, reflowStyleProps); + var diff = // grab the dimensions + outerEl.getBoundingClientRect().height - + innerEl.getBoundingClientRect().height; + // undo hack + var resetStyleProps = { position: '', left: '' }; + applyStyle(outerEl, resetStyleProps); + applyStyle(innerEl, resetStyleProps); + return diff; + } + /* Selection + ----------------------------------------------------------------------------------------------------------------------*/ + function preventSelection(el) { + el.classList.add('fc-unselectable'); + el.addEventListener('selectstart', preventDefault); + } + function allowSelection(el) { + el.classList.remove('fc-unselectable'); + el.removeEventListener('selectstart', preventDefault); + } + /* Context Menu + ----------------------------------------------------------------------------------------------------------------------*/ + function preventContextMenu(el) { + el.addEventListener('contextmenu', preventDefault); + } + function allowContextMenu(el) { + el.removeEventListener('contextmenu', preventDefault); + } + /* Object Ordering by Field + ----------------------------------------------------------------------------------------------------------------------*/ + function parseFieldSpecs(input) { + var specs = []; + var tokens = []; + var i; + var token; + if (typeof input === 'string') { + tokens = input.split(/\s*,\s*/); + } + else if (typeof input === 'function') { + tokens = [input]; + } + else if (Array.isArray(input)) { + tokens = input; + } + for (i = 0; i < tokens.length; i++) { + token = tokens[i]; + if (typeof token === 'string') { + specs.push(token.charAt(0) === '-' ? + { field: token.substring(1), order: -1 } : + { field: token, order: 1 }); + } + else if (typeof token === 'function') { + specs.push({ func: token }); + } + } + return specs; + } + function compareByFieldSpecs(obj0, obj1, fieldSpecs) { + var i; + var cmp; + for (i = 0; i < fieldSpecs.length; i++) { + cmp = compareByFieldSpec(obj0, obj1, fieldSpecs[i]); + if (cmp) { + return cmp; + } + } + return 0; + } + function compareByFieldSpec(obj0, obj1, fieldSpec) { + if (fieldSpec.func) { + return fieldSpec.func(obj0, obj1); + } + return flexibleCompare(obj0[fieldSpec.field], obj1[fieldSpec.field]) + * (fieldSpec.order || 1); + } + function flexibleCompare(a, b) { + if (!a && !b) { + return 0; + } + if (b == null) { + return -1; + } + if (a == null) { + return 1; + } + if (typeof a === 'string' || typeof b === 'string') { + return String(a).localeCompare(String(b)); + } + return a - b; + } + /* String Utilities + ----------------------------------------------------------------------------------------------------------------------*/ + function capitaliseFirstLetter(str) { + return str.charAt(0).toUpperCase() + str.slice(1); + } + function padStart(val, len) { + var s = String(val); + return '000'.substr(0, len - s.length) + s; + } + /* Number Utilities + ----------------------------------------------------------------------------------------------------------------------*/ + function compareNumbers(a, b) { + return a - b; + } + function isInt(n) { + return n % 1 === 0; + } + /* Weird Utilities + ----------------------------------------------------------------------------------------------------------------------*/ + function applyAll(functions, thisObj, args) { + if (typeof functions === 'function') { // supplied a single function + functions = [functions]; + } + if (functions) { + var i = void 0; + var ret = void 0; + for (i = 0; i < functions.length; i++) { + ret = functions[i].apply(thisObj, args) || ret; + } + return ret; + } + } + function firstDefined() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + for (var i = 0; i < args.length; i++) { + if (args[i] !== undefined) { + return args[i]; + } + } + } + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + // https://github.com/jashkenas/underscore/blob/1.6.0/underscore.js#L714 + function debounce(func, wait) { + var timeout; + var args; + var context; + var timestamp; + var result; + var later = function () { + var last = new Date().valueOf() - timestamp; + if (last < wait) { + timeout = setTimeout(later, wait - last); + } + else { + timeout = null; + result = func.apply(context, args); + context = args = null; + } + }; + return function () { + context = this; + args = arguments; + timestamp = new Date().valueOf(); + if (!timeout) { + timeout = setTimeout(later, wait); + } + return result; + }; + } + // Number and Boolean are only types that defaults or not computed for + // TODO: write more comments + function refineProps(rawProps, processors, defaults, leftoverProps) { + if (defaults === void 0) { defaults = {}; } + var refined = {}; + for (var key in processors) { + var processor = processors[key]; + if (rawProps[key] !== undefined) { + // found + if (processor === Function) { + refined[key] = typeof rawProps[key] === 'function' ? rawProps[key] : null; + } + else if (processor) { // a refining function? + refined[key] = processor(rawProps[key]); + } + else { + refined[key] = rawProps[key]; + } + } + else if (defaults[key] !== undefined) { + // there's an explicit default + refined[key] = defaults[key]; + } + else { + // must compute a default + if (processor === String) { + refined[key] = ''; // empty string is default for String + } + else if (!processor || processor === Number || processor === Boolean || processor === Function) { + refined[key] = null; // assign null for other non-custom processor funcs + } + else { + refined[key] = processor(null); // run the custom processor func + } + } + } + if (leftoverProps) { + for (var key in rawProps) { + if (processors[key] === undefined) { + leftoverProps[key] = rawProps[key]; + } + } + } + return refined; + } + /* Date stuff that doesn't belong in datelib core + ----------------------------------------------------------------------------------------------------------------------*/ + // given a timed range, computes an all-day range that has the same exact duration, + // but whose start time is aligned with the start of the day. + function computeAlignedDayRange(timedRange) { + var dayCnt = Math.floor(diffDays(timedRange.start, timedRange.end)) || 1; + var start = startOfDay(timedRange.start); + var end = addDays(start, dayCnt); + return { start: start, end: end }; + } + // given a timed range, computes an all-day range based on how for the end date bleeds into the next day + // TODO: give nextDayThreshold a default arg + function computeVisibleDayRange(timedRange, nextDayThreshold) { + if (nextDayThreshold === void 0) { nextDayThreshold = createDuration(0); } + var startDay = null; + var endDay = null; + if (timedRange.end) { + endDay = startOfDay(timedRange.end); + var endTimeMS = timedRange.end.valueOf() - endDay.valueOf(); // # of milliseconds into `endDay` + // If the end time is actually inclusively part of the next day and is equal to or + // beyond the next day threshold, adjust the end to be the exclusive end of `endDay`. + // Otherwise, leaving it as inclusive will cause it to exclude `endDay`. + if (endTimeMS && endTimeMS >= asRoughMs(nextDayThreshold)) { + endDay = addDays(endDay, 1); + } + } + if (timedRange.start) { + startDay = startOfDay(timedRange.start); // the beginning of the day the range starts + // If end is within `startDay` but not past nextDayThreshold, assign the default duration of one day. + if (endDay && endDay <= startDay) { + endDay = addDays(startDay, 1); + } + } + return { start: startDay, end: endDay }; + } + // spans from one day into another? + function isMultiDayRange(range) { + var visibleRange = computeVisibleDayRange(range); + return diffDays(visibleRange.start, visibleRange.end) > 1; + } + function diffDates(date0, date1, dateEnv, largeUnit) { + if (largeUnit === 'year') { + return createDuration(dateEnv.diffWholeYears(date0, date1), 'year'); + } + else if (largeUnit === 'month') { + return createDuration(dateEnv.diffWholeMonths(date0, date1), 'month'); + } + else { + return diffDayAndTime(date0, date1); // returns a duration + } + } + + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use + this file except in compliance with the License. You may obtain a copy of the + License at http://www.apache.org/licenses/LICENSE-2.0 + + THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED + WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, + MERCHANTABLITY OR NON-INFRINGEMENT. + + See the Apache Version 2.0 License for specific language governing permissions + and limitations under the License. + ***************************************************************************** */ + /* global Reflect, Promise */ + + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + + function __extends(d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + } + + var __assign = function() { + __assign = Object.assign || function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); + }; + + function parseRecurring(eventInput, allDayDefault, dateEnv, recurringTypes, leftovers) { + for (var i = 0; i < recurringTypes.length; i++) { + var localLeftovers = {}; + var parsed = recurringTypes[i].parse(eventInput, localLeftovers, dateEnv); + if (parsed) { + var allDay = localLeftovers.allDay; + delete localLeftovers.allDay; // remove from leftovers + if (allDay == null) { + allDay = allDayDefault; + if (allDay == null) { + allDay = parsed.allDayGuess; + if (allDay == null) { + allDay = false; + } + } + } + __assign(leftovers, localLeftovers); + return { + allDay: allDay, + duration: parsed.duration, + typeData: parsed.typeData, + typeId: i + }; + } + } + return null; + } + /* + Event MUST have a recurringDef + */ + function expandRecurringRanges(eventDef, duration, framingRange, dateEnv, recurringTypes) { + var typeDef = recurringTypes[eventDef.recurringDef.typeId]; + var markers = typeDef.expand(eventDef.recurringDef.typeData, { + start: dateEnv.subtract(framingRange.start, duration), + end: framingRange.end + }, dateEnv); + // the recurrence plugins don't guarantee that all-day events are start-of-day, so we have to + if (eventDef.allDay) { + markers = markers.map(startOfDay); + } + return markers; + } + + var hasOwnProperty = Object.prototype.hasOwnProperty; + // Merges an array of objects into a single object. + // The second argument allows for an array of property names who's object values will be merged together. + function mergeProps(propObjs, complexProps) { + var dest = {}; + var i; + var name; + var complexObjs; + var j; + var val; + var props; + if (complexProps) { + for (i = 0; i < complexProps.length; i++) { + name = complexProps[i]; + complexObjs = []; + // collect the trailing object values, stopping when a non-object is discovered + for (j = propObjs.length - 1; j >= 0; j--) { + val = propObjs[j][name]; + if (typeof val === 'object' && val) { // non-null object + complexObjs.unshift(val); + } + else if (val !== undefined) { + dest[name] = val; // if there were no objects, this value will be used + break; + } + } + // if the trailing values were objects, use the merged value + if (complexObjs.length) { + dest[name] = mergeProps(complexObjs); + } + } + } + // copy values into the destination, going from last to first + for (i = propObjs.length - 1; i >= 0; i--) { + props = propObjs[i]; + for (name in props) { + if (!(name in dest)) { // if already assigned by previous props or complex props, don't reassign + dest[name] = props[name]; + } + } + } + return dest; + } + function filterHash(hash, func) { + var filtered = {}; + for (var key in hash) { + if (func(hash[key], key)) { + filtered[key] = hash[key]; + } + } + return filtered; + } + function mapHash(hash, func) { + var newHash = {}; + for (var key in hash) { + newHash[key] = func(hash[key], key); + } + return newHash; + } + function arrayToHash(a) { + var hash = {}; + for (var _i = 0, a_1 = a; _i < a_1.length; _i++) { + var item = a_1[_i]; + hash[item] = true; + } + return hash; + } + function hashValuesToArray(obj) { + var a = []; + for (var key in obj) { + a.push(obj[key]); + } + return a; + } + function isPropsEqual(obj0, obj1) { + for (var key in obj0) { + if (hasOwnProperty.call(obj0, key)) { + if (!(key in obj1)) { + return false; + } + } + } + for (var key in obj1) { + if (hasOwnProperty.call(obj1, key)) { + if (obj0[key] !== obj1[key]) { + return false; + } + } + } + return true; + } + + function parseEvents(rawEvents, sourceId, calendar, allowOpenRange) { + var eventStore = createEmptyEventStore(); + for (var _i = 0, rawEvents_1 = rawEvents; _i < rawEvents_1.length; _i++) { + var rawEvent = rawEvents_1[_i]; + var tuple = parseEvent(rawEvent, sourceId, calendar, allowOpenRange); + if (tuple) { + eventTupleToStore(tuple, eventStore); + } + } + return eventStore; + } + function eventTupleToStore(tuple, eventStore) { + if (eventStore === void 0) { eventStore = createEmptyEventStore(); } + eventStore.defs[tuple.def.defId] = tuple.def; + if (tuple.instance) { + eventStore.instances[tuple.instance.instanceId] = tuple.instance; + } + return eventStore; + } + function expandRecurring(eventStore, framingRange, calendar) { + var dateEnv = calendar.dateEnv; + var defs = eventStore.defs, instances = eventStore.instances; + // remove existing recurring instances + instances = filterHash(instances, function (instance) { + return !defs[instance.defId].recurringDef; + }); + for (var defId in defs) { + var def = defs[defId]; + if (def.recurringDef) { + var duration = def.recurringDef.duration; + if (!duration) { + duration = def.allDay ? + calendar.defaultAllDayEventDuration : + calendar.defaultTimedEventDuration; + } + var starts = expandRecurringRanges(def, duration, framingRange, calendar.dateEnv, calendar.pluginSystem.hooks.recurringTypes); + for (var _i = 0, starts_1 = starts; _i < starts_1.length; _i++) { + var start = starts_1[_i]; + var instance = createEventInstance(defId, { + start: start, + end: dateEnv.add(start, duration) + }); + instances[instance.instanceId] = instance; + } + } + } + return { defs: defs, instances: instances }; + } + // retrieves events that have the same groupId as the instance specified by `instanceId` + // or they are the same as the instance. + // why might instanceId not be in the store? an event from another calendar? + function getRelevantEvents(eventStore, instanceId) { + var instance = eventStore.instances[instanceId]; + if (instance) { + var def_1 = eventStore.defs[instance.defId]; + // get events/instances with same group + var newStore = filterEventStoreDefs(eventStore, function (lookDef) { + return isEventDefsGrouped(def_1, lookDef); + }); + // add the original + // TODO: wish we could use eventTupleToStore or something like it + newStore.defs[def_1.defId] = def_1; + newStore.instances[instance.instanceId] = instance; + return newStore; + } + return createEmptyEventStore(); + } + function isEventDefsGrouped(def0, def1) { + return Boolean(def0.groupId && def0.groupId === def1.groupId); + } + function transformRawEvents(rawEvents, eventSource, calendar) { + var calEachTransform = calendar.opt('eventDataTransform'); + var sourceEachTransform = eventSource ? eventSource.eventDataTransform : null; + if (sourceEachTransform) { + rawEvents = transformEachRawEvent(rawEvents, sourceEachTransform); + } + if (calEachTransform) { + rawEvents = transformEachRawEvent(rawEvents, calEachTransform); + } + return rawEvents; + } + function transformEachRawEvent(rawEvents, func) { + var refinedEvents; + if (!func) { + refinedEvents = rawEvents; + } + else { + refinedEvents = []; + for (var _i = 0, rawEvents_2 = rawEvents; _i < rawEvents_2.length; _i++) { + var rawEvent = rawEvents_2[_i]; + var refinedEvent = func(rawEvent); + if (refinedEvent) { + refinedEvents.push(refinedEvent); + } + else if (refinedEvent == null) { + refinedEvents.push(rawEvent); + } // if a different falsy value, do nothing + } + } + return refinedEvents; + } + function createEmptyEventStore() { + return { defs: {}, instances: {} }; + } + function mergeEventStores(store0, store1) { + return { + defs: __assign({}, store0.defs, store1.defs), + instances: __assign({}, store0.instances, store1.instances) + }; + } + function filterEventStoreDefs(eventStore, filterFunc) { + var defs = filterHash(eventStore.defs, filterFunc); + var instances = filterHash(eventStore.instances, function (instance) { + return defs[instance.defId]; // still exists? + }); + return { defs: defs, instances: instances }; + } + + function parseRange(input, dateEnv) { + var start = null; + var end = null; + if (input.start) { + start = dateEnv.createMarker(input.start); + } + if (input.end) { + end = dateEnv.createMarker(input.end); + } + if (!start && !end) { + return null; + } + if (start && end && end < start) { + return null; + } + return { start: start, end: end }; + } + // SIDE-EFFECT: will mutate ranges. + // Will return a new array result. + function invertRanges(ranges, constraintRange) { + var invertedRanges = []; + var start = constraintRange.start; // the end of the previous range. the start of the new range + var i; + var dateRange; + // ranges need to be in order. required for our date-walking algorithm + ranges.sort(compareRanges); + for (i = 0; i < ranges.length; i++) { + dateRange = ranges[i]; + // add the span of time before the event (if there is any) + if (dateRange.start > start) { // compare millisecond time (skip any ambig logic) + invertedRanges.push({ start: start, end: dateRange.start }); + } + if (dateRange.end > start) { + start = dateRange.end; + } + } + // add the span of time after the last event (if there is any) + if (start < constraintRange.end) { // compare millisecond time (skip any ambig logic) + invertedRanges.push({ start: start, end: constraintRange.end }); + } + return invertedRanges; + } + function compareRanges(range0, range1) { + return range0.start.valueOf() - range1.start.valueOf(); // earlier ranges go first + } + function intersectRanges(range0, range1) { + var start = range0.start; + var end = range0.end; + var newRange = null; + if (range1.start !== null) { + if (start === null) { + start = range1.start; + } + else { + start = new Date(Math.max(start.valueOf(), range1.start.valueOf())); + } + } + if (range1.end != null) { + if (end === null) { + end = range1.end; + } + else { + end = new Date(Math.min(end.valueOf(), range1.end.valueOf())); + } + } + if (start === null || end === null || start < end) { + newRange = { start: start, end: end }; + } + return newRange; + } + function rangesEqual(range0, range1) { + return (range0.start === null ? null : range0.start.valueOf()) === (range1.start === null ? null : range1.start.valueOf()) && + (range0.end === null ? null : range0.end.valueOf()) === (range1.end === null ? null : range1.end.valueOf()); + } + function rangesIntersect(range0, range1) { + return (range0.end === null || range1.start === null || range0.end > range1.start) && + (range0.start === null || range1.end === null || range0.start < range1.end); + } + function rangeContainsRange(outerRange, innerRange) { + return (outerRange.start === null || (innerRange.start !== null && innerRange.start >= outerRange.start)) && + (outerRange.end === null || (innerRange.end !== null && innerRange.end <= outerRange.end)); + } + function rangeContainsMarker(range, date) { + return (range.start === null || date >= range.start) && + (range.end === null || date < range.end); + } + // If the given date is not within the given range, move it inside. + // (If it's past the end, make it one millisecond before the end). + function constrainMarkerToRange(date, range) { + if (range.start != null && date < range.start) { + return range.start; + } + if (range.end != null && date >= range.end) { + return new Date(range.end.valueOf() - 1); + } + return date; + } + + function removeExact(array, exactVal) { + var removeCnt = 0; + var i = 0; + while (i < array.length) { + if (array[i] === exactVal) { + array.splice(i, 1); + removeCnt++; + } + else { + i++; + } + } + return removeCnt; + } + function isArraysEqual(a0, a1) { + var len = a0.length; + var i; + if (len !== a1.length) { // not array? or not same length? + return false; + } + for (i = 0; i < len; i++) { + if (a0[i] !== a1[i]) { + return false; + } + } + return true; + } + + function memoize(workerFunc) { + var args; + var res; + return function () { + if (!args || !isArraysEqual(args, arguments)) { + args = arguments; + res = workerFunc.apply(this, arguments); + } + return res; + }; + } + /* + always executes the workerFunc, but if the result is equal to the previous result, + return the previous result instead. + */ + function memoizeOutput(workerFunc, equalityFunc) { + var cachedRes = null; + return function () { + var newRes = workerFunc.apply(this, arguments); + if (cachedRes === null || !(cachedRes === newRes || equalityFunc(cachedRes, newRes))) { + cachedRes = newRes; + } + return cachedRes; + }; + } + + var EXTENDED_SETTINGS_AND_SEVERITIES = { + week: 3, + separator: 0, + omitZeroMinute: 0, + meridiem: 0, + omitCommas: 0 + }; + var STANDARD_DATE_PROP_SEVERITIES = { + timeZoneName: 7, + era: 6, + year: 5, + month: 4, + day: 2, + weekday: 2, + hour: 1, + minute: 1, + second: 1 + }; + var MERIDIEM_RE = /\s*([ap])\.?m\.?/i; // eats up leading spaces too + var COMMA_RE = /,/g; // we need re for globalness + var MULTI_SPACE_RE = /\s+/g; + var LTR_RE = /\u200e/g; // control character + var UTC_RE = /UTC|GMT/; + var NativeFormatter = /** @class */ (function () { + function NativeFormatter(formatSettings) { + var standardDateProps = {}; + var extendedSettings = {}; + var severity = 0; + for (var name_1 in formatSettings) { + if (name_1 in EXTENDED_SETTINGS_AND_SEVERITIES) { + extendedSettings[name_1] = formatSettings[name_1]; + severity = Math.max(EXTENDED_SETTINGS_AND_SEVERITIES[name_1], severity); + } + else { + standardDateProps[name_1] = formatSettings[name_1]; + if (name_1 in STANDARD_DATE_PROP_SEVERITIES) { + severity = Math.max(STANDARD_DATE_PROP_SEVERITIES[name_1], severity); + } + } + } + this.standardDateProps = standardDateProps; + this.extendedSettings = extendedSettings; + this.severity = severity; + this.buildFormattingFunc = memoize(buildFormattingFunc); + } + NativeFormatter.prototype.format = function (date, context) { + return this.buildFormattingFunc(this.standardDateProps, this.extendedSettings, context)(date); + }; + NativeFormatter.prototype.formatRange = function (start, end, context) { + var _a = this, standardDateProps = _a.standardDateProps, extendedSettings = _a.extendedSettings; + var diffSeverity = computeMarkerDiffSeverity(start.marker, end.marker, context.calendarSystem); + if (!diffSeverity) { + return this.format(start, context); + } + var biggestUnitForPartial = diffSeverity; + if (biggestUnitForPartial > 1 && // the two dates are different in a way that's larger scale than time + (standardDateProps.year === 'numeric' || standardDateProps.year === '2-digit') && + (standardDateProps.month === 'numeric' || standardDateProps.month === '2-digit') && + (standardDateProps.day === 'numeric' || standardDateProps.day === '2-digit')) { + biggestUnitForPartial = 1; // make it look like the dates are only different in terms of time + } + var full0 = this.format(start, context); + var full1 = this.format(end, context); + if (full0 === full1) { + return full0; + } + var partialDateProps = computePartialFormattingOptions(standardDateProps, biggestUnitForPartial); + var partialFormattingFunc = buildFormattingFunc(partialDateProps, extendedSettings, context); + var partial0 = partialFormattingFunc(start); + var partial1 = partialFormattingFunc(end); + var insertion = findCommonInsertion(full0, partial0, full1, partial1); + var separator = extendedSettings.separator || ''; + if (insertion) { + return insertion.before + partial0 + separator + partial1 + insertion.after; + } + return full0 + separator + full1; + }; + NativeFormatter.prototype.getLargestUnit = function () { + switch (this.severity) { + case 7: + case 6: + case 5: + return 'year'; + case 4: + return 'month'; + case 3: + return 'week'; + default: + return 'day'; + } + }; + return NativeFormatter; + }()); + function buildFormattingFunc(standardDateProps, extendedSettings, context) { + var standardDatePropCnt = Object.keys(standardDateProps).length; + if (standardDatePropCnt === 1 && standardDateProps.timeZoneName === 'short') { + return function (date) { + return formatTimeZoneOffset(date.timeZoneOffset); + }; + } + if (standardDatePropCnt === 0 && extendedSettings.week) { + return function (date) { + return formatWeekNumber(context.computeWeekNumber(date.marker), context.weekLabel, context.locale, extendedSettings.week); + }; + } + return buildNativeFormattingFunc(standardDateProps, extendedSettings, context); + } + function buildNativeFormattingFunc(standardDateProps, extendedSettings, context) { + standardDateProps = __assign({}, standardDateProps); // copy + extendedSettings = __assign({}, extendedSettings); // copy + sanitizeSettings(standardDateProps, extendedSettings); + standardDateProps.timeZone = 'UTC'; // we leverage the only guaranteed timeZone for our UTC markers + var normalFormat = new Intl.DateTimeFormat(context.locale.codes, standardDateProps); + var zeroFormat; // needed? + if (extendedSettings.omitZeroMinute) { + var zeroProps = __assign({}, standardDateProps); + delete zeroProps.minute; // seconds and ms were already considered in sanitizeSettings + zeroFormat = new Intl.DateTimeFormat(context.locale.codes, zeroProps); + } + return function (date) { + var marker = date.marker; + var format; + if (zeroFormat && !marker.getUTCMinutes()) { + format = zeroFormat; + } + else { + format = normalFormat; + } + var s = format.format(marker); + return postProcess(s, date, standardDateProps, extendedSettings, context); + }; + } + function sanitizeSettings(standardDateProps, extendedSettings) { + // deal with a browser inconsistency where formatting the timezone + // requires that the hour/minute be present. + if (standardDateProps.timeZoneName) { + if (!standardDateProps.hour) { + standardDateProps.hour = '2-digit'; + } + if (!standardDateProps.minute) { + standardDateProps.minute = '2-digit'; + } + } + // only support short timezone names + if (standardDateProps.timeZoneName === 'long') { + standardDateProps.timeZoneName = 'short'; + } + // if requesting to display seconds, MUST display minutes + if (extendedSettings.omitZeroMinute && (standardDateProps.second || standardDateProps.millisecond)) { + delete extendedSettings.omitZeroMinute; + } + } + function postProcess(s, date, standardDateProps, extendedSettings, context) { + s = s.replace(LTR_RE, ''); // remove left-to-right control chars. do first. good for other regexes + if (standardDateProps.timeZoneName === 'short') { + s = injectTzoStr(s, (context.timeZone === 'UTC' || date.timeZoneOffset == null) ? + 'UTC' : // important to normalize for IE, which does "GMT" + formatTimeZoneOffset(date.timeZoneOffset)); + } + if (extendedSettings.omitCommas) { + s = s.replace(COMMA_RE, '').trim(); + } + if (extendedSettings.omitZeroMinute) { + s = s.replace(':00', ''); // zeroFormat doesn't always achieve this + } + // ^ do anything that might create adjacent spaces before this point, + // because MERIDIEM_RE likes to eat up loading spaces + if (extendedSettings.meridiem === false) { + s = s.replace(MERIDIEM_RE, '').trim(); + } + else if (extendedSettings.meridiem === 'narrow') { // a/p + s = s.replace(MERIDIEM_RE, function (m0, m1) { + return m1.toLocaleLowerCase(); + }); + } + else if (extendedSettings.meridiem === 'short') { // am/pm + s = s.replace(MERIDIEM_RE, function (m0, m1) { + return m1.toLocaleLowerCase() + 'm'; + }); + } + else if (extendedSettings.meridiem === 'lowercase') { // other meridiem transformers already converted to lowercase + s = s.replace(MERIDIEM_RE, function (m0) { + return m0.toLocaleLowerCase(); + }); + } + s = s.replace(MULTI_SPACE_RE, ' '); + s = s.trim(); + return s; + } + function injectTzoStr(s, tzoStr) { + var replaced = false; + s = s.replace(UTC_RE, function () { + replaced = true; + return tzoStr; + }); + // IE11 doesn't include UTC/GMT in the original string, so append to end + if (!replaced) { + s += ' ' + tzoStr; + } + return s; + } + function formatWeekNumber(num, weekLabel, locale, display) { + var parts = []; + if (display === 'narrow') { + parts.push(weekLabel); + } + else if (display === 'short') { + parts.push(weekLabel, ' '); + } + // otherwise, considered 'numeric' + parts.push(locale.simpleNumberFormat.format(num)); + if (locale.options.isRtl) { // TODO: use control characters instead? + parts.reverse(); + } + return parts.join(''); + } + // Range Formatting Utils + // 0 = exactly the same + // 1 = different by time + // and bigger + function computeMarkerDiffSeverity(d0, d1, ca) { + if (ca.getMarkerYear(d0) !== ca.getMarkerYear(d1)) { + return 5; + } + if (ca.getMarkerMonth(d0) !== ca.getMarkerMonth(d1)) { + return 4; + } + if (ca.getMarkerDay(d0) !== ca.getMarkerDay(d1)) { + return 2; + } + if (timeAsMs(d0) !== timeAsMs(d1)) { + return 1; + } + return 0; + } + function computePartialFormattingOptions(options, biggestUnit) { + var partialOptions = {}; + for (var name_2 in options) { + if (!(name_2 in STANDARD_DATE_PROP_SEVERITIES) || // not a date part prop (like timeZone) + STANDARD_DATE_PROP_SEVERITIES[name_2] <= biggestUnit) { + partialOptions[name_2] = options[name_2]; + } + } + return partialOptions; + } + function findCommonInsertion(full0, partial0, full1, partial1) { + var i0 = 0; + while (i0 < full0.length) { + var found0 = full0.indexOf(partial0, i0); + if (found0 === -1) { + break; + } + var before0 = full0.substr(0, found0); + i0 = found0 + partial0.length; + var after0 = full0.substr(i0); + var i1 = 0; + while (i1 < full1.length) { + var found1 = full1.indexOf(partial1, i1); + if (found1 === -1) { + break; + } + var before1 = full1.substr(0, found1); + i1 = found1 + partial1.length; + var after1 = full1.substr(i1); + if (before0 === before1 && after0 === after1) { + return { + before: before0, + after: after0 + }; + } + } + } + return null; + } + + /* + TODO: fix the terminology of "formatter" vs "formatting func" + */ + /* + At the time of instantiation, this object does not know which cmd-formatting system it will use. + It receives this at the time of formatting, as a setting. + */ + var CmdFormatter = /** @class */ (function () { + function CmdFormatter(cmdStr, separator) { + this.cmdStr = cmdStr; + this.separator = separator; + } + CmdFormatter.prototype.format = function (date, context) { + return context.cmdFormatter(this.cmdStr, createVerboseFormattingArg(date, null, context, this.separator)); + }; + CmdFormatter.prototype.formatRange = function (start, end, context) { + return context.cmdFormatter(this.cmdStr, createVerboseFormattingArg(start, end, context, this.separator)); + }; + return CmdFormatter; + }()); + + var FuncFormatter = /** @class */ (function () { + function FuncFormatter(func) { + this.func = func; + } + FuncFormatter.prototype.format = function (date, context) { + return this.func(createVerboseFormattingArg(date, null, context)); + }; + FuncFormatter.prototype.formatRange = function (start, end, context) { + return this.func(createVerboseFormattingArg(start, end, context)); + }; + return FuncFormatter; + }()); + + // Formatter Object Creation + function createFormatter(input, defaultSeparator) { + if (typeof input === 'object' && input) { // non-null object + if (typeof defaultSeparator === 'string') { + input = __assign({ separator: defaultSeparator }, input); + } + return new NativeFormatter(input); + } + else if (typeof input === 'string') { + return new CmdFormatter(input, defaultSeparator); + } + else if (typeof input === 'function') { + return new FuncFormatter(input); + } + } + // String Utils + // timeZoneOffset is in minutes + function buildIsoString(marker, timeZoneOffset, stripZeroTime) { + if (stripZeroTime === void 0) { stripZeroTime = false; } + var s = marker.toISOString(); + s = s.replace('.000', ''); + if (stripZeroTime) { + s = s.replace('T00:00:00Z', ''); + } + if (s.length > 10) { // time part wasn't stripped, can add timezone info + if (timeZoneOffset == null) { + s = s.replace('Z', ''); + } + else if (timeZoneOffset !== 0) { + s = s.replace('Z', formatTimeZoneOffset(timeZoneOffset, true)); + } + // otherwise, its UTC-0 and we want to keep the Z + } + return s; + } + function formatIsoTimeString(marker) { + return padStart(marker.getUTCHours(), 2) + ':' + + padStart(marker.getUTCMinutes(), 2) + ':' + + padStart(marker.getUTCSeconds(), 2); + } + function formatTimeZoneOffset(minutes, doIso) { + if (doIso === void 0) { doIso = false; } + var sign = minutes < 0 ? '-' : '+'; + var abs = Math.abs(minutes); + var hours = Math.floor(abs / 60); + var mins = Math.round(abs % 60); + if (doIso) { + return sign + padStart(hours, 2) + ':' + padStart(mins, 2); + } + else { + return 'GMT' + sign + hours + (mins ? ':' + padStart(mins, 2) : ''); + } + } + // Arg Utils + function createVerboseFormattingArg(start, end, context, separator) { + var startInfo = expandZonedMarker(start, context.calendarSystem); + var endInfo = end ? expandZonedMarker(end, context.calendarSystem) : null; + return { + date: startInfo, + start: startInfo, + end: endInfo, + timeZone: context.timeZone, + localeCodes: context.locale.codes, + separator: separator + }; + } + function expandZonedMarker(dateInfo, calendarSystem) { + var a = calendarSystem.markerToArray(dateInfo.marker); + return { + marker: dateInfo.marker, + timeZoneOffset: dateInfo.timeZoneOffset, + array: a, + year: a[0], + month: a[1], + day: a[2], + hour: a[3], + minute: a[4], + second: a[5], + millisecond: a[6] + }; + } + + var EventSourceApi = /** @class */ (function () { + function EventSourceApi(calendar, internalEventSource) { + this.calendar = calendar; + this.internalEventSource = internalEventSource; + } + EventSourceApi.prototype.remove = function () { + this.calendar.dispatch({ + type: 'REMOVE_EVENT_SOURCE', + sourceId: this.internalEventSource.sourceId + }); + }; + EventSourceApi.prototype.refetch = function () { + this.calendar.dispatch({ + type: 'FETCH_EVENT_SOURCES', + sourceIds: [this.internalEventSource.sourceId] + }); + }; + Object.defineProperty(EventSourceApi.prototype, "id", { + get: function () { + return this.internalEventSource.publicId; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EventSourceApi.prototype, "url", { + // only relevant to json-feed event sources + get: function () { + return this.internalEventSource.meta.url; + }, + enumerable: true, + configurable: true + }); + return EventSourceApi; + }()); + + var EventApi = /** @class */ (function () { + function EventApi(calendar, def, instance) { + this._calendar = calendar; + this._def = def; + this._instance = instance || null; + } + /* + TODO: make event struct more responsible for this + */ + EventApi.prototype.setProp = function (name, val) { + var _a, _b; + if (name in DATE_PROPS) ; + else if (name in NON_DATE_PROPS) { + if (typeof NON_DATE_PROPS[name] === 'function') { + val = NON_DATE_PROPS[name](val); + } + this.mutate({ + standardProps: (_a = {}, _a[name] = val, _a) + }); + } + else if (name in UNSCOPED_EVENT_UI_PROPS) { + var ui = void 0; + if (typeof UNSCOPED_EVENT_UI_PROPS[name] === 'function') { + val = UNSCOPED_EVENT_UI_PROPS[name](val); + } + if (name === 'color') { + ui = { backgroundColor: val, borderColor: val }; + } + else if (name === 'editable') { + ui = { startEditable: val, durationEditable: val }; + } + else { + ui = (_b = {}, _b[name] = val, _b); + } + this.mutate({ + standardProps: { ui: ui } + }); + } + }; + EventApi.prototype.setExtendedProp = function (name, val) { + var _a; + this.mutate({ + extendedProps: (_a = {}, _a[name] = val, _a) + }); + }; + EventApi.prototype.setStart = function (startInput, options) { + if (options === void 0) { options = {}; } + var dateEnv = this._calendar.dateEnv; + var start = dateEnv.createMarker(startInput); + if (start && this._instance) { // TODO: warning if parsed bad + var instanceRange = this._instance.range; + var startDelta = diffDates(instanceRange.start, start, dateEnv, options.granularity); // what if parsed bad!? + if (options.maintainDuration) { + this.mutate({ datesDelta: startDelta }); + } + else { + this.mutate({ startDelta: startDelta }); + } + } + }; + EventApi.prototype.setEnd = function (endInput, options) { + if (options === void 0) { options = {}; } + var dateEnv = this._calendar.dateEnv; + var end; + if (endInput != null) { + end = dateEnv.createMarker(endInput); + if (!end) { + return; // TODO: warning if parsed bad + } + } + if (this._instance) { + if (end) { + var endDelta = diffDates(this._instance.range.end, end, dateEnv, options.granularity); + this.mutate({ endDelta: endDelta }); + } + else { + this.mutate({ standardProps: { hasEnd: false } }); + } + } + }; + EventApi.prototype.setDates = function (startInput, endInput, options) { + if (options === void 0) { options = {}; } + var dateEnv = this._calendar.dateEnv; + var standardProps = { allDay: options.allDay }; + var start = dateEnv.createMarker(startInput); + var end; + if (!start) { + return; // TODO: warning if parsed bad + } + if (endInput != null) { + end = dateEnv.createMarker(endInput); + if (!end) { // TODO: warning if parsed bad + return; + } + } + if (this._instance) { + var instanceRange = this._instance.range; + // when computing the diff for an event being converted to all-day, + // compute diff off of the all-day values the way event-mutation does. + if (options.allDay === true) { + instanceRange = computeAlignedDayRange(instanceRange); + } + var startDelta = diffDates(instanceRange.start, start, dateEnv, options.granularity); + if (end) { + var endDelta = diffDates(instanceRange.end, end, dateEnv, options.granularity); + if (durationsEqual(startDelta, endDelta)) { + this.mutate({ datesDelta: startDelta, standardProps: standardProps }); + } + else { + this.mutate({ startDelta: startDelta, endDelta: endDelta, standardProps: standardProps }); + } + } + else { // means "clear the end" + standardProps.hasEnd = false; + this.mutate({ datesDelta: startDelta, standardProps: standardProps }); + } + } + }; + EventApi.prototype.moveStart = function (deltaInput) { + var delta = createDuration(deltaInput); + if (delta) { // TODO: warning if parsed bad + this.mutate({ startDelta: delta }); + } + }; + EventApi.prototype.moveEnd = function (deltaInput) { + var delta = createDuration(deltaInput); + if (delta) { // TODO: warning if parsed bad + this.mutate({ endDelta: delta }); + } + }; + EventApi.prototype.moveDates = function (deltaInput) { + var delta = createDuration(deltaInput); + if (delta) { // TODO: warning if parsed bad + this.mutate({ datesDelta: delta }); + } + }; + EventApi.prototype.setAllDay = function (allDay, options) { + if (options === void 0) { options = {}; } + var standardProps = { allDay: allDay }; + var maintainDuration = options.maintainDuration; + if (maintainDuration == null) { + maintainDuration = this._calendar.opt('allDayMaintainDuration'); + } + if (this._def.allDay !== allDay) { + standardProps.hasEnd = maintainDuration; + } + this.mutate({ standardProps: standardProps }); + }; + EventApi.prototype.formatRange = function (formatInput) { + var dateEnv = this._calendar.dateEnv; + var instance = this._instance; + var formatter = createFormatter(formatInput, this._calendar.opt('defaultRangeSeparator')); + if (this._def.hasEnd) { + return dateEnv.formatRange(instance.range.start, instance.range.end, formatter, { + forcedStartTzo: instance.forcedStartTzo, + forcedEndTzo: instance.forcedEndTzo + }); + } + else { + return dateEnv.format(instance.range.start, formatter, { + forcedTzo: instance.forcedStartTzo + }); + } + }; + EventApi.prototype.mutate = function (mutation) { + var def = this._def; + var instance = this._instance; + if (instance) { + this._calendar.dispatch({ + type: 'MUTATE_EVENTS', + instanceId: instance.instanceId, + mutation: mutation, + fromApi: true + }); + var eventStore = this._calendar.state.eventStore; + this._def = eventStore.defs[def.defId]; + this._instance = eventStore.instances[instance.instanceId]; + } + }; + EventApi.prototype.remove = function () { + this._calendar.dispatch({ + type: 'REMOVE_EVENT_DEF', + defId: this._def.defId + }); + }; + Object.defineProperty(EventApi.prototype, "source", { + get: function () { + var sourceId = this._def.sourceId; + if (sourceId) { + return new EventSourceApi(this._calendar, this._calendar.state.eventSources[sourceId]); + } + return null; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "start", { + get: function () { + return this._instance ? + this._calendar.dateEnv.toDate(this._instance.range.start) : + null; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "end", { + get: function () { + return (this._instance && this._def.hasEnd) ? + this._calendar.dateEnv.toDate(this._instance.range.end) : + null; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "id", { + // computable props that all access the def + // TODO: find a TypeScript-compatible way to do this at scale + get: function () { return this._def.publicId; }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "groupId", { + get: function () { return this._def.groupId; }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "allDay", { + get: function () { return this._def.allDay; }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "title", { + get: function () { return this._def.title; }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "url", { + get: function () { return this._def.url; }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "rendering", { + get: function () { return this._def.rendering; }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "startEditable", { + get: function () { return this._def.ui.startEditable; }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "durationEditable", { + get: function () { return this._def.ui.durationEditable; }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "constraint", { + get: function () { return this._def.ui.constraints[0] || null; }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "overlap", { + get: function () { return this._def.ui.overlap; }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "allow", { + get: function () { return this._def.ui.allows[0] || null; }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "backgroundColor", { + get: function () { return this._def.ui.backgroundColor; }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "borderColor", { + get: function () { return this._def.ui.borderColor; }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "textColor", { + get: function () { return this._def.ui.textColor; }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "classNames", { + // NOTE: user can't modify these because Object.freeze was called in event-def parsing + get: function () { return this._def.ui.classNames; }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EventApi.prototype, "extendedProps", { + get: function () { return this._def.extendedProps; }, + enumerable: true, + configurable: true + }); + return EventApi; + }()); + + /* + Specifying nextDayThreshold signals that all-day ranges should be sliced. + */ + function sliceEventStore(eventStore, eventUiBases, framingRange, nextDayThreshold) { + var inverseBgByGroupId = {}; + var inverseBgByDefId = {}; + var defByGroupId = {}; + var bgRanges = []; + var fgRanges = []; + var eventUis = compileEventUis(eventStore.defs, eventUiBases); + for (var defId in eventStore.defs) { + var def = eventStore.defs[defId]; + if (def.rendering === 'inverse-background') { + if (def.groupId) { + inverseBgByGroupId[def.groupId] = []; + if (!defByGroupId[def.groupId]) { + defByGroupId[def.groupId] = def; + } + } + else { + inverseBgByDefId[defId] = []; + } + } + } + for (var instanceId in eventStore.instances) { + var instance = eventStore.instances[instanceId]; + var def = eventStore.defs[instance.defId]; + var ui = eventUis[def.defId]; + var origRange = instance.range; + var normalRange = (!def.allDay && nextDayThreshold) ? + computeVisibleDayRange(origRange, nextDayThreshold) : + origRange; + var slicedRange = intersectRanges(normalRange, framingRange); + if (slicedRange) { + if (def.rendering === 'inverse-background') { + if (def.groupId) { + inverseBgByGroupId[def.groupId].push(slicedRange); + } + else { + inverseBgByDefId[instance.defId].push(slicedRange); + } + } + else { + (def.rendering === 'background' ? bgRanges : fgRanges).push({ + def: def, + ui: ui, + instance: instance, + range: slicedRange, + isStart: normalRange.start && normalRange.start.valueOf() === slicedRange.start.valueOf(), + isEnd: normalRange.end && normalRange.end.valueOf() === slicedRange.end.valueOf() + }); + } + } + } + for (var groupId in inverseBgByGroupId) { // BY GROUP + var ranges = inverseBgByGroupId[groupId]; + var invertedRanges = invertRanges(ranges, framingRange); + for (var _i = 0, invertedRanges_1 = invertedRanges; _i < invertedRanges_1.length; _i++) { + var invertedRange = invertedRanges_1[_i]; + var def = defByGroupId[groupId]; + var ui = eventUis[def.defId]; + bgRanges.push({ + def: def, + ui: ui, + instance: null, + range: invertedRange, + isStart: false, + isEnd: false + }); + } + } + for (var defId in inverseBgByDefId) { + var ranges = inverseBgByDefId[defId]; + var invertedRanges = invertRanges(ranges, framingRange); + for (var _a = 0, invertedRanges_2 = invertedRanges; _a < invertedRanges_2.length; _a++) { + var invertedRange = invertedRanges_2[_a]; + bgRanges.push({ + def: eventStore.defs[defId], + ui: eventUis[defId], + instance: null, + range: invertedRange, + isStart: false, + isEnd: false + }); + } + } + return { bg: bgRanges, fg: fgRanges }; + } + function hasBgRendering(def) { + return def.rendering === 'background' || def.rendering === 'inverse-background'; + } + function filterSegsViaEls(view, segs, isMirror) { + if (view.hasPublicHandlers('eventRender')) { + segs = segs.filter(function (seg) { + var custom = view.publiclyTrigger('eventRender', [ + { + event: new EventApi(view.calendar, seg.eventRange.def, seg.eventRange.instance), + isMirror: isMirror, + isStart: seg.isStart, + isEnd: seg.isEnd, + // TODO: include seg.range once all components consistently generate it + el: seg.el, + view: view + } + ]); + if (custom === false) { // means don't render at all + return false; + } + else if (custom && custom !== true) { + seg.el = custom; + } + return true; + }); + } + for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) { + var seg = segs_1[_i]; + setElSeg(seg.el, seg); + } + return segs; + } + function setElSeg(el, seg) { + el.fcSeg = seg; + } + function getElSeg(el) { + return el.fcSeg || null; + } + // event ui computation + function compileEventUis(eventDefs, eventUiBases) { + return mapHash(eventDefs, function (eventDef) { + return compileEventUi(eventDef, eventUiBases); + }); + } + function compileEventUi(eventDef, eventUiBases) { + var uis = []; + if (eventUiBases['']) { + uis.push(eventUiBases['']); + } + if (eventUiBases[eventDef.defId]) { + uis.push(eventUiBases[eventDef.defId]); + } + uis.push(eventDef.ui); + return combineEventUis(uis); + } + + // applies the mutation to ALL defs/instances within the event store + function applyMutationToEventStore(eventStore, eventConfigBase, mutation, calendar) { + var eventConfigs = compileEventUis(eventStore.defs, eventConfigBase); + var dest = createEmptyEventStore(); + for (var defId in eventStore.defs) { + var def = eventStore.defs[defId]; + dest.defs[defId] = applyMutationToEventDef(def, eventConfigs[defId], mutation, calendar.pluginSystem.hooks.eventDefMutationAppliers, calendar); + } + for (var instanceId in eventStore.instances) { + var instance = eventStore.instances[instanceId]; + var def = dest.defs[instance.defId]; // important to grab the newly modified def + dest.instances[instanceId] = applyMutationToEventInstance(instance, def, eventConfigs[instance.defId], mutation, calendar); + } + return dest; + } + function applyMutationToEventDef(eventDef, eventConfig, mutation, appliers, calendar) { + var standardProps = mutation.standardProps || {}; + // if hasEnd has not been specified, guess a good value based on deltas. + // if duration will change, there's no way the default duration will persist, + // and thus, we need to mark the event as having a real end + if (standardProps.hasEnd == null && + eventConfig.durationEditable && + (mutation.startDelta || mutation.endDelta)) { + standardProps.hasEnd = true; // TODO: is this mutation okay? + } + var copy = __assign({}, eventDef, standardProps, { ui: __assign({}, eventDef.ui, standardProps.ui) }); + if (mutation.extendedProps) { + copy.extendedProps = __assign({}, copy.extendedProps, mutation.extendedProps); + } + for (var _i = 0, appliers_1 = appliers; _i < appliers_1.length; _i++) { + var applier = appliers_1[_i]; + applier(copy, mutation, calendar); + } + if (!copy.hasEnd && calendar.opt('forceEventDuration')) { + copy.hasEnd = true; + } + return copy; + } + function applyMutationToEventInstance(eventInstance, eventDef, // must first be modified by applyMutationToEventDef + eventConfig, mutation, calendar) { + var dateEnv = calendar.dateEnv; + var forceAllDay = mutation.standardProps && mutation.standardProps.allDay === true; + var clearEnd = mutation.standardProps && mutation.standardProps.hasEnd === false; + var copy = __assign({}, eventInstance); + if (forceAllDay) { + copy.range = computeAlignedDayRange(copy.range); + } + if (mutation.datesDelta && eventConfig.startEditable) { + copy.range = { + start: dateEnv.add(copy.range.start, mutation.datesDelta), + end: dateEnv.add(copy.range.end, mutation.datesDelta) + }; + } + if (mutation.startDelta && eventConfig.durationEditable) { + copy.range = { + start: dateEnv.add(copy.range.start, mutation.startDelta), + end: copy.range.end + }; + } + if (mutation.endDelta && eventConfig.durationEditable) { + copy.range = { + start: copy.range.start, + end: dateEnv.add(copy.range.end, mutation.endDelta) + }; + } + if (clearEnd) { + copy.range = { + start: copy.range.start, + end: calendar.getDefaultEventEnd(eventDef.allDay, copy.range.start) + }; + } + // in case event was all-day but the supplied deltas were not + // better util for this? + if (eventDef.allDay) { + copy.range = { + start: startOfDay(copy.range.start), + end: startOfDay(copy.range.end) + }; + } + // handle invalid durations + if (copy.range.end < copy.range.start) { + copy.range.end = calendar.getDefaultEventEnd(eventDef.allDay, copy.range.start); + } + return copy; + } + + function reduceEventStore (eventStore, action, eventSources, dateProfile, calendar) { + switch (action.type) { + case 'RECEIVE_EVENTS': // raw + return receiveRawEvents(eventStore, eventSources[action.sourceId], action.fetchId, action.fetchRange, action.rawEvents, calendar); + case 'ADD_EVENTS': // already parsed, but not expanded + return addEvent(eventStore, action.eventStore, // new ones + dateProfile ? dateProfile.activeRange : null, calendar); + case 'MERGE_EVENTS': // already parsed and expanded + return mergeEventStores(eventStore, action.eventStore); + case 'PREV': // TODO: how do we track all actions that affect dateProfile :( + case 'NEXT': + case 'SET_DATE': + case 'SET_VIEW_TYPE': + if (dateProfile) { + return expandRecurring(eventStore, dateProfile.activeRange, calendar); + } + else { + return eventStore; + } + case 'CHANGE_TIMEZONE': + return rezoneDates(eventStore, action.oldDateEnv, calendar.dateEnv); + case 'MUTATE_EVENTS': + return applyMutationToRelated(eventStore, action.instanceId, action.mutation, action.fromApi, calendar); + case 'REMOVE_EVENT_INSTANCES': + return excludeInstances(eventStore, action.instances); + case 'REMOVE_EVENT_DEF': + return filterEventStoreDefs(eventStore, function (eventDef) { + return eventDef.defId !== action.defId; + }); + case 'REMOVE_EVENT_SOURCE': + return excludeEventsBySourceId(eventStore, action.sourceId); + case 'REMOVE_ALL_EVENT_SOURCES': + return filterEventStoreDefs(eventStore, function (eventDef) { + return !eventDef.sourceId; // only keep events with no source id + }); + case 'REMOVE_ALL_EVENTS': + return createEmptyEventStore(); + case 'RESET_EVENTS': + return { + defs: eventStore.defs, + instances: eventStore.instances + }; + default: + return eventStore; + } + } + function receiveRawEvents(eventStore, eventSource, fetchId, fetchRange, rawEvents, calendar) { + if (eventSource && // not already removed + fetchId === eventSource.latestFetchId // TODO: wish this logic was always in event-sources + ) { + var subset = parseEvents(transformRawEvents(rawEvents, eventSource, calendar), eventSource.sourceId, calendar); + if (fetchRange) { + subset = expandRecurring(subset, fetchRange, calendar); + } + return mergeEventStores(excludeEventsBySourceId(eventStore, eventSource.sourceId), subset); + } + return eventStore; + } + function addEvent(eventStore, subset, expandRange, calendar) { + if (expandRange) { + subset = expandRecurring(subset, expandRange, calendar); + } + return mergeEventStores(eventStore, subset); + } + function rezoneDates(eventStore, oldDateEnv, newDateEnv) { + var defs = eventStore.defs; + var instances = mapHash(eventStore.instances, function (instance) { + var def = defs[instance.defId]; + if (def.allDay || def.recurringDef) { + return instance; // isn't dependent on timezone + } + else { + return __assign({}, instance, { range: { + start: newDateEnv.createMarker(oldDateEnv.toDate(instance.range.start, instance.forcedStartTzo)), + end: newDateEnv.createMarker(oldDateEnv.toDate(instance.range.end, instance.forcedEndTzo)) + }, forcedStartTzo: newDateEnv.canComputeOffset ? null : instance.forcedStartTzo, forcedEndTzo: newDateEnv.canComputeOffset ? null : instance.forcedEndTzo }); + } + }); + return { defs: defs, instances: instances }; + } + function applyMutationToRelated(eventStore, instanceId, mutation, fromApi, calendar) { + var relevant = getRelevantEvents(eventStore, instanceId); + var eventConfigBase = fromApi ? + { '': { + startEditable: true, + durationEditable: true, + constraints: [], + overlap: null, + allows: [], + backgroundColor: '', + borderColor: '', + textColor: '', + classNames: [] + } } : + calendar.eventUiBases; + relevant = applyMutationToEventStore(relevant, eventConfigBase, mutation, calendar); + return mergeEventStores(eventStore, relevant); + } + function excludeEventsBySourceId(eventStore, sourceId) { + return filterEventStoreDefs(eventStore, function (eventDef) { + return eventDef.sourceId !== sourceId; + }); + } + // QUESTION: why not just return instances? do a general object-property-exclusion util + function excludeInstances(eventStore, removals) { + return { + defs: eventStore.defs, + instances: filterHash(eventStore.instances, function (instance) { + return !removals[instance.instanceId]; + }) + }; + } + + // high-level segmenting-aware tester functions + // ------------------------------------------------------------------------------------------------------------------------ + function isInteractionValid(interaction, calendar) { + return isNewPropsValid({ eventDrag: interaction }, calendar); // HACK: the eventDrag props is used for ALL interactions + } + function isDateSelectionValid(dateSelection, calendar) { + return isNewPropsValid({ dateSelection: dateSelection }, calendar); + } + function isNewPropsValid(newProps, calendar) { + var view = calendar.view; + var props = __assign({ businessHours: view ? view.props.businessHours : createEmptyEventStore(), dateSelection: '', eventStore: calendar.state.eventStore, eventUiBases: calendar.eventUiBases, eventSelection: '', eventDrag: null, eventResize: null }, newProps); + return (calendar.pluginSystem.hooks.isPropsValid || isPropsValid)(props, calendar); + } + function isPropsValid(state, calendar, dateSpanMeta, filterConfig) { + if (dateSpanMeta === void 0) { dateSpanMeta = {}; } + if (state.eventDrag && !isInteractionPropsValid(state, calendar, dateSpanMeta, filterConfig)) { + return false; + } + if (state.dateSelection && !isDateSelectionPropsValid(state, calendar, dateSpanMeta, filterConfig)) { + return false; + } + return true; + } + // Moving Event Validation + // ------------------------------------------------------------------------------------------------------------------------ + function isInteractionPropsValid(state, calendar, dateSpanMeta, filterConfig) { + var interaction = state.eventDrag; // HACK: the eventDrag props is used for ALL interactions + var subjectEventStore = interaction.mutatedEvents; + var subjectDefs = subjectEventStore.defs; + var subjectInstances = subjectEventStore.instances; + var subjectConfigs = compileEventUis(subjectDefs, interaction.isEvent ? + state.eventUiBases : + { '': calendar.selectionConfig } // if not a real event, validate as a selection + ); + if (filterConfig) { + subjectConfigs = mapHash(subjectConfigs, filterConfig); + } + var otherEventStore = excludeInstances(state.eventStore, interaction.affectedEvents.instances); // exclude the subject events. TODO: exclude defs too? + var otherDefs = otherEventStore.defs; + var otherInstances = otherEventStore.instances; + var otherConfigs = compileEventUis(otherDefs, state.eventUiBases); + for (var subjectInstanceId in subjectInstances) { + var subjectInstance = subjectInstances[subjectInstanceId]; + var subjectRange = subjectInstance.range; + var subjectConfig = subjectConfigs[subjectInstance.defId]; + var subjectDef = subjectDefs[subjectInstance.defId]; + // constraint + if (!allConstraintsPass(subjectConfig.constraints, subjectRange, otherEventStore, state.businessHours, calendar)) { + return false; + } + // overlap + var overlapFunc = calendar.opt('eventOverlap'); + if (typeof overlapFunc !== 'function') { + overlapFunc = null; + } + for (var otherInstanceId in otherInstances) { + var otherInstance = otherInstances[otherInstanceId]; + // intersect! evaluate + if (rangesIntersect(subjectRange, otherInstance.range)) { + var otherOverlap = otherConfigs[otherInstance.defId].overlap; + // consider the other event's overlap. only do this if the subject event is a "real" event + if (otherOverlap === false && interaction.isEvent) { + return false; + } + if (subjectConfig.overlap === false) { + return false; + } + if (overlapFunc && !overlapFunc(new EventApi(calendar, otherDefs[otherInstance.defId], otherInstance), // still event + new EventApi(calendar, subjectDef, subjectInstance) // moving event + )) { + return false; + } + } + } + // allow (a function) + var calendarEventStore = calendar.state.eventStore; // need global-to-calendar, not local to component (splittable)state + for (var _i = 0, _a = subjectConfig.allows; _i < _a.length; _i++) { + var subjectAllow = _a[_i]; + var subjectDateSpan = __assign({}, dateSpanMeta, { range: subjectInstance.range, allDay: subjectDef.allDay }); + var origDef = calendarEventStore.defs[subjectDef.defId]; + var origInstance = calendarEventStore.instances[subjectInstanceId]; + var eventApi = void 0; + if (origDef) { // was previously in the calendar + eventApi = new EventApi(calendar, origDef, origInstance); + } + else { // was an external event + eventApi = new EventApi(calendar, subjectDef); // no instance, because had no dates + } + if (!subjectAllow(calendar.buildDateSpanApi(subjectDateSpan), eventApi)) { + return false; + } + } + } + return true; + } + // Date Selection Validation + // ------------------------------------------------------------------------------------------------------------------------ + function isDateSelectionPropsValid(state, calendar, dateSpanMeta, filterConfig) { + var relevantEventStore = state.eventStore; + var relevantDefs = relevantEventStore.defs; + var relevantInstances = relevantEventStore.instances; + var selection = state.dateSelection; + var selectionRange = selection.range; + var selectionConfig = calendar.selectionConfig; + if (filterConfig) { + selectionConfig = filterConfig(selectionConfig); + } + // constraint + if (!allConstraintsPass(selectionConfig.constraints, selectionRange, relevantEventStore, state.businessHours, calendar)) { + return false; + } + // overlap + var overlapFunc = calendar.opt('selectOverlap'); + if (typeof overlapFunc !== 'function') { + overlapFunc = null; + } + for (var relevantInstanceId in relevantInstances) { + var relevantInstance = relevantInstances[relevantInstanceId]; + // intersect! evaluate + if (rangesIntersect(selectionRange, relevantInstance.range)) { + if (selectionConfig.overlap === false) { + return false; + } + if (overlapFunc && !overlapFunc(new EventApi(calendar, relevantDefs[relevantInstance.defId], relevantInstance))) { + return false; + } + } + } + // allow (a function) + for (var _i = 0, _a = selectionConfig.allows; _i < _a.length; _i++) { + var selectionAllow = _a[_i]; + var fullDateSpan = __assign({}, dateSpanMeta, selection); + if (!selectionAllow(calendar.buildDateSpanApi(fullDateSpan), null)) { + return false; + } + } + return true; + } + // Constraint Utils + // ------------------------------------------------------------------------------------------------------------------------ + function allConstraintsPass(constraints, subjectRange, otherEventStore, businessHoursUnexpanded, calendar) { + for (var _i = 0, constraints_1 = constraints; _i < constraints_1.length; _i++) { + var constraint = constraints_1[_i]; + if (!anyRangesContainRange(constraintToRanges(constraint, subjectRange, otherEventStore, businessHoursUnexpanded, calendar), subjectRange)) { + return false; + } + } + return true; + } + function constraintToRanges(constraint, subjectRange, // for expanding a recurring constraint, or expanding business hours + otherEventStore, // for if constraint is an even group ID + businessHoursUnexpanded, // for if constraint is 'businessHours' + calendar // for expanding businesshours + ) { + if (constraint === 'businessHours') { + return eventStoreToRanges(expandRecurring(businessHoursUnexpanded, subjectRange, calendar)); + } + else if (typeof constraint === 'string') { // an group ID + return eventStoreToRanges(filterEventStoreDefs(otherEventStore, function (eventDef) { + return eventDef.groupId === constraint; + })); + } + else if (typeof constraint === 'object' && constraint) { // non-null object + return eventStoreToRanges(expandRecurring(constraint, subjectRange, calendar)); + } + return []; // if it's false + } + // TODO: move to event-store file? + function eventStoreToRanges(eventStore) { + var instances = eventStore.instances; + var ranges = []; + for (var instanceId in instances) { + ranges.push(instances[instanceId].range); + } + return ranges; + } + // TODO: move to geom file? + function anyRangesContainRange(outerRanges, innerRange) { + for (var _i = 0, outerRanges_1 = outerRanges; _i < outerRanges_1.length; _i++) { + var outerRange = outerRanges_1[_i]; + if (rangeContainsRange(outerRange, innerRange)) { + return true; + } + } + return false; + } + // Parsing + // ------------------------------------------------------------------------------------------------------------------------ + function normalizeConstraint(input, calendar) { + if (Array.isArray(input)) { + return parseEvents(input, '', calendar, true); // allowOpenRange=true + } + else if (typeof input === 'object' && input) { // non-null object + return parseEvents([input], '', calendar, true); // allowOpenRange=true + } + else if (input != null) { + return String(input); + } + else { + return null; + } + } + + function htmlEscape(s) { + return (s + '').replace(/&/g, '&') + .replace(//g, '>') + .replace(/'/g, ''') + .replace(/"/g, '"') + .replace(/\n/g, '
'); + } + // Given a hash of CSS properties, returns a string of CSS. + // Uses property names as-is (no camel-case conversion). Will not make statements for null/undefined values. + function cssToStr(cssProps) { + var statements = []; + for (var name_1 in cssProps) { + var val = cssProps[name_1]; + if (val != null && val !== '') { + statements.push(name_1 + ':' + val); + } + } + return statements.join(';'); + } + // Given an object hash of HTML attribute names to values, + // generates a string that can be injected between < > in HTML + function attrsToStr(attrs) { + var parts = []; + for (var name_2 in attrs) { + var val = attrs[name_2]; + if (val != null) { + parts.push(name_2 + '="' + htmlEscape(val) + '"'); + } + } + return parts.join(' '); + } + function parseClassName(raw) { + if (Array.isArray(raw)) { + return raw; + } + else if (typeof raw === 'string') { + return raw.split(/\s+/); + } + else { + return []; + } + } + + var UNSCOPED_EVENT_UI_PROPS = { + editable: Boolean, + startEditable: Boolean, + durationEditable: Boolean, + constraint: null, + overlap: null, + allow: null, + className: parseClassName, + classNames: parseClassName, + color: String, + backgroundColor: String, + borderColor: String, + textColor: String + }; + function processUnscopedUiProps(rawProps, calendar, leftovers) { + var props = refineProps(rawProps, UNSCOPED_EVENT_UI_PROPS, {}, leftovers); + var constraint = normalizeConstraint(props.constraint, calendar); + return { + startEditable: props.startEditable != null ? props.startEditable : props.editable, + durationEditable: props.durationEditable != null ? props.durationEditable : props.editable, + constraints: constraint != null ? [constraint] : [], + overlap: props.overlap, + allows: props.allow != null ? [props.allow] : [], + backgroundColor: props.backgroundColor || props.color, + borderColor: props.borderColor || props.color, + textColor: props.textColor, + classNames: props.classNames.concat(props.className) + }; + } + function processScopedUiProps(prefix, rawScoped, calendar, leftovers) { + var rawUnscoped = {}; + var wasFound = {}; + for (var key in UNSCOPED_EVENT_UI_PROPS) { + var scopedKey = prefix + capitaliseFirstLetter(key); + rawUnscoped[key] = rawScoped[scopedKey]; + wasFound[scopedKey] = true; + } + if (prefix === 'event') { + rawUnscoped.editable = rawScoped.editable; // special case. there is no 'eventEditable', just 'editable' + } + if (leftovers) { + for (var key in rawScoped) { + if (!wasFound[key]) { + leftovers[key] = rawScoped[key]; + } + } + } + return processUnscopedUiProps(rawUnscoped, calendar); + } + var EMPTY_EVENT_UI = { + startEditable: null, + durationEditable: null, + constraints: [], + overlap: null, + allows: [], + backgroundColor: '', + borderColor: '', + textColor: '', + classNames: [] + }; + // prevent against problems with <2 args! + function combineEventUis(uis) { + return uis.reduce(combineTwoEventUis, EMPTY_EVENT_UI); + } + function combineTwoEventUis(item0, item1) { + return { + startEditable: item1.startEditable != null ? item1.startEditable : item0.startEditable, + durationEditable: item1.durationEditable != null ? item1.durationEditable : item0.durationEditable, + constraints: item0.constraints.concat(item1.constraints), + overlap: typeof item1.overlap === 'boolean' ? item1.overlap : item0.overlap, + allows: item0.allows.concat(item1.allows), + backgroundColor: item1.backgroundColor || item0.backgroundColor, + borderColor: item1.borderColor || item0.borderColor, + textColor: item1.textColor || item0.textColor, + classNames: item0.classNames.concat(item1.classNames) + }; + } + + var NON_DATE_PROPS = { + id: String, + groupId: String, + title: String, + url: String, + rendering: String, + extendedProps: null + }; + var DATE_PROPS = { + start: null, + date: null, + end: null, + allDay: null + }; + var uid = 0; + function parseEvent(raw, sourceId, calendar, allowOpenRange) { + var allDayDefault = computeIsAllDayDefault(sourceId, calendar); + var leftovers0 = {}; + var recurringRes = parseRecurring(raw, // raw, but with single-event stuff stripped out + allDayDefault, calendar.dateEnv, calendar.pluginSystem.hooks.recurringTypes, leftovers0 // will populate with non-recurring props + ); + if (recurringRes) { + var def = parseEventDef(leftovers0, sourceId, recurringRes.allDay, Boolean(recurringRes.duration), calendar); + def.recurringDef = { + typeId: recurringRes.typeId, + typeData: recurringRes.typeData, + duration: recurringRes.duration + }; + return { def: def, instance: null }; + } + else { + var leftovers1 = {}; + var singleRes = parseSingle(raw, allDayDefault, calendar, leftovers1, allowOpenRange); + if (singleRes) { + var def = parseEventDef(leftovers1, sourceId, singleRes.allDay, singleRes.hasEnd, calendar); + var instance = createEventInstance(def.defId, singleRes.range, singleRes.forcedStartTzo, singleRes.forcedEndTzo); + return { def: def, instance: instance }; + } + } + return null; + } + /* + Will NOT populate extendedProps with the leftover properties. + Will NOT populate date-related props. + The EventNonDateInput has been normalized (id => publicId, etc). + */ + function parseEventDef(raw, sourceId, allDay, hasEnd, calendar) { + var leftovers = {}; + var def = pluckNonDateProps(raw, calendar, leftovers); + def.defId = String(uid++); + def.sourceId = sourceId; + def.allDay = allDay; + def.hasEnd = hasEnd; + for (var _i = 0, _a = calendar.pluginSystem.hooks.eventDefParsers; _i < _a.length; _i++) { + var eventDefParser = _a[_i]; + var newLeftovers = {}; + eventDefParser(def, leftovers, newLeftovers); + leftovers = newLeftovers; + } + def.extendedProps = __assign(leftovers, def.extendedProps || {}); + // help out EventApi from having user modify props + Object.freeze(def.ui.classNames); + Object.freeze(def.extendedProps); + return def; + } + function createEventInstance(defId, range, forcedStartTzo, forcedEndTzo) { + return { + instanceId: String(uid++), + defId: defId, + range: range, + forcedStartTzo: forcedStartTzo == null ? null : forcedStartTzo, + forcedEndTzo: forcedEndTzo == null ? null : forcedEndTzo + }; + } + function parseSingle(raw, allDayDefault, calendar, leftovers, allowOpenRange) { + var props = pluckDateProps(raw, leftovers); + var allDay = props.allDay; + var startMeta; + var startMarker = null; + var hasEnd = false; + var endMeta; + var endMarker = null; + startMeta = calendar.dateEnv.createMarkerMeta(props.start); + if (startMeta) { + startMarker = startMeta.marker; + } + else if (!allowOpenRange) { + return null; + } + if (props.end != null) { + endMeta = calendar.dateEnv.createMarkerMeta(props.end); + } + if (allDay == null) { + if (allDayDefault != null) { + allDay = allDayDefault; + } + else { + // fall back to the date props LAST + allDay = (!startMeta || startMeta.isTimeUnspecified) && + (!endMeta || endMeta.isTimeUnspecified); + } + } + if (allDay && startMarker) { + startMarker = startOfDay(startMarker); + } + if (endMeta) { + endMarker = endMeta.marker; + if (allDay) { + endMarker = startOfDay(endMarker); + } + if (startMarker && endMarker <= startMarker) { + endMarker = null; + } + } + if (endMarker) { + hasEnd = true; + } + else if (!allowOpenRange) { + hasEnd = calendar.opt('forceEventDuration') || false; + endMarker = calendar.dateEnv.add(startMarker, allDay ? + calendar.defaultAllDayEventDuration : + calendar.defaultTimedEventDuration); + } + return { + allDay: allDay, + hasEnd: hasEnd, + range: { start: startMarker, end: endMarker }, + forcedStartTzo: startMeta ? startMeta.forcedTzo : null, + forcedEndTzo: endMeta ? endMeta.forcedTzo : null + }; + } + function pluckDateProps(raw, leftovers) { + var props = refineProps(raw, DATE_PROPS, {}, leftovers); + props.start = (props.start !== null) ? props.start : props.date; + delete props.date; + return props; + } + function pluckNonDateProps(raw, calendar, leftovers) { + var preLeftovers = {}; + var props = refineProps(raw, NON_DATE_PROPS, {}, preLeftovers); + var ui = processUnscopedUiProps(preLeftovers, calendar, leftovers); + props.publicId = props.id; + delete props.id; + props.ui = ui; + return props; + } + function computeIsAllDayDefault(sourceId, calendar) { + var res = null; + if (sourceId) { + var source = calendar.state.eventSources[sourceId]; + res = source.allDayDefault; + } + if (res == null) { + res = calendar.opt('allDayDefault'); + } + return res; + } + + var DEF_DEFAULTS = { + startTime: '09:00', + endTime: '17:00', + daysOfWeek: [1, 2, 3, 4, 5], + rendering: 'inverse-background', + classNames: 'fc-nonbusiness', + groupId: '_businessHours' // so multiple defs get grouped + }; + /* + TODO: pass around as EventDefHash!!! + */ + function parseBusinessHours(input, calendar) { + return parseEvents(refineInputs(input), '', calendar); + } + function refineInputs(input) { + var rawDefs; + if (input === true) { + rawDefs = [{}]; // will get DEF_DEFAULTS verbatim + } + else if (Array.isArray(input)) { + // if specifying an array, every sub-definition NEEDS a day-of-week + rawDefs = input.filter(function (rawDef) { + return rawDef.daysOfWeek; + }); + } + else if (typeof input === 'object' && input) { // non-null object + rawDefs = [input]; + } + else { // is probably false + rawDefs = []; + } + rawDefs = rawDefs.map(function (rawDef) { + return __assign({}, DEF_DEFAULTS, rawDef); + }); + return rawDefs; + } + + function memoizeRendering(renderFunc, unrenderFunc, dependencies) { + if (dependencies === void 0) { dependencies = []; } + var dependents = []; + var thisContext; + var prevArgs; + function unrender() { + if (prevArgs) { + for (var _i = 0, dependents_1 = dependents; _i < dependents_1.length; _i++) { + var dependent = dependents_1[_i]; + dependent.unrender(); + } + if (unrenderFunc) { + unrenderFunc.apply(thisContext, prevArgs); + } + prevArgs = null; + } + } + function res() { + if (!prevArgs || !isArraysEqual(prevArgs, arguments)) { + unrender(); + thisContext = this; + prevArgs = arguments; + renderFunc.apply(this, arguments); + } + } + res.dependents = dependents; + res.unrender = unrender; + for (var _i = 0, dependencies_1 = dependencies; _i < dependencies_1.length; _i++) { + var dependency = dependencies_1[_i]; + dependency.dependents.push(res); + } + return res; + } + + var EMPTY_EVENT_STORE = createEmptyEventStore(); // for purecomponents. TODO: keep elsewhere + var Splitter = /** @class */ (function () { + function Splitter() { + this.getKeysForEventDefs = memoize(this._getKeysForEventDefs); + this.splitDateSelection = memoize(this._splitDateSpan); + this.splitEventStore = memoize(this._splitEventStore); + this.splitIndividualUi = memoize(this._splitIndividualUi); + this.splitEventDrag = memoize(this._splitInteraction); + this.splitEventResize = memoize(this._splitInteraction); + this.eventUiBuilders = {}; // TODO: typescript protection + } + Splitter.prototype.splitProps = function (props) { + var _this = this; + var keyInfos = this.getKeyInfo(props); + var defKeys = this.getKeysForEventDefs(props.eventStore); + var dateSelections = this.splitDateSelection(props.dateSelection); + var individualUi = this.splitIndividualUi(props.eventUiBases, defKeys); // the individual *bases* + var eventStores = this.splitEventStore(props.eventStore, defKeys); + var eventDrags = this.splitEventDrag(props.eventDrag); + var eventResizes = this.splitEventResize(props.eventResize); + var splitProps = {}; + this.eventUiBuilders = mapHash(keyInfos, function (info, key) { + return _this.eventUiBuilders[key] || memoize(buildEventUiForKey); + }); + for (var key in keyInfos) { + var keyInfo = keyInfos[key]; + var eventStore = eventStores[key] || EMPTY_EVENT_STORE; + var buildEventUi = this.eventUiBuilders[key]; + splitProps[key] = { + businessHours: keyInfo.businessHours || props.businessHours, + dateSelection: dateSelections[key] || null, + eventStore: eventStore, + eventUiBases: buildEventUi(props.eventUiBases[''], keyInfo.ui, individualUi[key]), + eventSelection: eventStore.instances[props.eventSelection] ? props.eventSelection : '', + eventDrag: eventDrags[key] || null, + eventResize: eventResizes[key] || null + }; + } + return splitProps; + }; + Splitter.prototype._splitDateSpan = function (dateSpan) { + var dateSpans = {}; + if (dateSpan) { + var keys = this.getKeysForDateSpan(dateSpan); + for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) { + var key = keys_1[_i]; + dateSpans[key] = dateSpan; + } + } + return dateSpans; + }; + Splitter.prototype._getKeysForEventDefs = function (eventStore) { + var _this = this; + return mapHash(eventStore.defs, function (eventDef) { + return _this.getKeysForEventDef(eventDef); + }); + }; + Splitter.prototype._splitEventStore = function (eventStore, defKeys) { + var defs = eventStore.defs, instances = eventStore.instances; + var splitStores = {}; + for (var defId in defs) { + for (var _i = 0, _a = defKeys[defId]; _i < _a.length; _i++) { + var key = _a[_i]; + if (!splitStores[key]) { + splitStores[key] = createEmptyEventStore(); + } + splitStores[key].defs[defId] = defs[defId]; + } + } + for (var instanceId in instances) { + var instance = instances[instanceId]; + for (var _b = 0, _c = defKeys[instance.defId]; _b < _c.length; _b++) { + var key = _c[_b]; + if (splitStores[key]) { // must have already been created + splitStores[key].instances[instanceId] = instance; + } + } + } + return splitStores; + }; + Splitter.prototype._splitIndividualUi = function (eventUiBases, defKeys) { + var splitHashes = {}; + for (var defId in eventUiBases) { + if (defId) { // not the '' key + for (var _i = 0, _a = defKeys[defId]; _i < _a.length; _i++) { + var key = _a[_i]; + if (!splitHashes[key]) { + splitHashes[key] = {}; + } + splitHashes[key][defId] = eventUiBases[defId]; + } + } + } + return splitHashes; + }; + Splitter.prototype._splitInteraction = function (interaction) { + var splitStates = {}; + if (interaction) { + var affectedStores_1 = this._splitEventStore(interaction.affectedEvents, this._getKeysForEventDefs(interaction.affectedEvents) // can't use cached. might be events from other calendar + ); + // can't rely on defKeys because event data is mutated + var mutatedKeysByDefId = this._getKeysForEventDefs(interaction.mutatedEvents); + var mutatedStores_1 = this._splitEventStore(interaction.mutatedEvents, mutatedKeysByDefId); + var populate = function (key) { + if (!splitStates[key]) { + splitStates[key] = { + affectedEvents: affectedStores_1[key] || EMPTY_EVENT_STORE, + mutatedEvents: mutatedStores_1[key] || EMPTY_EVENT_STORE, + isEvent: interaction.isEvent, + origSeg: interaction.origSeg + }; + } + }; + for (var key in affectedStores_1) { + populate(key); + } + for (var key in mutatedStores_1) { + populate(key); + } + } + return splitStates; + }; + return Splitter; + }()); + function buildEventUiForKey(allUi, eventUiForKey, individualUi) { + var baseParts = []; + if (allUi) { + baseParts.push(allUi); + } + if (eventUiForKey) { + baseParts.push(eventUiForKey); + } + var stuff = { + '': combineEventUis(baseParts) + }; + if (individualUi) { + __assign(stuff, individualUi); + } + return stuff; + } + + // Generates HTML for an anchor to another view into the calendar. + // Will either generate an
tag or a non-clickable tag, depending on enabled settings. + // `gotoOptions` can either be a DateMarker, or an object with the form: + // { date, type, forceOff } + // `type` is a view-type like "day" or "week". default value is "day". + // `attrs` and `innerHtml` are use to generate the rest of the HTML tag. + function buildGotoAnchorHtml(component, gotoOptions, attrs, innerHtml) { + var dateEnv = component.dateEnv; + var date; + var type; + var forceOff; + var finalOptions; + if (gotoOptions instanceof Date) { + date = gotoOptions; // a single date-like input + } + else { + date = gotoOptions.date; + type = gotoOptions.type; + forceOff = gotoOptions.forceOff; + } + finalOptions = { + date: dateEnv.formatIso(date, { omitTime: true }), + type: type || 'day' + }; + if (typeof attrs === 'string') { + innerHtml = attrs; + attrs = null; + } + attrs = attrs ? ' ' + attrsToStr(attrs) : ''; // will have a leading space + innerHtml = innerHtml || ''; + if (!forceOff && component.opt('navLinks')) { + return '' + + innerHtml + + ''; + } + else { + return '' + + innerHtml + + ''; + } + } + function getAllDayHtml(component) { + return component.opt('allDayHtml') || htmlEscape(component.opt('allDayText')); + } + // Computes HTML classNames for a single-day element + function getDayClasses(date, dateProfile, context, noThemeHighlight) { + var calendar = context.calendar, view = context.view, theme = context.theme, dateEnv = context.dateEnv; + var classes = []; + var todayStart; + var todayEnd; + if (!rangeContainsMarker(dateProfile.activeRange, date)) { + classes.push('fc-disabled-day'); + } + else { + classes.push('fc-' + DAY_IDS[date.getUTCDay()]); + if (view.opt('monthMode') && + dateEnv.getMonth(date) !== dateEnv.getMonth(dateProfile.currentRange.start)) { + classes.push('fc-other-month'); + } + todayStart = startOfDay(calendar.getNow()); + todayEnd = addDays(todayStart, 1); + if (date < todayStart) { + classes.push('fc-past'); + } + else if (date >= todayEnd) { + classes.push('fc-future'); + } + else { + classes.push('fc-today'); + if (noThemeHighlight !== true) { + classes.push(theme.getClass('today')); + } + } + } + return classes; + } + + // given a function that resolves a result asynchronously. + // the function can either call passed-in success and failure callbacks, + // or it can return a promise. + // if you need to pass additional params to func, bind them first. + function unpromisify(func, success, failure) { + // guard against success/failure callbacks being called more than once + // and guard against a promise AND callback being used together. + var isResolved = false; + var wrappedSuccess = function () { + if (!isResolved) { + isResolved = true; + success.apply(this, arguments); + } + }; + var wrappedFailure = function () { + if (!isResolved) { + isResolved = true; + if (failure) { + failure.apply(this, arguments); + } + } + }; + var res = func(wrappedSuccess, wrappedFailure); + if (res && typeof res.then === 'function') { + res.then(wrappedSuccess, wrappedFailure); + } + } + + var Mixin = /** @class */ (function () { + function Mixin() { + } + // mix into a CLASS + Mixin.mixInto = function (destClass) { + this.mixIntoObj(destClass.prototype); + }; + // mix into ANY object + Mixin.mixIntoObj = function (destObj) { + var _this = this; + Object.getOwnPropertyNames(this.prototype).forEach(function (name) { + if (!destObj[name]) { // if destination doesn't already define it + destObj[name] = _this.prototype[name]; + } + }); + }; + /* + will override existing methods + TODO: remove! not used anymore + */ + Mixin.mixOver = function (destClass) { + var _this = this; + Object.getOwnPropertyNames(this.prototype).forEach(function (name) { + destClass.prototype[name] = _this.prototype[name]; + }); + }; + return Mixin; + }()); + + /* + USAGE: + import { default as EmitterMixin, EmitterInterface } from './EmitterMixin' + in class: + on: EmitterInterface['on'] + one: EmitterInterface['one'] + off: EmitterInterface['off'] + trigger: EmitterInterface['trigger'] + triggerWith: EmitterInterface['triggerWith'] + hasHandlers: EmitterInterface['hasHandlers'] + after class: + EmitterMixin.mixInto(TheClass) + */ + var EmitterMixin = /** @class */ (function (_super) { + __extends(EmitterMixin, _super); + function EmitterMixin() { + return _super !== null && _super.apply(this, arguments) || this; + } + EmitterMixin.prototype.on = function (type, handler) { + addToHash(this._handlers || (this._handlers = {}), type, handler); + return this; // for chaining + }; + // todo: add comments + EmitterMixin.prototype.one = function (type, handler) { + addToHash(this._oneHandlers || (this._oneHandlers = {}), type, handler); + return this; // for chaining + }; + EmitterMixin.prototype.off = function (type, handler) { + if (this._handlers) { + removeFromHash(this._handlers, type, handler); + } + if (this._oneHandlers) { + removeFromHash(this._oneHandlers, type, handler); + } + return this; // for chaining + }; + EmitterMixin.prototype.trigger = function (type) { + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + this.triggerWith(type, this, args); + return this; // for chaining + }; + EmitterMixin.prototype.triggerWith = function (type, context, args) { + if (this._handlers) { + applyAll(this._handlers[type], context, args); + } + if (this._oneHandlers) { + applyAll(this._oneHandlers[type], context, args); + delete this._oneHandlers[type]; // will never fire again + } + return this; // for chaining + }; + EmitterMixin.prototype.hasHandlers = function (type) { + return (this._handlers && this._handlers[type] && this._handlers[type].length) || + (this._oneHandlers && this._oneHandlers[type] && this._oneHandlers[type].length); + }; + return EmitterMixin; + }(Mixin)); + function addToHash(hash, type, handler) { + (hash[type] || (hash[type] = [])) + .push(handler); + } + function removeFromHash(hash, type, handler) { + if (handler) { + if (hash[type]) { + hash[type] = hash[type].filter(function (func) { + return func !== handler; + }); + } + } + else { + delete hash[type]; // remove all handler funcs for this type + } + } + + /* + Records offset information for a set of elements, relative to an origin element. + Can record the left/right OR the top/bottom OR both. + Provides methods for querying the cache by position. + */ + var PositionCache = /** @class */ (function () { + function PositionCache(originEl, els, isHorizontal, isVertical) { + this.originEl = originEl; + this.els = els; + this.isHorizontal = isHorizontal; + this.isVertical = isVertical; + } + // Queries the els for coordinates and stores them. + // Call this method before using and of the get* methods below. + PositionCache.prototype.build = function () { + var originEl = this.originEl; + var originClientRect = this.originClientRect = + originEl.getBoundingClientRect(); // relative to viewport top-left + if (this.isHorizontal) { + this.buildElHorizontals(originClientRect.left); + } + if (this.isVertical) { + this.buildElVerticals(originClientRect.top); + } + }; + // Populates the left/right internal coordinate arrays + PositionCache.prototype.buildElHorizontals = function (originClientLeft) { + var lefts = []; + var rights = []; + for (var _i = 0, _a = this.els; _i < _a.length; _i++) { + var el = _a[_i]; + var rect = el.getBoundingClientRect(); + lefts.push(rect.left - originClientLeft); + rights.push(rect.right - originClientLeft); + } + this.lefts = lefts; + this.rights = rights; + }; + // Populates the top/bottom internal coordinate arrays + PositionCache.prototype.buildElVerticals = function (originClientTop) { + var tops = []; + var bottoms = []; + for (var _i = 0, _a = this.els; _i < _a.length; _i++) { + var el = _a[_i]; + var rect = el.getBoundingClientRect(); + tops.push(rect.top - originClientTop); + bottoms.push(rect.bottom - originClientTop); + } + this.tops = tops; + this.bottoms = bottoms; + }; + // Given a left offset (from document left), returns the index of the el that it horizontally intersects. + // If no intersection is made, returns undefined. + PositionCache.prototype.leftToIndex = function (leftPosition) { + var lefts = this.lefts; + var rights = this.rights; + var len = lefts.length; + var i; + for (i = 0; i < len; i++) { + if (leftPosition >= lefts[i] && leftPosition < rights[i]) { + return i; + } + } + }; + // Given a top offset (from document top), returns the index of the el that it vertically intersects. + // If no intersection is made, returns undefined. + PositionCache.prototype.topToIndex = function (topPosition) { + var tops = this.tops; + var bottoms = this.bottoms; + var len = tops.length; + var i; + for (i = 0; i < len; i++) { + if (topPosition >= tops[i] && topPosition < bottoms[i]) { + return i; + } + } + }; + // Gets the width of the element at the given index + PositionCache.prototype.getWidth = function (leftIndex) { + return this.rights[leftIndex] - this.lefts[leftIndex]; + }; + // Gets the height of the element at the given index + PositionCache.prototype.getHeight = function (topIndex) { + return this.bottoms[topIndex] - this.tops[topIndex]; + }; + return PositionCache; + }()); + + /* + An object for getting/setting scroll-related information for an element. + Internally, this is done very differently for window versus DOM element, + so this object serves as a common interface. + */ + var ScrollController = /** @class */ (function () { + function ScrollController() { + } + ScrollController.prototype.getMaxScrollTop = function () { + return this.getScrollHeight() - this.getClientHeight(); + }; + ScrollController.prototype.getMaxScrollLeft = function () { + return this.getScrollWidth() - this.getClientWidth(); + }; + ScrollController.prototype.canScrollVertically = function () { + return this.getMaxScrollTop() > 0; + }; + ScrollController.prototype.canScrollHorizontally = function () { + return this.getMaxScrollLeft() > 0; + }; + ScrollController.prototype.canScrollUp = function () { + return this.getScrollTop() > 0; + }; + ScrollController.prototype.canScrollDown = function () { + return this.getScrollTop() < this.getMaxScrollTop(); + }; + ScrollController.prototype.canScrollLeft = function () { + return this.getScrollLeft() > 0; + }; + ScrollController.prototype.canScrollRight = function () { + return this.getScrollLeft() < this.getMaxScrollLeft(); + }; + return ScrollController; + }()); + var ElementScrollController = /** @class */ (function (_super) { + __extends(ElementScrollController, _super); + function ElementScrollController(el) { + var _this = _super.call(this) || this; + _this.el = el; + return _this; + } + ElementScrollController.prototype.getScrollTop = function () { + return this.el.scrollTop; + }; + ElementScrollController.prototype.getScrollLeft = function () { + return this.el.scrollLeft; + }; + ElementScrollController.prototype.setScrollTop = function (top) { + this.el.scrollTop = top; + }; + ElementScrollController.prototype.setScrollLeft = function (left) { + this.el.scrollLeft = left; + }; + ElementScrollController.prototype.getScrollWidth = function () { + return this.el.scrollWidth; + }; + ElementScrollController.prototype.getScrollHeight = function () { + return this.el.scrollHeight; + }; + ElementScrollController.prototype.getClientHeight = function () { + return this.el.clientHeight; + }; + ElementScrollController.prototype.getClientWidth = function () { + return this.el.clientWidth; + }; + return ElementScrollController; + }(ScrollController)); + var WindowScrollController = /** @class */ (function (_super) { + __extends(WindowScrollController, _super); + function WindowScrollController() { + return _super !== null && _super.apply(this, arguments) || this; + } + WindowScrollController.prototype.getScrollTop = function () { + return window.pageYOffset; + }; + WindowScrollController.prototype.getScrollLeft = function () { + return window.pageXOffset; + }; + WindowScrollController.prototype.setScrollTop = function (n) { + window.scroll(window.pageXOffset, n); + }; + WindowScrollController.prototype.setScrollLeft = function (n) { + window.scroll(n, window.pageYOffset); + }; + WindowScrollController.prototype.getScrollWidth = function () { + return document.documentElement.scrollWidth; + }; + WindowScrollController.prototype.getScrollHeight = function () { + return document.documentElement.scrollHeight; + }; + WindowScrollController.prototype.getClientHeight = function () { + return document.documentElement.clientHeight; + }; + WindowScrollController.prototype.getClientWidth = function () { + return document.documentElement.clientWidth; + }; + return WindowScrollController; + }(ScrollController)); + + /* + Embodies a div that has potential scrollbars + */ + var ScrollComponent = /** @class */ (function (_super) { + __extends(ScrollComponent, _super); + function ScrollComponent(overflowX, overflowY) { + var _this = _super.call(this, createElement('div', { + className: 'fc-scroller' + })) || this; + _this.overflowX = overflowX; + _this.overflowY = overflowY; + _this.applyOverflow(); + return _this; + } + // sets to natural height, unlocks overflow + ScrollComponent.prototype.clear = function () { + this.setHeight('auto'); + this.applyOverflow(); + }; + ScrollComponent.prototype.destroy = function () { + removeElement(this.el); + }; + // Overflow + // ----------------------------------------------------------------------------------------------------------------- + ScrollComponent.prototype.applyOverflow = function () { + applyStyle(this.el, { + overflowX: this.overflowX, + overflowY: this.overflowY + }); + }; + // Causes any 'auto' overflow values to resolves to 'scroll' or 'hidden'. + // Useful for preserving scrollbar widths regardless of future resizes. + // Can pass in scrollbarWidths for optimization. + ScrollComponent.prototype.lockOverflow = function (scrollbarWidths) { + var overflowX = this.overflowX; + var overflowY = this.overflowY; + scrollbarWidths = scrollbarWidths || this.getScrollbarWidths(); + if (overflowX === 'auto') { + overflowX = (scrollbarWidths.bottom || // horizontal scrollbars? + this.canScrollHorizontally() // OR scrolling pane with massless scrollbars? + ) ? 'scroll' : 'hidden'; + } + if (overflowY === 'auto') { + overflowY = (scrollbarWidths.left || scrollbarWidths.right || // horizontal scrollbars? + this.canScrollVertically() // OR scrolling pane with massless scrollbars? + ) ? 'scroll' : 'hidden'; + } + applyStyle(this.el, { overflowX: overflowX, overflowY: overflowY }); + }; + ScrollComponent.prototype.setHeight = function (height) { + applyStyleProp(this.el, 'height', height); + }; + ScrollComponent.prototype.getScrollbarWidths = function () { + var edges = computeEdges(this.el); + return { + left: edges.scrollbarLeft, + right: edges.scrollbarRight, + bottom: edges.scrollbarBottom + }; + }; + return ScrollComponent; + }(ElementScrollController)); + + var Theme = /** @class */ (function () { + function Theme(calendarOptions) { + this.calendarOptions = calendarOptions; + this.processIconOverride(); + } + Theme.prototype.processIconOverride = function () { + if (this.iconOverrideOption) { + this.setIconOverride(this.calendarOptions[this.iconOverrideOption]); + } + }; + Theme.prototype.setIconOverride = function (iconOverrideHash) { + var iconClassesCopy; + var buttonName; + if (typeof iconOverrideHash === 'object' && iconOverrideHash) { // non-null object + iconClassesCopy = __assign({}, this.iconClasses); + for (buttonName in iconOverrideHash) { + iconClassesCopy[buttonName] = this.applyIconOverridePrefix(iconOverrideHash[buttonName]); + } + this.iconClasses = iconClassesCopy; + } + else if (iconOverrideHash === false) { + this.iconClasses = {}; + } + }; + Theme.prototype.applyIconOverridePrefix = function (className) { + var prefix = this.iconOverridePrefix; + if (prefix && className.indexOf(prefix) !== 0) { // if not already present + className = prefix + className; + } + return className; + }; + Theme.prototype.getClass = function (key) { + return this.classes[key] || ''; + }; + Theme.prototype.getIconClass = function (buttonName) { + var className = this.iconClasses[buttonName]; + if (className) { + return this.baseIconClass + ' ' + className; + } + return ''; + }; + Theme.prototype.getCustomButtonIconClass = function (customButtonProps) { + var className; + if (this.iconOverrideCustomButtonOption) { + className = customButtonProps[this.iconOverrideCustomButtonOption]; + if (className) { + return this.baseIconClass + ' ' + this.applyIconOverridePrefix(className); + } + } + return ''; + }; + return Theme; + }()); + Theme.prototype.classes = {}; + Theme.prototype.iconClasses = {}; + Theme.prototype.baseIconClass = ''; + Theme.prototype.iconOverridePrefix = ''; + + var guid = 0; + var Component = /** @class */ (function () { + function Component(context, isView) { + // HACK to populate view at top of component instantiation call chain + if (isView) { + context.view = this; + } + this.uid = String(guid++); + this.context = context; + this.dateEnv = context.dateEnv; + this.theme = context.theme; + this.view = context.view; + this.calendar = context.calendar; + this.isRtl = this.opt('dir') === 'rtl'; + } + Component.addEqualityFuncs = function (newFuncs) { + this.prototype.equalityFuncs = __assign({}, this.prototype.equalityFuncs, newFuncs); + }; + Component.prototype.opt = function (name) { + return this.context.options[name]; + }; + Component.prototype.receiveProps = function (props) { + var _a = recycleProps(this.props || {}, props, this.equalityFuncs), anyChanges = _a.anyChanges, comboProps = _a.comboProps; + this.props = comboProps; + if (anyChanges) { + this.render(comboProps); + } + }; + Component.prototype.render = function (props) { + }; + // after destroy is called, this component won't ever be used again + Component.prototype.destroy = function () { + }; + return Component; + }()); + Component.prototype.equalityFuncs = {}; + /* + Reuses old values when equal. If anything is unequal, returns newProps as-is. + Great for PureComponent, but won't be feasible with React, so just eliminate and use React's DOM diffing. + */ + function recycleProps(oldProps, newProps, equalityFuncs) { + var comboProps = {}; // some old, some new + var anyChanges = false; + for (var key in newProps) { + if (key in oldProps && (oldProps[key] === newProps[key] || + (equalityFuncs[key] && equalityFuncs[key](oldProps[key], newProps[key])))) { + // equal to old? use old prop + comboProps[key] = oldProps[key]; + } + else { + comboProps[key] = newProps[key]; + anyChanges = true; + } + } + for (var key in oldProps) { + if (!(key in newProps)) { + anyChanges = true; + break; + } + } + return { anyChanges: anyChanges, comboProps: comboProps }; + } + + /* + PURPOSES: + - hook up to fg, fill, and mirror renderers + - interface for dragging and hits + */ + var DateComponent = /** @class */ (function (_super) { + __extends(DateComponent, _super); + function DateComponent(context, el, isView) { + var _this = _super.call(this, context, isView) || this; + _this.el = el; + return _this; + } + DateComponent.prototype.destroy = function () { + _super.prototype.destroy.call(this); + removeElement(this.el); + }; + // TODO: WHAT ABOUT (sourceSeg && sourceSeg.component.doesDragMirror) + // + // Event Drag-n-Drop Rendering (for both events and external elements) + // --------------------------------------------------------------------------------------------------------------- + /* + renderEventDragSegs(state: EventSegUiInteractionState) { + if (state) { + let { isEvent, segs, sourceSeg } = state + + if (this.eventRenderer) { + this.eventRenderer.hideByHash(state.affectedInstances) + } + + // if the user is dragging something that is considered an event with real event data, + // and this component likes to do drag mirrors OR the component where the seg came from + // likes to do drag mirrors, then render a drag mirror. + if (isEvent && (this.doesDragMirror || sourceSeg && sourceSeg.component.doesDragMirror)) { + if (this.mirrorRenderer) { + this.mirrorRenderer.renderSegs(segs, { isDragging: true, sourceSeg }) + } + } + + // if it would be impossible to render a drag mirror OR this component likes to render + // highlights, then render a highlight. + if (!isEvent || this.doesDragHighlight) { + if (this.fillRenderer) { + this.fillRenderer.renderSegs('highlight', segs) + } + } + } + } + */ + // Hit System + // ----------------------------------------------------------------------------------------------------------------- + DateComponent.prototype.buildPositionCaches = function () { + }; + DateComponent.prototype.queryHit = function (positionLeft, positionTop, elWidth, elHeight) { + return null; // this should be abstract + }; + // Validation + // ----------------------------------------------------------------------------------------------------------------- + DateComponent.prototype.isInteractionValid = function (interaction) { + var calendar = this.calendar; + var dateProfile = this.props.dateProfile; // HACK + var instances = interaction.mutatedEvents.instances; + if (dateProfile) { // HACK for DayTile + for (var instanceId in instances) { + if (!rangeContainsRange(dateProfile.validRange, instances[instanceId].range)) { + return false; + } + } + } + return isInteractionValid(interaction, calendar); + }; + DateComponent.prototype.isDateSelectionValid = function (selection) { + var dateProfile = this.props.dateProfile; // HACK + if (dateProfile && // HACK for DayTile + !rangeContainsRange(dateProfile.validRange, selection.range)) { + return false; + } + return isDateSelectionValid(selection, this.calendar); + }; + // Triggering + // ----------------------------------------------------------------------------------------------------------------- + // TODO: move to Calendar + DateComponent.prototype.publiclyTrigger = function (name, args) { + var calendar = this.calendar; + return calendar.publiclyTrigger(name, args); + }; + DateComponent.prototype.publiclyTriggerAfterSizing = function (name, args) { + var calendar = this.calendar; + return calendar.publiclyTriggerAfterSizing(name, args); + }; + DateComponent.prototype.hasPublicHandlers = function (name) { + var calendar = this.calendar; + return calendar.hasPublicHandlers(name); + }; + DateComponent.prototype.triggerRenderedSegs = function (segs, isMirrors) { + var calendar = this.calendar; + if (this.hasPublicHandlers('eventPositioned')) { + for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) { + var seg = segs_1[_i]; + this.publiclyTriggerAfterSizing('eventPositioned', [ + { + event: new EventApi(calendar, seg.eventRange.def, seg.eventRange.instance), + isMirror: isMirrors, + isStart: seg.isStart, + isEnd: seg.isEnd, + el: seg.el, + view: this // safe to cast because this method is only called on context.view + } + ]); + } + } + if (!calendar.state.loadingLevel) { // avoid initial empty state while pending + calendar.afterSizingTriggers._eventsPositioned = [null]; // fire once + } + }; + DateComponent.prototype.triggerWillRemoveSegs = function (segs, isMirrors) { + var calendar = this.calendar; + for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) { + var seg = segs_2[_i]; + calendar.trigger('eventElRemove', seg.el); + } + if (this.hasPublicHandlers('eventDestroy')) { + for (var _a = 0, segs_3 = segs; _a < segs_3.length; _a++) { + var seg = segs_3[_a]; + this.publiclyTrigger('eventDestroy', [ + { + event: new EventApi(calendar, seg.eventRange.def, seg.eventRange.instance), + isMirror: isMirrors, + el: seg.el, + view: this // safe to cast because this method is only called on context.view + } + ]); + } + } + }; + // Pointer Interaction Utils + // ----------------------------------------------------------------------------------------------------------------- + DateComponent.prototype.isValidSegDownEl = function (el) { + return !this.props.eventDrag && // HACK + !this.props.eventResize && // HACK + !elementClosest(el, '.fc-mirror') && + (this.isPopover() || !this.isInPopover(el)); + // ^above line ensures we don't detect a seg interaction within a nested component. + // it's a HACK because it only supports a popover as the nested component. + }; + DateComponent.prototype.isValidDateDownEl = function (el) { + var segEl = elementClosest(el, this.fgSegSelector); + return (!segEl || segEl.classList.contains('fc-mirror')) && + !elementClosest(el, '.fc-more') && // a "more.." link + !elementClosest(el, 'a[data-goto]') && // a clickable nav link + !this.isInPopover(el); + }; + DateComponent.prototype.isPopover = function () { + return this.el.classList.contains('fc-popover'); + }; + DateComponent.prototype.isInPopover = function (el) { + return Boolean(elementClosest(el, '.fc-popover')); + }; + return DateComponent; + }(Component)); + DateComponent.prototype.fgSegSelector = '.fc-event-container > *'; + DateComponent.prototype.bgSegSelector = '.fc-bgevent:not(.fc-nonbusiness)'; + + var uid$1 = 0; + function createPlugin(input) { + return { + id: String(uid$1++), + deps: input.deps || [], + reducers: input.reducers || [], + eventDefParsers: input.eventDefParsers || [], + isDraggableTransformers: input.isDraggableTransformers || [], + eventDragMutationMassagers: input.eventDragMutationMassagers || [], + eventDefMutationAppliers: input.eventDefMutationAppliers || [], + dateSelectionTransformers: input.dateSelectionTransformers || [], + datePointTransforms: input.datePointTransforms || [], + dateSpanTransforms: input.dateSpanTransforms || [], + views: input.views || {}, + viewPropsTransformers: input.viewPropsTransformers || [], + isPropsValid: input.isPropsValid || null, + externalDefTransforms: input.externalDefTransforms || [], + eventResizeJoinTransforms: input.eventResizeJoinTransforms || [], + viewContainerModifiers: input.viewContainerModifiers || [], + eventDropTransformers: input.eventDropTransformers || [], + componentInteractions: input.componentInteractions || [], + calendarInteractions: input.calendarInteractions || [], + themeClasses: input.themeClasses || {}, + eventSourceDefs: input.eventSourceDefs || [], + cmdFormatter: input.cmdFormatter, + recurringTypes: input.recurringTypes || [], + namedTimeZonedImpl: input.namedTimeZonedImpl, + defaultView: input.defaultView || '', + elementDraggingImpl: input.elementDraggingImpl, + optionChangeHandlers: input.optionChangeHandlers || {} + }; + } + var PluginSystem = /** @class */ (function () { + function PluginSystem() { + this.hooks = { + reducers: [], + eventDefParsers: [], + isDraggableTransformers: [], + eventDragMutationMassagers: [], + eventDefMutationAppliers: [], + dateSelectionTransformers: [], + datePointTransforms: [], + dateSpanTransforms: [], + views: {}, + viewPropsTransformers: [], + isPropsValid: null, + externalDefTransforms: [], + eventResizeJoinTransforms: [], + viewContainerModifiers: [], + eventDropTransformers: [], + componentInteractions: [], + calendarInteractions: [], + themeClasses: {}, + eventSourceDefs: [], + cmdFormatter: null, + recurringTypes: [], + namedTimeZonedImpl: null, + defaultView: '', + elementDraggingImpl: null, + optionChangeHandlers: {} + }; + this.addedHash = {}; + } + PluginSystem.prototype.add = function (plugin) { + if (!this.addedHash[plugin.id]) { + this.addedHash[plugin.id] = true; + for (var _i = 0, _a = plugin.deps; _i < _a.length; _i++) { + var dep = _a[_i]; + this.add(dep); + } + this.hooks = combineHooks(this.hooks, plugin); + } + }; + return PluginSystem; + }()); + function combineHooks(hooks0, hooks1) { + return { + reducers: hooks0.reducers.concat(hooks1.reducers), + eventDefParsers: hooks0.eventDefParsers.concat(hooks1.eventDefParsers), + isDraggableTransformers: hooks0.isDraggableTransformers.concat(hooks1.isDraggableTransformers), + eventDragMutationMassagers: hooks0.eventDragMutationMassagers.concat(hooks1.eventDragMutationMassagers), + eventDefMutationAppliers: hooks0.eventDefMutationAppliers.concat(hooks1.eventDefMutationAppliers), + dateSelectionTransformers: hooks0.dateSelectionTransformers.concat(hooks1.dateSelectionTransformers), + datePointTransforms: hooks0.datePointTransforms.concat(hooks1.datePointTransforms), + dateSpanTransforms: hooks0.dateSpanTransforms.concat(hooks1.dateSpanTransforms), + views: __assign({}, hooks0.views, hooks1.views), + viewPropsTransformers: hooks0.viewPropsTransformers.concat(hooks1.viewPropsTransformers), + isPropsValid: hooks1.isPropsValid || hooks0.isPropsValid, + externalDefTransforms: hooks0.externalDefTransforms.concat(hooks1.externalDefTransforms), + eventResizeJoinTransforms: hooks0.eventResizeJoinTransforms.concat(hooks1.eventResizeJoinTransforms), + viewContainerModifiers: hooks0.viewContainerModifiers.concat(hooks1.viewContainerModifiers), + eventDropTransformers: hooks0.eventDropTransformers.concat(hooks1.eventDropTransformers), + calendarInteractions: hooks0.calendarInteractions.concat(hooks1.calendarInteractions), + componentInteractions: hooks0.componentInteractions.concat(hooks1.componentInteractions), + themeClasses: __assign({}, hooks0.themeClasses, hooks1.themeClasses), + eventSourceDefs: hooks0.eventSourceDefs.concat(hooks1.eventSourceDefs), + cmdFormatter: hooks1.cmdFormatter || hooks0.cmdFormatter, + recurringTypes: hooks0.recurringTypes.concat(hooks1.recurringTypes), + namedTimeZonedImpl: hooks1.namedTimeZonedImpl || hooks0.namedTimeZonedImpl, + defaultView: hooks0.defaultView || hooks1.defaultView, + elementDraggingImpl: hooks0.elementDraggingImpl || hooks1.elementDraggingImpl, + optionChangeHandlers: __assign({}, hooks0.optionChangeHandlers, hooks1.optionChangeHandlers) + }; + } + + var eventSourceDef = { + ignoreRange: true, + parseMeta: function (raw) { + if (Array.isArray(raw)) { // short form + return raw; + } + else if (Array.isArray(raw.events)) { + return raw.events; + } + return null; + }, + fetch: function (arg, success) { + success({ + rawEvents: arg.eventSource.meta + }); + } + }; + var ArrayEventSourcePlugin = createPlugin({ + eventSourceDefs: [eventSourceDef] + }); + + var eventSourceDef$1 = { + parseMeta: function (raw) { + if (typeof raw === 'function') { // short form + return raw; + } + else if (typeof raw.events === 'function') { + return raw.events; + } + return null; + }, + fetch: function (arg, success, failure) { + var dateEnv = arg.calendar.dateEnv; + var func = arg.eventSource.meta; + unpromisify(func.bind(null, { + start: dateEnv.toDate(arg.range.start), + end: dateEnv.toDate(arg.range.end), + startStr: dateEnv.formatIso(arg.range.start), + endStr: dateEnv.formatIso(arg.range.end), + timeZone: dateEnv.timeZone + }), function (rawEvents) { + success({ rawEvents: rawEvents }); // needs an object response + }, failure // send errorObj directly to failure callback + ); + } + }; + var FuncEventSourcePlugin = createPlugin({ + eventSourceDefs: [eventSourceDef$1] + }); + + function requestJson(method, url, params, successCallback, failureCallback) { + method = method.toUpperCase(); + var body = null; + if (method === 'GET') { + url = injectQueryStringParams(url, params); + } + else { + body = encodeParams(params); + } + var xhr = new XMLHttpRequest(); + xhr.open(method, url, true); + if (method !== 'GET') { + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + } + xhr.onload = function () { + if (xhr.status >= 200 && xhr.status < 400) { + try { + var res = JSON.parse(xhr.responseText); + successCallback(res, xhr); + } + catch (err) { + failureCallback('Failure parsing JSON', xhr); + } + } + else { + failureCallback('Request failed', xhr); + } + }; + xhr.onerror = function () { + failureCallback('Request failed', xhr); + }; + xhr.send(body); + } + function injectQueryStringParams(url, params) { + return url + + (url.indexOf('?') === -1 ? '?' : '&') + + encodeParams(params); + } + function encodeParams(params) { + var parts = []; + for (var key in params) { + parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key])); + } + return parts.join('&'); + } + + var eventSourceDef$2 = { + parseMeta: function (raw) { + if (typeof raw === 'string') { // short form + raw = { url: raw }; + } + else if (!raw || typeof raw !== 'object' || !raw.url) { + return null; + } + return { + url: raw.url, + method: (raw.method || 'GET').toUpperCase(), + extraParams: raw.extraParams, + startParam: raw.startParam, + endParam: raw.endParam, + timeZoneParam: raw.timeZoneParam + }; + }, + fetch: function (arg, success, failure) { + var meta = arg.eventSource.meta; + var requestParams = buildRequestParams(meta, arg.range, arg.calendar); + requestJson(meta.method, meta.url, requestParams, function (rawEvents, xhr) { + success({ rawEvents: rawEvents, xhr: xhr }); + }, function (errorMessage, xhr) { + failure({ message: errorMessage, xhr: xhr }); + }); + } + }; + var JsonFeedEventSourcePlugin = createPlugin({ + eventSourceDefs: [eventSourceDef$2] + }); + function buildRequestParams(meta, range, calendar) { + var dateEnv = calendar.dateEnv; + var startParam; + var endParam; + var timeZoneParam; + var customRequestParams; + var params = {}; + startParam = meta.startParam; + if (startParam == null) { + startParam = calendar.opt('startParam'); + } + endParam = meta.endParam; + if (endParam == null) { + endParam = calendar.opt('endParam'); + } + timeZoneParam = meta.timeZoneParam; + if (timeZoneParam == null) { + timeZoneParam = calendar.opt('timeZoneParam'); + } + // retrieve any outbound GET/POST data from the options + if (typeof meta.extraParams === 'function') { + // supplied as a function that returns a key/value object + customRequestParams = meta.extraParams(); + } + else { + // probably supplied as a straight key/value object + customRequestParams = meta.extraParams || {}; + } + __assign(params, customRequestParams); + params[startParam] = dateEnv.formatIso(range.start); + params[endParam] = dateEnv.formatIso(range.end); + if (dateEnv.timeZone !== 'local') { + params[timeZoneParam] = dateEnv.timeZone; + } + return params; + } + + var recurring = { + parse: function (rawEvent, leftoverProps, dateEnv) { + var createMarker = dateEnv.createMarker.bind(dateEnv); + var processors = { + daysOfWeek: null, + startTime: createDuration, + endTime: createDuration, + startRecur: createMarker, + endRecur: createMarker + }; + var props = refineProps(rawEvent, processors, {}, leftoverProps); + var anyValid = false; + for (var propName in props) { + if (props[propName] != null) { + anyValid = true; + break; + } + } + if (anyValid) { + var duration = null; + if ('duration' in leftoverProps) { + duration = createDuration(leftoverProps.duration); + delete leftoverProps.duration; + } + if (!duration && props.startTime && props.endTime) { + duration = subtractDurations(props.endTime, props.startTime); + } + return { + allDayGuess: Boolean(!props.startTime && !props.endTime), + duration: duration, + typeData: props // doesn't need endTime anymore but oh well + }; + } + return null; + }, + expand: function (typeData, framingRange, dateEnv) { + var clippedFramingRange = intersectRanges(framingRange, { start: typeData.startRecur, end: typeData.endRecur }); + if (clippedFramingRange) { + return expandRanges(typeData.daysOfWeek, typeData.startTime, clippedFramingRange, dateEnv); + } + else { + return []; + } + } + }; + var SimpleRecurrencePlugin = createPlugin({ + recurringTypes: [recurring] + }); + function expandRanges(daysOfWeek, startTime, framingRange, dateEnv) { + var dowHash = daysOfWeek ? arrayToHash(daysOfWeek) : null; + var dayMarker = startOfDay(framingRange.start); + var endMarker = framingRange.end; + var instanceStarts = []; + while (dayMarker < endMarker) { + var instanceStart + // if everyday, or this particular day-of-week + = void 0; + // if everyday, or this particular day-of-week + if (!dowHash || dowHash[dayMarker.getUTCDay()]) { + if (startTime) { + instanceStart = dateEnv.add(dayMarker, startTime); + } + else { + instanceStart = dayMarker; + } + instanceStarts.push(instanceStart); + } + dayMarker = addDays(dayMarker, 1); + } + return instanceStarts; + } + + var DefaultOptionChangeHandlers = createPlugin({ + optionChangeHandlers: { + events: function (events, calendar, deepEqual) { + handleEventSources([events], calendar, deepEqual); + }, + eventSources: handleEventSources, + plugins: handlePlugins + } + }); + function handleEventSources(inputs, calendar, deepEqual) { + var unfoundSources = hashValuesToArray(calendar.state.eventSources); + var newInputs = []; + for (var _i = 0, inputs_1 = inputs; _i < inputs_1.length; _i++) { + var input = inputs_1[_i]; + var inputFound = false; + for (var i = 0; i < unfoundSources.length; i++) { + if (deepEqual(unfoundSources[i]._raw, input)) { + unfoundSources.splice(i, 1); // delete + inputFound = true; + break; + } + } + if (!inputFound) { + newInputs.push(input); + } + } + for (var _a = 0, unfoundSources_1 = unfoundSources; _a < unfoundSources_1.length; _a++) { + var unfoundSource = unfoundSources_1[_a]; + calendar.dispatch({ + type: 'REMOVE_EVENT_SOURCE', + sourceId: unfoundSource.sourceId + }); + } + for (var _b = 0, newInputs_1 = newInputs; _b < newInputs_1.length; _b++) { + var newInput = newInputs_1[_b]; + calendar.addEventSource(newInput); + } + } + // shortcoming: won't remove plugins + function handlePlugins(inputs, calendar) { + calendar.addPluginInputs(inputs); // will gracefully handle duplicates + } + + var config = {}; // TODO: make these options + var globalDefaults = { + defaultRangeSeparator: ' - ', + titleRangeSeparator: ' \u2013 ', + defaultTimedEventDuration: '01:00:00', + defaultAllDayEventDuration: { day: 1 }, + forceEventDuration: false, + nextDayThreshold: '00:00:00', + // display + columnHeader: true, + defaultView: '', + aspectRatio: 1.35, + header: { + left: 'title', + center: '', + right: 'today prev,next' + }, + weekends: true, + weekNumbers: false, + weekNumberCalculation: 'local', + editable: false, + // nowIndicator: false, + scrollTime: '06:00:00', + minTime: '00:00:00', + maxTime: '24:00:00', + showNonCurrentDates: true, + // event ajax + lazyFetching: true, + startParam: 'start', + endParam: 'end', + timeZoneParam: 'timeZone', + timeZone: 'local', + // allDayDefault: undefined, + // locale + locales: [], + locale: '', + // dir: will get this from the default locale + // buttonIcons: null, + // allows setting a min-height to the event segment to prevent short events overlapping each other + timeGridEventMinHeight: 0, + themeSystem: 'standard', + // eventResizableFromStart: false, + dragRevertDuration: 500, + dragScroll: true, + allDayMaintainDuration: false, + // selectable: false, + unselectAuto: true, + // selectMinDistance: 0, + dropAccept: '*', + eventOrder: 'start,-duration,allDay,title', + // ^ if start tie, longer events go before shorter. final tie-breaker is title text + // rerenderDelay: null, + eventLimit: false, + eventLimitClick: 'popover', + dayPopoverFormat: { month: 'long', day: 'numeric', year: 'numeric' }, + handleWindowResize: true, + windowResizeDelay: 100, + longPressDelay: 1000, + eventDragMinDistance: 5 // only applies to mouse + }; + var rtlDefaults = { + header: { + left: 'next,prev today', + center: '', + right: 'title' + }, + buttonIcons: { + // TODO: make RTL support the responibility of the theme + prev: 'fc-icon-chevron-right', + next: 'fc-icon-chevron-left', + prevYear: 'fc-icon-chevrons-right', + nextYear: 'fc-icon-chevrons-left' + } + }; + var complexOptions = [ + 'header', + 'footer', + 'buttonText', + 'buttonIcons' + ]; + // Merges an array of option objects into a single object + function mergeOptions(optionObjs) { + return mergeProps(optionObjs, complexOptions); + } + // TODO: move this stuff to a "plugin"-related file... + var INTERNAL_PLUGINS = [ + ArrayEventSourcePlugin, + FuncEventSourcePlugin, + JsonFeedEventSourcePlugin, + SimpleRecurrencePlugin, + DefaultOptionChangeHandlers + ]; + function refinePluginDefs(pluginInputs) { + var plugins = []; + for (var _i = 0, pluginInputs_1 = pluginInputs; _i < pluginInputs_1.length; _i++) { + var pluginInput = pluginInputs_1[_i]; + if (typeof pluginInput === 'string') { + var globalName = 'FullCalendar' + capitaliseFirstLetter(pluginInput); + if (!window[globalName]) { + console.warn('Plugin file not loaded for ' + pluginInput); + } + else { + plugins.push(window[globalName].default); // is an ES6 module + } + } + else { + plugins.push(pluginInput); + } + } + return INTERNAL_PLUGINS.concat(plugins); + } + + var RAW_EN_LOCALE = { + code: 'en', + week: { + dow: 0, + doy: 4 // 4 days need to be within the year to be considered the first week + }, + dir: 'ltr', + buttonText: { + prev: 'prev', + next: 'next', + prevYear: 'prev year', + nextYear: 'next year', + year: 'year', + today: 'today', + month: 'month', + week: 'week', + day: 'day', + list: 'list' + }, + weekLabel: 'W', + allDayText: 'all-day', + eventLimitText: 'more', + noEventsMessage: 'No events to display' + }; + function parseRawLocales(explicitRawLocales) { + var defaultCode = explicitRawLocales.length > 0 ? explicitRawLocales[0].code : 'en'; + var globalArray = window['FullCalendarLocalesAll'] || []; // from locales-all.js + var globalObject = window['FullCalendarLocales'] || {}; // from locales/*.js. keys are meaningless + var allRawLocales = globalArray.concat(// globalArray is low prio + hashValuesToArray(globalObject), // medium prio + explicitRawLocales // highest prio + ); + var rawLocaleMap = { + en: RAW_EN_LOCALE // necessary? + }; + for (var _i = 0, allRawLocales_1 = allRawLocales; _i < allRawLocales_1.length; _i++) { + var rawLocale = allRawLocales_1[_i]; + rawLocaleMap[rawLocale.code] = rawLocale; + } + return { + map: rawLocaleMap, + defaultCode: defaultCode + }; + } + function buildLocale(inputSingular, available) { + if (typeof inputSingular === 'object' && !Array.isArray(inputSingular)) { + return parseLocale(inputSingular.code, [inputSingular.code], inputSingular); + } + else { + return queryLocale(inputSingular, available); + } + } + function queryLocale(codeArg, available) { + var codes = [].concat(codeArg || []); // will convert to array + var raw = queryRawLocale(codes, available) || RAW_EN_LOCALE; + return parseLocale(codeArg, codes, raw); + } + function queryRawLocale(codes, available) { + for (var i = 0; i < codes.length; i++) { + var parts = codes[i].toLocaleLowerCase().split('-'); + for (var j = parts.length; j > 0; j--) { + var simpleId = parts.slice(0, j).join('-'); + if (available[simpleId]) { + return available[simpleId]; + } + } + } + return null; + } + function parseLocale(codeArg, codes, raw) { + var merged = mergeProps([RAW_EN_LOCALE, raw], ['buttonText']); + delete merged.code; // don't want this part of the options + var week = merged.week; + delete merged.week; + return { + codeArg: codeArg, + codes: codes, + week: week, + simpleNumberFormat: new Intl.NumberFormat(codeArg), + options: merged + }; + } + + var OptionsManager = /** @class */ (function () { + function OptionsManager(overrides) { + this.overrides = __assign({}, overrides); // make a copy + this.dynamicOverrides = {}; + this.compute(); + } + OptionsManager.prototype.mutate = function (updates, removals, isDynamic) { + var overrideHash = isDynamic ? this.dynamicOverrides : this.overrides; + __assign(overrideHash, updates); + for (var _i = 0, removals_1 = removals; _i < removals_1.length; _i++) { + var propName = removals_1[_i]; + delete overrideHash[propName]; + } + this.compute(); + }; + // Computes the flattened options hash for the calendar and assigns to `this.options`. + // Assumes this.overrides and this.dynamicOverrides have already been initialized. + OptionsManager.prototype.compute = function () { + // TODO: not a very efficient system + var locales = firstDefined(// explicit locale option given? + this.dynamicOverrides.locales, this.overrides.locales, globalDefaults.locales); + var locale = firstDefined(// explicit locales option given? + this.dynamicOverrides.locale, this.overrides.locale, globalDefaults.locale); + var available = parseRawLocales(locales); + var localeDefaults = buildLocale(locale || available.defaultCode, available.map).options; + var dir = firstDefined(// based on options computed so far, is direction RTL? + this.dynamicOverrides.dir, this.overrides.dir, localeDefaults.dir); + var dirDefaults = dir === 'rtl' ? rtlDefaults : {}; + this.dirDefaults = dirDefaults; + this.localeDefaults = localeDefaults; + this.computed = mergeOptions([ + globalDefaults, + dirDefaults, + localeDefaults, + this.overrides, + this.dynamicOverrides + ]); + }; + return OptionsManager; + }()); + + var calendarSystemClassMap = {}; + function registerCalendarSystem(name, theClass) { + calendarSystemClassMap[name] = theClass; + } + function createCalendarSystem(name) { + return new calendarSystemClassMap[name](); + } + var GregorianCalendarSystem = /** @class */ (function () { + function GregorianCalendarSystem() { + } + GregorianCalendarSystem.prototype.getMarkerYear = function (d) { + return d.getUTCFullYear(); + }; + GregorianCalendarSystem.prototype.getMarkerMonth = function (d) { + return d.getUTCMonth(); + }; + GregorianCalendarSystem.prototype.getMarkerDay = function (d) { + return d.getUTCDate(); + }; + GregorianCalendarSystem.prototype.arrayToMarker = function (arr) { + return arrayToUtcDate(arr); + }; + GregorianCalendarSystem.prototype.markerToArray = function (marker) { + return dateToUtcArray(marker); + }; + return GregorianCalendarSystem; + }()); + registerCalendarSystem('gregory', GregorianCalendarSystem); + + var ISO_RE = /^\s*(\d{4})(-(\d{2})(-(\d{2})([T ](\d{2}):(\d{2})(:(\d{2})(\.(\d+))?)?(Z|(([-+])(\d{2})(:?(\d{2}))?))?)?)?)?$/; + function parse(str) { + var m = ISO_RE.exec(str); + if (m) { + var marker = new Date(Date.UTC(Number(m[1]), m[3] ? Number(m[3]) - 1 : 0, Number(m[5] || 1), Number(m[7] || 0), Number(m[8] || 0), Number(m[10] || 0), m[12] ? Number('0.' + m[12]) * 1000 : 0)); + if (isValidDate(marker)) { + var timeZoneOffset = null; + if (m[13]) { + timeZoneOffset = (m[15] === '-' ? -1 : 1) * (Number(m[16] || 0) * 60 + + Number(m[18] || 0)); + } + return { + marker: marker, + isTimeUnspecified: !m[6], + timeZoneOffset: timeZoneOffset + }; + } + } + return null; + } + + var DateEnv = /** @class */ (function () { + function DateEnv(settings) { + var timeZone = this.timeZone = settings.timeZone; + var isNamedTimeZone = timeZone !== 'local' && timeZone !== 'UTC'; + if (settings.namedTimeZoneImpl && isNamedTimeZone) { + this.namedTimeZoneImpl = new settings.namedTimeZoneImpl(timeZone); + } + this.canComputeOffset = Boolean(!isNamedTimeZone || this.namedTimeZoneImpl); + this.calendarSystem = createCalendarSystem(settings.calendarSystem); + this.locale = settings.locale; + this.weekDow = settings.locale.week.dow; + this.weekDoy = settings.locale.week.doy; + if (settings.weekNumberCalculation === 'ISO') { + this.weekDow = 1; + this.weekDoy = 4; + } + if (typeof settings.firstDay === 'number') { + this.weekDow = settings.firstDay; + } + if (typeof settings.weekNumberCalculation === 'function') { + this.weekNumberFunc = settings.weekNumberCalculation; + } + this.weekLabel = settings.weekLabel != null ? settings.weekLabel : settings.locale.options.weekLabel; + this.cmdFormatter = settings.cmdFormatter; + } + // Creating / Parsing + DateEnv.prototype.createMarker = function (input) { + var meta = this.createMarkerMeta(input); + if (meta === null) { + return null; + } + return meta.marker; + }; + DateEnv.prototype.createNowMarker = function () { + if (this.canComputeOffset) { + return this.timestampToMarker(new Date().valueOf()); + } + else { + // if we can't compute the current date val for a timezone, + // better to give the current local date vals than UTC + return arrayToUtcDate(dateToLocalArray(new Date())); + } + }; + DateEnv.prototype.createMarkerMeta = function (input) { + if (typeof input === 'string') { + return this.parse(input); + } + var marker = null; + if (typeof input === 'number') { + marker = this.timestampToMarker(input); + } + else if (input instanceof Date) { + input = input.valueOf(); + if (!isNaN(input)) { + marker = this.timestampToMarker(input); + } + } + else if (Array.isArray(input)) { + marker = arrayToUtcDate(input); + } + if (marker === null || !isValidDate(marker)) { + return null; + } + return { marker: marker, isTimeUnspecified: false, forcedTzo: null }; + }; + DateEnv.prototype.parse = function (s) { + var parts = parse(s); + if (parts === null) { + return null; + } + var marker = parts.marker; + var forcedTzo = null; + if (parts.timeZoneOffset !== null) { + if (this.canComputeOffset) { + marker = this.timestampToMarker(marker.valueOf() - parts.timeZoneOffset * 60 * 1000); + } + else { + forcedTzo = parts.timeZoneOffset; + } + } + return { marker: marker, isTimeUnspecified: parts.isTimeUnspecified, forcedTzo: forcedTzo }; + }; + // Accessors + DateEnv.prototype.getYear = function (marker) { + return this.calendarSystem.getMarkerYear(marker); + }; + DateEnv.prototype.getMonth = function (marker) { + return this.calendarSystem.getMarkerMonth(marker); + }; + // Adding / Subtracting + DateEnv.prototype.add = function (marker, dur) { + var a = this.calendarSystem.markerToArray(marker); + a[0] += dur.years; + a[1] += dur.months; + a[2] += dur.days; + a[6] += dur.milliseconds; + return this.calendarSystem.arrayToMarker(a); + }; + DateEnv.prototype.subtract = function (marker, dur) { + var a = this.calendarSystem.markerToArray(marker); + a[0] -= dur.years; + a[1] -= dur.months; + a[2] -= dur.days; + a[6] -= dur.milliseconds; + return this.calendarSystem.arrayToMarker(a); + }; + DateEnv.prototype.addYears = function (marker, n) { + var a = this.calendarSystem.markerToArray(marker); + a[0] += n; + return this.calendarSystem.arrayToMarker(a); + }; + DateEnv.prototype.addMonths = function (marker, n) { + var a = this.calendarSystem.markerToArray(marker); + a[1] += n; + return this.calendarSystem.arrayToMarker(a); + }; + // Diffing Whole Units + DateEnv.prototype.diffWholeYears = function (m0, m1) { + var calendarSystem = this.calendarSystem; + if (timeAsMs(m0) === timeAsMs(m1) && + calendarSystem.getMarkerDay(m0) === calendarSystem.getMarkerDay(m1) && + calendarSystem.getMarkerMonth(m0) === calendarSystem.getMarkerMonth(m1)) { + return calendarSystem.getMarkerYear(m1) - calendarSystem.getMarkerYear(m0); + } + return null; + }; + DateEnv.prototype.diffWholeMonths = function (m0, m1) { + var calendarSystem = this.calendarSystem; + if (timeAsMs(m0) === timeAsMs(m1) && + calendarSystem.getMarkerDay(m0) === calendarSystem.getMarkerDay(m1)) { + return (calendarSystem.getMarkerMonth(m1) - calendarSystem.getMarkerMonth(m0)) + + (calendarSystem.getMarkerYear(m1) - calendarSystem.getMarkerYear(m0)) * 12; + } + return null; + }; + // Range / Duration + DateEnv.prototype.greatestWholeUnit = function (m0, m1) { + var n = this.diffWholeYears(m0, m1); + if (n !== null) { + return { unit: 'year', value: n }; + } + n = this.diffWholeMonths(m0, m1); + if (n !== null) { + return { unit: 'month', value: n }; + } + n = diffWholeWeeks(m0, m1); + if (n !== null) { + return { unit: 'week', value: n }; + } + n = diffWholeDays(m0, m1); + if (n !== null) { + return { unit: 'day', value: n }; + } + n = diffHours(m0, m1); + if (isInt(n)) { + return { unit: 'hour', value: n }; + } + n = diffMinutes(m0, m1); + if (isInt(n)) { + return { unit: 'minute', value: n }; + } + n = diffSeconds(m0, m1); + if (isInt(n)) { + return { unit: 'second', value: n }; + } + return { unit: 'millisecond', value: m1.valueOf() - m0.valueOf() }; + }; + DateEnv.prototype.countDurationsBetween = function (m0, m1, d) { + // TODO: can use greatestWholeUnit + var diff; + if (d.years) { + diff = this.diffWholeYears(m0, m1); + if (diff !== null) { + return diff / asRoughYears(d); + } + } + if (d.months) { + diff = this.diffWholeMonths(m0, m1); + if (diff !== null) { + return diff / asRoughMonths(d); + } + } + if (d.days) { + diff = diffWholeDays(m0, m1); + if (diff !== null) { + return diff / asRoughDays(d); + } + } + return (m1.valueOf() - m0.valueOf()) / asRoughMs(d); + }; + // Start-Of + DateEnv.prototype.startOf = function (m, unit) { + if (unit === 'year') { + return this.startOfYear(m); + } + else if (unit === 'month') { + return this.startOfMonth(m); + } + else if (unit === 'week') { + return this.startOfWeek(m); + } + else if (unit === 'day') { + return startOfDay(m); + } + else if (unit === 'hour') { + return startOfHour(m); + } + else if (unit === 'minute') { + return startOfMinute(m); + } + else if (unit === 'second') { + return startOfSecond(m); + } + }; + DateEnv.prototype.startOfYear = function (m) { + return this.calendarSystem.arrayToMarker([ + this.calendarSystem.getMarkerYear(m) + ]); + }; + DateEnv.prototype.startOfMonth = function (m) { + return this.calendarSystem.arrayToMarker([ + this.calendarSystem.getMarkerYear(m), + this.calendarSystem.getMarkerMonth(m) + ]); + }; + DateEnv.prototype.startOfWeek = function (m) { + return this.calendarSystem.arrayToMarker([ + this.calendarSystem.getMarkerYear(m), + this.calendarSystem.getMarkerMonth(m), + m.getUTCDate() - ((m.getUTCDay() - this.weekDow + 7) % 7) + ]); + }; + // Week Number + DateEnv.prototype.computeWeekNumber = function (marker) { + if (this.weekNumberFunc) { + return this.weekNumberFunc(this.toDate(marker)); + } + else { + return weekOfYear(marker, this.weekDow, this.weekDoy); + } + }; + // TODO: choke on timeZoneName: long + DateEnv.prototype.format = function (marker, formatter, dateOptions) { + if (dateOptions === void 0) { dateOptions = {}; } + return formatter.format({ + marker: marker, + timeZoneOffset: dateOptions.forcedTzo != null ? + dateOptions.forcedTzo : + this.offsetForMarker(marker) + }, this); + }; + DateEnv.prototype.formatRange = function (start, end, formatter, dateOptions) { + if (dateOptions === void 0) { dateOptions = {}; } + if (dateOptions.isEndExclusive) { + end = addMs(end, -1); + } + return formatter.formatRange({ + marker: start, + timeZoneOffset: dateOptions.forcedStartTzo != null ? + dateOptions.forcedStartTzo : + this.offsetForMarker(start) + }, { + marker: end, + timeZoneOffset: dateOptions.forcedEndTzo != null ? + dateOptions.forcedEndTzo : + this.offsetForMarker(end) + }, this); + }; + DateEnv.prototype.formatIso = function (marker, extraOptions) { + if (extraOptions === void 0) { extraOptions = {}; } + var timeZoneOffset = null; + if (!extraOptions.omitTimeZoneOffset) { + if (extraOptions.forcedTzo != null) { + timeZoneOffset = extraOptions.forcedTzo; + } + else { + timeZoneOffset = this.offsetForMarker(marker); + } + } + return buildIsoString(marker, timeZoneOffset, extraOptions.omitTime); + }; + // TimeZone + DateEnv.prototype.timestampToMarker = function (ms) { + if (this.timeZone === 'local') { + return arrayToUtcDate(dateToLocalArray(new Date(ms))); + } + else if (this.timeZone === 'UTC' || !this.namedTimeZoneImpl) { + return new Date(ms); + } + else { + return arrayToUtcDate(this.namedTimeZoneImpl.timestampToArray(ms)); + } + }; + DateEnv.prototype.offsetForMarker = function (m) { + if (this.timeZone === 'local') { + return -arrayToLocalDate(dateToUtcArray(m)).getTimezoneOffset(); // convert "inverse" offset to "normal" offset + } + else if (this.timeZone === 'UTC') { + return 0; + } + else if (this.namedTimeZoneImpl) { + return this.namedTimeZoneImpl.offsetForArray(dateToUtcArray(m)); + } + return null; + }; + // Conversion + DateEnv.prototype.toDate = function (m, forcedTzo) { + if (this.timeZone === 'local') { + return arrayToLocalDate(dateToUtcArray(m)); + } + else if (this.timeZone === 'UTC') { + return new Date(m.valueOf()); // make sure it's a copy + } + else if (!this.namedTimeZoneImpl) { + return new Date(m.valueOf() - (forcedTzo || 0)); + } + else { + return new Date(m.valueOf() - + this.namedTimeZoneImpl.offsetForArray(dateToUtcArray(m)) * 1000 * 60 // convert minutes -> ms + ); + } + }; + return DateEnv; + }()); + + var SIMPLE_SOURCE_PROPS = { + id: String, + allDayDefault: Boolean, + eventDataTransform: Function, + success: Function, + failure: Function + }; + var uid$2 = 0; + function doesSourceNeedRange(eventSource, calendar) { + var defs = calendar.pluginSystem.hooks.eventSourceDefs; + return !defs[eventSource.sourceDefId].ignoreRange; + } + function parseEventSource(raw, calendar) { + var defs = calendar.pluginSystem.hooks.eventSourceDefs; + for (var i = defs.length - 1; i >= 0; i--) { // later-added plugins take precedence + var def = defs[i]; + var meta = def.parseMeta(raw); + if (meta) { + var res = parseEventSourceProps(typeof raw === 'object' ? raw : {}, meta, i, calendar); + res._raw = raw; + return res; + } + } + return null; + } + function parseEventSourceProps(raw, meta, sourceDefId, calendar) { + var leftovers0 = {}; + var props = refineProps(raw, SIMPLE_SOURCE_PROPS, {}, leftovers0); + var leftovers1 = {}; + var ui = processUnscopedUiProps(leftovers0, calendar, leftovers1); + props.isFetching = false; + props.latestFetchId = ''; + props.fetchRange = null; + props.publicId = String(raw.id || ''); + props.sourceId = String(uid$2++); + props.sourceDefId = sourceDefId; + props.meta = meta; + props.ui = ui; + props.extendedProps = leftovers1; + return props; + } + + function reduceEventSources (eventSources, action, dateProfile, calendar) { + switch (action.type) { + case 'ADD_EVENT_SOURCES': // already parsed + return addSources(eventSources, action.sources, dateProfile ? dateProfile.activeRange : null, calendar); + case 'REMOVE_EVENT_SOURCE': + return removeSource(eventSources, action.sourceId); + case 'PREV': // TODO: how do we track all actions that affect dateProfile :( + case 'NEXT': + case 'SET_DATE': + case 'SET_VIEW_TYPE': + if (dateProfile) { + return fetchDirtySources(eventSources, dateProfile.activeRange, calendar); + } + else { + return eventSources; + } + case 'FETCH_EVENT_SOURCES': + case 'CHANGE_TIMEZONE': + return fetchSourcesByIds(eventSources, action.sourceIds ? + arrayToHash(action.sourceIds) : + excludeStaticSources(eventSources, calendar), dateProfile ? dateProfile.activeRange : null, calendar); + case 'RECEIVE_EVENTS': + case 'RECEIVE_EVENT_ERROR': + return receiveResponse(eventSources, action.sourceId, action.fetchId, action.fetchRange); + case 'REMOVE_ALL_EVENT_SOURCES': + return {}; + default: + return eventSources; + } + } + var uid$3 = 0; + function addSources(eventSourceHash, sources, fetchRange, calendar) { + var hash = {}; + for (var _i = 0, sources_1 = sources; _i < sources_1.length; _i++) { + var source = sources_1[_i]; + hash[source.sourceId] = source; + } + if (fetchRange) { + hash = fetchDirtySources(hash, fetchRange, calendar); + } + return __assign({}, eventSourceHash, hash); + } + function removeSource(eventSourceHash, sourceId) { + return filterHash(eventSourceHash, function (eventSource) { + return eventSource.sourceId !== sourceId; + }); + } + function fetchDirtySources(sourceHash, fetchRange, calendar) { + return fetchSourcesByIds(sourceHash, filterHash(sourceHash, function (eventSource) { + return isSourceDirty(eventSource, fetchRange, calendar); + }), fetchRange, calendar); + } + function isSourceDirty(eventSource, fetchRange, calendar) { + if (!doesSourceNeedRange(eventSource, calendar)) { + return !eventSource.latestFetchId; + } + else { + return !calendar.opt('lazyFetching') || + !eventSource.fetchRange || + fetchRange.start < eventSource.fetchRange.start || + fetchRange.end > eventSource.fetchRange.end; + } + } + function fetchSourcesByIds(prevSources, sourceIdHash, fetchRange, calendar) { + var nextSources = {}; + for (var sourceId in prevSources) { + var source = prevSources[sourceId]; + if (sourceIdHash[sourceId]) { + nextSources[sourceId] = fetchSource(source, fetchRange, calendar); + } + else { + nextSources[sourceId] = source; + } + } + return nextSources; + } + function fetchSource(eventSource, fetchRange, calendar) { + var sourceDef = calendar.pluginSystem.hooks.eventSourceDefs[eventSource.sourceDefId]; + var fetchId = String(uid$3++); + sourceDef.fetch({ + eventSource: eventSource, + calendar: calendar, + range: fetchRange + }, function (res) { + var rawEvents = res.rawEvents; + var calSuccess = calendar.opt('eventSourceSuccess'); + var calSuccessRes; + var sourceSuccessRes; + if (eventSource.success) { + sourceSuccessRes = eventSource.success(rawEvents, res.xhr); + } + if (calSuccess) { + calSuccessRes = calSuccess(rawEvents, res.xhr); + } + rawEvents = sourceSuccessRes || calSuccessRes || rawEvents; + calendar.dispatch({ + type: 'RECEIVE_EVENTS', + sourceId: eventSource.sourceId, + fetchId: fetchId, + fetchRange: fetchRange, + rawEvents: rawEvents + }); + }, function (error) { + var callFailure = calendar.opt('eventSourceFailure'); + console.warn(error.message, error); + if (eventSource.failure) { + eventSource.failure(error); + } + if (callFailure) { + callFailure(error); + } + calendar.dispatch({ + type: 'RECEIVE_EVENT_ERROR', + sourceId: eventSource.sourceId, + fetchId: fetchId, + fetchRange: fetchRange, + error: error + }); + }); + return __assign({}, eventSource, { isFetching: true, latestFetchId: fetchId }); + } + function receiveResponse(sourceHash, sourceId, fetchId, fetchRange) { + var _a; + var eventSource = sourceHash[sourceId]; + if (eventSource && // not already removed + fetchId === eventSource.latestFetchId) { + return __assign({}, sourceHash, (_a = {}, _a[sourceId] = __assign({}, eventSource, { isFetching: false, fetchRange: fetchRange }), _a)); + } + return sourceHash; + } + function excludeStaticSources(eventSources, calendar) { + return filterHash(eventSources, function (eventSource) { + return doesSourceNeedRange(eventSource, calendar); + }); + } + + var DateProfileGenerator = /** @class */ (function () { + function DateProfileGenerator(viewSpec, calendar) { + this.viewSpec = viewSpec; + this.options = viewSpec.options; + this.dateEnv = calendar.dateEnv; + this.calendar = calendar; + this.initHiddenDays(); + } + /* Date Range Computation + ------------------------------------------------------------------------------------------------------------------*/ + // Builds a structure with info about what the dates/ranges will be for the "prev" view. + DateProfileGenerator.prototype.buildPrev = function (currentDateProfile, currentDate) { + var dateEnv = this.dateEnv; + var prevDate = dateEnv.subtract(dateEnv.startOf(currentDate, currentDateProfile.currentRangeUnit), // important for start-of-month + currentDateProfile.dateIncrement); + return this.build(prevDate, -1); + }; + // Builds a structure with info about what the dates/ranges will be for the "next" view. + DateProfileGenerator.prototype.buildNext = function (currentDateProfile, currentDate) { + var dateEnv = this.dateEnv; + var nextDate = dateEnv.add(dateEnv.startOf(currentDate, currentDateProfile.currentRangeUnit), // important for start-of-month + currentDateProfile.dateIncrement); + return this.build(nextDate, 1); + }; + // Builds a structure holding dates/ranges for rendering around the given date. + // Optional direction param indicates whether the date is being incremented/decremented + // from its previous value. decremented = -1, incremented = 1 (default). + DateProfileGenerator.prototype.build = function (currentDate, direction, forceToValid) { + if (forceToValid === void 0) { forceToValid = false; } + var validRange; + var minTime = null; + var maxTime = null; + var currentInfo; + var isRangeAllDay; + var renderRange; + var activeRange; + var isValid; + validRange = this.buildValidRange(); + validRange = this.trimHiddenDays(validRange); + if (forceToValid) { + currentDate = constrainMarkerToRange(currentDate, validRange); + } + currentInfo = this.buildCurrentRangeInfo(currentDate, direction); + isRangeAllDay = /^(year|month|week|day)$/.test(currentInfo.unit); + renderRange = this.buildRenderRange(this.trimHiddenDays(currentInfo.range), currentInfo.unit, isRangeAllDay); + renderRange = this.trimHiddenDays(renderRange); + activeRange = renderRange; + if (!this.options.showNonCurrentDates) { + activeRange = intersectRanges(activeRange, currentInfo.range); + } + minTime = createDuration(this.options.minTime); + maxTime = createDuration(this.options.maxTime); + activeRange = this.adjustActiveRange(activeRange, minTime, maxTime); + activeRange = intersectRanges(activeRange, validRange); // might return null + // it's invalid if the originally requested date is not contained, + // or if the range is completely outside of the valid range. + isValid = rangesIntersect(currentInfo.range, validRange); + return { + // constraint for where prev/next operations can go and where events can be dragged/resized to. + // an object with optional start and end properties. + validRange: validRange, + // range the view is formally responsible for. + // for example, a month view might have 1st-31st, excluding padded dates + currentRange: currentInfo.range, + // name of largest unit being displayed, like "month" or "week" + currentRangeUnit: currentInfo.unit, + isRangeAllDay: isRangeAllDay, + // dates that display events and accept drag-n-drop + // will be `null` if no dates accept events + activeRange: activeRange, + // date range with a rendered skeleton + // includes not-active days that need some sort of DOM + renderRange: renderRange, + // Duration object that denotes the first visible time of any given day + minTime: minTime, + // Duration object that denotes the exclusive visible end time of any given day + maxTime: maxTime, + isValid: isValid, + // how far the current date will move for a prev/next operation + dateIncrement: this.buildDateIncrement(currentInfo.duration) + // pass a fallback (might be null) ^ + }; + }; + // Builds an object with optional start/end properties. + // Indicates the minimum/maximum dates to display. + // not responsible for trimming hidden days. + DateProfileGenerator.prototype.buildValidRange = function () { + return this.getRangeOption('validRange', this.calendar.getNow()) || + { start: null, end: null }; // completely open-ended + }; + // Builds a structure with info about the "current" range, the range that is + // highlighted as being the current month for example. + // See build() for a description of `direction`. + // Guaranteed to have `range` and `unit` properties. `duration` is optional. + DateProfileGenerator.prototype.buildCurrentRangeInfo = function (date, direction) { + var _a = this, viewSpec = _a.viewSpec, dateEnv = _a.dateEnv; + var duration = null; + var unit = null; + var range = null; + var dayCount; + if (viewSpec.duration) { + duration = viewSpec.duration; + unit = viewSpec.durationUnit; + range = this.buildRangeFromDuration(date, direction, duration, unit); + } + else if ((dayCount = this.options.dayCount)) { + unit = 'day'; + range = this.buildRangeFromDayCount(date, direction, dayCount); + } + else if ((range = this.buildCustomVisibleRange(date))) { + unit = dateEnv.greatestWholeUnit(range.start, range.end).unit; + } + else { + duration = this.getFallbackDuration(); + unit = greatestDurationDenominator(duration).unit; + range = this.buildRangeFromDuration(date, direction, duration, unit); + } + return { duration: duration, unit: unit, range: range }; + }; + DateProfileGenerator.prototype.getFallbackDuration = function () { + return createDuration({ day: 1 }); + }; + // Returns a new activeRange to have time values (un-ambiguate) + // minTime or maxTime causes the range to expand. + DateProfileGenerator.prototype.adjustActiveRange = function (range, minTime, maxTime) { + var dateEnv = this.dateEnv; + var start = range.start; + var end = range.end; + if (this.viewSpec.class.prototype.usesMinMaxTime) { + // expand active range if minTime is negative (why not when positive?) + if (asRoughDays(minTime) < 0) { + start = startOfDay(start); // necessary? + start = dateEnv.add(start, minTime); + } + // expand active range if maxTime is beyond one day (why not when positive?) + if (asRoughDays(maxTime) > 1) { + end = startOfDay(end); // necessary? + end = addDays(end, -1); + end = dateEnv.add(end, maxTime); + } + } + return { start: start, end: end }; + }; + // Builds the "current" range when it is specified as an explicit duration. + // `unit` is the already-computed greatestDurationDenominator unit of duration. + DateProfileGenerator.prototype.buildRangeFromDuration = function (date, direction, duration, unit) { + var dateEnv = this.dateEnv; + var alignment = this.options.dateAlignment; + var dateIncrementInput; + var dateIncrementDuration; + var start; + var end; + var res; + // compute what the alignment should be + if (!alignment) { + dateIncrementInput = this.options.dateIncrement; + if (dateIncrementInput) { + dateIncrementDuration = createDuration(dateIncrementInput); + // use the smaller of the two units + if (asRoughMs(dateIncrementDuration) < asRoughMs(duration)) { + alignment = greatestDurationDenominator(dateIncrementDuration, !getWeeksFromInput(dateIncrementInput)).unit; + } + else { + alignment = unit; + } + } + else { + alignment = unit; + } + } + // if the view displays a single day or smaller + if (asRoughDays(duration) <= 1) { + if (this.isHiddenDay(start)) { + start = this.skipHiddenDays(start, direction); + start = startOfDay(start); + } + } + function computeRes() { + start = dateEnv.startOf(date, alignment); + end = dateEnv.add(start, duration); + res = { start: start, end: end }; + } + computeRes(); + // if range is completely enveloped by hidden days, go past the hidden days + if (!this.trimHiddenDays(res)) { + date = this.skipHiddenDays(date, direction); + computeRes(); + } + return res; + }; + // Builds the "current" range when a dayCount is specified. + DateProfileGenerator.prototype.buildRangeFromDayCount = function (date, direction, dayCount) { + var dateEnv = this.dateEnv; + var customAlignment = this.options.dateAlignment; + var runningCount = 0; + var start = date; + var end; + if (customAlignment) { + start = dateEnv.startOf(start, customAlignment); + } + start = startOfDay(start); + start = this.skipHiddenDays(start, direction); + end = start; + do { + end = addDays(end, 1); + if (!this.isHiddenDay(end)) { + runningCount++; + } + } while (runningCount < dayCount); + return { start: start, end: end }; + }; + // Builds a normalized range object for the "visible" range, + // which is a way to define the currentRange and activeRange at the same time. + DateProfileGenerator.prototype.buildCustomVisibleRange = function (date) { + var dateEnv = this.dateEnv; + var visibleRange = this.getRangeOption('visibleRange', dateEnv.toDate(date)); + if (visibleRange && (visibleRange.start == null || visibleRange.end == null)) { + return null; + } + return visibleRange; + }; + // Computes the range that will represent the element/cells for *rendering*, + // but which may have voided days/times. + // not responsible for trimming hidden days. + DateProfileGenerator.prototype.buildRenderRange = function (currentRange, currentRangeUnit, isRangeAllDay) { + return currentRange; + }; + // Compute the duration value that should be added/substracted to the current date + // when a prev/next operation happens. + DateProfileGenerator.prototype.buildDateIncrement = function (fallback) { + var dateIncrementInput = this.options.dateIncrement; + var customAlignment; + if (dateIncrementInput) { + return createDuration(dateIncrementInput); + } + else if ((customAlignment = this.options.dateAlignment)) { + return createDuration(1, customAlignment); + } + else if (fallback) { + return fallback; + } + else { + return createDuration({ days: 1 }); + } + }; + // Arguments after name will be forwarded to a hypothetical function value + // WARNING: passed-in arguments will be given to generator functions as-is and can cause side-effects. + // Always clone your objects if you fear mutation. + DateProfileGenerator.prototype.getRangeOption = function (name) { + var otherArgs = []; + for (var _i = 1; _i < arguments.length; _i++) { + otherArgs[_i - 1] = arguments[_i]; + } + var val = this.options[name]; + if (typeof val === 'function') { + val = val.apply(null, otherArgs); + } + if (val) { + val = parseRange(val, this.dateEnv); + } + if (val) { + val = computeVisibleDayRange(val); + } + return val; + }; + /* Hidden Days + ------------------------------------------------------------------------------------------------------------------*/ + // Initializes internal variables related to calculating hidden days-of-week + DateProfileGenerator.prototype.initHiddenDays = function () { + var hiddenDays = this.options.hiddenDays || []; // array of day-of-week indices that are hidden + var isHiddenDayHash = []; // is the day-of-week hidden? (hash with day-of-week-index -> bool) + var dayCnt = 0; + var i; + if (this.options.weekends === false) { + hiddenDays.push(0, 6); // 0=sunday, 6=saturday + } + for (i = 0; i < 7; i++) { + if (!(isHiddenDayHash[i] = hiddenDays.indexOf(i) !== -1)) { + dayCnt++; + } + } + if (!dayCnt) { + throw new Error('invalid hiddenDays'); // all days were hidden? bad. + } + this.isHiddenDayHash = isHiddenDayHash; + }; + // Remove days from the beginning and end of the range that are computed as hidden. + // If the whole range is trimmed off, returns null + DateProfileGenerator.prototype.trimHiddenDays = function (range) { + var start = range.start; + var end = range.end; + if (start) { + start = this.skipHiddenDays(start); + } + if (end) { + end = this.skipHiddenDays(end, -1, true); + } + if (start == null || end == null || start < end) { + return { start: start, end: end }; + } + return null; + }; + // Is the current day hidden? + // `day` is a day-of-week index (0-6), or a Date (used for UTC) + DateProfileGenerator.prototype.isHiddenDay = function (day) { + if (day instanceof Date) { + day = day.getUTCDay(); + } + return this.isHiddenDayHash[day]; + }; + // Incrementing the current day until it is no longer a hidden day, returning a copy. + // DOES NOT CONSIDER validRange! + // If the initial value of `date` is not a hidden day, don't do anything. + // Pass `isExclusive` as `true` if you are dealing with an end date. + // `inc` defaults to `1` (increment one day forward each time) + DateProfileGenerator.prototype.skipHiddenDays = function (date, inc, isExclusive) { + if (inc === void 0) { inc = 1; } + if (isExclusive === void 0) { isExclusive = false; } + while (this.isHiddenDayHash[(date.getUTCDay() + (isExclusive ? inc : 0) + 7) % 7]) { + date = addDays(date, inc); + } + return date; + }; + return DateProfileGenerator; + }()); + // TODO: find a way to avoid comparing DateProfiles. it's tedious + function isDateProfilesEqual(p0, p1) { + return rangesEqual(p0.validRange, p1.validRange) && + rangesEqual(p0.activeRange, p1.activeRange) && + rangesEqual(p0.renderRange, p1.renderRange) && + durationsEqual(p0.minTime, p1.minTime) && + durationsEqual(p0.maxTime, p1.maxTime); + /* + TODO: compare more? + currentRange: DateRange + currentRangeUnit: string + isRangeAllDay: boolean + isValid: boolean + dateIncrement: Duration + */ + } + + function reduce (state, action, calendar) { + var viewType = reduceViewType(state.viewType, action); + var dateProfile = reduceDateProfile(state.dateProfile, action, state.currentDate, viewType, calendar); + var eventSources = reduceEventSources(state.eventSources, action, dateProfile, calendar); + var nextState = __assign({}, state, { viewType: viewType, + dateProfile: dateProfile, currentDate: reduceCurrentDate(state.currentDate, action, dateProfile), eventSources: eventSources, eventStore: reduceEventStore(state.eventStore, action, eventSources, dateProfile, calendar), dateSelection: reduceDateSelection(state.dateSelection, action, calendar), eventSelection: reduceSelectedEvent(state.eventSelection, action), eventDrag: reduceEventDrag(state.eventDrag, action, eventSources, calendar), eventResize: reduceEventResize(state.eventResize, action, eventSources, calendar), eventSourceLoadingLevel: computeLoadingLevel(eventSources), loadingLevel: computeLoadingLevel(eventSources) }); + for (var _i = 0, _a = calendar.pluginSystem.hooks.reducers; _i < _a.length; _i++) { + var reducerFunc = _a[_i]; + nextState = reducerFunc(nextState, action, calendar); + } + // console.log(action.type, nextState) + return nextState; + } + function reduceViewType(currentViewType, action) { + switch (action.type) { + case 'SET_VIEW_TYPE': + return action.viewType; + default: + return currentViewType; + } + } + function reduceDateProfile(currentDateProfile, action, currentDate, viewType, calendar) { + var newDateProfile; + switch (action.type) { + case 'PREV': + newDateProfile = calendar.dateProfileGenerators[viewType].buildPrev(currentDateProfile, currentDate); + break; + case 'NEXT': + newDateProfile = calendar.dateProfileGenerators[viewType].buildNext(currentDateProfile, currentDate); + break; + case 'SET_DATE': + if (!currentDateProfile.activeRange || + !rangeContainsMarker(currentDateProfile.currentRange, action.dateMarker)) { + newDateProfile = calendar.dateProfileGenerators[viewType].build(action.dateMarker, undefined, true // forceToValid + ); + } + break; + case 'SET_VIEW_TYPE': + var generator = calendar.dateProfileGenerators[viewType]; + if (!generator) { + throw new Error(viewType ? + 'The FullCalendar view "' + viewType + '" does not exist. Make sure your plugins are loaded correctly.' : + 'No available FullCalendar view plugins.'); + } + newDateProfile = generator.build(action.dateMarker || currentDate, undefined, true // forceToValid + ); + break; + } + if (newDateProfile && + newDateProfile.isValid && + !(currentDateProfile && isDateProfilesEqual(currentDateProfile, newDateProfile))) { + return newDateProfile; + } + else { + return currentDateProfile; + } + } + function reduceCurrentDate(currentDate, action, dateProfile) { + switch (action.type) { + case 'PREV': + case 'NEXT': + if (!rangeContainsMarker(dateProfile.currentRange, currentDate)) { + return dateProfile.currentRange.start; + } + else { + return currentDate; + } + case 'SET_DATE': + case 'SET_VIEW_TYPE': + var newDate = action.dateMarker || currentDate; + if (dateProfile.activeRange && !rangeContainsMarker(dateProfile.activeRange, newDate)) { + return dateProfile.currentRange.start; + } + else { + return newDate; + } + default: + return currentDate; + } + } + function reduceDateSelection(currentSelection, action, calendar) { + switch (action.type) { + case 'SELECT_DATES': + return action.selection; + case 'UNSELECT_DATES': + return null; + default: + return currentSelection; + } + } + function reduceSelectedEvent(currentInstanceId, action) { + switch (action.type) { + case 'SELECT_EVENT': + return action.eventInstanceId; + case 'UNSELECT_EVENT': + return ''; + default: + return currentInstanceId; + } + } + function reduceEventDrag(currentDrag, action, sources, calendar) { + switch (action.type) { + case 'SET_EVENT_DRAG': + var newDrag = action.state; + return { + affectedEvents: newDrag.affectedEvents, + mutatedEvents: newDrag.mutatedEvents, + isEvent: newDrag.isEvent, + origSeg: newDrag.origSeg + }; + case 'UNSET_EVENT_DRAG': + return null; + default: + return currentDrag; + } + } + function reduceEventResize(currentResize, action, sources, calendar) { + switch (action.type) { + case 'SET_EVENT_RESIZE': + var newResize = action.state; + return { + affectedEvents: newResize.affectedEvents, + mutatedEvents: newResize.mutatedEvents, + isEvent: newResize.isEvent, + origSeg: newResize.origSeg + }; + case 'UNSET_EVENT_RESIZE': + return null; + default: + return currentResize; + } + } + function computeLoadingLevel(eventSources) { + var cnt = 0; + for (var sourceId in eventSources) { + if (eventSources[sourceId].isFetching) { + cnt++; + } + } + return cnt; + } + + var STANDARD_PROPS = { + start: null, + end: null, + allDay: Boolean + }; + function parseDateSpan(raw, dateEnv, defaultDuration) { + var span = parseOpenDateSpan(raw, dateEnv); + var range = span.range; + if (!range.start) { + return null; + } + if (!range.end) { + if (defaultDuration == null) { + return null; + } + else { + range.end = dateEnv.add(range.start, defaultDuration); + } + } + return span; + } + /* + TODO: somehow combine with parseRange? + Will return null if the start/end props were present but parsed invalidly. + */ + function parseOpenDateSpan(raw, dateEnv) { + var leftovers = {}; + var standardProps = refineProps(raw, STANDARD_PROPS, {}, leftovers); + var startMeta = standardProps.start ? dateEnv.createMarkerMeta(standardProps.start) : null; + var endMeta = standardProps.end ? dateEnv.createMarkerMeta(standardProps.end) : null; + var allDay = standardProps.allDay; + if (allDay == null) { + allDay = (startMeta && startMeta.isTimeUnspecified) && + (!endMeta || endMeta.isTimeUnspecified); + } + // use this leftover object as the selection object + leftovers.range = { + start: startMeta ? startMeta.marker : null, + end: endMeta ? endMeta.marker : null + }; + leftovers.allDay = allDay; + return leftovers; + } + function isDateSpansEqual(span0, span1) { + return rangesEqual(span0.range, span1.range) && + span0.allDay === span1.allDay && + isSpanPropsEqual(span0, span1); + } + // the NON-DATE-RELATED props + function isSpanPropsEqual(span0, span1) { + for (var propName in span1) { + if (propName !== 'range' && propName !== 'allDay') { + if (span0[propName] !== span1[propName]) { + return false; + } + } + } + // are there any props that span0 has that span1 DOESN'T have? + // both have range/allDay, so no need to special-case. + for (var propName in span0) { + if (!(propName in span1)) { + return false; + } + } + return true; + } + function buildDateSpanApi(span, dateEnv) { + return { + start: dateEnv.toDate(span.range.start), + end: dateEnv.toDate(span.range.end), + startStr: dateEnv.formatIso(span.range.start, { omitTime: span.allDay }), + endStr: dateEnv.formatIso(span.range.end, { omitTime: span.allDay }), + allDay: span.allDay + }; + } + function buildDatePointApi(span, dateEnv) { + return { + date: dateEnv.toDate(span.range.start), + dateStr: dateEnv.formatIso(span.range.start, { omitTime: span.allDay }), + allDay: span.allDay + }; + } + function fabricateEventRange(dateSpan, eventUiBases, calendar) { + var def = parseEventDef({ editable: false }, '', // sourceId + dateSpan.allDay, true, // hasEnd + calendar); + return { + def: def, + ui: compileEventUi(def, eventUiBases), + instance: createEventInstance(def.defId, dateSpan.range), + range: dateSpan.range, + isStart: true, + isEnd: true + }; + } + + function compileViewDefs(defaultConfigs, overrideConfigs) { + var hash = {}; + var viewType; + for (viewType in defaultConfigs) { + ensureViewDef(viewType, hash, defaultConfigs, overrideConfigs); + } + for (viewType in overrideConfigs) { + ensureViewDef(viewType, hash, defaultConfigs, overrideConfigs); + } + return hash; + } + function ensureViewDef(viewType, hash, defaultConfigs, overrideConfigs) { + if (hash[viewType]) { + return hash[viewType]; + } + var viewDef = buildViewDef(viewType, hash, defaultConfigs, overrideConfigs); + if (viewDef) { + hash[viewType] = viewDef; + } + return viewDef; + } + function buildViewDef(viewType, hash, defaultConfigs, overrideConfigs) { + var defaultConfig = defaultConfigs[viewType]; + var overrideConfig = overrideConfigs[viewType]; + var queryProp = function (name) { + return (defaultConfig && defaultConfig[name] !== null) ? defaultConfig[name] : + ((overrideConfig && overrideConfig[name] !== null) ? overrideConfig[name] : null); + }; + var theClass = queryProp('class'); + var superType = queryProp('superType'); + if (!superType && theClass) { + superType = + findViewNameBySubclass(theClass, overrideConfigs) || + findViewNameBySubclass(theClass, defaultConfigs); + } + var superDef = null; + if (superType) { + if (superType === viewType) { + throw new Error('Can\'t have a custom view type that references itself'); + } + superDef = ensureViewDef(superType, hash, defaultConfigs, overrideConfigs); + } + if (!theClass && superDef) { + theClass = superDef.class; + } + if (!theClass) { + return null; // don't throw a warning, might be settings for a single-unit view + } + return { + type: viewType, + class: theClass, + defaults: __assign({}, (superDef ? superDef.defaults : {}), (defaultConfig ? defaultConfig.options : {})), + overrides: __assign({}, (superDef ? superDef.overrides : {}), (overrideConfig ? overrideConfig.options : {})) + }; + } + function findViewNameBySubclass(viewSubclass, configs) { + var superProto = Object.getPrototypeOf(viewSubclass.prototype); + for (var viewType in configs) { + var parsed = configs[viewType]; + // need DIRECT subclass, so instanceof won't do it + if (parsed.class && parsed.class.prototype === superProto) { + return viewType; + } + } + return ''; + } + + function parseViewConfigs(inputs) { + return mapHash(inputs, parseViewConfig); + } + var VIEW_DEF_PROPS = { + type: String, + class: null + }; + function parseViewConfig(input) { + if (typeof input === 'function') { + input = { class: input }; + } + var options = {}; + var props = refineProps(input, VIEW_DEF_PROPS, {}, options); + return { + superType: props.type, + class: props.class, + options: options + }; + } + + function buildViewSpecs(defaultInputs, optionsManager) { + var defaultConfigs = parseViewConfigs(defaultInputs); + var overrideConfigs = parseViewConfigs(optionsManager.overrides.views); + var viewDefs = compileViewDefs(defaultConfigs, overrideConfigs); + return mapHash(viewDefs, function (viewDef) { + return buildViewSpec(viewDef, overrideConfigs, optionsManager); + }); + } + function buildViewSpec(viewDef, overrideConfigs, optionsManager) { + var durationInput = viewDef.overrides.duration || + viewDef.defaults.duration || + optionsManager.dynamicOverrides.duration || + optionsManager.overrides.duration; + var duration = null; + var durationUnit = ''; + var singleUnit = ''; + var singleUnitOverrides = {}; + if (durationInput) { + duration = createDuration(durationInput); + if (duration) { // valid? + var denom = greatestDurationDenominator(duration, !getWeeksFromInput(durationInput)); + durationUnit = denom.unit; + if (denom.value === 1) { + singleUnit = durationUnit; + singleUnitOverrides = overrideConfigs[durationUnit] ? overrideConfigs[durationUnit].options : {}; + } + } + } + var queryButtonText = function (options) { + var buttonTextMap = options.buttonText || {}; + var buttonTextKey = viewDef.defaults.buttonTextKey; + if (buttonTextKey != null && buttonTextMap[buttonTextKey] != null) { + return buttonTextMap[buttonTextKey]; + } + if (buttonTextMap[viewDef.type] != null) { + return buttonTextMap[viewDef.type]; + } + if (buttonTextMap[singleUnit] != null) { + return buttonTextMap[singleUnit]; + } + }; + return { + type: viewDef.type, + class: viewDef.class, + duration: duration, + durationUnit: durationUnit, + singleUnit: singleUnit, + options: __assign({}, globalDefaults, viewDef.defaults, optionsManager.dirDefaults, optionsManager.localeDefaults, optionsManager.overrides, singleUnitOverrides, viewDef.overrides, optionsManager.dynamicOverrides), + buttonTextOverride: queryButtonText(optionsManager.dynamicOverrides) || + queryButtonText(optionsManager.overrides) || // constructor-specified buttonText lookup hash takes precedence + viewDef.overrides.buttonText, + buttonTextDefault: queryButtonText(optionsManager.localeDefaults) || + queryButtonText(optionsManager.dirDefaults) || + viewDef.defaults.buttonText || + queryButtonText(globalDefaults) || + viewDef.type // fall back to given view name + }; + } + + var Toolbar = /** @class */ (function (_super) { + __extends(Toolbar, _super); + function Toolbar(context, extraClassName) { + var _this = _super.call(this, context) || this; + _this._renderLayout = memoizeRendering(_this.renderLayout, _this.unrenderLayout); + _this._updateTitle = memoizeRendering(_this.updateTitle, null, [_this._renderLayout]); + _this._updateActiveButton = memoizeRendering(_this.updateActiveButton, null, [_this._renderLayout]); + _this._updateToday = memoizeRendering(_this.updateToday, null, [_this._renderLayout]); + _this._updatePrev = memoizeRendering(_this.updatePrev, null, [_this._renderLayout]); + _this._updateNext = memoizeRendering(_this.updateNext, null, [_this._renderLayout]); + _this.el = createElement('div', { className: 'fc-toolbar ' + extraClassName }); + return _this; + } + Toolbar.prototype.destroy = function () { + _super.prototype.destroy.call(this); + this._renderLayout.unrender(); // should unrender everything else + removeElement(this.el); + }; + Toolbar.prototype.render = function (props) { + this._renderLayout(props.layout); + this._updateTitle(props.title); + this._updateActiveButton(props.activeButton); + this._updateToday(props.isTodayEnabled); + this._updatePrev(props.isPrevEnabled); + this._updateNext(props.isNextEnabled); + }; + Toolbar.prototype.renderLayout = function (layout) { + var el = this.el; + this.viewsWithButtons = []; + appendToElement(el, this.renderSection('left', layout.left)); + appendToElement(el, this.renderSection('center', layout.center)); + appendToElement(el, this.renderSection('right', layout.right)); + }; + Toolbar.prototype.unrenderLayout = function () { + this.el.innerHTML = ''; + }; + Toolbar.prototype.renderSection = function (position, buttonStr) { + var _this = this; + var _a = this, theme = _a.theme, calendar = _a.calendar; + var optionsManager = calendar.optionsManager; + var viewSpecs = calendar.viewSpecs; + var sectionEl = createElement('div', { className: 'fc-' + position }); + var calendarCustomButtons = optionsManager.computed.customButtons || {}; + var calendarButtonTextOverrides = optionsManager.overrides.buttonText || {}; + var calendarButtonText = optionsManager.computed.buttonText || {}; + if (buttonStr) { + buttonStr.split(' ').forEach(function (buttonGroupStr, i) { + var groupChildren = []; + var isOnlyButtons = true; + var groupEl; + buttonGroupStr.split(',').forEach(function (buttonName, j) { + var customButtonProps; + var viewSpec; + var buttonClick; + var buttonIcon; // only one of these will be set + var buttonText; // " + var buttonInnerHtml; + var buttonClasses; + var buttonEl; + var buttonAriaAttr; + if (buttonName === 'title') { + groupChildren.push(htmlToElement('

 

')); // we always want it to take up height + isOnlyButtons = false; + } + else { + if ((customButtonProps = calendarCustomButtons[buttonName])) { + buttonClick = function (ev) { + if (customButtonProps.click) { + customButtonProps.click.call(buttonEl, ev); + } + }; + (buttonIcon = theme.getCustomButtonIconClass(customButtonProps)) || + (buttonIcon = theme.getIconClass(buttonName)) || + (buttonText = customButtonProps.text); + } + else if ((viewSpec = viewSpecs[buttonName])) { + _this.viewsWithButtons.push(buttonName); + buttonClick = function () { + calendar.changeView(buttonName); + }; + (buttonText = viewSpec.buttonTextOverride) || + (buttonIcon = theme.getIconClass(buttonName)) || + (buttonText = viewSpec.buttonTextDefault); + } + else if (calendar[buttonName]) { // a calendar method + buttonClick = function () { + calendar[buttonName](); + }; + (buttonText = calendarButtonTextOverrides[buttonName]) || + (buttonIcon = theme.getIconClass(buttonName)) || + (buttonText = calendarButtonText[buttonName]); + // ^ everything else is considered default + } + if (buttonClick) { + buttonClasses = [ + 'fc-' + buttonName + '-button', + theme.getClass('button') + ]; + if (buttonText) { + buttonInnerHtml = htmlEscape(buttonText); + buttonAriaAttr = ''; + } + else if (buttonIcon) { + buttonInnerHtml = ""; + buttonAriaAttr = ' aria-label="' + buttonName + '"'; + } + buttonEl = htmlToElement(// type="button" so that it doesn't submit a form + ''); + buttonEl.addEventListener('click', buttonClick); + groupChildren.push(buttonEl); + } + } + }); + if (groupChildren.length > 1) { + groupEl = document.createElement('div'); + var buttonGroupClassName = theme.getClass('buttonGroup'); + if (isOnlyButtons && buttonGroupClassName) { + groupEl.classList.add(buttonGroupClassName); + } + appendToElement(groupEl, groupChildren); + sectionEl.appendChild(groupEl); + } + else { + appendToElement(sectionEl, groupChildren); // 1 or 0 children + } + }); + } + return sectionEl; + }; + Toolbar.prototype.updateToday = function (isTodayEnabled) { + this.toggleButtonEnabled('today', isTodayEnabled); + }; + Toolbar.prototype.updatePrev = function (isPrevEnabled) { + this.toggleButtonEnabled('prev', isPrevEnabled); + }; + Toolbar.prototype.updateNext = function (isNextEnabled) { + this.toggleButtonEnabled('next', isNextEnabled); + }; + Toolbar.prototype.updateTitle = function (text) { + findElements(this.el, 'h2').forEach(function (titleEl) { + titleEl.innerText = text; + }); + }; + Toolbar.prototype.updateActiveButton = function (buttonName) { + var className = this.theme.getClass('buttonActive'); + findElements(this.el, 'button').forEach(function (buttonEl) { + if (buttonName && buttonEl.classList.contains('fc-' + buttonName + '-button')) { + buttonEl.classList.add(className); + } + else { + buttonEl.classList.remove(className); + } + }); + }; + Toolbar.prototype.toggleButtonEnabled = function (buttonName, bool) { + findElements(this.el, '.fc-' + buttonName + '-button').forEach(function (buttonEl) { + buttonEl.disabled = !bool; + }); + }; + return Toolbar; + }(Component)); + + var CalendarComponent = /** @class */ (function (_super) { + __extends(CalendarComponent, _super); + function CalendarComponent(context, el) { + var _this = _super.call(this, context) || this; + _this._renderToolbars = memoizeRendering(_this.renderToolbars); + _this.buildViewPropTransformers = memoize(buildViewPropTransformers); + _this.el = el; + prependToElement(el, _this.contentEl = createElement('div', { className: 'fc-view-container' })); + var calendar = _this.calendar; + for (var _i = 0, _a = calendar.pluginSystem.hooks.viewContainerModifiers; _i < _a.length; _i++) { + var modifyViewContainer = _a[_i]; + modifyViewContainer(_this.contentEl, calendar); + } + _this.toggleElClassNames(true); + _this.computeTitle = memoize(computeTitle); + _this.parseBusinessHours = memoize(function (input) { + return parseBusinessHours(input, _this.calendar); + }); + return _this; + } + CalendarComponent.prototype.destroy = function () { + if (this.header) { + this.header.destroy(); + } + if (this.footer) { + this.footer.destroy(); + } + if (this.view) { + this.view.destroy(); + } + removeElement(this.contentEl); + this.toggleElClassNames(false); + _super.prototype.destroy.call(this); + }; + CalendarComponent.prototype.toggleElClassNames = function (bool) { + var classList = this.el.classList; + var dirClassName = 'fc-' + this.opt('dir'); + var themeClassName = this.theme.getClass('widget'); + if (bool) { + classList.add('fc'); + classList.add(dirClassName); + classList.add(themeClassName); + } + else { + classList.remove('fc'); + classList.remove(dirClassName); + classList.remove(themeClassName); + } + }; + CalendarComponent.prototype.render = function (props) { + this.freezeHeight(); + var title = this.computeTitle(props.dateProfile, props.viewSpec.options); + this._renderToolbars(props.viewSpec, props.dateProfile, props.currentDate, props.dateProfileGenerator, title); + this.renderView(props, title); + this.updateSize(); + this.thawHeight(); + }; + CalendarComponent.prototype.renderToolbars = function (viewSpec, dateProfile, currentDate, dateProfileGenerator, title) { + var headerLayout = this.opt('header'); + var footerLayout = this.opt('footer'); + var now = this.calendar.getNow(); + var todayInfo = dateProfileGenerator.build(now); + var prevInfo = dateProfileGenerator.buildPrev(dateProfile, currentDate); + var nextInfo = dateProfileGenerator.buildNext(dateProfile, currentDate); + var toolbarProps = { + title: title, + activeButton: viewSpec.type, + isTodayEnabled: todayInfo.isValid && !rangeContainsMarker(dateProfile.currentRange, now), + isPrevEnabled: prevInfo.isValid, + isNextEnabled: nextInfo.isValid + }; + if (headerLayout) { + if (!this.header) { + this.header = new Toolbar(this.context, 'fc-header-toolbar'); + prependToElement(this.el, this.header.el); + } + this.header.receiveProps(__assign({ layout: headerLayout }, toolbarProps)); + } + else if (this.header) { + this.header.destroy(); + this.header = null; + } + if (footerLayout) { + if (!this.footer) { + this.footer = new Toolbar(this.context, 'fc-footer-toolbar'); + appendToElement(this.el, this.footer.el); + } + this.footer.receiveProps(__assign({ layout: footerLayout }, toolbarProps)); + } + else if (this.footer) { + this.footer.destroy(); + this.footer = null; + } + }; + CalendarComponent.prototype.renderView = function (props, title) { + var view = this.view; + var viewSpec = props.viewSpec, dateProfileGenerator = props.dateProfileGenerator; + if (!view || view.viewSpec !== viewSpec) { + if (view) { + view.destroy(); + } + view = this.view = new viewSpec['class']({ + calendar: this.calendar, + view: null, + dateEnv: this.dateEnv, + theme: this.theme, + options: viewSpec.options + }, viewSpec, dateProfileGenerator, this.contentEl); + } + else { + view.addScroll(view.queryScroll()); + } + view.title = title; // for the API + var viewProps = { + dateProfile: props.dateProfile, + businessHours: this.parseBusinessHours(viewSpec.options.businessHours), + eventStore: props.eventStore, + eventUiBases: props.eventUiBases, + dateSelection: props.dateSelection, + eventSelection: props.eventSelection, + eventDrag: props.eventDrag, + eventResize: props.eventResize + }; + var transformers = this.buildViewPropTransformers(this.calendar.pluginSystem.hooks.viewPropsTransformers); + for (var _i = 0, transformers_1 = transformers; _i < transformers_1.length; _i++) { + var transformer = transformers_1[_i]; + __assign(viewProps, transformer.transform(viewProps, viewSpec, props, view)); + } + view.receiveProps(viewProps); + }; + // Sizing + // ----------------------------------------------------------------------------------------------------------------- + CalendarComponent.prototype.updateSize = function (isResize) { + if (isResize === void 0) { isResize = false; } + var view = this.view; + if (isResize) { + view.addScroll(view.queryScroll()); + } + if (isResize || this.isHeightAuto == null) { + this.computeHeightVars(); + } + view.updateSize(isResize, this.viewHeight, this.isHeightAuto); + view.updateNowIndicator(); // we need to guarantee this will run after updateSize + view.popScroll(isResize); + }; + CalendarComponent.prototype.computeHeightVars = function () { + var calendar = this.calendar; // yuck. need to handle dynamic options + var heightInput = calendar.opt('height'); + var contentHeightInput = calendar.opt('contentHeight'); + this.isHeightAuto = heightInput === 'auto' || contentHeightInput === 'auto'; + if (typeof contentHeightInput === 'number') { // exists and not 'auto' + this.viewHeight = contentHeightInput; + } + else if (typeof contentHeightInput === 'function') { // exists and is a function + this.viewHeight = contentHeightInput(); + } + else if (typeof heightInput === 'number') { // exists and not 'auto' + this.viewHeight = heightInput - this.queryToolbarsHeight(); + } + else if (typeof heightInput === 'function') { // exists and is a function + this.viewHeight = heightInput() - this.queryToolbarsHeight(); + } + else if (heightInput === 'parent') { // set to height of parent element + var parentEl = this.el.parentNode; + this.viewHeight = parentEl.getBoundingClientRect().height - this.queryToolbarsHeight(); + } + else { + this.viewHeight = Math.round(this.contentEl.getBoundingClientRect().width / + Math.max(calendar.opt('aspectRatio'), .5)); + } + }; + CalendarComponent.prototype.queryToolbarsHeight = function () { + var height = 0; + if (this.header) { + height += computeHeightAndMargins(this.header.el); + } + if (this.footer) { + height += computeHeightAndMargins(this.footer.el); + } + return height; + }; + // Height "Freezing" + // ----------------------------------------------------------------------------------------------------------------- + CalendarComponent.prototype.freezeHeight = function () { + applyStyle(this.el, { + height: this.el.getBoundingClientRect().height, + overflow: 'hidden' + }); + }; + CalendarComponent.prototype.thawHeight = function () { + applyStyle(this.el, { + height: '', + overflow: '' + }); + }; + return CalendarComponent; + }(Component)); + // Title and Date Formatting + // ----------------------------------------------------------------------------------------------------------------- + // Computes what the title at the top of the calendar should be for this view + function computeTitle(dateProfile, viewOptions) { + var range; + // for views that span a large unit of time, show the proper interval, ignoring stray days before and after + if (/^(year|month)$/.test(dateProfile.currentRangeUnit)) { + range = dateProfile.currentRange; + } + else { // for day units or smaller, use the actual day range + range = dateProfile.activeRange; + } + return this.dateEnv.formatRange(range.start, range.end, createFormatter(viewOptions.titleFormat || computeTitleFormat(dateProfile), viewOptions.titleRangeSeparator), { isEndExclusive: dateProfile.isRangeAllDay }); + } + // Generates the format string that should be used to generate the title for the current date range. + // Attempts to compute the most appropriate format if not explicitly specified with `titleFormat`. + function computeTitleFormat(dateProfile) { + var currentRangeUnit = dateProfile.currentRangeUnit; + if (currentRangeUnit === 'year') { + return { year: 'numeric' }; + } + else if (currentRangeUnit === 'month') { + return { year: 'numeric', month: 'long' }; // like "September 2014" + } + else { + var days = diffWholeDays(dateProfile.currentRange.start, dateProfile.currentRange.end); + if (days !== null && days > 1) { + // multi-day range. shorter, like "Sep 9 - 10 2014" + return { year: 'numeric', month: 'short', day: 'numeric' }; + } + else { + // one day. longer, like "September 9 2014" + return { year: 'numeric', month: 'long', day: 'numeric' }; + } + } + } + // Plugin + // ----------------------------------------------------------------------------------------------------------------- + function buildViewPropTransformers(theClasses) { + return theClasses.map(function (theClass) { + return new theClass(); + }); + } + + var Interaction = /** @class */ (function () { + function Interaction(settings) { + this.component = settings.component; + } + Interaction.prototype.destroy = function () { + }; + return Interaction; + }()); + function parseInteractionSettings(component, input) { + return { + component: component, + el: input.el, + useEventCenter: input.useEventCenter != null ? input.useEventCenter : true + }; + } + function interactionSettingsToStore(settings) { + var _a; + return _a = {}, + _a[settings.component.uid] = settings, + _a; + } + // global state + var interactionSettingsStore = {}; + + /* + Detects when the user clicks on an event within a DateComponent + */ + var EventClicking = /** @class */ (function (_super) { + __extends(EventClicking, _super); + function EventClicking(settings) { + var _this = _super.call(this, settings) || this; + _this.handleSegClick = function (ev, segEl) { + var component = _this.component; + var seg = getElSeg(segEl); + if (seg && // might be the
surrounding the more link + component.isValidSegDownEl(ev.target)) { + // our way to simulate a link click for elements that can't be tags + // grab before trigger fired in case trigger trashes DOM thru rerendering + var hasUrlContainer = elementClosest(ev.target, '.fc-has-url'); + var url = hasUrlContainer ? hasUrlContainer.querySelector('a[href]').href : ''; + component.publiclyTrigger('eventClick', [ + { + el: segEl, + event: new EventApi(component.calendar, seg.eventRange.def, seg.eventRange.instance), + jsEvent: ev, + view: component.view + } + ]); + if (url && !ev.defaultPrevented) { + window.location.href = url; + } + } + }; + var component = settings.component; + _this.destroy = listenBySelector(component.el, 'click', component.fgSegSelector + ',' + component.bgSegSelector, _this.handleSegClick); + return _this; + } + return EventClicking; + }(Interaction)); + + /* + Triggers events and adds/removes core classNames when the user's pointer + enters/leaves event-elements of a component. + */ + var EventHovering = /** @class */ (function (_super) { + __extends(EventHovering, _super); + function EventHovering(settings) { + var _this = _super.call(this, settings) || this; + // for simulating an eventMouseLeave when the event el is destroyed while mouse is over it + _this.handleEventElRemove = function (el) { + if (el === _this.currentSegEl) { + _this.handleSegLeave(null, _this.currentSegEl); + } + }; + _this.handleSegEnter = function (ev, segEl) { + if (getElSeg(segEl)) { // TODO: better way to make sure not hovering over more+ link or its wrapper + segEl.classList.add('fc-allow-mouse-resize'); + _this.currentSegEl = segEl; + _this.triggerEvent('eventMouseEnter', ev, segEl); + } + }; + _this.handleSegLeave = function (ev, segEl) { + if (_this.currentSegEl) { + segEl.classList.remove('fc-allow-mouse-resize'); + _this.currentSegEl = null; + _this.triggerEvent('eventMouseLeave', ev, segEl); + } + }; + var component = settings.component; + _this.removeHoverListeners = listenToHoverBySelector(component.el, component.fgSegSelector + ',' + component.bgSegSelector, _this.handleSegEnter, _this.handleSegLeave); + component.calendar.on('eventElRemove', _this.handleEventElRemove); + return _this; + } + EventHovering.prototype.destroy = function () { + this.removeHoverListeners(); + this.component.calendar.off('eventElRemove', this.handleEventElRemove); + }; + EventHovering.prototype.triggerEvent = function (publicEvName, ev, segEl) { + var component = this.component; + var seg = getElSeg(segEl); + if (!ev || component.isValidSegDownEl(ev.target)) { + component.publiclyTrigger(publicEvName, [ + { + el: segEl, + event: new EventApi(this.component.calendar, seg.eventRange.def, seg.eventRange.instance), + jsEvent: ev, + view: component.view + } + ]); + } + }; + return EventHovering; + }(Interaction)); + + var StandardTheme = /** @class */ (function (_super) { + __extends(StandardTheme, _super); + function StandardTheme() { + return _super !== null && _super.apply(this, arguments) || this; + } + return StandardTheme; + }(Theme)); + StandardTheme.prototype.classes = { + widget: 'fc-unthemed', + widgetHeader: 'fc-widget-header', + widgetContent: 'fc-widget-content', + buttonGroup: 'fc-button-group', + button: 'fc-button fc-button-primary', + buttonActive: 'fc-button-active', + popoverHeader: 'fc-widget-header', + popoverContent: 'fc-widget-content', + // day grid + headerRow: 'fc-widget-header', + dayRow: 'fc-widget-content', + // list view + listView: 'fc-widget-content' + }; + StandardTheme.prototype.baseIconClass = 'fc-icon'; + StandardTheme.prototype.iconClasses = { + close: 'fc-icon-x', + prev: 'fc-icon-chevron-left', + next: 'fc-icon-chevron-right', + prevYear: 'fc-icon-chevrons-left', + nextYear: 'fc-icon-chevrons-right' + }; + StandardTheme.prototype.iconOverrideOption = 'buttonIcons'; + StandardTheme.prototype.iconOverrideCustomButtonOption = 'icon'; + StandardTheme.prototype.iconOverridePrefix = 'fc-icon-'; + + var Calendar = /** @class */ (function () { + function Calendar(el, overrides) { + var _this = this; + this.parseRawLocales = memoize(parseRawLocales); + this.buildLocale = memoize(buildLocale); + this.buildDateEnv = memoize(buildDateEnv); + this.buildTheme = memoize(buildTheme); + this.buildEventUiSingleBase = memoize(this._buildEventUiSingleBase); + this.buildSelectionConfig = memoize(this._buildSelectionConfig); + this.buildEventUiBySource = memoizeOutput(buildEventUiBySource, isPropsEqual); + this.buildEventUiBases = memoize(buildEventUiBases); + this.interactionsStore = {}; + this.actionQueue = []; + this.isReducing = false; + // isDisplaying: boolean = false // installed in DOM? accepting renders? + this.needsRerender = false; // needs a render? + this.needsFullRerender = false; + this.isRendering = false; // currently in the executeRender function? + this.renderingPauseDepth = 0; + this.buildDelayedRerender = memoize(buildDelayedRerender); + this.afterSizingTriggers = {}; + this.isViewUpdated = false; + this.isDatesUpdated = false; + this.isEventsUpdated = false; + this.el = el; + this.optionsManager = new OptionsManager(overrides || {}); + this.pluginSystem = new PluginSystem(); + // only do once. don't do in handleOptions. because can't remove plugins + this.addPluginInputs(this.optionsManager.computed.plugins || []); + this.handleOptions(this.optionsManager.computed); + this.publiclyTrigger('_init'); // for tests + this.hydrate(); + this.calendarInteractions = this.pluginSystem.hooks.calendarInteractions + .map(function (calendarInteractionClass) { + return new calendarInteractionClass(_this); + }); + } + Calendar.prototype.addPluginInputs = function (pluginInputs) { + var pluginDefs = refinePluginDefs(pluginInputs); + for (var _i = 0, pluginDefs_1 = pluginDefs; _i < pluginDefs_1.length; _i++) { + var pluginDef = pluginDefs_1[_i]; + this.pluginSystem.add(pluginDef); + } + }; + Object.defineProperty(Calendar.prototype, "view", { + // public API + get: function () { + return this.component ? this.component.view : null; + }, + enumerable: true, + configurable: true + }); + // Public API for rendering + // ----------------------------------------------------------------------------------------------------------------- + Calendar.prototype.render = function () { + if (!this.component) { + this.renderableEventStore = createEmptyEventStore(); + this.bindHandlers(); + this.executeRender(); + } + else { + this.requestRerender(true); + } + }; + Calendar.prototype.destroy = function () { + if (this.component) { + this.unbindHandlers(); + this.component.destroy(); // don't null-out. in case API needs access + this.component = null; // umm ??? + for (var _i = 0, _a = this.calendarInteractions; _i < _a.length; _i++) { + var interaction = _a[_i]; + interaction.destroy(); + } + this.publiclyTrigger('_destroyed'); + } + }; + // Handlers + // ----------------------------------------------------------------------------------------------------------------- + Calendar.prototype.bindHandlers = function () { + var _this = this; + // event delegation for nav links + this.removeNavLinkListener = listenBySelector(this.el, 'click', 'a[data-goto]', function (ev, anchorEl) { + var gotoOptions = anchorEl.getAttribute('data-goto'); + gotoOptions = gotoOptions ? JSON.parse(gotoOptions) : {}; + var dateEnv = _this.dateEnv; + var dateMarker = dateEnv.createMarker(gotoOptions.date); + var viewType = gotoOptions.type; + // property like "navLinkDayClick". might be a string or a function + var customAction = _this.viewOpt('navLink' + capitaliseFirstLetter(viewType) + 'Click'); + if (typeof customAction === 'function') { + customAction(dateEnv.toDate(dateMarker), ev); + } + else { + if (typeof customAction === 'string') { + viewType = customAction; + } + _this.zoomTo(dateMarker, viewType); + } + }); + if (this.opt('handleWindowResize')) { + window.addEventListener('resize', this.windowResizeProxy = debounce(// prevents rapid calls + this.windowResize.bind(this), this.opt('windowResizeDelay'))); + } + }; + Calendar.prototype.unbindHandlers = function () { + this.removeNavLinkListener(); + if (this.windowResizeProxy) { + window.removeEventListener('resize', this.windowResizeProxy); + this.windowResizeProxy = null; + } + }; + // Dispatcher + // ----------------------------------------------------------------------------------------------------------------- + Calendar.prototype.hydrate = function () { + var _this = this; + this.state = this.buildInitialState(); + var rawSources = this.opt('eventSources') || []; + var singleRawSource = this.opt('events'); + var sources = []; // parsed + if (singleRawSource) { + rawSources.unshift(singleRawSource); + } + for (var _i = 0, rawSources_1 = rawSources; _i < rawSources_1.length; _i++) { + var rawSource = rawSources_1[_i]; + var source = parseEventSource(rawSource, this); + if (source) { + sources.push(source); + } + } + this.batchRendering(function () { + _this.dispatch({ type: 'INIT' }); // pass in sources here? + _this.dispatch({ type: 'ADD_EVENT_SOURCES', sources: sources }); + _this.dispatch({ + type: 'SET_VIEW_TYPE', + viewType: _this.opt('defaultView') || _this.pluginSystem.hooks.defaultView + }); + }); + }; + Calendar.prototype.buildInitialState = function () { + return { + viewType: null, + loadingLevel: 0, + eventSourceLoadingLevel: 0, + currentDate: this.getInitialDate(), + dateProfile: null, + eventSources: {}, + eventStore: createEmptyEventStore(), + dateSelection: null, + eventSelection: '', + eventDrag: null, + eventResize: null + }; + }; + Calendar.prototype.dispatch = function (action) { + this.actionQueue.push(action); + if (!this.isReducing) { + this.isReducing = true; + var oldState = this.state; + while (this.actionQueue.length) { + this.state = this.reduce(this.state, this.actionQueue.shift(), this); + } + var newState = this.state; + this.isReducing = false; + if (!oldState.loadingLevel && newState.loadingLevel) { + this.publiclyTrigger('loading', [true]); + } + else if (oldState.loadingLevel && !newState.loadingLevel) { + this.publiclyTrigger('loading', [false]); + } + var view = this.component && this.component.view; + if (oldState.eventStore !== newState.eventStore || this.needsFullRerender) { + if (oldState.eventStore) { + this.isEventsUpdated = true; + } + } + if (oldState.dateProfile !== newState.dateProfile || this.needsFullRerender) { + if (oldState.dateProfile && view) { // why would view be null!? + this.publiclyTrigger('datesDestroy', [ + { + view: view, + el: view.el + } + ]); + } + this.isDatesUpdated = true; + } + if (oldState.viewType !== newState.viewType || this.needsFullRerender) { + if (oldState.viewType && view) { // why would view be null!? + this.publiclyTrigger('viewSkeletonDestroy', [ + { + view: view, + el: view.el + } + ]); + } + this.isViewUpdated = true; + } + this.requestRerender(); + } + }; + Calendar.prototype.reduce = function (state, action, calendar) { + return reduce(state, action, calendar); + }; + // Render Queue + // ----------------------------------------------------------------------------------------------------------------- + Calendar.prototype.requestRerender = function (needsFull) { + if (needsFull === void 0) { needsFull = false; } + this.needsRerender = true; + this.needsFullRerender = this.needsFullRerender || needsFull; + this.delayedRerender(); // will call a debounced-version of tryRerender + }; + Calendar.prototype.tryRerender = function () { + if (this.component && // must be accepting renders + this.needsRerender && // indicates that a rerender was requested + !this.renderingPauseDepth && // not paused + !this.isRendering // not currently in the render loop + ) { + this.executeRender(); + } + }; + Calendar.prototype.batchRendering = function (func) { + this.renderingPauseDepth++; + func(); + this.renderingPauseDepth--; + if (this.needsRerender) { + this.requestRerender(); + } + }; + // Rendering + // ----------------------------------------------------------------------------------------------------------------- + Calendar.prototype.executeRender = function () { + var needsFullRerender = this.needsFullRerender; // save before clearing + // clear these BEFORE the render so that new values will accumulate during render + this.needsRerender = false; + this.needsFullRerender = false; + this.isRendering = true; + this.renderComponent(needsFullRerender); + this.isRendering = false; + // received a rerender request while rendering + if (this.needsRerender) { + this.delayedRerender(); + } + }; + /* + don't call this directly. use executeRender instead + */ + Calendar.prototype.renderComponent = function (needsFull) { + var _a = this, state = _a.state, component = _a.component; + var viewType = state.viewType; + var viewSpec = this.viewSpecs[viewType]; + var savedScroll = (needsFull && component) ? component.view.queryScroll() : null; + if (!viewSpec) { + throw new Error("View type \"" + viewType + "\" is not valid"); + } + // if event sources are still loading and progressive rendering hasn't been enabled, + // keep rendering the last fully loaded set of events + var renderableEventStore = this.renderableEventStore = + (state.eventSourceLoadingLevel && !this.opt('progressiveEventRendering')) ? + this.renderableEventStore : + state.eventStore; + var eventUiSingleBase = this.buildEventUiSingleBase(viewSpec.options); + var eventUiBySource = this.buildEventUiBySource(state.eventSources); + var eventUiBases = this.eventUiBases = this.buildEventUiBases(renderableEventStore.defs, eventUiSingleBase, eventUiBySource); + if (needsFull || !component) { + if (component) { + component.freezeHeight(); // next component will unfreeze it + component.destroy(); + } + component = this.component = new CalendarComponent({ + calendar: this, + view: null, + dateEnv: this.dateEnv, + theme: this.theme, + options: this.optionsManager.computed + }, this.el); + this.isViewUpdated = true; + this.isDatesUpdated = true; + this.isEventsUpdated = true; + } + component.receiveProps(__assign({}, state, { viewSpec: viewSpec, dateProfile: state.dateProfile, dateProfileGenerator: this.dateProfileGenerators[viewType], eventStore: renderableEventStore, eventUiBases: eventUiBases, dateSelection: state.dateSelection, eventSelection: state.eventSelection, eventDrag: state.eventDrag, eventResize: state.eventResize })); + if (savedScroll) { + component.view.applyScroll(savedScroll, false); + } + if (this.isViewUpdated) { + this.isViewUpdated = false; + this.publiclyTrigger('viewSkeletonRender', [ + { + view: component.view, + el: component.view.el + } + ]); + } + if (this.isDatesUpdated) { + this.isDatesUpdated = false; + this.publiclyTrigger('datesRender', [ + { + view: component.view, + el: component.view.el + } + ]); + } + if (this.isEventsUpdated) { + this.isEventsUpdated = false; + } + this.releaseAfterSizingTriggers(); + }; + // Options + // ----------------------------------------------------------------------------------------------------------------- + Calendar.prototype.setOption = function (name, val) { + var _a; + this.mutateOptions((_a = {}, _a[name] = val, _a), [], true); + }; + Calendar.prototype.getOption = function (name) { + return this.optionsManager.computed[name]; + }; + Calendar.prototype.opt = function (name) { + return this.optionsManager.computed[name]; + }; + Calendar.prototype.viewOpt = function (name) { + return this.viewOpts()[name]; + }; + Calendar.prototype.viewOpts = function () { + return this.viewSpecs[this.state.viewType].options; + }; + /* + handles option changes (like a diff) + */ + Calendar.prototype.mutateOptions = function (updates, removals, isDynamic, deepEqual) { + var _this = this; + var changeHandlers = this.pluginSystem.hooks.optionChangeHandlers; + var normalUpdates = {}; + var specialUpdates = {}; + var oldDateEnv = this.dateEnv; // do this before handleOptions + var isTimeZoneDirty = false; + var isSizeDirty = false; + var anyDifficultOptions = Boolean(removals.length); + for (var name_1 in updates) { + if (changeHandlers[name_1]) { + specialUpdates[name_1] = updates[name_1]; + } + else { + normalUpdates[name_1] = updates[name_1]; + } + } + for (var name_2 in normalUpdates) { + if (/^(height|contentHeight|aspectRatio)$/.test(name_2)) { + isSizeDirty = true; + } + else if (/^(defaultDate|defaultView)$/.test(name_2)) ; + else { + anyDifficultOptions = true; + if (name_2 === 'timeZone') { + isTimeZoneDirty = true; + } + } + } + this.optionsManager.mutate(normalUpdates, removals, isDynamic); + if (anyDifficultOptions) { + this.handleOptions(this.optionsManager.computed); + this.needsFullRerender = true; + } + this.batchRendering(function () { + if (anyDifficultOptions) { + if (isTimeZoneDirty) { + _this.dispatch({ + type: 'CHANGE_TIMEZONE', + oldDateEnv: oldDateEnv + }); + } + /* HACK + has the same effect as calling this.requestRerender(true) + but recomputes the state's dateProfile + */ + _this.dispatch({ + type: 'SET_VIEW_TYPE', + viewType: _this.state.viewType + }); + } + else if (isSizeDirty) { + _this.updateSize(); + } + // special updates + if (deepEqual) { + for (var name_3 in specialUpdates) { + changeHandlers[name_3](specialUpdates[name_3], _this, deepEqual); + } + } + }); + }; + /* + rebuilds things based off of a complete set of refined options + */ + Calendar.prototype.handleOptions = function (options) { + var _this = this; + var pluginHooks = this.pluginSystem.hooks; + this.defaultAllDayEventDuration = createDuration(options.defaultAllDayEventDuration); + this.defaultTimedEventDuration = createDuration(options.defaultTimedEventDuration); + this.delayedRerender = this.buildDelayedRerender(options.rerenderDelay); + this.theme = this.buildTheme(options); + var available = this.parseRawLocales(options.locales); + this.availableRawLocales = available.map; + var locale = this.buildLocale(options.locale || available.defaultCode, available.map); + this.dateEnv = this.buildDateEnv(locale, options.timeZone, pluginHooks.namedTimeZonedImpl, options.firstDay, options.weekNumberCalculation, options.weekLabel, pluginHooks.cmdFormatter); + this.selectionConfig = this.buildSelectionConfig(options); // needs dateEnv. do after :( + // ineffecient to do every time? + this.viewSpecs = buildViewSpecs(pluginHooks.views, this.optionsManager); + // ineffecient to do every time? + this.dateProfileGenerators = mapHash(this.viewSpecs, function (viewSpec) { + return new viewSpec.class.prototype.dateProfileGeneratorClass(viewSpec, _this); + }); + }; + Calendar.prototype.getAvailableLocaleCodes = function () { + return Object.keys(this.availableRawLocales); + }; + Calendar.prototype._buildSelectionConfig = function (rawOpts) { + return processScopedUiProps('select', rawOpts, this); + }; + Calendar.prototype._buildEventUiSingleBase = function (rawOpts) { + if (rawOpts.editable) { // so 'editable' affected events + rawOpts = __assign({}, rawOpts, { eventEditable: true }); + } + return processScopedUiProps('event', rawOpts, this); + }; + // Trigger + // ----------------------------------------------------------------------------------------------------------------- + Calendar.prototype.hasPublicHandlers = function (name) { + return this.hasHandlers(name) || + this.opt(name); // handler specified in options + }; + Calendar.prototype.publiclyTrigger = function (name, args) { + var optHandler = this.opt(name); + this.triggerWith(name, this, args); + if (optHandler) { + return optHandler.apply(this, args); + } + }; + Calendar.prototype.publiclyTriggerAfterSizing = function (name, args) { + var afterSizingTriggers = this.afterSizingTriggers; + (afterSizingTriggers[name] || (afterSizingTriggers[name] = [])).push(args); + }; + Calendar.prototype.releaseAfterSizingTriggers = function () { + var afterSizingTriggers = this.afterSizingTriggers; + for (var name_4 in afterSizingTriggers) { + for (var _i = 0, _a = afterSizingTriggers[name_4]; _i < _a.length; _i++) { + var args = _a[_i]; + this.publiclyTrigger(name_4, args); + } + } + this.afterSizingTriggers = {}; + }; + // View + // ----------------------------------------------------------------------------------------------------------------- + // Returns a boolean about whether the view is okay to instantiate at some point + Calendar.prototype.isValidViewType = function (viewType) { + return Boolean(this.viewSpecs[viewType]); + }; + Calendar.prototype.changeView = function (viewType, dateOrRange) { + var dateMarker = null; + if (dateOrRange) { + if (dateOrRange.start && dateOrRange.end) { // a range + this.optionsManager.mutate({ visibleRange: dateOrRange }, []); // will not rerender + this.handleOptions(this.optionsManager.computed); // ...but yuck + } + else { // a date + dateMarker = this.dateEnv.createMarker(dateOrRange); // just like gotoDate + } + } + this.unselect(); + this.dispatch({ + type: 'SET_VIEW_TYPE', + viewType: viewType, + dateMarker: dateMarker + }); + }; + // Forces navigation to a view for the given date. + // `viewType` can be a specific view name or a generic one like "week" or "day". + // needs to change + Calendar.prototype.zoomTo = function (dateMarker, viewType) { + var spec; + viewType = viewType || 'day'; // day is default zoom + spec = this.viewSpecs[viewType] || + this.getUnitViewSpec(viewType); + this.unselect(); + if (spec) { + this.dispatch({ + type: 'SET_VIEW_TYPE', + viewType: spec.type, + dateMarker: dateMarker + }); + } + else { + this.dispatch({ + type: 'SET_DATE', + dateMarker: dateMarker + }); + } + }; + // Given a duration singular unit, like "week" or "day", finds a matching view spec. + // Preference is given to views that have corresponding buttons. + Calendar.prototype.getUnitViewSpec = function (unit) { + var component = this.component; + var viewTypes = []; + var i; + var spec; + // put views that have buttons first. there will be duplicates, but oh + if (component.header) { + viewTypes.push.apply(viewTypes, component.header.viewsWithButtons); + } + if (component.footer) { + viewTypes.push.apply(viewTypes, component.footer.viewsWithButtons); + } + for (var viewType in this.viewSpecs) { + viewTypes.push(viewType); + } + for (i = 0; i < viewTypes.length; i++) { + spec = this.viewSpecs[viewTypes[i]]; + if (spec) { + if (spec.singleUnit === unit) { + return spec; + } + } + } + }; + // Current Date + // ----------------------------------------------------------------------------------------------------------------- + Calendar.prototype.getInitialDate = function () { + var defaultDateInput = this.opt('defaultDate'); + // compute the initial ambig-timezone date + if (defaultDateInput != null) { + return this.dateEnv.createMarker(defaultDateInput); + } + else { + return this.getNow(); // getNow already returns unzoned + } + }; + Calendar.prototype.prev = function () { + this.unselect(); + this.dispatch({ type: 'PREV' }); + }; + Calendar.prototype.next = function () { + this.unselect(); + this.dispatch({ type: 'NEXT' }); + }; + Calendar.prototype.prevYear = function () { + this.unselect(); + this.dispatch({ + type: 'SET_DATE', + dateMarker: this.dateEnv.addYears(this.state.currentDate, -1) + }); + }; + Calendar.prototype.nextYear = function () { + this.unselect(); + this.dispatch({ + type: 'SET_DATE', + dateMarker: this.dateEnv.addYears(this.state.currentDate, 1) + }); + }; + Calendar.prototype.today = function () { + this.unselect(); + this.dispatch({ + type: 'SET_DATE', + dateMarker: this.getNow() + }); + }; + Calendar.prototype.gotoDate = function (zonedDateInput) { + this.unselect(); + this.dispatch({ + type: 'SET_DATE', + dateMarker: this.dateEnv.createMarker(zonedDateInput) + }); + }; + Calendar.prototype.incrementDate = function (deltaInput) { + var delta = createDuration(deltaInput); + if (delta) { // else, warn about invalid input? + this.unselect(); + this.dispatch({ + type: 'SET_DATE', + dateMarker: this.dateEnv.add(this.state.currentDate, delta) + }); + } + }; + // for external API + Calendar.prototype.getDate = function () { + return this.dateEnv.toDate(this.state.currentDate); + }; + // Date Formatting Utils + // ----------------------------------------------------------------------------------------------------------------- + Calendar.prototype.formatDate = function (d, formatter) { + var dateEnv = this.dateEnv; + return dateEnv.format(dateEnv.createMarker(d), createFormatter(formatter)); + }; + // `settings` is for formatter AND isEndExclusive + Calendar.prototype.formatRange = function (d0, d1, settings) { + var dateEnv = this.dateEnv; + return dateEnv.formatRange(dateEnv.createMarker(d0), dateEnv.createMarker(d1), createFormatter(settings, this.opt('defaultRangeSeparator')), settings); + }; + Calendar.prototype.formatIso = function (d, omitTime) { + var dateEnv = this.dateEnv; + return dateEnv.formatIso(dateEnv.createMarker(d), { omitTime: omitTime }); + }; + // Sizing + // ----------------------------------------------------------------------------------------------------------------- + Calendar.prototype.windowResize = function (ev) { + if (!this.isHandlingWindowResize && + this.component && // why? + ev.target === window // not a jqui resize event + ) { + this.isHandlingWindowResize = true; + this.updateSize(); + this.publiclyTrigger('windowResize', [this.view]); + this.isHandlingWindowResize = false; + } + }; + Calendar.prototype.updateSize = function () { + if (this.component) { // when? + this.component.updateSize(true); + } + }; + // Component Registration + // ----------------------------------------------------------------------------------------------------------------- + Calendar.prototype.registerInteractiveComponent = function (component, settingsInput) { + var settings = parseInteractionSettings(component, settingsInput); + var DEFAULT_INTERACTIONS = [ + EventClicking, + EventHovering + ]; + var interactionClasses = DEFAULT_INTERACTIONS.concat(this.pluginSystem.hooks.componentInteractions); + var interactions = interactionClasses.map(function (interactionClass) { + return new interactionClass(settings); + }); + this.interactionsStore[component.uid] = interactions; + interactionSettingsStore[component.uid] = settings; + }; + Calendar.prototype.unregisterInteractiveComponent = function (component) { + for (var _i = 0, _a = this.interactionsStore[component.uid]; _i < _a.length; _i++) { + var listener = _a[_i]; + listener.destroy(); + } + delete this.interactionsStore[component.uid]; + delete interactionSettingsStore[component.uid]; + }; + // Date Selection / Event Selection / DayClick + // ----------------------------------------------------------------------------------------------------------------- + // this public method receives start/end dates in any format, with any timezone + // NOTE: args were changed from v3 + Calendar.prototype.select = function (dateOrObj, endDate) { + var selectionInput; + if (endDate == null) { + if (dateOrObj.start != null) { + selectionInput = dateOrObj; + } + else { + selectionInput = { + start: dateOrObj, + end: null + }; + } + } + else { + selectionInput = { + start: dateOrObj, + end: endDate + }; + } + var selection = parseDateSpan(selectionInput, this.dateEnv, createDuration({ days: 1 }) // TODO: cache this? + ); + if (selection) { // throw parse error otherwise? + this.dispatch({ type: 'SELECT_DATES', selection: selection }); + this.triggerDateSelect(selection); + } + }; + // public method + Calendar.prototype.unselect = function (pev) { + if (this.state.dateSelection) { + this.dispatch({ type: 'UNSELECT_DATES' }); + this.triggerDateUnselect(pev); + } + }; + Calendar.prototype.triggerDateSelect = function (selection, pev) { + var arg = __assign({}, this.buildDateSpanApi(selection), { jsEvent: pev ? pev.origEvent : null, view: this.view }); + this.publiclyTrigger('select', [arg]); + }; + Calendar.prototype.triggerDateUnselect = function (pev) { + this.publiclyTrigger('unselect', [ + { + jsEvent: pev ? pev.origEvent : null, + view: this.view + } + ]); + }; + // TODO: receive pev? + Calendar.prototype.triggerDateClick = function (dateSpan, dayEl, view, ev) { + var arg = __assign({}, this.buildDatePointApi(dateSpan), { dayEl: dayEl, jsEvent: ev, // Is this always a mouse event? See #4655 + view: view }); + this.publiclyTrigger('dateClick', [arg]); + }; + Calendar.prototype.buildDatePointApi = function (dateSpan) { + var props = {}; + for (var _i = 0, _a = this.pluginSystem.hooks.datePointTransforms; _i < _a.length; _i++) { + var transform = _a[_i]; + __assign(props, transform(dateSpan, this)); + } + __assign(props, buildDatePointApi(dateSpan, this.dateEnv)); + return props; + }; + Calendar.prototype.buildDateSpanApi = function (dateSpan) { + var props = {}; + for (var _i = 0, _a = this.pluginSystem.hooks.dateSpanTransforms; _i < _a.length; _i++) { + var transform = _a[_i]; + __assign(props, transform(dateSpan, this)); + } + __assign(props, buildDateSpanApi(dateSpan, this.dateEnv)); + return props; + }; + // Date Utils + // ----------------------------------------------------------------------------------------------------------------- + // Returns a DateMarker for the current date, as defined by the client's computer or from the `now` option + Calendar.prototype.getNow = function () { + var now = this.opt('now'); + if (typeof now === 'function') { + now = now(); + } + if (now == null) { + return this.dateEnv.createNowMarker(); + } + return this.dateEnv.createMarker(now); + }; + // Event-Date Utilities + // ----------------------------------------------------------------------------------------------------------------- + // Given an event's allDay status and start date, return what its fallback end date should be. + // TODO: rename to computeDefaultEventEnd + Calendar.prototype.getDefaultEventEnd = function (allDay, marker) { + var end = marker; + if (allDay) { + end = startOfDay(end); + end = this.dateEnv.add(end, this.defaultAllDayEventDuration); + } + else { + end = this.dateEnv.add(end, this.defaultTimedEventDuration); + } + return end; + }; + // Public Events API + // ----------------------------------------------------------------------------------------------------------------- + Calendar.prototype.addEvent = function (eventInput, sourceInput) { + if (eventInput instanceof EventApi) { + var def = eventInput._def; + var instance = eventInput._instance; + // not already present? don't want to add an old snapshot + if (!this.state.eventStore.defs[def.defId]) { + this.dispatch({ + type: 'ADD_EVENTS', + eventStore: eventTupleToStore({ def: def, instance: instance }) // TODO: better util for two args? + }); + } + return eventInput; + } + var sourceId; + if (sourceInput instanceof EventSourceApi) { + sourceId = sourceInput.internalEventSource.sourceId; + } + else if (sourceInput != null) { + var sourceApi = this.getEventSourceById(sourceInput); // TODO: use an internal function + if (!sourceApi) { + console.warn('Could not find an event source with ID "' + sourceInput + '"'); // TODO: test + return null; + } + else { + sourceId = sourceApi.internalEventSource.sourceId; + } + } + var tuple = parseEvent(eventInput, sourceId, this); + if (tuple) { + this.dispatch({ + type: 'ADD_EVENTS', + eventStore: eventTupleToStore(tuple) + }); + return new EventApi(this, tuple.def, tuple.def.recurringDef ? null : tuple.instance); + } + return null; + }; + // TODO: optimize + Calendar.prototype.getEventById = function (id) { + var _a = this.state.eventStore, defs = _a.defs, instances = _a.instances; + id = String(id); + for (var defId in defs) { + var def = defs[defId]; + if (def.publicId === id) { + if (def.recurringDef) { + return new EventApi(this, def, null); + } + else { + for (var instanceId in instances) { + var instance = instances[instanceId]; + if (instance.defId === def.defId) { + return new EventApi(this, def, instance); + } + } + } + } + } + return null; + }; + Calendar.prototype.getEvents = function () { + var _a = this.state.eventStore, defs = _a.defs, instances = _a.instances; + var eventApis = []; + for (var id in instances) { + var instance = instances[id]; + var def = defs[instance.defId]; + eventApis.push(new EventApi(this, def, instance)); + } + return eventApis; + }; + Calendar.prototype.removeAllEvents = function () { + this.dispatch({ type: 'REMOVE_ALL_EVENTS' }); + }; + Calendar.prototype.rerenderEvents = function () { + this.dispatch({ type: 'RESET_EVENTS' }); + }; + // Public Event Sources API + // ----------------------------------------------------------------------------------------------------------------- + Calendar.prototype.getEventSources = function () { + var sourceHash = this.state.eventSources; + var sourceApis = []; + for (var internalId in sourceHash) { + sourceApis.push(new EventSourceApi(this, sourceHash[internalId])); + } + return sourceApis; + }; + Calendar.prototype.getEventSourceById = function (id) { + var sourceHash = this.state.eventSources; + id = String(id); + for (var sourceId in sourceHash) { + if (sourceHash[sourceId].publicId === id) { + return new EventSourceApi(this, sourceHash[sourceId]); + } + } + return null; + }; + Calendar.prototype.addEventSource = function (sourceInput) { + if (sourceInput instanceof EventSourceApi) { + // not already present? don't want to add an old snapshot + if (!this.state.eventSources[sourceInput.internalEventSource.sourceId]) { + this.dispatch({ + type: 'ADD_EVENT_SOURCES', + sources: [sourceInput.internalEventSource] + }); + } + return sourceInput; + } + var eventSource = parseEventSource(sourceInput, this); + if (eventSource) { // TODO: error otherwise? + this.dispatch({ type: 'ADD_EVENT_SOURCES', sources: [eventSource] }); + return new EventSourceApi(this, eventSource); + } + return null; + }; + Calendar.prototype.removeAllEventSources = function () { + this.dispatch({ type: 'REMOVE_ALL_EVENT_SOURCES' }); + }; + Calendar.prototype.refetchEvents = function () { + this.dispatch({ type: 'FETCH_EVENT_SOURCES' }); + }; + // Scroll + // ----------------------------------------------------------------------------------------------------------------- + Calendar.prototype.scrollToTime = function (timeInput) { + var duration = createDuration(timeInput); + if (duration) { + this.component.view.scrollToDuration(duration); + } + }; + return Calendar; + }()); + EmitterMixin.mixInto(Calendar); + // for memoizers + // ----------------------------------------------------------------------------------------------------------------- + function buildDateEnv(locale, timeZone, namedTimeZoneImpl, firstDay, weekNumberCalculation, weekLabel, cmdFormatter) { + return new DateEnv({ + calendarSystem: 'gregory', + timeZone: timeZone, + namedTimeZoneImpl: namedTimeZoneImpl, + locale: locale, + weekNumberCalculation: weekNumberCalculation, + firstDay: firstDay, + weekLabel: weekLabel, + cmdFormatter: cmdFormatter + }); + } + function buildTheme(calendarOptions) { + var themeClass = this.pluginSystem.hooks.themeClasses[calendarOptions.themeSystem] || StandardTheme; + return new themeClass(calendarOptions); + } + function buildDelayedRerender(wait) { + var func = this.tryRerender.bind(this); + if (wait != null) { + func = debounce(func, wait); + } + return func; + } + function buildEventUiBySource(eventSources) { + return mapHash(eventSources, function (eventSource) { + return eventSource.ui; + }); + } + function buildEventUiBases(eventDefs, eventUiSingleBase, eventUiBySource) { + var eventUiBases = { '': eventUiSingleBase }; + for (var defId in eventDefs) { + var def = eventDefs[defId]; + if (def.sourceId && eventUiBySource[def.sourceId]) { + eventUiBases[defId] = eventUiBySource[def.sourceId]; + } + } + return eventUiBases; + } + + var View = /** @class */ (function (_super) { + __extends(View, _super); + function View(context, viewSpec, dateProfileGenerator, parentEl) { + var _this = _super.call(this, context, createElement('div', { className: 'fc-view fc-' + viewSpec.type + '-view' }), true // isView (HACK) + ) || this; + _this.renderDatesMem = memoizeRendering(_this.renderDatesWrap, _this.unrenderDatesWrap); + _this.renderBusinessHoursMem = memoizeRendering(_this.renderBusinessHours, _this.unrenderBusinessHours, [_this.renderDatesMem]); + _this.renderDateSelectionMem = memoizeRendering(_this.renderDateSelectionWrap, _this.unrenderDateSelectionWrap, [_this.renderDatesMem]); + _this.renderEventsMem = memoizeRendering(_this.renderEvents, _this.unrenderEvents, [_this.renderDatesMem]); + _this.renderEventSelectionMem = memoizeRendering(_this.renderEventSelectionWrap, _this.unrenderEventSelectionWrap, [_this.renderEventsMem]); + _this.renderEventDragMem = memoizeRendering(_this.renderEventDragWrap, _this.unrenderEventDragWrap, [_this.renderDatesMem]); + _this.renderEventResizeMem = memoizeRendering(_this.renderEventResizeWrap, _this.unrenderEventResizeWrap, [_this.renderDatesMem]); + _this.viewSpec = viewSpec; + _this.dateProfileGenerator = dateProfileGenerator; + _this.type = viewSpec.type; + _this.eventOrderSpecs = parseFieldSpecs(_this.opt('eventOrder')); + _this.nextDayThreshold = createDuration(_this.opt('nextDayThreshold')); + parentEl.appendChild(_this.el); + _this.initialize(); + return _this; + } + View.prototype.initialize = function () { + }; + Object.defineProperty(View.prototype, "activeStart", { + // Date Setting/Unsetting + // ----------------------------------------------------------------------------------------------------------------- + get: function () { + return this.dateEnv.toDate(this.props.dateProfile.activeRange.start); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(View.prototype, "activeEnd", { + get: function () { + return this.dateEnv.toDate(this.props.dateProfile.activeRange.end); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(View.prototype, "currentStart", { + get: function () { + return this.dateEnv.toDate(this.props.dateProfile.currentRange.start); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(View.prototype, "currentEnd", { + get: function () { + return this.dateEnv.toDate(this.props.dateProfile.currentRange.end); + }, + enumerable: true, + configurable: true + }); + // General Rendering + // ----------------------------------------------------------------------------------------------------------------- + View.prototype.render = function (props) { + this.renderDatesMem(props.dateProfile); + this.renderBusinessHoursMem(props.businessHours); + this.renderDateSelectionMem(props.dateSelection); + this.renderEventsMem(props.eventStore); + this.renderEventSelectionMem(props.eventSelection); + this.renderEventDragMem(props.eventDrag); + this.renderEventResizeMem(props.eventResize); + }; + View.prototype.destroy = function () { + _super.prototype.destroy.call(this); + this.renderDatesMem.unrender(); // should unrender everything else + }; + // Sizing + // ----------------------------------------------------------------------------------------------------------------- + View.prototype.updateSize = function (isResize, viewHeight, isAuto) { + var calendar = this.calendar; + if (isResize || // HACKS... + calendar.isViewUpdated || + calendar.isDatesUpdated || + calendar.isEventsUpdated) { + // sort of the catch-all sizing + // anything that might cause dimension changes + this.updateBaseSize(isResize, viewHeight, isAuto); + } + }; + View.prototype.updateBaseSize = function (isResize, viewHeight, isAuto) { + }; + // Date Rendering + // ----------------------------------------------------------------------------------------------------------------- + View.prototype.renderDatesWrap = function (dateProfile) { + this.renderDates(dateProfile); + this.addScroll({ + duration: createDuration(this.opt('scrollTime')) + }); + this.startNowIndicator(dateProfile); // shouldn't render yet because updateSize will be called soon + }; + View.prototype.unrenderDatesWrap = function () { + this.stopNowIndicator(); + this.unrenderDates(); + }; + View.prototype.renderDates = function (dateProfile) { }; + View.prototype.unrenderDates = function () { }; + // Business Hours + // ----------------------------------------------------------------------------------------------------------------- + View.prototype.renderBusinessHours = function (businessHours) { }; + View.prototype.unrenderBusinessHours = function () { }; + // Date Selection + // ----------------------------------------------------------------------------------------------------------------- + View.prototype.renderDateSelectionWrap = function (selection) { + if (selection) { + this.renderDateSelection(selection); + } + }; + View.prototype.unrenderDateSelectionWrap = function (selection) { + if (selection) { + this.unrenderDateSelection(selection); + } + }; + View.prototype.renderDateSelection = function (selection) { }; + View.prototype.unrenderDateSelection = function (selection) { }; + // Event Rendering + // ----------------------------------------------------------------------------------------------------------------- + View.prototype.renderEvents = function (eventStore) { }; + View.prototype.unrenderEvents = function () { }; + // util for subclasses + View.prototype.sliceEvents = function (eventStore, allDay) { + var props = this.props; + return sliceEventStore(eventStore, props.eventUiBases, props.dateProfile.activeRange, allDay ? this.nextDayThreshold : null).fg; + }; + View.prototype.computeEventDraggable = function (eventDef, eventUi) { + var transformers = this.calendar.pluginSystem.hooks.isDraggableTransformers; + var val = eventUi.startEditable; + for (var _i = 0, transformers_1 = transformers; _i < transformers_1.length; _i++) { + var transformer = transformers_1[_i]; + val = transformer(val, eventDef, eventUi, this); + } + return val; + }; + View.prototype.computeEventStartResizable = function (eventDef, eventUi) { + return eventUi.durationEditable && this.opt('eventResizableFromStart'); + }; + View.prototype.computeEventEndResizable = function (eventDef, eventUi) { + return eventUi.durationEditable; + }; + // Event Selection + // ----------------------------------------------------------------------------------------------------------------- + View.prototype.renderEventSelectionWrap = function (instanceId) { + if (instanceId) { + this.renderEventSelection(instanceId); + } + }; + View.prototype.unrenderEventSelectionWrap = function (instanceId) { + if (instanceId) { + this.unrenderEventSelection(instanceId); + } + }; + View.prototype.renderEventSelection = function (instanceId) { }; + View.prototype.unrenderEventSelection = function (instanceId) { }; + // Event Drag + // ----------------------------------------------------------------------------------------------------------------- + View.prototype.renderEventDragWrap = function (state) { + if (state) { + this.renderEventDrag(state); + } + }; + View.prototype.unrenderEventDragWrap = function (state) { + if (state) { + this.unrenderEventDrag(state); + } + }; + View.prototype.renderEventDrag = function (state) { }; + View.prototype.unrenderEventDrag = function (state) { }; + // Event Resize + // ----------------------------------------------------------------------------------------------------------------- + View.prototype.renderEventResizeWrap = function (state) { + if (state) { + this.renderEventResize(state); + } + }; + View.prototype.unrenderEventResizeWrap = function (state) { + if (state) { + this.unrenderEventResize(state); + } + }; + View.prototype.renderEventResize = function (state) { }; + View.prototype.unrenderEventResize = function (state) { }; + /* Now Indicator + ------------------------------------------------------------------------------------------------------------------*/ + // Immediately render the current time indicator and begins re-rendering it at an interval, + // which is defined by this.getNowIndicatorUnit(). + // TODO: somehow do this for the current whole day's background too + View.prototype.startNowIndicator = function (dateProfile) { + var _this = this; + var dateEnv = this.dateEnv; + var unit; + var update; + var delay; // ms wait value + if (this.opt('nowIndicator')) { + unit = this.getNowIndicatorUnit(dateProfile); + if (unit) { + update = this.updateNowIndicator.bind(this); + this.initialNowDate = this.calendar.getNow(); + this.initialNowQueriedMs = new Date().valueOf(); + // wait until the beginning of the next interval + delay = dateEnv.add(dateEnv.startOf(this.initialNowDate, unit), createDuration(1, unit)).valueOf() - this.initialNowDate.valueOf(); + // TODO: maybe always use setTimeout, waiting until start of next unit + this.nowIndicatorTimeoutID = setTimeout(function () { + _this.nowIndicatorTimeoutID = null; + update(); + if (unit === 'second') { + delay = 1000; // every second + } + else { + delay = 1000 * 60; // otherwise, every minute + } + _this.nowIndicatorIntervalID = setInterval(update, delay); // update every interval + }, delay); + } + // rendering will be initiated in updateSize + } + }; + // rerenders the now indicator, computing the new current time from the amount of time that has passed + // since the initial getNow call. + View.prototype.updateNowIndicator = function () { + if (this.props.dateProfile && // a way to determine if dates were rendered yet + this.initialNowDate // activated before? + ) { + this.unrenderNowIndicator(); // won't unrender if unnecessary + this.renderNowIndicator(addMs(this.initialNowDate, new Date().valueOf() - this.initialNowQueriedMs)); + this.isNowIndicatorRendered = true; + } + }; + // Immediately unrenders the view's current time indicator and stops any re-rendering timers. + // Won't cause side effects if indicator isn't rendered. + View.prototype.stopNowIndicator = function () { + if (this.isNowIndicatorRendered) { + if (this.nowIndicatorTimeoutID) { + clearTimeout(this.nowIndicatorTimeoutID); + this.nowIndicatorTimeoutID = null; + } + if (this.nowIndicatorIntervalID) { + clearInterval(this.nowIndicatorIntervalID); + this.nowIndicatorIntervalID = null; + } + this.unrenderNowIndicator(); + this.isNowIndicatorRendered = false; + } + }; + View.prototype.getNowIndicatorUnit = function (dateProfile) { + // subclasses should implement + }; + // Renders a current time indicator at the given datetime + View.prototype.renderNowIndicator = function (date) { + // SUBCLASSES MUST PASS TO CHILDREN! + }; + // Undoes the rendering actions from renderNowIndicator + View.prototype.unrenderNowIndicator = function () { + // SUBCLASSES MUST PASS TO CHILDREN! + }; + /* Scroller + ------------------------------------------------------------------------------------------------------------------*/ + View.prototype.addScroll = function (scroll) { + var queuedScroll = this.queuedScroll || (this.queuedScroll = {}); + __assign(queuedScroll, scroll); + }; + View.prototype.popScroll = function (isResize) { + this.applyQueuedScroll(isResize); + this.queuedScroll = null; + }; + View.prototype.applyQueuedScroll = function (isResize) { + this.applyScroll(this.queuedScroll || {}, isResize); + }; + View.prototype.queryScroll = function () { + var scroll = {}; + if (this.props.dateProfile) { // dates rendered yet? + __assign(scroll, this.queryDateScroll()); + } + return scroll; + }; + View.prototype.applyScroll = function (scroll, isResize) { + var duration = scroll.duration; + if (duration != null) { + delete scroll.duration; + if (this.props.dateProfile) { // dates rendered yet? + __assign(scroll, this.computeDateScroll(duration)); + } + } + if (this.props.dateProfile) { // dates rendered yet? + this.applyDateScroll(scroll); + } + }; + View.prototype.computeDateScroll = function (duration) { + return {}; // subclasses must implement + }; + View.prototype.queryDateScroll = function () { + return {}; // subclasses must implement + }; + View.prototype.applyDateScroll = function (scroll) { + // subclasses must implement + }; + // for API + View.prototype.scrollToDuration = function (duration) { + this.applyScroll({ duration: duration }, false); + }; + return View; + }(DateComponent)); + EmitterMixin.mixInto(View); + View.prototype.usesMinMaxTime = false; + View.prototype.dateProfileGeneratorClass = DateProfileGenerator; + + var FgEventRenderer = /** @class */ (function () { + function FgEventRenderer(context) { + this.segs = []; + this.isSizeDirty = false; + this.context = context; + } + FgEventRenderer.prototype.renderSegs = function (segs, mirrorInfo) { + this.rangeUpdated(); // called too frequently :( + // render an `.el` on each seg + // returns a subset of the segs. segs that were actually rendered + segs = this.renderSegEls(segs, mirrorInfo); + this.segs = segs; + this.attachSegs(segs, mirrorInfo); + this.isSizeDirty = true; + this.context.view.triggerRenderedSegs(this.segs, Boolean(mirrorInfo)); + }; + FgEventRenderer.prototype.unrender = function (_segs, mirrorInfo) { + this.context.view.triggerWillRemoveSegs(this.segs, Boolean(mirrorInfo)); + this.detachSegs(this.segs); + this.segs = []; + }; + // Updates values that rely on options and also relate to range + FgEventRenderer.prototype.rangeUpdated = function () { + var options = this.context.options; + var displayEventTime; + var displayEventEnd; + this.eventTimeFormat = createFormatter(options.eventTimeFormat || this.computeEventTimeFormat(), options.defaultRangeSeparator); + displayEventTime = options.displayEventTime; + if (displayEventTime == null) { + displayEventTime = this.computeDisplayEventTime(); // might be based off of range + } + displayEventEnd = options.displayEventEnd; + if (displayEventEnd == null) { + displayEventEnd = this.computeDisplayEventEnd(); // might be based off of range + } + this.displayEventTime = displayEventTime; + this.displayEventEnd = displayEventEnd; + }; + // Renders and assigns an `el` property for each foreground event segment. + // Only returns segments that successfully rendered. + FgEventRenderer.prototype.renderSegEls = function (segs, mirrorInfo) { + var html = ''; + var i; + if (segs.length) { // don't build an empty html string + // build a large concatenation of event segment HTML + for (i = 0; i < segs.length; i++) { + html += this.renderSegHtml(segs[i], mirrorInfo); + } + // Grab individual elements from the combined HTML string. Use each as the default rendering. + // Then, compute the 'el' for each segment. An el might be null if the eventRender callback returned false. + htmlToElements(html).forEach(function (el, i) { + var seg = segs[i]; + if (el) { + seg.el = el; + } + }); + segs = filterSegsViaEls(this.context.view, segs, Boolean(mirrorInfo)); + } + return segs; + }; + // Generic utility for generating the HTML classNames for an event segment's element + FgEventRenderer.prototype.getSegClasses = function (seg, isDraggable, isResizable, mirrorInfo) { + var classes = [ + 'fc-event', + seg.isStart ? 'fc-start' : 'fc-not-start', + seg.isEnd ? 'fc-end' : 'fc-not-end' + ].concat(seg.eventRange.ui.classNames); + if (isDraggable) { + classes.push('fc-draggable'); + } + if (isResizable) { + classes.push('fc-resizable'); + } + if (mirrorInfo) { + classes.push('fc-mirror'); + if (mirrorInfo.isDragging) { + classes.push('fc-dragging'); + } + if (mirrorInfo.isResizing) { + classes.push('fc-resizing'); + } + } + return classes; + }; + // Compute the text that should be displayed on an event's element. + // `range` can be the Event object itself, or something range-like, with at least a `start`. + // If event times are disabled, or the event has no time, will return a blank string. + // If not specified, formatter will default to the eventTimeFormat setting, + // and displayEnd will default to the displayEventEnd setting. + FgEventRenderer.prototype.getTimeText = function (eventRange, formatter, displayEnd) { + var def = eventRange.def, instance = eventRange.instance; + return this._getTimeText(instance.range.start, def.hasEnd ? instance.range.end : null, def.allDay, formatter, displayEnd, instance.forcedStartTzo, instance.forcedEndTzo); + }; + FgEventRenderer.prototype._getTimeText = function (start, end, allDay, formatter, displayEnd, forcedStartTzo, forcedEndTzo) { + var dateEnv = this.context.dateEnv; + if (formatter == null) { + formatter = this.eventTimeFormat; + } + if (displayEnd == null) { + displayEnd = this.displayEventEnd; + } + if (this.displayEventTime && !allDay) { + if (displayEnd && end) { + return dateEnv.formatRange(start, end, formatter, { + forcedStartTzo: forcedStartTzo, + forcedEndTzo: forcedEndTzo + }); + } + else { + return dateEnv.format(start, formatter, { + forcedTzo: forcedStartTzo + }); + } + } + return ''; + }; + FgEventRenderer.prototype.computeEventTimeFormat = function () { + return { + hour: 'numeric', + minute: '2-digit', + omitZeroMinute: true + }; + }; + FgEventRenderer.prototype.computeDisplayEventTime = function () { + return true; + }; + FgEventRenderer.prototype.computeDisplayEventEnd = function () { + return true; + }; + // Utility for generating event skin-related CSS properties + FgEventRenderer.prototype.getSkinCss = function (ui) { + return { + 'background-color': ui.backgroundColor, + 'border-color': ui.borderColor, + color: ui.textColor + }; + }; + FgEventRenderer.prototype.sortEventSegs = function (segs) { + var specs = this.context.view.eventOrderSpecs; + var objs = segs.map(buildSegCompareObj); + objs.sort(function (obj0, obj1) { + return compareByFieldSpecs(obj0, obj1, specs); + }); + return objs.map(function (c) { + return c._seg; + }); + }; + FgEventRenderer.prototype.computeSizes = function (force) { + if (force || this.isSizeDirty) { + this.computeSegSizes(this.segs); + } + }; + FgEventRenderer.prototype.assignSizes = function (force) { + if (force || this.isSizeDirty) { + this.assignSegSizes(this.segs); + this.isSizeDirty = false; + } + }; + FgEventRenderer.prototype.computeSegSizes = function (segs) { + }; + FgEventRenderer.prototype.assignSegSizes = function (segs) { + }; + // Manipulation on rendered segs + FgEventRenderer.prototype.hideByHash = function (hash) { + if (hash) { + for (var _i = 0, _a = this.segs; _i < _a.length; _i++) { + var seg = _a[_i]; + if (hash[seg.eventRange.instance.instanceId]) { + seg.el.style.visibility = 'hidden'; + } + } + } + }; + FgEventRenderer.prototype.showByHash = function (hash) { + if (hash) { + for (var _i = 0, _a = this.segs; _i < _a.length; _i++) { + var seg = _a[_i]; + if (hash[seg.eventRange.instance.instanceId]) { + seg.el.style.visibility = ''; + } + } + } + }; + FgEventRenderer.prototype.selectByInstanceId = function (instanceId) { + if (instanceId) { + for (var _i = 0, _a = this.segs; _i < _a.length; _i++) { + var seg = _a[_i]; + var eventInstance = seg.eventRange.instance; + if (eventInstance && eventInstance.instanceId === instanceId && + seg.el // necessary? + ) { + seg.el.classList.add('fc-selected'); + } + } + } + }; + FgEventRenderer.prototype.unselectByInstanceId = function (instanceId) { + if (instanceId) { + for (var _i = 0, _a = this.segs; _i < _a.length; _i++) { + var seg = _a[_i]; + if (seg.el) { // necessary? + seg.el.classList.remove('fc-selected'); + } + } + } + }; + return FgEventRenderer; + }()); + // returns a object with all primitive props that can be compared + function buildSegCompareObj(seg) { + var eventDef = seg.eventRange.def; + var range = seg.eventRange.instance.range; + var start = range.start ? range.start.valueOf() : 0; // TODO: better support for open-range events + var end = range.end ? range.end.valueOf() : 0; // " + return __assign({}, eventDef.extendedProps, eventDef, { id: eventDef.publicId, start: start, + end: end, duration: end - start, allDay: Number(eventDef.allDay), _seg: seg // for later retrieval + }); + } + + var FillRenderer = /** @class */ (function () { + function FillRenderer(context) { + this.fillSegTag = 'div'; + this.dirtySizeFlags = {}; + this.context = context; + this.containerElsByType = {}; + this.segsByType = {}; + } + FillRenderer.prototype.getSegsByType = function (type) { + return this.segsByType[type] || []; + }; + FillRenderer.prototype.renderSegs = function (type, segs) { + var _a; + var renderedSegs = this.renderSegEls(type, segs); // assignes `.el` to each seg. returns successfully rendered segs + var containerEls = this.attachSegs(type, renderedSegs); + if (containerEls) { + (_a = (this.containerElsByType[type] || (this.containerElsByType[type] = []))).push.apply(_a, containerEls); + } + this.segsByType[type] = renderedSegs; + if (type === 'bgEvent') { + this.context.view.triggerRenderedSegs(renderedSegs, false); // isMirror=false + } + this.dirtySizeFlags[type] = true; + }; + // Unrenders a specific type of fill that is currently rendered on the grid + FillRenderer.prototype.unrender = function (type) { + var segs = this.segsByType[type]; + if (segs) { + if (type === 'bgEvent') { + this.context.view.triggerWillRemoveSegs(segs, false); // isMirror=false + } + this.detachSegs(type, segs); + } + }; + // Renders and assigns an `el` property for each fill segment. Generic enough to work with different types. + // Only returns segments that successfully rendered. + FillRenderer.prototype.renderSegEls = function (type, segs) { + var _this = this; + var html = ''; + var i; + if (segs.length) { + // build a large concatenation of segment HTML + for (i = 0; i < segs.length; i++) { + html += this.renderSegHtml(type, segs[i]); + } + // Grab individual elements from the combined HTML string. Use each as the default rendering. + // Then, compute the 'el' for each segment. + htmlToElements(html).forEach(function (el, i) { + var seg = segs[i]; + if (el) { + seg.el = el; + } + }); + if (type === 'bgEvent') { + segs = filterSegsViaEls(this.context.view, segs, false // isMirror. background events can never be mirror elements + ); + } + // correct element type? (would be bad if a non-TD were inserted into a table for example) + segs = segs.filter(function (seg) { + return elementMatches(seg.el, _this.fillSegTag); + }); + } + return segs; + }; + // Builds the HTML needed for one fill segment. Generic enough to work with different types. + FillRenderer.prototype.renderSegHtml = function (type, seg) { + var css = null; + var classNames = []; + if (type !== 'highlight' && type !== 'businessHours') { + css = { + 'background-color': seg.eventRange.ui.backgroundColor + }; + } + if (type !== 'highlight') { + classNames = classNames.concat(seg.eventRange.ui.classNames); + } + if (type === 'businessHours') { + classNames.push('fc-bgevent'); + } + else { + classNames.push('fc-' + type.toLowerCase()); + } + return '<' + this.fillSegTag + + (classNames.length ? ' class="' + classNames.join(' ') + '"' : '') + + (css ? ' style="' + cssToStr(css) + '"' : '') + + '>'; + }; + FillRenderer.prototype.detachSegs = function (type, segs) { + var containerEls = this.containerElsByType[type]; + if (containerEls) { + containerEls.forEach(removeElement); + delete this.containerElsByType[type]; + } + }; + FillRenderer.prototype.computeSizes = function (force) { + for (var type in this.segsByType) { + if (force || this.dirtySizeFlags[type]) { + this.computeSegSizes(this.segsByType[type]); + } + } + }; + FillRenderer.prototype.assignSizes = function (force) { + for (var type in this.segsByType) { + if (force || this.dirtySizeFlags[type]) { + this.assignSegSizes(this.segsByType[type]); + } + } + this.dirtySizeFlags = {}; + }; + FillRenderer.prototype.computeSegSizes = function (segs) { + }; + FillRenderer.prototype.assignSegSizes = function (segs) { + }; + return FillRenderer; + }()); + + var NamedTimeZoneImpl = /** @class */ (function () { + function NamedTimeZoneImpl(timeZoneName) { + this.timeZoneName = timeZoneName; + } + return NamedTimeZoneImpl; + }()); + + /* + An abstraction for a dragging interaction originating on an event. + Does higher-level things than PointerDragger, such as possibly: + - a "mirror" that moves with the pointer + - a minimum number of pixels or other criteria for a true drag to begin + + subclasses must emit: + - pointerdown + - dragstart + - dragmove + - pointerup + - dragend + */ + var ElementDragging = /** @class */ (function () { + function ElementDragging(el) { + this.emitter = new EmitterMixin(); + } + ElementDragging.prototype.destroy = function () { + }; + ElementDragging.prototype.setMirrorIsVisible = function (bool) { + // optional if subclass doesn't want to support a mirror + }; + ElementDragging.prototype.setMirrorNeedsRevert = function (bool) { + // optional if subclass doesn't want to support a mirror + }; + ElementDragging.prototype.setAutoScrollEnabled = function (bool) { + // optional + }; + return ElementDragging; + }()); + + function formatDate(dateInput, settings) { + if (settings === void 0) { settings = {}; } + var dateEnv = buildDateEnv$1(settings); + var formatter = createFormatter(settings); + var dateMeta = dateEnv.createMarkerMeta(dateInput); + if (!dateMeta) { // TODO: warning? + return ''; + } + return dateEnv.format(dateMeta.marker, formatter, { + forcedTzo: dateMeta.forcedTzo + }); + } + function formatRange(startInput, endInput, settings // mixture of env and formatter settings + ) { + var dateEnv = buildDateEnv$1(typeof settings === 'object' && settings ? settings : {}); // pass in if non-null object + var formatter = createFormatter(settings, globalDefaults.defaultRangeSeparator); + var startMeta = dateEnv.createMarkerMeta(startInput); + var endMeta = dateEnv.createMarkerMeta(endInput); + if (!startMeta || !endMeta) { // TODO: warning? + return ''; + } + return dateEnv.formatRange(startMeta.marker, endMeta.marker, formatter, { + forcedStartTzo: startMeta.forcedTzo, + forcedEndTzo: endMeta.forcedTzo, + isEndExclusive: settings.isEndExclusive + }); + } + // TODO: more DRY and optimized + function buildDateEnv$1(settings) { + var locale = buildLocale(settings.locale || 'en', parseRawLocales([]).map); // TODO: don't hardcode 'en' everywhere + // ensure required settings + settings = __assign({ timeZone: globalDefaults.timeZone, calendarSystem: 'gregory' }, settings, { locale: locale }); + return new DateEnv(settings); + } + + var DRAG_META_PROPS = { + startTime: createDuration, + duration: createDuration, + create: Boolean, + sourceId: String + }; + var DRAG_META_DEFAULTS = { + create: true + }; + function parseDragMeta(raw) { + var leftoverProps = {}; + var refined = refineProps(raw, DRAG_META_PROPS, DRAG_META_DEFAULTS, leftoverProps); + refined.leftoverProps = leftoverProps; + return refined; + } + + // Computes a default column header formatting string if `colFormat` is not explicitly defined + function computeFallbackHeaderFormat(datesRepDistinctDays, dayCnt) { + // if more than one week row, or if there are a lot of columns with not much space, + // put just the day numbers will be in each cell + if (!datesRepDistinctDays || dayCnt > 10) { + return { weekday: 'short' }; // "Sat" + } + else if (dayCnt > 1) { + return { weekday: 'short', month: 'numeric', day: 'numeric', omitCommas: true }; // "Sat 11/12" + } + else { + return { weekday: 'long' }; // "Saturday" + } + } + function renderDateCell(dateMarker, dateProfile, datesRepDistinctDays, colCnt, colHeadFormat, context, colspan, otherAttrs) { + var view = context.view, dateEnv = context.dateEnv, theme = context.theme, options = context.options; + var isDateValid = rangeContainsMarker(dateProfile.activeRange, dateMarker); // TODO: called too frequently. cache somehow. + var classNames = [ + 'fc-day-header', + theme.getClass('widgetHeader') + ]; + var innerHtml; + if (typeof options.columnHeaderHtml === 'function') { + innerHtml = options.columnHeaderHtml(dateEnv.toDate(dateMarker)); + } + else if (typeof options.columnHeaderText === 'function') { + innerHtml = htmlEscape(options.columnHeaderText(dateEnv.toDate(dateMarker))); + } + else { + innerHtml = htmlEscape(dateEnv.format(dateMarker, colHeadFormat)); + } + // if only one row of days, the classNames on the header can represent the specific days beneath + if (datesRepDistinctDays) { + classNames = classNames.concat( + // includes the day-of-week class + // noThemeHighlight=true (don't highlight the header) + getDayClasses(dateMarker, dateProfile, context, true)); + } + else { + classNames.push('fc-' + DAY_IDS[dateMarker.getUTCDay()]); // only add the day-of-week class + } + return '' + + ' 1 ? + ' colspan="' + colspan + '"' : + '') + + (otherAttrs ? + ' ' + otherAttrs : + '') + + '>' + + (isDateValid ? + // don't make a link if the heading could represent multiple days, or if there's only one day (forceOff) + buildGotoAnchorHtml(view, { date: dateMarker, forceOff: !datesRepDistinctDays || colCnt === 1 }, innerHtml) : + // if not valid, display text, but no link + innerHtml) + + ''; + } + + var DayHeader = /** @class */ (function (_super) { + __extends(DayHeader, _super); + function DayHeader(context, parentEl) { + var _this = _super.call(this, context) || this; + parentEl.innerHTML = ''; // because might be nbsp + parentEl.appendChild(_this.el = htmlToElement('
' + + '' + + '' + + '
' + + '
')); + _this.thead = _this.el.querySelector('thead'); + return _this; + } + DayHeader.prototype.destroy = function () { + removeElement(this.el); + }; + DayHeader.prototype.render = function (props) { + var dates = props.dates, datesRepDistinctDays = props.datesRepDistinctDays; + var parts = []; + if (props.renderIntroHtml) { + parts.push(props.renderIntroHtml()); + } + var colHeadFormat = createFormatter(this.opt('columnHeaderFormat') || + computeFallbackHeaderFormat(datesRepDistinctDays, dates.length)); + for (var _i = 0, dates_1 = dates; _i < dates_1.length; _i++) { + var date = dates_1[_i]; + parts.push(renderDateCell(date, props.dateProfile, datesRepDistinctDays, dates.length, colHeadFormat, this.context)); + } + if (this.isRtl) { + parts.reverse(); + } + this.thead.innerHTML = '' + parts.join('') + ''; + }; + return DayHeader; + }(Component)); + + var DaySeries = /** @class */ (function () { + function DaySeries(range, dateProfileGenerator) { + var date = range.start; + var end = range.end; + var indices = []; + var dates = []; + var dayIndex = -1; + while (date < end) { // loop each day from start to end + if (dateProfileGenerator.isHiddenDay(date)) { + indices.push(dayIndex + 0.5); // mark that it's between indices + } + else { + dayIndex++; + indices.push(dayIndex); + dates.push(date); + } + date = addDays(date, 1); + } + this.dates = dates; + this.indices = indices; + this.cnt = dates.length; + } + DaySeries.prototype.sliceRange = function (range) { + var firstIndex = this.getDateDayIndex(range.start); // inclusive first index + var lastIndex = this.getDateDayIndex(addDays(range.end, -1)); // inclusive last index + var clippedFirstIndex = Math.max(0, firstIndex); + var clippedLastIndex = Math.min(this.cnt - 1, lastIndex); + // deal with in-between indices + clippedFirstIndex = Math.ceil(clippedFirstIndex); // in-between starts round to next cell + clippedLastIndex = Math.floor(clippedLastIndex); // in-between ends round to prev cell + if (clippedFirstIndex <= clippedLastIndex) { + return { + firstIndex: clippedFirstIndex, + lastIndex: clippedLastIndex, + isStart: firstIndex === clippedFirstIndex, + isEnd: lastIndex === clippedLastIndex + }; + } + else { + return null; + } + }; + // Given a date, returns its chronolocial cell-index from the first cell of the grid. + // If the date lies between cells (because of hiddenDays), returns a floating-point value between offsets. + // If before the first offset, returns a negative number. + // If after the last offset, returns an offset past the last cell offset. + // Only works for *start* dates of cells. Will not work for exclusive end dates for cells. + DaySeries.prototype.getDateDayIndex = function (date) { + var indices = this.indices; + var dayOffset = Math.floor(diffDays(this.dates[0], date)); + if (dayOffset < 0) { + return indices[0] - 1; + } + else if (dayOffset >= indices.length) { + return indices[indices.length - 1] + 1; + } + else { + return indices[dayOffset]; + } + }; + return DaySeries; + }()); + + var DayTable = /** @class */ (function () { + function DayTable(daySeries, breakOnWeeks) { + var dates = daySeries.dates; + var daysPerRow; + var firstDay; + var rowCnt; + if (breakOnWeeks) { + // count columns until the day-of-week repeats + firstDay = dates[0].getUTCDay(); + for (daysPerRow = 1; daysPerRow < dates.length; daysPerRow++) { + if (dates[daysPerRow].getUTCDay() === firstDay) { + break; + } + } + rowCnt = Math.ceil(dates.length / daysPerRow); + } + else { + rowCnt = 1; + daysPerRow = dates.length; + } + this.rowCnt = rowCnt; + this.colCnt = daysPerRow; + this.daySeries = daySeries; + this.cells = this.buildCells(); + this.headerDates = this.buildHeaderDates(); + } + DayTable.prototype.buildCells = function () { + var rows = []; + for (var row = 0; row < this.rowCnt; row++) { + var cells = []; + for (var col = 0; col < this.colCnt; col++) { + cells.push(this.buildCell(row, col)); + } + rows.push(cells); + } + return rows; + }; + DayTable.prototype.buildCell = function (row, col) { + return { + date: this.daySeries.dates[row * this.colCnt + col] + }; + }; + DayTable.prototype.buildHeaderDates = function () { + var dates = []; + for (var col = 0; col < this.colCnt; col++) { + dates.push(this.cells[0][col].date); + } + return dates; + }; + DayTable.prototype.sliceRange = function (range) { + var colCnt = this.colCnt; + var seriesSeg = this.daySeries.sliceRange(range); + var segs = []; + if (seriesSeg) { + var firstIndex = seriesSeg.firstIndex, lastIndex = seriesSeg.lastIndex; + var index = firstIndex; + while (index <= lastIndex) { + var row = Math.floor(index / colCnt); + var nextIndex = Math.min((row + 1) * colCnt, lastIndex + 1); + segs.push({ + row: row, + firstCol: index % colCnt, + lastCol: (nextIndex - 1) % colCnt, + isStart: seriesSeg.isStart && index === firstIndex, + isEnd: seriesSeg.isEnd && (nextIndex - 1) === lastIndex + }); + index = nextIndex; + } + } + return segs; + }; + return DayTable; + }()); + + var Slicer = /** @class */ (function () { + function Slicer() { + this.sliceBusinessHours = memoize(this._sliceBusinessHours); + this.sliceDateSelection = memoize(this._sliceDateSpan); + this.sliceEventStore = memoize(this._sliceEventStore); + this.sliceEventDrag = memoize(this._sliceInteraction); + this.sliceEventResize = memoize(this._sliceInteraction); + } + Slicer.prototype.sliceProps = function (props, dateProfile, nextDayThreshold, component) { + var extraArgs = []; + for (var _i = 4; _i < arguments.length; _i++) { + extraArgs[_i - 4] = arguments[_i]; + } + var eventUiBases = props.eventUiBases; + var eventSegs = this.sliceEventStore.apply(this, [props.eventStore, eventUiBases, dateProfile, nextDayThreshold, component].concat(extraArgs)); + return { + dateSelectionSegs: this.sliceDateSelection.apply(this, [props.dateSelection, eventUiBases, component].concat(extraArgs)), + businessHourSegs: this.sliceBusinessHours.apply(this, [props.businessHours, dateProfile, nextDayThreshold, component].concat(extraArgs)), + fgEventSegs: eventSegs.fg, + bgEventSegs: eventSegs.bg, + eventDrag: this.sliceEventDrag.apply(this, [props.eventDrag, eventUiBases, dateProfile, nextDayThreshold, component].concat(extraArgs)), + eventResize: this.sliceEventResize.apply(this, [props.eventResize, eventUiBases, dateProfile, nextDayThreshold, component].concat(extraArgs)), + eventSelection: props.eventSelection + }; // TODO: give interactionSegs? + }; + Slicer.prototype.sliceNowDate = function (// does not memoize + date, component) { + var extraArgs = []; + for (var _i = 2; _i < arguments.length; _i++) { + extraArgs[_i - 2] = arguments[_i]; + } + return this._sliceDateSpan.apply(this, [{ range: { start: date, end: addMs(date, 1) }, allDay: false }, + {}, + component].concat(extraArgs)); + }; + Slicer.prototype._sliceBusinessHours = function (businessHours, dateProfile, nextDayThreshold, component) { + var extraArgs = []; + for (var _i = 4; _i < arguments.length; _i++) { + extraArgs[_i - 4] = arguments[_i]; + } + if (!businessHours) { + return []; + } + return this._sliceEventStore.apply(this, [expandRecurring(businessHours, computeActiveRange(dateProfile, Boolean(nextDayThreshold)), component.calendar), + {}, + dateProfile, + nextDayThreshold, + component].concat(extraArgs)).bg; + }; + Slicer.prototype._sliceEventStore = function (eventStore, eventUiBases, dateProfile, nextDayThreshold, component) { + var extraArgs = []; + for (var _i = 5; _i < arguments.length; _i++) { + extraArgs[_i - 5] = arguments[_i]; + } + if (eventStore) { + var rangeRes = sliceEventStore(eventStore, eventUiBases, computeActiveRange(dateProfile, Boolean(nextDayThreshold)), nextDayThreshold); + return { + bg: this.sliceEventRanges(rangeRes.bg, component, extraArgs), + fg: this.sliceEventRanges(rangeRes.fg, component, extraArgs) + }; + } + else { + return { bg: [], fg: [] }; + } + }; + Slicer.prototype._sliceInteraction = function (interaction, eventUiBases, dateProfile, nextDayThreshold, component) { + var extraArgs = []; + for (var _i = 5; _i < arguments.length; _i++) { + extraArgs[_i - 5] = arguments[_i]; + } + if (!interaction) { + return null; + } + var rangeRes = sliceEventStore(interaction.mutatedEvents, eventUiBases, computeActiveRange(dateProfile, Boolean(nextDayThreshold)), nextDayThreshold); + return { + segs: this.sliceEventRanges(rangeRes.fg, component, extraArgs), + affectedInstances: interaction.affectedEvents.instances, + isEvent: interaction.isEvent, + sourceSeg: interaction.origSeg + }; + }; + Slicer.prototype._sliceDateSpan = function (dateSpan, eventUiBases, component) { + var extraArgs = []; + for (var _i = 3; _i < arguments.length; _i++) { + extraArgs[_i - 3] = arguments[_i]; + } + if (!dateSpan) { + return []; + } + var eventRange = fabricateEventRange(dateSpan, eventUiBases, component.calendar); + var segs = this.sliceRange.apply(this, [dateSpan.range].concat(extraArgs)); + for (var _a = 0, segs_1 = segs; _a < segs_1.length; _a++) { + var seg = segs_1[_a]; + seg.component = component; + seg.eventRange = eventRange; + } + return segs; + }; + /* + "complete" seg means it has component and eventRange + */ + Slicer.prototype.sliceEventRanges = function (eventRanges, component, // TODO: kill + extraArgs) { + var segs = []; + for (var _i = 0, eventRanges_1 = eventRanges; _i < eventRanges_1.length; _i++) { + var eventRange = eventRanges_1[_i]; + segs.push.apply(segs, this.sliceEventRange(eventRange, component, extraArgs)); + } + return segs; + }; + /* + "complete" seg means it has component and eventRange + */ + Slicer.prototype.sliceEventRange = function (eventRange, component, // TODO: kill + extraArgs) { + var segs = this.sliceRange.apply(this, [eventRange.range].concat(extraArgs)); + for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) { + var seg = segs_2[_i]; + seg.component = component; + seg.eventRange = eventRange; + seg.isStart = eventRange.isStart && seg.isStart; + seg.isEnd = eventRange.isEnd && seg.isEnd; + } + return segs; + }; + return Slicer; + }()); + /* + for incorporating minTime/maxTime if appropriate + TODO: should be part of DateProfile! + TimelineDateProfile already does this btw + */ + function computeActiveRange(dateProfile, isComponentAllDay) { + var range = dateProfile.activeRange; + if (isComponentAllDay) { + return range; + } + return { + start: addMs(range.start, dateProfile.minTime.milliseconds), + end: addMs(range.end, dateProfile.maxTime.milliseconds - 864e5) // 864e5 = ms in a day + }; + } + + // exports + // -------------------------------------------------------------------------------------------------- + var version = '4.3.1'; + + exports.Calendar = Calendar; + exports.Component = Component; + exports.DateComponent = DateComponent; + exports.DateEnv = DateEnv; + exports.DateProfileGenerator = DateProfileGenerator; + exports.DayHeader = DayHeader; + exports.DaySeries = DaySeries; + exports.DayTable = DayTable; + exports.ElementDragging = ElementDragging; + exports.ElementScrollController = ElementScrollController; + exports.EmitterMixin = EmitterMixin; + exports.EventApi = EventApi; + exports.FgEventRenderer = FgEventRenderer; + exports.FillRenderer = FillRenderer; + exports.Interaction = Interaction; + exports.Mixin = Mixin; + exports.NamedTimeZoneImpl = NamedTimeZoneImpl; + exports.PositionCache = PositionCache; + exports.ScrollComponent = ScrollComponent; + exports.ScrollController = ScrollController; + exports.Slicer = Slicer; + exports.Splitter = Splitter; + exports.Theme = Theme; + exports.View = View; + exports.WindowScrollController = WindowScrollController; + exports.addDays = addDays; + exports.addDurations = addDurations; + exports.addMs = addMs; + exports.addWeeks = addWeeks; + exports.allowContextMenu = allowContextMenu; + exports.allowSelection = allowSelection; + exports.appendToElement = appendToElement; + exports.applyAll = applyAll; + exports.applyMutationToEventStore = applyMutationToEventStore; + exports.applyStyle = applyStyle; + exports.applyStyleProp = applyStyleProp; + exports.asRoughMinutes = asRoughMinutes; + exports.asRoughMs = asRoughMs; + exports.asRoughSeconds = asRoughSeconds; + exports.buildGotoAnchorHtml = buildGotoAnchorHtml; + exports.buildSegCompareObj = buildSegCompareObj; + exports.capitaliseFirstLetter = capitaliseFirstLetter; + exports.combineEventUis = combineEventUis; + exports.compareByFieldSpec = compareByFieldSpec; + exports.compareByFieldSpecs = compareByFieldSpecs; + exports.compareNumbers = compareNumbers; + exports.compensateScroll = compensateScroll; + exports.computeClippingRect = computeClippingRect; + exports.computeEdges = computeEdges; + exports.computeFallbackHeaderFormat = computeFallbackHeaderFormat; + exports.computeHeightAndMargins = computeHeightAndMargins; + exports.computeInnerRect = computeInnerRect; + exports.computeRect = computeRect; + exports.computeVisibleDayRange = computeVisibleDayRange; + exports.config = config; + exports.constrainPoint = constrainPoint; + exports.createDuration = createDuration; + exports.createElement = createElement; + exports.createEmptyEventStore = createEmptyEventStore; + exports.createEventInstance = createEventInstance; + exports.createFormatter = createFormatter; + exports.createPlugin = createPlugin; + exports.cssToStr = cssToStr; + exports.debounce = debounce; + exports.diffDates = diffDates; + exports.diffDayAndTime = diffDayAndTime; + exports.diffDays = diffDays; + exports.diffPoints = diffPoints; + exports.diffWeeks = diffWeeks; + exports.diffWholeDays = diffWholeDays; + exports.diffWholeWeeks = diffWholeWeeks; + exports.disableCursor = disableCursor; + exports.distributeHeight = distributeHeight; + exports.elementClosest = elementClosest; + exports.elementMatches = elementMatches; + exports.enableCursor = enableCursor; + exports.eventTupleToStore = eventTupleToStore; + exports.filterEventStoreDefs = filterEventStoreDefs; + exports.filterHash = filterHash; + exports.findChildren = findChildren; + exports.findElements = findElements; + exports.flexibleCompare = flexibleCompare; + exports.forceClassName = forceClassName; + exports.formatDate = formatDate; + exports.formatIsoTimeString = formatIsoTimeString; + exports.formatRange = formatRange; + exports.getAllDayHtml = getAllDayHtml; + exports.getClippingParents = getClippingParents; + exports.getDayClasses = getDayClasses; + exports.getElSeg = getElSeg; + exports.getRectCenter = getRectCenter; + exports.getRelevantEvents = getRelevantEvents; + exports.globalDefaults = globalDefaults; + exports.greatestDurationDenominator = greatestDurationDenominator; + exports.hasBgRendering = hasBgRendering; + exports.htmlEscape = htmlEscape; + exports.htmlToElement = htmlToElement; + exports.insertAfterElement = insertAfterElement; + exports.interactionSettingsStore = interactionSettingsStore; + exports.interactionSettingsToStore = interactionSettingsToStore; + exports.intersectRanges = intersectRanges; + exports.intersectRects = intersectRects; + exports.isArraysEqual = isArraysEqual; + exports.isDateSpansEqual = isDateSpansEqual; + exports.isInt = isInt; + exports.isInteractionValid = isInteractionValid; + exports.isMultiDayRange = isMultiDayRange; + exports.isPropsEqual = isPropsEqual; + exports.isPropsValid = isPropsValid; + exports.isSingleDay = isSingleDay; + exports.isValidDate = isValidDate; + exports.listenBySelector = listenBySelector; + exports.mapHash = mapHash; + exports.matchCellWidths = matchCellWidths; + exports.memoize = memoize; + exports.memoizeOutput = memoizeOutput; + exports.memoizeRendering = memoizeRendering; + exports.mergeEventStores = mergeEventStores; + exports.multiplyDuration = multiplyDuration; + exports.padStart = padStart; + exports.parseBusinessHours = parseBusinessHours; + exports.parseDragMeta = parseDragMeta; + exports.parseEventDef = parseEventDef; + exports.parseFieldSpecs = parseFieldSpecs; + exports.parseMarker = parse; + exports.pointInsideRect = pointInsideRect; + exports.prependToElement = prependToElement; + exports.preventContextMenu = preventContextMenu; + exports.preventDefault = preventDefault; + exports.preventSelection = preventSelection; + exports.processScopedUiProps = processScopedUiProps; + exports.rangeContainsMarker = rangeContainsMarker; + exports.rangeContainsRange = rangeContainsRange; + exports.rangesEqual = rangesEqual; + exports.rangesIntersect = rangesIntersect; + exports.refineProps = refineProps; + exports.removeElement = removeElement; + exports.removeExact = removeExact; + exports.renderDateCell = renderDateCell; + exports.requestJson = requestJson; + exports.sliceEventStore = sliceEventStore; + exports.startOfDay = startOfDay; + exports.subtractInnerElHeight = subtractInnerElHeight; + exports.translateRect = translateRect; + exports.uncompensateScroll = uncompensateScroll; + exports.undistributeHeight = undistributeHeight; + exports.unpromisify = unpromisify; + exports.version = version; + exports.whenTransitionDone = whenTransitionDone; + exports.wholeDivideDurations = wholeDivideDurations; + + Object.defineProperty(exports, '__esModule', { value: true }); + +})); diff --git a/static/assets/libs/fullcalendar/core/main.min.css b/static/assets/libs/fullcalendar/core/main.min.css new file mode 100644 index 0000000..8948b53 --- /dev/null +++ b/static/assets/libs/fullcalendar/core/main.min.css @@ -0,0 +1 @@ +@charset "UTF-8";.fc-button:not(:disabled),.fc-event.fc-draggable,.fc-event[href],.fc-popover .fc-header .fc-close,a.fc-more,a[data-goto]{cursor:pointer}.fc-bg,.fc-row .fc-bgevent-skeleton,.fc-row .fc-highlight-skeleton{bottom:0}.fc{direction:ltr;text-align:left}.fc-rtl{text-align:right}body .fc{font-size:1em}.fc-highlight{background:#bce8f1;opacity:.3}.fc-bgevent{background:#8fdf82;opacity:.3}.fc-nonbusiness{background:#d7d7d7}.fc-popover{position:absolute;box-shadow:0 2px 6px rgba(0,0,0,.15)}.fc-popover .fc-header{display:flex;flex-direction:row;justify-content:space-between;align-items:center;padding:2px 4px}.fc-rtl .fc-popover .fc-header{flex-direction:row-reverse}.fc-popover .fc-header .fc-title{margin:0 2px}.fc-popover .fc-header .fc-close{opacity:.65;font-size:1.1em}.fc-divider{border-style:solid;border-width:1px}hr.fc-divider{height:0;margin:0;padding:0 0 2px;border-width:1px 0}.fc-bg table,.fc-row .fc-bgevent-skeleton table,.fc-row .fc-highlight-skeleton table{height:100%}.fc-bg,.fc-bgevent-skeleton,.fc-highlight-skeleton,.fc-mirror-skeleton{position:absolute;top:0;left:0;right:0}.fc table{width:100%;box-sizing:border-box;table-layout:fixed;border-collapse:collapse;border-spacing:0;font-size:1em}.fc th{text-align:center}.fc td,.fc th{border-style:solid;border-width:1px;padding:0;vertical-align:top}.fc td.fc-today{border-style:double}a[data-goto]:hover{text-decoration:underline}.fc .fc-row{border-style:solid;border-width:0}.fc-row table{border-left:0 hidden transparent;border-right:0 hidden transparent;border-bottom:0 hidden transparent}.fc-row:first-child table{border-top:0 hidden transparent}.fc-row{position:relative}.fc-row .fc-bg{z-index:1}.fc-row .fc-bgevent-skeleton td,.fc-row .fc-highlight-skeleton td{border-color:transparent}.fc-row .fc-bgevent-skeleton{z-index:2}.fc-row .fc-highlight-skeleton{z-index:3}.fc-row .fc-content-skeleton{position:relative;z-index:4;padding-bottom:2px}.fc-row .fc-mirror-skeleton{z-index:5}.fc .fc-row .fc-content-skeleton table,.fc .fc-row .fc-content-skeleton td,.fc .fc-row .fc-mirror-skeleton td{background:0 0;border-color:transparent}.fc-row .fc-content-skeleton td,.fc-row .fc-mirror-skeleton td{border-bottom:0}.fc-row .fc-content-skeleton tbody td,.fc-row .fc-mirror-skeleton tbody td{border-top:0}.fc-scroller{-webkit-overflow-scrolling:touch}.fc-scroller>.fc-day-grid,.fc-scroller>.fc-time-grid{position:relative;width:100%}.fc-event{position:relative;display:block;font-size:.85em;line-height:1.4;border-radius:3px;border:1px solid #3788d8}.fc-event,.fc-event-dot{background-color:#3788d8}.fc-event,.fc-event:hover{color:#fff;text-decoration:none}.fc-not-allowed,.fc-not-allowed .fc-event{cursor:not-allowed}.fc-event .fc-content{position:relative;z-index:2}.fc-event .fc-resizer{position:absolute;z-index:4;display:none}.fc-event.fc-allow-mouse-resize .fc-resizer,.fc-event.fc-selected .fc-resizer{display:block}.fc-event.fc-selected .fc-resizer:before{content:"";position:absolute;z-index:9999;top:50%;left:50%;width:40px;height:40px;margin-left:-20px;margin-top:-20px}.fc-event.fc-selected{z-index:9999!important;box-shadow:0 2px 5px rgba(0,0,0,.2)}.fc-event.fc-selected:after{content:"";position:absolute;z-index:1;top:-1px;right:-1px;bottom:-1px;left:-1px;background:#000;opacity:.25}.fc-event.fc-dragging.fc-selected{box-shadow:0 2px 7px rgba(0,0,0,.3)}.fc-event.fc-dragging:not(.fc-selected){opacity:.75}.fc-h-event.fc-selected:before{content:"";position:absolute;z-index:3;top:-10px;bottom:-10px;left:0;right:0}.fc-ltr .fc-h-event.fc-not-start,.fc-rtl .fc-h-event.fc-not-end{margin-left:0;border-left-width:0;padding-left:1px;border-top-left-radius:0;border-bottom-left-radius:0}.fc-ltr .fc-h-event.fc-not-end,.fc-rtl .fc-h-event.fc-not-start{margin-right:0;border-right-width:0;padding-right:1px;border-top-right-radius:0;border-bottom-right-radius:0}.fc-ltr .fc-h-event .fc-start-resizer,.fc-rtl .fc-h-event .fc-end-resizer{cursor:w-resize;left:-1px}.fc-ltr .fc-h-event .fc-end-resizer,.fc-rtl .fc-h-event .fc-start-resizer{cursor:e-resize;right:-1px}.fc-h-event.fc-allow-mouse-resize .fc-resizer{width:7px;top:-1px;bottom:-1px}.fc-h-event.fc-selected .fc-resizer{border-radius:4px;border-width:1px;width:6px;height:6px;border-style:solid;border-color:inherit;background:#fff;top:50%;margin-top:-4px}.fc-ltr .fc-h-event.fc-selected .fc-start-resizer,.fc-rtl .fc-h-event.fc-selected .fc-end-resizer{margin-left:-4px}.fc-ltr .fc-h-event.fc-selected .fc-end-resizer,.fc-rtl .fc-h-event.fc-selected .fc-start-resizer{margin-right:-4px}.fc-day-grid-event{margin:1px 2px 0;padding:0 1px}tr:first-child>td>.fc-day-grid-event{margin-top:2px}.fc-mirror-skeleton tr:first-child>td>.fc-day-grid-event{margin-top:0}.fc-day-grid-event .fc-content{white-space:nowrap;overflow:hidden}.fc-day-grid-event .fc-time{font-weight:700}.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer,.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer{margin-left:-2px}.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer,.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer{margin-right:-2px}a.fc-more{margin:1px 3px;font-size:.85em;text-decoration:none}a.fc-more:hover{text-decoration:underline}.fc-limited{display:none}.fc-button,.fc-icon{display:inline-block;font-weight:400;text-align:center}.fc-day-grid .fc-row{z-index:1}.fc-more-popover{z-index:2;width:220px}.fc-more-popover .fc-event-container{padding:10px}.fc-now-indicator{position:absolute;border:0 solid red}.fc-unselectable{-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent}.fc-unthemed .fc-content,.fc-unthemed .fc-divider,.fc-unthemed .fc-list-heading td,.fc-unthemed .fc-list-view,.fc-unthemed .fc-popover,.fc-unthemed .fc-row,.fc-unthemed tbody,.fc-unthemed td,.fc-unthemed th,.fc-unthemed thead{border-color:#ddd}.fc-unthemed .fc-popover{background-color:#fff}.fc-unthemed .fc-divider,.fc-unthemed .fc-list-heading td,.fc-unthemed .fc-popover .fc-header{background:#eee}.fc-unthemed td.fc-today{background:#fcf8e3}.fc-unthemed .fc-disabled-day{background:#d7d7d7;opacity:.3}@font-face{font-family:fcicons;src:url("data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SBfAAAAC8AAAAYGNtYXAXVtKNAAABHAAAAFRnYXNwAAAAEAAAAXAAAAAIZ2x5ZgYydxIAAAF4AAAFNGhlYWQUJ7cIAAAGrAAAADZoaGVhB20DzAAABuQAAAAkaG10eCIABhQAAAcIAAAALGxvY2ED4AU6AAAHNAAAABhtYXhwAA8AjAAAB0wAAAAgbmFtZXsr690AAAdsAAABhnBvc3QAAwAAAAAI9AAAACAAAwPAAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADpBgPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAOAAAAAoACAACAAIAAQAg6Qb//f//AAAAAAAg6QD//f//AAH/4xcEAAMAAQAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAABAWIAjQKeAskAEwAAJSc3NjQnJiIHAQYUFwEWMjc2NCcCnuLiDQ0MJAz/AA0NAQAMJAwNDcni4gwjDQwM/wANIwz/AA0NDCMNAAAAAQFiAI0CngLJABMAACUBNjQnASYiBwYUHwEHBhQXFjI3AZ4BAA0N/wAMJAwNDeLiDQ0MJAyNAQAMIw0BAAwMDSMM4uINIwwNDQAAAAIA4gC3Ax4CngATACcAACUnNzY0JyYiDwEGFB8BFjI3NjQnISc3NjQnJiIPAQYUHwEWMjc2NCcB87e3DQ0MIw3VDQ3VDSMMDQ0BK7e3DQ0MJAzVDQ3VDCQMDQ3zuLcMJAwNDdUNIwzWDAwNIwy4twwkDA0N1Q0jDNYMDA0jDAAAAgDiALcDHgKeABMAJwAAJTc2NC8BJiIHBhQfAQcGFBcWMjchNzY0LwEmIgcGFB8BBwYUFxYyNwJJ1Q0N1Q0jDA0Nt7cNDQwjDf7V1Q0N1QwkDA0Nt7cNDQwkDLfWDCMN1Q0NDCQMt7gMIw0MDNYMIw3VDQ0MJAy3uAwjDQwMAAADAFUAAAOrA1UAMwBoAHcAABMiBgcOAQcOAQcOARURFBYXHgEXHgEXHgEzITI2Nz4BNz4BNz4BNRE0JicuAScuAScuASMFITIWFx4BFx4BFx4BFREUBgcOAQcOAQcOASMhIiYnLgEnLgEnLgE1ETQ2Nz4BNz4BNz4BMxMhMjY1NCYjISIGFRQWM9UNGAwLFQkJDgUFBQUFBQ4JCRULDBgNAlYNGAwLFQkJDgUFBQUFBQ4JCRULDBgN/aoCVgQIBAQHAwMFAQIBAQIBBQMDBwQECAT9qgQIBAQHAwMFAQIBAQIBBQMDBwQECASAAVYRGRkR/qoRGRkRA1UFBAUOCQkVDAsZDf2rDRkLDBUJCA4FBQUFBQUOCQgVDAsZDQJVDRkLDBUJCQ4FBAVVAgECBQMCBwQECAX9qwQJAwQHAwMFAQICAgIBBQMDBwQDCQQCVQUIBAQHAgMFAgEC/oAZEhEZGRESGQAAAAADAFUAAAOrA1UAMwBoAIkAABMiBgcOAQcOAQcOARURFBYXHgEXHgEXHgEzITI2Nz4BNz4BNz4BNRE0JicuAScuAScuASMFITIWFx4BFx4BFx4BFREUBgcOAQcOAQcOASMhIiYnLgEnLgEnLgE1ETQ2Nz4BNz4BNz4BMxMzFRQWMzI2PQEzMjY1NCYrATU0JiMiBh0BIyIGFRQWM9UNGAwLFQkJDgUFBQUFBQ4JCRULDBgNAlYNGAwLFQkJDgUFBQUFBQ4JCRULDBgN/aoCVgQIBAQHAwMFAQIBAQIBBQMDBwQECAT9qgQIBAQHAwMFAQIBAQIBBQMDBwQECASAgBkSEhmAERkZEYAZEhIZgBEZGREDVQUEBQ4JCRUMCxkN/asNGQsMFQkIDgUFBQUFBQ4JCBUMCxkNAlUNGQsMFQkJDgUEBVUCAQIFAwIHBAQIBf2rBAkDBAcDAwUBAgICAgEFAwMHBAMJBAJVBQgEBAcCAwUCAQL+gIASGRkSgBkSERmAEhkZEoAZERIZAAABAOIAjQMeAskAIAAAExcHBhQXFjI/ARcWMjc2NC8BNzY0JyYiDwEnJiIHBhQX4uLiDQ0MJAzi4gwkDA0N4uINDQwkDOLiDCQMDQ0CjeLiDSMMDQ3h4Q0NDCMN4uIMIw0MDOLiDAwNIwwAAAABAAAAAQAAa5n0y18PPPUACwQAAAAAANivOVsAAAAA2K85WwAAAAADqwNVAAAACAACAAAAAAAAAAEAAAPA/8AAAAQAAAAAAAOrAAEAAAAAAAAAAAAAAAAAAAALBAAAAAAAAAAAAAAAAgAAAAQAAWIEAAFiBAAA4gQAAOIEAABVBAAAVQQAAOIAAAAAAAoAFAAeAEQAagCqAOoBngJkApoAAQAAAAsAigADAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAA4ArgABAAAAAAABAAcAAAABAAAAAAACAAcAYAABAAAAAAADAAcANgABAAAAAAAEAAcAdQABAAAAAAAFAAsAFQABAAAAAAAGAAcASwABAAAAAAAKABoAigADAAEECQABAA4ABwADAAEECQACAA4AZwADAAEECQADAA4APQADAAEECQAEAA4AfAADAAEECQAFABYAIAADAAEECQAGAA4AUgADAAEECQAKADQApGZjaWNvbnMAZgBjAGkAYwBvAG4Ac1ZlcnNpb24gMS4wAFYAZQByAHMAaQBvAG4AIAAxAC4AMGZjaWNvbnMAZgBjAGkAYwBvAG4Ac2ZjaWNvbnMAZgBjAGkAYwBvAG4Ac1JlZ3VsYXIAUgBlAGcAdQBsAGEAcmZjaWNvbnMAZgBjAGkAYwBvAG4Ac0ZvbnQgZ2VuZXJhdGVkIGJ5IEljb01vb24uAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") format("truetype");font-weight:400;font-style:normal}.fc-icon{font-family:fcicons!important;speak:none;font-style:normal;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:1em;height:1em}.fc-icon-chevron-left:before{content:""}.fc-icon-chevron-right:before{content:""}.fc-icon-chevrons-left:before{content:""}.fc-icon-chevrons-right:before{content:""}.fc-icon-minus-square:before{content:""}.fc-icon-plus-square:before{content:""}.fc-icon-x:before{content:""}.fc-button{overflow:visible;text-transform:none;margin:0;font-family:inherit}.fc-button::-moz-focus-inner{padding:0;border-style:none}.fc-button{-webkit-appearance:button;color:#212529;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.4em .65em;font-size:1em;line-height:1.5;border-radius:.25em}.fc-button:hover{color:#212529;text-decoration:none}.fc-button:focus{outline:0;-webkit-box-shadow:0 0 0 .2rem rgba(44,62,80,.25);box-shadow:0 0 0 .2rem rgba(44,62,80,.25)}.fc-button:disabled{opacity:.65}.fc-button-primary{color:#fff;background-color:#2C3E50;border-color:#2C3E50}.fc-button-primary:hover{color:#fff;background-color:#1e2b37;border-color:#1a252f}.fc-button-primary:focus{-webkit-box-shadow:0 0 0 .2rem rgba(76,91,106,.5);box-shadow:0 0 0 .2rem rgba(76,91,106,.5)}.fc-button-primary:disabled{color:#fff;background-color:#2C3E50;border-color:#2C3E50}.fc-button-primary:not(:disabled).fc-button-active,.fc-button-primary:not(:disabled):active{color:#fff;background-color:#1a252f;border-color:#151e27}.fc-button-primary:not(:disabled).fc-button-active:focus,.fc-button-primary:not(:disabled):active:focus{-webkit-box-shadow:0 0 0 .2rem rgba(76,91,106,.5);box-shadow:0 0 0 .2rem rgba(76,91,106,.5)}.fc-button .fc-icon{vertical-align:middle;font-size:1.5em}.fc-button-group{position:relative;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.fc-button-group>.fc-button{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto}.fc-button-group>.fc-button.fc-button-active,.fc-button-group>.fc-button:active,.fc-button-group>.fc-button:focus,.fc-button-group>.fc-button:hover{z-index:1}.fc-button-group>.fc-button:not(:first-child){margin-left:-1px;border-top-left-radius:0;border-bottom-left-radius:0}.fc-button-group>.fc-button:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.fc-unthemed .fc-popover{border-width:1px;border-style:solid}.fc-unthemed .fc-list-item:hover td{background-color:#f5f5f5}.fc-toolbar{display:flex;justify-content:space-between;align-items:center}.fc-toolbar.fc-header-toolbar{margin-bottom:1.5em}.fc-toolbar.fc-footer-toolbar{margin-top:1.5em}.fc-toolbar>*>:not(:first-child){margin-left:.75em}.fc-toolbar h2{font-size:1.75em;margin:0}.fc-view-container{position:relative}.fc-view-container *,.fc-view-container :after,.fc-view-container :before{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.fc-view,.fc-view>table{position:relative;z-index:1}@media print{.fc-bg,.fc-bgevent-container,.fc-bgevent-skeleton,.fc-business-container,.fc-event .fc-resizer,.fc-highlight-container,.fc-highlight-skeleton,.fc-mirror-container,.fc-mirror-skeleton{display:none}.fc tbody .fc-row,.fc-time-grid{min-height:0!important}.fc-time-grid .fc-event.fc-not-end:after,.fc-time-grid .fc-event.fc-not-start:before{content:"..."}.fc{max-width:100%!important}.fc-event{background:#fff!important;color:#000!important;page-break-inside:avoid}.fc hr,.fc tbody,.fc td,.fc th,.fc thead,.fc-row{border-color:#ccc!important;background:#fff!important}.fc tbody .fc-row{height:auto!important}.fc tbody .fc-row .fc-content-skeleton{position:static;padding-bottom:0!important}.fc tbody .fc-row .fc-content-skeleton tbody tr:last-child td{padding-bottom:1em}.fc tbody .fc-row .fc-content-skeleton table{height:1em}.fc-more,.fc-more-cell{display:none!important}.fc tr.fc-limited{display:table-row!important}.fc td.fc-limited{display:table-cell!important}.fc-popover,.fc-timeGrid-view .fc-axis{display:none}.fc-slats,.fc-time-grid hr{display:none!important}.fc button,.fc-button-group,.fc-time-grid .fc-event .fc-time span{display:none}.fc-time-grid .fc-content-skeleton{position:static}.fc-time-grid .fc-content-skeleton table{height:4em}.fc-time-grid .fc-event-container{margin:0!important}.fc-time-grid .fc-event{position:static!important;margin:3px 2px!important}.fc-time-grid .fc-event.fc-not-end{border-bottom-width:1px!important}.fc-time-grid .fc-event.fc-not-start{border-top-width:1px!important}.fc-time-grid .fc-event .fc-time{white-space:normal!important}.fc-time-grid .fc-event .fc-time:after{content:attr(data-full)}.fc-day-grid-container,.fc-scroller,.fc-time-grid-container{overflow:visible!important;height:auto!important}.fc-row{border:0!important;margin:0!important}} \ No newline at end of file diff --git a/static/assets/libs/fullcalendar/core/main.min.js b/static/assets/libs/fullcalendar/core/main.min.js new file mode 100644 index 0000000..6b72fff --- /dev/null +++ b/static/assets/libs/fullcalendar/core/main.min.js @@ -0,0 +1,6 @@ +/*! +FullCalendar Core Package v4.3.1 +Docs & License: https://fullcalendar.io/ +(c) 2019 Adam Shaw +*/ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).FullCalendar={})}(this,function(e){"use strict";var t={className:!0,colSpan:!0,rowSpan:!0},n={"
");document.body.appendChild(e);var t=e.firstChild.getBoundingClientRect().left>e.getBoundingClientRect().left;return c(e),t}()),S}function D(e){return e=Math.max(0,e),e=Math.round(e)}function T(e,t){void 0===t&&(t=!1);var n=window.getComputedStyle(e),r=parseInt(n.borderLeftWidth,10)||0,i=parseInt(n.borderRightWidth,10)||0,o=parseInt(n.borderTopWidth,10)||0,a=parseInt(n.borderBottomWidth,10)||0,s=D(e.offsetWidth-e.clientWidth-r-i),u={borderLeft:r,borderRight:i,borderTop:o,borderBottom:a,scrollbarBottom:D(e.offsetHeight-e.clientHeight-o-a),scrollbarLeft:0,scrollbarRight:0};return b()&&"rtl"===n.direction?u.scrollbarLeft=s:u.scrollbarRight=s,t&&(u.paddingLeft=parseInt(n.paddingLeft,10)||0,u.paddingRight=parseInt(n.paddingRight,10)||0,u.paddingTop=parseInt(n.paddingTop,10)||0,u.paddingBottom=parseInt(n.paddingBottom,10)||0),u}function w(e,t){void 0===t&&(t=!1);var n=R(e),r=T(e,t),i={left:n.left+r.borderLeft+r.scrollbarLeft,right:n.right-r.borderRight-r.scrollbarRight,top:n.top+r.borderTop,bottom:n.bottom-r.borderBottom-r.scrollbarBottom};return t&&(i.left+=r.paddingLeft,i.right-=r.paddingRight,i.top+=r.paddingTop,i.bottom-=r.paddingBottom),i}function R(e){var t=e.getBoundingClientRect();return{left:t.left+window.pageXOffset,top:t.top+window.pageYOffset,right:t.right+window.pageXOffset,bottom:t.bottom+window.pageYOffset}}function I(e){return e.getBoundingClientRect().height+C(e)}function C(e){var t=window.getComputedStyle(e);return parseInt(t.marginTop,10)+parseInt(t.marginBottom,10)}function M(e){for(var t=[];e instanceof HTMLElement;){var n=window.getComputedStyle(e);if("fixed"===n.position)break;/(auto|scroll)/.test(n.overflow+n.overflowY+n.overflowX)&&t.push(e),e=e.parentNode}return t}function k(e){e.preventDefault()}function O(e,t,n,r){function i(e){var t=p(e.target,n);t&&r.call(t,e,t)}return e.addEventListener(t,i),function(){e.removeEventListener(t,i)}}var _=["webkitTransitionEnd","otransitionend","oTransitionEnd","msTransitionEnd","transitionend"];var P=["sun","mon","tue","wed","thu","fri","sat"];function x(e,t){var n=Z(e);return n[2]+=t,j(n)}function H(e,t){var n=Z(e);return n[6]+=t,j(n)}function N(e,t){return(t.valueOf()-e.valueOf())/864e5}function z(e,t){var n=B(e),r=B(t);return{years:0,months:0,days:Math.round(N(n,r)),milliseconds:t.valueOf()-r.valueOf()-(e.valueOf()-n.valueOf())}}function U(e,t){var n=L(e,t);return null!==n&&n%7==0?n/7:null}function L(e,t){return q(e)===q(t)?Math.round(N(e,t)):null}function B(e){return j([e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()])}function V(e,t,n,r){var i=j([t,0,1+A(t,n,r)]),o=B(e),a=Math.round(N(i,o));return Math.floor(a/7)+1}function A(e,t,n){var r=7+t-n;return-((7+j([e,0,r]).getUTCDay()-t)%7)+r-1}function F(e){return[e.getFullYear(),e.getMonth(),e.getDate(),e.getHours(),e.getMinutes(),e.getSeconds(),e.getMilliseconds()]}function W(e){return new Date(e[0],e[1]||0,null==e[2]?1:e[2],e[3]||0,e[4]||0,e[5]||0)}function Z(e){return[e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds(),e.getUTCMilliseconds()]}function j(e){return 1===e.length&&(e=e.concat([0])),new Date(Date.UTC.apply(Date,e))}function Y(e){return!isNaN(e.valueOf())}function q(e){return 1e3*e.getUTCHours()*60*60+1e3*e.getUTCMinutes()*60+1e3*e.getUTCSeconds()+e.getUTCMilliseconds()}var G=["years","months","days","milliseconds"],X=/^(-?)(?:(\d+)\.)?(\d+):(\d\d)(?::(\d\d)(?:\.(\d\d\d))?)?/;function J(e,t){var n;return"string"==typeof e?function(e){var t=X.exec(e);if(t){var n=t[1]?-1:1;return{years:0,months:0,days:n*(t[2]?parseInt(t[2],10):0),milliseconds:n*(60*(t[3]?parseInt(t[3],10):0)*60*1e3+60*(t[4]?parseInt(t[4],10):0)*1e3+1e3*(t[5]?parseInt(t[5],10):0)+(t[6]?parseInt(t[6],10):0))}}return null}(e):"object"==typeof e&&e?K(e):"number"==typeof e?K(((n={})[t||"milliseconds"]=e,n)):null}function K(e){return{years:e.years||e.year||0,months:e.months||e.month||0,days:(e.days||e.day||0)+7*Q(e),milliseconds:60*(e.hours||e.hour||0)*60*1e3+60*(e.minutes||e.minute||0)*1e3+1e3*(e.seconds||e.second||0)+(e.milliseconds||e.millisecond||e.ms||0)}}function Q(e){return e.weeks||e.week||0}function $(e,t){return e.years===t.years&&e.months===t.months&&e.days===t.days&&e.milliseconds===t.milliseconds}function ee(e){return te(e)/864e5}function te(e){return 31536e6*e.years+2592e6*e.months+864e5*e.days+e.milliseconds}function ne(e,t){var n=e.milliseconds;if(n){if(n%1e3!=0)return{unit:"millisecond",value:n};if(n%6e4!=0)return{unit:"second",value:n/1e3};if(n%36e5!=0)return{unit:"minute",value:n/6e4};if(n)return{unit:"hour",value:n/36e5}}return e.days?t||e.days%7!=0?{unit:"day",value:e.days}:{unit:"week",value:e.days/7}:e.months?{unit:"month",value:e.months}:e.years?{unit:"year",value:e.years}:{unit:"millisecond",value:0}}function re(e){e.forEach(function(e){e.style.height=""})}function ie(e){var t,n,r=[],i=[];for("string"==typeof e?i=e.split(/\s*,\s*/):"function"==typeof e?i=[e]:Array.isArray(e)&&(i=e),t=0;t=te(t)&&(r=x(r,1))}return e.start&&(n=B(e.start),r&&r<=n&&(r=x(n,1))),{start:n,end:r}}function ye(e,t,n,r){return"year"===r?J(n.diffWholeYears(e,t),"year"):"month"===r?J(n.diffWholeMonths(e,t),"month"):z(e,t)}var me=function(e,t){return(me=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)};function Ee(e,t){function n(){this.constructor=e}me(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var Se=function(){return(Se=Object.assign||function(e){for(var t,n=1,r=arguments.length;n=0;o--)if("object"==typeof(a=e[o][r])&&a)i.unshift(a);else if(void 0!==a){u[r]=a;break}i.length&&(u[r]=Te(i))}for(n=e.length-1;n>=0;n--)for(r in s=e[n])r in u||(u[r]=s[r]);return u}function we(e,t){var n={};for(var r in e)t(e[r],r)&&(n[r]=e[r]);return n}function Re(e,t){var n={};for(var r in e)n[r]=t(e[r],r);return n}function Ie(e){for(var t={},n=0,r=e;no&&i.push({start:o,end:r.start}),r.end>o&&(o=r.end);return ot.start)&&(null===e.start||null===t.end||e.start=e.start)&&(null===e.end||null!==t.end&&t.end<=e.end)}function Ze(e,t){return(null===e.start||t>=e.start)&&(null===e.end||t1)||"numeric"!==r.year&&"2-digit"!==r.year||"numeric"!==r.month&&"2-digit"!==r.month||"numeric"!==r.day&&"2-digit"!==r.day||(a=1);var s=this.format(e,n),u=this.format(t,n);if(s===u)return s;var l=nt(function(e,t){var n={};for(var r in e)r in Xe&&!(Xe[r]<=t)||(n[r]=e[r]);return n}(r,a),i,n),c=l(e),d=l(t),f=function(e,t,n,r){var i=0;for(;i/g,">").replace(/'/g,"'").replace(/"/g,""").replace(/\n/g,"
")}function _t(e){var t=[];for(var n in e){var r=e[n];null!=r&&""!==r&&t.push(n+":"+r)}return t.join(";")}function Pt(e){return Array.isArray(e)?e:"string"==typeof e?e.split(/\s+/):[]}var xt={editable:Boolean,startEditable:Boolean,durationEditable:Boolean,constraint:null,overlap:null,allow:null,className:Pt,classNames:Pt,color:String,backgroundColor:String,borderColor:String,textColor:String};function Ht(e,t,n){var r=he(e,xt,{},n),i=function(e,t){return Array.isArray(e)?ke(e,"",t,!0):"object"==typeof e&&e?ke([e],"",t,!0):null!=e?String(e):null}(r.constraint,t);return{startEditable:null!=r.startEditable?r.startEditable:r.editable,durationEditable:null!=r.durationEditable?r.durationEditable:r.editable,constraints:null!=i?[i]:[],overlap:r.overlap,allows:null!=r.allow?[r.allow]:[],backgroundColor:r.backgroundColor||r.color,borderColor:r.borderColor||r.color,textColor:r.textColor,classNames:r.classNames.concat(r.className)}}function Nt(e,t,n,r){var i={},o={};for(var a in xt){var s=e+ue(a);i[a]=t[s],o[s]=!0}if("event"===e&&(i.editable=t.editable),r)for(var a in t)o[a]||(r[a]=t[a]);return Ht(i,n)}var zt={startEditable:null,durationEditable:null,constraints:[],overlap:null,allows:[],backgroundColor:"",borderColor:"",textColor:"",classNames:[]};function Ut(e){return e.reduce(Lt,zt)}function Lt(e,t){return{startEditable:null!=t.startEditable?t.startEditable:e.startEditable,durationEditable:null!=t.durationEditable?t.durationEditable:e.durationEditable,constraints:e.constraints.concat(t.constraints),overlap:"boolean"==typeof t.overlap?t.overlap:e.overlap,allows:e.allows.concat(t.allows),backgroundColor:t.backgroundColor||e.backgroundColor,borderColor:t.borderColor||e.borderColor,textColor:t.textColor||e.textColor,classNames:e.classNames.concat(t.classNames)}}var Bt={id:String,groupId:String,title:String,url:String,rendering:String,extendedProps:null},Vt={start:null,date:null,end:null,allDay:null},At=0;function Ft(e,t,n,r){var i=function(e,t){var n=null;if(e){var r=t.state.eventSources[e];n=r.allDayDefault}null==n&&(n=t.opt("allDayDefault"));return n}(t,n),o={},a=function(e,t,n,r,i){for(var o=0;o'+r+"
":""+r+""}function Qt(e,t,n,r){var i,o,a=n.calendar,s=n.view,u=n.theme,l=n.dateEnv,c=[];return Ze(t.activeRange,e)?(c.push("fc-"+P[e.getUTCDay()]),s.opt("monthMode")&&l.getMonth(e)!==l.getMonth(t.currentRange.start)&&c.push("fc-other-month"),o=x(i=B(a.getNow()),1),e=o?c.push("fc-future"):(c.push("fc-today"),!0!==r&&c.push(u.getClass("today")))):c.push("fc-disabled-day"),c}function $t(e,t,n){var r=!1,i=function(){r||(r=!0,t.apply(this,arguments))},o=function(){r||(r=!0,n&&n.apply(this,arguments))},a=e(i,o);a&&"function"==typeof a.then&&a.then(i,o)}var en=function(){function e(){}return e.mixInto=function(e){this.mixIntoObj(e.prototype)},e.mixIntoObj=function(e){var t=this;Object.getOwnPropertyNames(this.prototype).forEach(function(n){e[n]||(e[n]=t.prototype[n])})},e.mixOver=function(e){var t=this;Object.getOwnPropertyNames(this.prototype).forEach(function(n){e.prototype[n]=t.prototype[n]})},e}(),tn=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return Ee(t,e),t.prototype.on=function(e,t){return nn(this._handlers||(this._handlers={}),e,t),this},t.prototype.one=function(e,t){return nn(this._oneHandlers||(this._oneHandlers={}),e,t),this},t.prototype.off=function(e,t){return this._handlers&&rn(this._handlers,e,t),this._oneHandlers&&rn(this._oneHandlers,e,t),this},t.prototype.trigger=function(e){for(var t=[],n=1;n=n[t]&&e=n[t]&&e0},e.prototype.canScrollHorizontally=function(){return this.getMaxScrollLeft()>0},e.prototype.canScrollUp=function(){return this.getScrollTop()>0},e.prototype.canScrollDown=function(){return this.getScrollTop()0},e.prototype.canScrollRight=function(){return this.getScrollLeft()=200&&a.status<400)try{var e=JSON.parse(a.responseText);r(e,a)}catch(e){i("Failure parsing JSON",a)}else i("Request failed",a)},a.onerror=function(){i("Request failed",a)},a.send(o)}function Sn(e){var t=[];for(var n in e)t.push(encodeURIComponent(n)+"="+encodeURIComponent(e[n]));return t.join("&")}var bn=vn({eventSourceDefs:[{parseMeta:function(e){if("string"==typeof e)e={url:e};else if(!e||"object"!=typeof e||!e.url)return null;return{url:e.url,method:(e.method||"GET").toUpperCase(),extraParams:e.extraParams,startParam:e.startParam,endParam:e.endParam,timeZoneParam:e.timeZoneParam}},fetch:function(e,t,n){var r=e.eventSource.meta,i=function(e,t,n){var r,i,o,a,s=n.dateEnv,u={};null==(r=e.startParam)&&(r=n.opt("startParam"));null==(i=e.endParam)&&(i=n.opt("endParam"));null==(o=e.timeZoneParam)&&(o=n.opt("timeZoneParam"));a="function"==typeof e.extraParams?e.extraParams():e.extraParams||{};Se(u,a),u[r]=s.formatIso(t.start),u[i]=s.formatIso(t.end),"local"!==s.timeZone&&(u[o]=s.timeZone);return u}(r,e.range,e.calendar);En(r.method,r.url,i,function(e,n){t({rawEvents:e,xhr:n})},function(e,t){n({message:e,xhr:t})})}}]});var Dn=vn({recurringTypes:[{parse:function(e,t,n){var r,i,o=n.createMarker.bind(n),a=he(e,{daysOfWeek:null,startTime:J,endTime:J,startRecur:o,endRecur:o},{},t),s=!1;for(var u in a)if(null!=a[u]){s=!0;break}if(s){var l=null;return"duration"in t&&(l=J(t.duration),delete t.duration),!l&&a.startTime&&a.endTime&&(r=a.endTime,i=a.startTime,l={years:r.years-i.years,months:r.months-i.months,days:r.days-i.days,milliseconds:r.milliseconds-i.milliseconds}),{allDayGuess:Boolean(!a.startTime&&!a.endTime),duration:l,typeData:a}}return null},expand:function(e,t,n){var r=Ve(t,{start:e.startRecur,end:e.endRecur});return r?function(e,t,n,r){var i=e?Ie(e):null,o=B(n.start),a=n.end,s=[];for(;o0?e[0].code:"en",n=window.FullCalendarLocalesAll||[],r=window.FullCalendarLocales||{},i=n.concat(Ce(r),e),o={en:kn},a=0,s=i;a0;i--){var o=r.slice(0,i).join("-");if(t[o])return t[o]}return null}(n,t)||kn;return Pn(e,n,r)}(e,t):Pn(e.code,[e.code],e)}function Pn(e,t,n){var r=Te([kn,n],["buttonText"]);delete r.code;var i=r.week;return delete r.week,{codeArg:e,codes:t,week:i,simpleNumberFormat:new Intl.NumberFormat(e),options:r}}var xn=function(){function e(e){this.overrides=Se({},e),this.dynamicOverrides={},this.compute()}return e.prototype.mutate=function(e,t,n){var r=n?this.dynamicOverrides:this.overrides;Se(r,e);for(var i=0,o=t;i=1?Math.min(i,o):i}(e,this.weekDow,this.weekDoy)},e.prototype.format=function(e,t,n){return void 0===n&&(n={}),t.format({marker:e,timeZoneOffset:null!=n.forcedTzo?n.forcedTzo:this.offsetForMarker(e)},this)},e.prototype.formatRange=function(e,t,n,r){return void 0===r&&(r={}),r.isEndExclusive&&(t=H(t,-1)),n.formatRange({marker:e,timeZoneOffset:null!=r.forcedStartTzo?r.forcedStartTzo:this.offsetForMarker(e)},{marker:t,timeZoneOffset:null!=r.forcedEndTzo?r.forcedEndTzo:this.offsetForMarker(t)},this)},e.prototype.formatIso=function(e,t){void 0===t&&(t={});var n=null;return t.omitTimeZoneOffset||(n=null!=t.forcedTzo?t.forcedTzo:this.offsetForMarker(e)),function(e,t,n){void 0===n&&(n=!1);var r=e.toISOString();return r=r.replace(".000",""),n&&(r=r.replace("T00:00:00Z","")),r.length>10&&(null==t?r=r.replace("Z",""):0!==t&&(r=r.replace("Z",at(t,!0)))),r}(e,n,t.omitTime)},e.prototype.timestampToMarker=function(e){return"local"===this.timeZone?j(F(new Date(e))):"UTC"!==this.timeZone&&this.namedTimeZoneImpl?j(this.namedTimeZoneImpl.timestampToArray(e)):new Date(e)},e.prototype.offsetForMarker=function(e){return"local"===this.timeZone?-W(Z(e)).getTimezoneOffset():"UTC"===this.timeZone?0:this.namedTimeZoneImpl?this.namedTimeZoneImpl.offsetForArray(Z(e)):null},e.prototype.toDate=function(e,t){return"local"===this.timeZone?W(Z(e)):"UTC"===this.timeZone?new Date(e.valueOf()):this.namedTimeZoneImpl?new Date(e.valueOf()-1e3*this.namedTimeZoneImpl.offsetForArray(Z(e))*60):new Date(e.valueOf()-(t||0))},e}(),Vn={id:String,allDayDefault:Boolean,eventDataTransform:Function,success:Function,failure:Function},An=0;function Fn(e,t){return!t.pluginSystem.hooks.eventSourceDefs[e.sourceDefId].ignoreRange}function Wn(e,t){for(var n=t.pluginSystem.hooks.eventSourceDefs,r=n.length-1;r>=0;r--){var i=n[r].parseMeta(e);if(i){var o=Zn("object"==typeof e?e:{},i,r,t);return o._raw=e,o}}return null}function Zn(e,t,n,r){var i={},o=he(e,Vn,{},i),a={},s=Ht(i,r,a);return o.isFetching=!1,o.latestFetchId="",o.fetchRange=null,o.publicId=String(e.id||""),o.sourceId=String(An++),o.sourceDefId=n,o.meta=t,o.ui=s,o.extendedProps=a,o}function jn(e,t,n,r){switch(t.type){case"ADD_EVENT_SOURCES":return function(e,t,n,r){for(var i={},o=0,a=t;oe.fetchRange.end:!e.latestFetchId}(e,t,n)}),t,n)}function Gn(e,t,n,r){var i={};for(var o in e){var a=e[o];t[o]?i[o]=Xn(a,n,r):i[o]=a}return i}function Xn(e,t,n){var r=n.pluginSystem.hooks.eventSourceDefs[e.sourceDefId],i=String(Yn++);return r.fetch({eventSource:e,calendar:n,range:t},function(r){var o,a,s=r.rawEvents,u=n.opt("eventSourceSuccess");e.success&&(a=e.success(s,r.xhr)),u&&(o=u(s,r.xhr)),s=a||o||s,n.dispatch({type:"RECEIVE_EVENTS",sourceId:e.sourceId,fetchId:i,fetchRange:t,rawEvents:s})},function(r){var o=n.opt("eventSourceFailure");console.warn(r.message,r),e.failure&&e.failure(r),o&&o(r),n.dispatch({type:"RECEIVE_EVENT_ERROR",sourceId:e.sourceId,fetchId:i,fetchRange:t,error:r})}),Se({},e,{isFetching:!0,latestFetchId:i})}var Jn=function(){function e(e,t){this.viewSpec=e,this.options=e.options,this.dateEnv=t.dateEnv,this.calendar=t,this.initHiddenDays()}return e.prototype.buildPrev=function(e,t){var n=this.dateEnv,r=n.subtract(n.startOf(t,e.currentRangeUnit),e.dateIncrement);return this.build(r,-1)},e.prototype.buildNext=function(e,t){var n=this.dateEnv,r=n.add(n.startOf(t,e.currentRangeUnit),e.dateIncrement);return this.build(r,1)},e.prototype.build=function(e,t,n){var r;void 0===n&&(n=!1);var i,o,a,s,u,l,c,d,f;return r=this.buildValidRange(),r=this.trimHiddenDays(r),n&&(d=e,e=null!=(f=r).start&&d=f.end?new Date(f.end.valueOf()-1):d),a=this.buildCurrentRangeInfo(e,t),s=/^(year|month|week|day)$/.test(a.unit),u=this.buildRenderRange(this.trimHiddenDays(a.range),a.unit,s),l=u=this.trimHiddenDays(u),this.options.showNonCurrentDates||(l=Ve(l,a.range)),i=J(this.options.minTime),o=J(this.options.maxTime),l=Ve(l=this.adjustActiveRange(l,i,o),r),c=Fe(a.range,r),{validRange:r,currentRange:a.range,currentRangeUnit:a.unit,isRangeAllDay:s,activeRange:l,renderRange:u,minTime:i,maxTime:o,isValid:c,dateIncrement:this.buildDateIncrement(a.duration)}},e.prototype.buildValidRange=function(){return this.getRangeOption("validRange",this.calendar.getNow())||{start:null,end:null}},e.prototype.buildCurrentRangeInfo=function(e,t){var n,r=this.viewSpec,i=this.dateEnv,o=null,a=null,s=null;return r.duration?(o=r.duration,a=r.durationUnit,s=this.buildRangeFromDuration(e,t,o,a)):(n=this.options.dayCount)?(a="day",s=this.buildRangeFromDayCount(e,t,n)):(s=this.buildCustomVisibleRange(e))?a=i.greatestWholeUnit(s.start,s.end).unit:(a=ne(o=this.getFallbackDuration()).unit,s=this.buildRangeFromDuration(e,t,o,a)),{duration:o,unit:a,range:s}},e.prototype.getFallbackDuration=function(){return J({day:1})},e.prototype.adjustActiveRange=function(e,t,n){var r=this.dateEnv,i=e.start,o=e.end;return this.viewSpec.class.prototype.usesMinMaxTime&&(ee(t)<0&&(i=B(i),i=r.add(i,t)),ee(n)>1&&(o=x(o=B(o),-1),o=r.add(o,n))),{start:i,end:o}},e.prototype.buildRangeFromDuration=function(e,t,n,r){var i,o,a,s,u,l=this.dateEnv,c=this.options.dateAlignment;function d(){a=l.startOf(e,c),s=l.add(a,n),u={start:a,end:s}}return c||((i=this.options.dateIncrement)?(o=J(i),c=te(o) ")),h=!1):((r=d[e])?(c=function(e){r.click&&r.click.call(E,e)},(v=o.getCustomButtonIconClass(r))||(v=o.getIconClass(e))||(g=r.text)):(s=l[e])?(n.viewsWithButtons.push(e),c=function(){a.changeView(e)},(g=s.buttonTextOverride)||(v=o.getIconClass(e))||(g=s.buttonTextDefault)):a[e]&&(c=function(){a[e]()},(g=f[e])||(v=o.getIconClass(e))||(g=p[e])),c&&(m=["fc-"+e+"-button",o.getClass("button")],g?(y=Ot(g),S=""):v&&(y="",S=' aria-label="'+e+'"'),(E=i('")).addEventListener("click",c),u.push(E)))}),u.length>1){r=document.createElement("div");var v=o.getClass("buttonGroup");h&&v&&r.classList.add(v),s(r,u),c.appendChild(r)}else s(c,u)}),c},t.prototype.updateToday=function(e){this.toggleButtonEnabled("today",e)},t.prototype.updatePrev=function(e){this.toggleButtonEnabled("prev",e)},t.prototype.updateNext=function(e){this.toggleButtonEnabled("next",e)},t.prototype.updateTitle=function(e){v(this.el,"h2").forEach(function(t){t.innerText=e})},t.prototype.updateActiveButton=function(e){var t=this.theme.getClass("buttonActive");v(this.el,"button").forEach(function(n){e&&n.classList.contains("fc-"+e+"-button")?n.classList.add(t):n.classList.remove(t)})},t.prototype.toggleButtonEnabled=function(e,t){v(this.el,".fc-"+e+"-button").forEach(function(e){e.disabled=!t})},t}(fn),hr=function(e){function t(t,n){var i=e.call(this,t)||this;i._renderToolbars=qt(i.renderToolbars),i.buildViewPropTransformers=Ye(gr),i.el=n,u(n,i.contentEl=r("div",{className:"fc-view-container"}));for(var o=i.calendar,a=0,s=o.pluginSystem.hooks.viewContainerModifiers;a1?{year:"numeric",month:"short",day:"numeric"}:{year:"numeric",month:"long",day:"numeric"}}(e),t.titleRangeSeparator),{isEndExclusive:e.isRangeAllDay})}function gr(e){return e.map(function(e){return new e})}var yr=function(){function e(e){this.component=e.component}return e.prototype.destroy=function(){},e}();var mr={},Er=function(e){function t(t){var n=e.call(this,t)||this;n.handleSegClick=function(e,t){var r=n.component,i=ht(t);if(i&&r.isValidSegDownEl(e.target)){var o=p(e.target,".fc-has-url"),a=o?o.querySelector("a[href]").href:"";r.publiclyTrigger("eventClick",[{el:t,event:new ct(r.calendar,i.eventRange.def,i.eventRange.instance),jsEvent:e,view:r.view}]),a&&!e.defaultPrevented&&(window.location.href=a)}};var r=t.component;return n.destroy=O(r.el,"click",r.fgSegSelector+","+r.bgSegSelector,n.handleSegClick),n}return Ee(t,e),t}(yr),Sr=function(e){function t(t){var n=e.call(this,t)||this;n.handleEventElRemove=function(e){e===n.currentSegEl&&n.handleSegLeave(null,n.currentSegEl)},n.handleSegEnter=function(e,t){ht(t)&&(t.classList.add("fc-allow-mouse-resize"),n.currentSegEl=t,n.triggerEvent("eventMouseEnter",e,t))},n.handleSegLeave=function(e,t){n.currentSegEl&&(t.classList.remove("fc-allow-mouse-resize"),n.currentSegEl=null,n.triggerEvent("eventMouseLeave",e,t))};var r,i,o,a,s,u=t.component;return n.removeHoverListeners=(r=u.el,i=u.fgSegSelector+","+u.bgSegSelector,o=n.handleSegEnter,a=n.handleSegLeave,O(r,"mouseover",i,function(e,t){if(t!==s){s=t,o(e,t);var n=function(e){s=null,a(e,t),t.removeEventListener("mouseleave",n)};t.addEventListener("mouseleave",n)}})),u.calendar.on("eventElRemove",n.handleEventElRemove),n}return Ee(t,e),t.prototype.destroy=function(){this.removeHoverListeners(),this.component.calendar.off("eventElRemove",this.handleEventElRemove)},t.prototype.triggerEvent=function(e,t,n){var r=this.component,i=ht(n);t&&!r.isValidSegDownEl(t.target)||r.publiclyTrigger(e,[{el:n,event:new ct(this.component.calendar,i.eventRange.def,i.eventRange.instance),jsEvent:t,view:r.view}])},t}(yr),br=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return Ee(t,e),t}(cn);br.prototype.classes={widget:"fc-unthemed",widgetHeader:"fc-widget-header",widgetContent:"fc-widget-content",buttonGroup:"fc-button-group",button:"fc-button fc-button-primary",buttonActive:"fc-button-active",popoverHeader:"fc-widget-header",popoverContent:"fc-widget-content",headerRow:"fc-widget-header",dayRow:"fc-widget-content",listView:"fc-widget-content"},br.prototype.baseIconClass="fc-icon",br.prototype.iconClasses={close:"fc-icon-x",prev:"fc-icon-chevron-left",next:"fc-icon-chevron-right",prevYear:"fc-icon-chevrons-left",nextYear:"fc-icon-chevrons-right"},br.prototype.iconOverrideOption="buttonIcons",br.prototype.iconOverrideCustomButtonOption="icon",br.prototype.iconOverridePrefix="fc-icon-";var Dr=function(){function e(e,t){var n=this;this.parseRawLocales=Ye(On),this.buildLocale=Ye(_n),this.buildDateEnv=Ye(Tr),this.buildTheme=Ye(wr),this.buildEventUiSingleBase=Ye(this._buildEventUiSingleBase),this.buildSelectionConfig=Ye(this._buildSelectionConfig),this.buildEventUiBySource=qe(Ir,Me),this.buildEventUiBases=Ye(Cr),this.interactionsStore={},this.actionQueue=[],this.isReducing=!1,this.needsRerender=!1,this.needsFullRerender=!1,this.isRendering=!1,this.renderingPauseDepth=0,this.buildDelayedRerender=Ye(Rr),this.afterSizingTriggers={},this.isViewUpdated=!1,this.isDatesUpdated=!1,this.isEventsUpdated=!1,this.el=e,this.optionsManager=new xn(t||{}),this.pluginSystem=new gn,this.addPluginInputs(this.optionsManager.computed.plugins||[]),this.handleOptions(this.optionsManager.computed),this.publiclyTrigger("_init"),this.hydrate(),this.calendarInteractions=this.pluginSystem.hooks.calendarInteractions.map(function(e){return new e(n)})}return e.prototype.addPluginInputs=function(e){for(var t=function(e){for(var t=[],n=0,r=e;n"},e.prototype.detachSegs=function(e,t){var n=this.containerElsByType[e];n&&(n.forEach(c),delete this.containerElsByType[e])},e.prototype.computeSizes=function(e){for(var t in this.segsByType)(e||this.dirtySizeFlags[t])&&this.computeSegSizes(this.segsByType[t])},e.prototype.assignSizes=function(e){for(var t in this.segsByType)(e||this.dirtySizeFlags[t])&&this.assignSegSizes(this.segsByType[t]);this.dirtySizeFlags={}},e.prototype.computeSegSizes=function(e){},e.prototype.assignSegSizes=function(e){},e}(),Pr=function(e){this.timeZoneName=e},xr=function(){function e(e){this.emitter=new tn}return e.prototype.destroy=function(){},e.prototype.setMirrorIsVisible=function(e){},e.prototype.setMirrorNeedsRevert=function(e){},e.prototype.setAutoScrollEnabled=function(e){},e}();function Hr(e){var t=_n(e.locale||"en",On([]).map);return e=Se({timeZone:Rn.timeZone,calendarSystem:"gregory"},e,{locale:t}),new Bn(e)}var Nr={startTime:J,duration:J,create:Boolean,sourceId:String},zr={create:!0};function Ur(e,t){return!e||t>10?{weekday:"short"}:t>1?{weekday:"short",month:"numeric",day:"numeric",omitCommas:!0}:{weekday:"long"}}function Lr(e,t,n,r,i,o,a,s){var u,l=o.view,c=o.dateEnv,d=o.theme,f=o.options,p=Ze(t.activeRange,e),h=["fc-day-header",d.getClass("widgetHeader")];return u="function"==typeof f.columnHeaderHtml?f.columnHeaderHtml(c.toDate(e)):"function"==typeof f.columnHeaderText?Ot(f.columnHeaderText(c.toDate(e))):Ot(c.format(e,i)),n?h=h.concat(Qt(e,t,o,!0)):h.push("fc-"+P[e.getUTCDay()]),'1?' colspan="'+a+'"':"")+(s?" "+s:"")+">"+(p?Kt(l,{date:e,forceOff:!n||1===r},u):u)+""}var Br=function(e){function t(t,n){var r=e.call(this,t)||this;return n.innerHTML="",n.appendChild(r.el=i('
')),r.thead=r.el.querySelector("thead"),r}return Ee(t,e),t.prototype.destroy=function(){c(this.el)},t.prototype.render=function(e){var t=e.dates,n=e.datesRepDistinctDays,r=[];e.renderIntroHtml&&r.push(e.renderIntroHtml());for(var i=ot(this.opt("columnHeaderFormat")||Ur(n,t.length)),o=0,a=t;o"+r.join("")+""},t}(fn),Vr=function(){function e(e,t){for(var n=e.start,r=e.end,i=[],o=[],a=-1;n=t.length?t[t.length-1]+1:t[n]},e}(),Ar=function(){function e(e,t){var n,r,i,o=e.dates;if(t){for(r=o[0].getUTCDay(),n=1;n1},e.isPropsEqual=Me,e.isPropsValid=Rt,e.isSingleDay=function(e){return 0===e.years&&0===e.months&&1===e.days&&0===e.milliseconds},e.isValidDate=Y,e.listenBySelector=O,e.mapHash=Re,e.matchCellWidths=function(e){var t=0;return e.forEach(function(e){var n=e.firstChild;if(n instanceof HTMLElement){var r=n.getBoundingClientRect().width;r>t&&(t=r)}}),t++,e.forEach(function(e){e.style.width=t+"px"}),t},e.memoize=Ye,e.memoizeOutput=qe,e.memoizeRendering=qt,e.mergeEventStores=Ne,e.multiplyDuration=function(e,t){return{years:e.years*t,months:e.months*t,days:e.days*t,milliseconds:e.milliseconds*t}},e.padStart=le,e.parseBusinessHours=Yt,e.parseDragMeta=function(e){var t={},n=he(e,Nr,zr,t);return n.leftoverProps=t,n},e.parseEventDef=Wt,e.parseFieldSpecs=ie,e.parseMarker=Ln,e.pointInsideRect=function(e,t){return e.left>=t.left&&e.left=t.top&&e.top * { + /* work around the way we do column resizing and ensure a minimum width */ + display: inline-block; + min-width: 1.25em; +} diff --git a/static/assets/libs/fullcalendar/daygrid/main.js b/static/assets/libs/fullcalendar/daygrid/main.js new file mode 100644 index 0000000..d1cc407 --- /dev/null +++ b/static/assets/libs/fullcalendar/daygrid/main.js @@ -0,0 +1,1641 @@ +/*! +FullCalendar Day Grid Plugin v4.3.0 +Docs & License: https://fullcalendar.io/ +(c) 2019 Adam Shaw +*/ + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core')) : + typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core'], factory) : + (global = global || self, factory(global.FullCalendarDayGrid = {}, global.FullCalendar)); +}(this, function (exports, core) { 'use strict'; + + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use + this file except in compliance with the License. You may obtain a copy of the + License at http://www.apache.org/licenses/LICENSE-2.0 + + THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED + WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, + MERCHANTABLITY OR NON-INFRINGEMENT. + + See the Apache Version 2.0 License for specific language governing permissions + and limitations under the License. + ***************************************************************************** */ + /* global Reflect, Promise */ + + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + + function __extends(d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + } + + var __assign = function() { + __assign = Object.assign || function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); + }; + + var DayGridDateProfileGenerator = /** @class */ (function (_super) { + __extends(DayGridDateProfileGenerator, _super); + function DayGridDateProfileGenerator() { + return _super !== null && _super.apply(this, arguments) || this; + } + // Computes the date range that will be rendered. + DayGridDateProfileGenerator.prototype.buildRenderRange = function (currentRange, currentRangeUnit, isRangeAllDay) { + var dateEnv = this.dateEnv; + var renderRange = _super.prototype.buildRenderRange.call(this, currentRange, currentRangeUnit, isRangeAllDay); + var start = renderRange.start; + var end = renderRange.end; + var endOfWeek; + // year and month views should be aligned with weeks. this is already done for week + if (/^(year|month)$/.test(currentRangeUnit)) { + start = dateEnv.startOfWeek(start); + // make end-of-week if not already + endOfWeek = dateEnv.startOfWeek(end); + if (endOfWeek.valueOf() !== end.valueOf()) { + end = core.addWeeks(endOfWeek, 1); + } + } + // ensure 6 weeks + if (this.options.monthMode && + this.options.fixedWeekCount) { + var rowCnt = Math.ceil(// could be partial weeks due to hiddenDays + core.diffWeeks(start, end)); + end = core.addWeeks(end, 6 - rowCnt); + } + return { start: start, end: end }; + }; + return DayGridDateProfileGenerator; + }(core.DateProfileGenerator)); + + /* A rectangular panel that is absolutely positioned over other content + ------------------------------------------------------------------------------------------------------------------------ + Options: + - className (string) + - content (HTML string, element, or element array) + - parentEl + - top + - left + - right (the x coord of where the right edge should be. not a "CSS" right) + - autoHide (boolean) + - show (callback) + - hide (callback) + */ + var Popover = /** @class */ (function () { + function Popover(options) { + var _this = this; + this.isHidden = true; + this.margin = 10; // the space required between the popover and the edges of the scroll container + // Triggered when the user clicks *anywhere* in the document, for the autoHide feature + this.documentMousedown = function (ev) { + // only hide the popover if the click happened outside the popover + if (_this.el && !_this.el.contains(ev.target)) { + _this.hide(); + } + }; + this.options = options; + } + // Shows the popover on the specified position. Renders it if not already + Popover.prototype.show = function () { + if (this.isHidden) { + if (!this.el) { + this.render(); + } + this.el.style.display = ''; + this.position(); + this.isHidden = false; + this.trigger('show'); + } + }; + // Hides the popover, through CSS, but does not remove it from the DOM + Popover.prototype.hide = function () { + if (!this.isHidden) { + this.el.style.display = 'none'; + this.isHidden = true; + this.trigger('hide'); + } + }; + // Creates `this.el` and renders content inside of it + Popover.prototype.render = function () { + var _this = this; + var options = this.options; + var el = this.el = core.createElement('div', { + className: 'fc-popover ' + (options.className || ''), + style: { + top: '0', + left: '0' + } + }); + if (typeof options.content === 'function') { + options.content(el); + } + options.parentEl.appendChild(el); + // when a click happens on anything inside with a 'fc-close' className, hide the popover + core.listenBySelector(el, 'click', '.fc-close', function (ev) { + _this.hide(); + }); + if (options.autoHide) { + document.addEventListener('mousedown', this.documentMousedown); + } + }; + // Hides and unregisters any handlers + Popover.prototype.destroy = function () { + this.hide(); + if (this.el) { + core.removeElement(this.el); + this.el = null; + } + document.removeEventListener('mousedown', this.documentMousedown); + }; + // Positions the popover optimally, using the top/left/right options + Popover.prototype.position = function () { + var options = this.options; + var el = this.el; + var elDims = el.getBoundingClientRect(); // only used for width,height + var origin = core.computeRect(el.offsetParent); + var clippingRect = core.computeClippingRect(options.parentEl); + var top; // the "position" (not "offset") values for the popover + var left; // + // compute top and left + top = options.top || 0; + if (options.left !== undefined) { + left = options.left; + } + else if (options.right !== undefined) { + left = options.right - elDims.width; // derive the left value from the right value + } + else { + left = 0; + } + // constrain to the view port. if constrained by two edges, give precedence to top/left + top = Math.min(top, clippingRect.bottom - elDims.height - this.margin); + top = Math.max(top, clippingRect.top + this.margin); + left = Math.min(left, clippingRect.right - elDims.width - this.margin); + left = Math.max(left, clippingRect.left + this.margin); + core.applyStyle(el, { + top: top - origin.top, + left: left - origin.left + }); + }; + // Triggers a callback. Calls a function in the option hash of the same name. + // Arguments beyond the first `name` are forwarded on. + // TODO: better code reuse for this. Repeat code + // can kill this??? + Popover.prototype.trigger = function (name) { + if (this.options[name]) { + this.options[name].apply(this, Array.prototype.slice.call(arguments, 1)); + } + }; + return Popover; + }()); + + /* Event-rendering methods for the DayGrid class + ----------------------------------------------------------------------------------------------------------------------*/ + // "Simple" is bad a name. has nothing to do with SimpleDayGrid + var SimpleDayGridEventRenderer = /** @class */ (function (_super) { + __extends(SimpleDayGridEventRenderer, _super); + function SimpleDayGridEventRenderer() { + return _super !== null && _super.apply(this, arguments) || this; + } + // Builds the HTML to be used for the default element for an individual segment + SimpleDayGridEventRenderer.prototype.renderSegHtml = function (seg, mirrorInfo) { + var _a = this.context, view = _a.view, options = _a.options; + var eventRange = seg.eventRange; + var eventDef = eventRange.def; + var eventUi = eventRange.ui; + var allDay = eventDef.allDay; + var isDraggable = view.computeEventDraggable(eventDef, eventUi); + var isResizableFromStart = allDay && seg.isStart && view.computeEventStartResizable(eventDef, eventUi); + var isResizableFromEnd = allDay && seg.isEnd && view.computeEventEndResizable(eventDef, eventUi); + var classes = this.getSegClasses(seg, isDraggable, isResizableFromStart || isResizableFromEnd, mirrorInfo); + var skinCss = core.cssToStr(this.getSkinCss(eventUi)); + var timeHtml = ''; + var timeText; + var titleHtml; + classes.unshift('fc-day-grid-event', 'fc-h-event'); + // Only display a timed events time if it is the starting segment + if (seg.isStart) { + timeText = this.getTimeText(eventRange); + if (timeText) { + timeHtml = '' + core.htmlEscape(timeText) + ''; + } + } + titleHtml = + '' + + (core.htmlEscape(eventDef.title || '') || ' ') + // we always want one line of height + ''; + return '' + + '
' + + (options.dir === 'rtl' ? + titleHtml + ' ' + timeHtml : // put a natural space in between + timeHtml + ' ' + titleHtml // + ) + + '
' + + (isResizableFromStart ? + '
' : + '') + + (isResizableFromEnd ? + '
' : + '') + + '
'; + }; + // Computes a default event time formatting string if `eventTimeFormat` is not explicitly defined + SimpleDayGridEventRenderer.prototype.computeEventTimeFormat = function () { + return { + hour: 'numeric', + minute: '2-digit', + omitZeroMinute: true, + meridiem: 'narrow' + }; + }; + SimpleDayGridEventRenderer.prototype.computeDisplayEventEnd = function () { + return false; // TODO: somehow consider the originating DayGrid's column count + }; + return SimpleDayGridEventRenderer; + }(core.FgEventRenderer)); + + /* Event-rendering methods for the DayGrid class + ----------------------------------------------------------------------------------------------------------------------*/ + var DayGridEventRenderer = /** @class */ (function (_super) { + __extends(DayGridEventRenderer, _super); + function DayGridEventRenderer(dayGrid) { + var _this = _super.call(this, dayGrid.context) || this; + _this.dayGrid = dayGrid; + return _this; + } + // Renders the given foreground event segments onto the grid + DayGridEventRenderer.prototype.attachSegs = function (segs, mirrorInfo) { + var rowStructs = this.rowStructs = this.renderSegRows(segs); + // append to each row's content skeleton + this.dayGrid.rowEls.forEach(function (rowNode, i) { + rowNode.querySelector('.fc-content-skeleton > table').appendChild(rowStructs[i].tbodyEl); + }); + // removes the "more.." events popover + if (!mirrorInfo) { + this.dayGrid.removeSegPopover(); + } + }; + // Unrenders all currently rendered foreground event segments + DayGridEventRenderer.prototype.detachSegs = function () { + var rowStructs = this.rowStructs || []; + var rowStruct; + while ((rowStruct = rowStructs.pop())) { + core.removeElement(rowStruct.tbodyEl); + } + this.rowStructs = null; + }; + // Uses the given events array to generate elements that should be appended to each row's content skeleton. + // Returns an array of rowStruct objects (see the bottom of `renderSegRow`). + // PRECONDITION: each segment shoud already have a rendered and assigned `.el` + DayGridEventRenderer.prototype.renderSegRows = function (segs) { + var rowStructs = []; + var segRows; + var row; + segRows = this.groupSegRows(segs); // group into nested arrays + // iterate each row of segment groupings + for (row = 0; row < segRows.length; row++) { + rowStructs.push(this.renderSegRow(row, segRows[row])); + } + return rowStructs; + }; + // Given a row # and an array of segments all in the same row, render a element, a skeleton that contains + // the segments. Returns object with a bunch of internal data about how the render was calculated. + // NOTE: modifies rowSegs + DayGridEventRenderer.prototype.renderSegRow = function (row, rowSegs) { + var dayGrid = this.dayGrid; + var colCnt = dayGrid.colCnt, isRtl = dayGrid.isRtl; + var segLevels = this.buildSegLevels(rowSegs); // group into sub-arrays of levels + var levelCnt = Math.max(1, segLevels.length); // ensure at least one level + var tbody = document.createElement('tbody'); + var segMatrix = []; // lookup for which segments are rendered into which level+col cells + var cellMatrix = []; // lookup for all elements of the level+col matrix + var loneCellMatrix = []; // lookup for elements that only take up a single column + var i; + var levelSegs; + var col; + var tr; + var j; + var seg; + var td; + // populates empty cells from the current column (`col`) to `endCol` + function emptyCellsUntil(endCol) { + while (col < endCol) { + // try to grab a cell from the level above and extend its rowspan. otherwise, create a fresh cell + td = (loneCellMatrix[i - 1] || [])[col]; + if (td) { + td.rowSpan = (td.rowSpan || 1) + 1; + } + else { + td = document.createElement('td'); + tr.appendChild(td); + } + cellMatrix[i][col] = td; + loneCellMatrix[i][col] = td; + col++; + } + } + for (i = 0; i < levelCnt; i++) { // iterate through all levels + levelSegs = segLevels[i]; + col = 0; + tr = document.createElement('tr'); + segMatrix.push([]); + cellMatrix.push([]); + loneCellMatrix.push([]); + // levelCnt might be 1 even though there are no actual levels. protect against this. + // this single empty row is useful for styling. + if (levelSegs) { + for (j = 0; j < levelSegs.length; j++) { // iterate through segments in level + seg = levelSegs[j]; + var leftCol = isRtl ? (colCnt - 1 - seg.lastCol) : seg.firstCol; + var rightCol = isRtl ? (colCnt - 1 - seg.firstCol) : seg.lastCol; + emptyCellsUntil(leftCol); + // create a container that occupies or more columns. append the event element. + td = core.createElement('td', { className: 'fc-event-container' }, seg.el); + if (leftCol !== rightCol) { + td.colSpan = rightCol - leftCol + 1; + } + else { // a single-column segment + loneCellMatrix[i][col] = td; + } + while (col <= rightCol) { + cellMatrix[i][col] = td; + segMatrix[i][col] = seg; + col++; + } + tr.appendChild(td); + } + } + emptyCellsUntil(colCnt); // finish off the row + var introHtml = dayGrid.renderProps.renderIntroHtml(); + if (introHtml) { + if (dayGrid.isRtl) { + core.appendToElement(tr, introHtml); + } + else { + core.prependToElement(tr, introHtml); + } + } + tbody.appendChild(tr); + } + return { + row: row, + tbodyEl: tbody, + cellMatrix: cellMatrix, + segMatrix: segMatrix, + segLevels: segLevels, + segs: rowSegs + }; + }; + // Stacks a flat array of segments, which are all assumed to be in the same row, into subarrays of vertical levels. + // NOTE: modifies segs + DayGridEventRenderer.prototype.buildSegLevels = function (segs) { + var _a = this.dayGrid, isRtl = _a.isRtl, colCnt = _a.colCnt; + var levels = []; + var i; + var seg; + var j; + // Give preference to elements with certain criteria, so they have + // a chance to be closer to the top. + segs = this.sortEventSegs(segs); + for (i = 0; i < segs.length; i++) { + seg = segs[i]; + // loop through levels, starting with the topmost, until the segment doesn't collide with other segments + for (j = 0; j < levels.length; j++) { + if (!isDaySegCollision(seg, levels[j])) { + break; + } + } + // `j` now holds the desired subrow index + seg.level = j; + seg.leftCol = isRtl ? (colCnt - 1 - seg.lastCol) : seg.firstCol; // for sorting only + seg.rightCol = isRtl ? (colCnt - 1 - seg.firstCol) : seg.lastCol // for sorting only + ; + (levels[j] || (levels[j] = [])).push(seg); + } + // order segments left-to-right. very important if calendar is RTL + for (j = 0; j < levels.length; j++) { + levels[j].sort(compareDaySegCols); + } + return levels; + }; + // Given a flat array of segments, return an array of sub-arrays, grouped by each segment's row + DayGridEventRenderer.prototype.groupSegRows = function (segs) { + var segRows = []; + var i; + for (i = 0; i < this.dayGrid.rowCnt; i++) { + segRows.push([]); + } + for (i = 0; i < segs.length; i++) { + segRows[segs[i].row].push(segs[i]); + } + return segRows; + }; + // Computes a default `displayEventEnd` value if one is not expliclty defined + DayGridEventRenderer.prototype.computeDisplayEventEnd = function () { + return this.dayGrid.colCnt === 1; // we'll likely have space if there's only one day + }; + return DayGridEventRenderer; + }(SimpleDayGridEventRenderer)); + // Computes whether two segments' columns collide. They are assumed to be in the same row. + function isDaySegCollision(seg, otherSegs) { + var i; + var otherSeg; + for (i = 0; i < otherSegs.length; i++) { + otherSeg = otherSegs[i]; + if (otherSeg.firstCol <= seg.lastCol && + otherSeg.lastCol >= seg.firstCol) { + return true; + } + } + return false; + } + // A cmp function for determining the leftmost event + function compareDaySegCols(a, b) { + return a.leftCol - b.leftCol; + } + + var DayGridMirrorRenderer = /** @class */ (function (_super) { + __extends(DayGridMirrorRenderer, _super); + function DayGridMirrorRenderer() { + return _super !== null && _super.apply(this, arguments) || this; + } + DayGridMirrorRenderer.prototype.attachSegs = function (segs, mirrorInfo) { + var sourceSeg = mirrorInfo.sourceSeg; + var rowStructs = this.rowStructs = this.renderSegRows(segs); + // inject each new event skeleton into each associated row + this.dayGrid.rowEls.forEach(function (rowNode, row) { + var skeletonEl = core.htmlToElement('
'); // will be absolutely positioned + var skeletonTopEl; + var skeletonTop; + // If there is an original segment, match the top position. Otherwise, put it at the row's top level + if (sourceSeg && sourceSeg.row === row) { + skeletonTopEl = sourceSeg.el; + } + else { + skeletonTopEl = rowNode.querySelector('.fc-content-skeleton tbody'); + if (!skeletonTopEl) { // when no events + skeletonTopEl = rowNode.querySelector('.fc-content-skeleton table'); + } + } + skeletonTop = skeletonTopEl.getBoundingClientRect().top - + rowNode.getBoundingClientRect().top; // the offsetParent origin + skeletonEl.style.top = skeletonTop + 'px'; + skeletonEl.querySelector('table').appendChild(rowStructs[row].tbodyEl); + rowNode.appendChild(skeletonEl); + }); + }; + return DayGridMirrorRenderer; + }(DayGridEventRenderer)); + + var EMPTY_CELL_HTML = ''; + var DayGridFillRenderer = /** @class */ (function (_super) { + __extends(DayGridFillRenderer, _super); + function DayGridFillRenderer(dayGrid) { + var _this = _super.call(this, dayGrid.context) || this; + _this.fillSegTag = 'td'; // override the default tag name + _this.dayGrid = dayGrid; + return _this; + } + DayGridFillRenderer.prototype.renderSegs = function (type, segs) { + // don't render timed background events + if (type === 'bgEvent') { + segs = segs.filter(function (seg) { + return seg.eventRange.def.allDay; + }); + } + _super.prototype.renderSegs.call(this, type, segs); + }; + DayGridFillRenderer.prototype.attachSegs = function (type, segs) { + var els = []; + var i; + var seg; + var skeletonEl; + for (i = 0; i < segs.length; i++) { + seg = segs[i]; + skeletonEl = this.renderFillRow(type, seg); + this.dayGrid.rowEls[seg.row].appendChild(skeletonEl); + els.push(skeletonEl); + } + return els; + }; + // Generates the HTML needed for one row of a fill. Requires the seg's el to be rendered. + DayGridFillRenderer.prototype.renderFillRow = function (type, seg) { + var dayGrid = this.dayGrid; + var colCnt = dayGrid.colCnt, isRtl = dayGrid.isRtl; + var leftCol = isRtl ? (colCnt - 1 - seg.lastCol) : seg.firstCol; + var rightCol = isRtl ? (colCnt - 1 - seg.firstCol) : seg.lastCol; + var startCol = leftCol; + var endCol = rightCol + 1; + var className; + var skeletonEl; + var trEl; + if (type === 'businessHours') { + className = 'bgevent'; + } + else { + className = type.toLowerCase(); + } + skeletonEl = core.htmlToElement('
' + + '
' + + '
'); + trEl = skeletonEl.getElementsByTagName('tr')[0]; + if (startCol > 0) { + core.appendToElement(trEl, + // will create (startCol + 1) td's + new Array(startCol + 1).join(EMPTY_CELL_HTML)); + } + seg.el.colSpan = endCol - startCol; + trEl.appendChild(seg.el); + if (endCol < colCnt) { + core.appendToElement(trEl, + // will create (colCnt - endCol) td's + new Array(colCnt - endCol + 1).join(EMPTY_CELL_HTML)); + } + var introHtml = dayGrid.renderProps.renderIntroHtml(); + if (introHtml) { + if (dayGrid.isRtl) { + core.appendToElement(trEl, introHtml); + } + else { + core.prependToElement(trEl, introHtml); + } + } + return skeletonEl; + }; + return DayGridFillRenderer; + }(core.FillRenderer)); + + var DayTile = /** @class */ (function (_super) { + __extends(DayTile, _super); + function DayTile(context, el) { + var _this = _super.call(this, context, el) || this; + var eventRenderer = _this.eventRenderer = new DayTileEventRenderer(_this); + var renderFrame = _this.renderFrame = core.memoizeRendering(_this._renderFrame); + _this.renderFgEvents = core.memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer), [renderFrame]); + _this.renderEventSelection = core.memoizeRendering(eventRenderer.selectByInstanceId.bind(eventRenderer), eventRenderer.unselectByInstanceId.bind(eventRenderer), [_this.renderFgEvents]); + _this.renderEventDrag = core.memoizeRendering(eventRenderer.hideByHash.bind(eventRenderer), eventRenderer.showByHash.bind(eventRenderer), [renderFrame]); + _this.renderEventResize = core.memoizeRendering(eventRenderer.hideByHash.bind(eventRenderer), eventRenderer.showByHash.bind(eventRenderer), [renderFrame]); + context.calendar.registerInteractiveComponent(_this, { + el: _this.el, + useEventCenter: false + }); + return _this; + } + DayTile.prototype.render = function (props) { + this.renderFrame(props.date); + this.renderFgEvents(props.fgSegs); + this.renderEventSelection(props.eventSelection); + this.renderEventDrag(props.eventDragInstances); + this.renderEventResize(props.eventResizeInstances); + }; + DayTile.prototype.destroy = function () { + _super.prototype.destroy.call(this); + this.renderFrame.unrender(); // should unrender everything else + this.calendar.unregisterInteractiveComponent(this); + }; + DayTile.prototype._renderFrame = function (date) { + var _a = this, theme = _a.theme, dateEnv = _a.dateEnv; + var title = dateEnv.format(date, core.createFormatter(this.opt('dayPopoverFormat')) // TODO: cache + ); + this.el.innerHTML = + '
' + + '' + + core.htmlEscape(title) + + '' + + '' + + '
' + + '
' + + '
' + + '
'; + this.segContainerEl = this.el.querySelector('.fc-event-container'); + }; + DayTile.prototype.queryHit = function (positionLeft, positionTop, elWidth, elHeight) { + var date = this.props.date; // HACK + if (positionLeft < elWidth && positionTop < elHeight) { + return { + component: this, + dateSpan: { + allDay: true, + range: { start: date, end: core.addDays(date, 1) } + }, + dayEl: this.el, + rect: { + left: 0, + top: 0, + right: elWidth, + bottom: elHeight + }, + layer: 1 + }; + } + }; + return DayTile; + }(core.DateComponent)); + var DayTileEventRenderer = /** @class */ (function (_super) { + __extends(DayTileEventRenderer, _super); + function DayTileEventRenderer(dayTile) { + var _this = _super.call(this, dayTile.context) || this; + _this.dayTile = dayTile; + return _this; + } + DayTileEventRenderer.prototype.attachSegs = function (segs) { + for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) { + var seg = segs_1[_i]; + this.dayTile.segContainerEl.appendChild(seg.el); + } + }; + DayTileEventRenderer.prototype.detachSegs = function (segs) { + for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) { + var seg = segs_2[_i]; + core.removeElement(seg.el); + } + }; + return DayTileEventRenderer; + }(SimpleDayGridEventRenderer)); + + var DayBgRow = /** @class */ (function () { + function DayBgRow(context) { + this.context = context; + } + DayBgRow.prototype.renderHtml = function (props) { + var parts = []; + if (props.renderIntroHtml) { + parts.push(props.renderIntroHtml()); + } + for (var _i = 0, _a = props.cells; _i < _a.length; _i++) { + var cell = _a[_i]; + parts.push(renderCellHtml(cell.date, props.dateProfile, this.context, cell.htmlAttrs)); + } + if (!props.cells.length) { + parts.push(''); + } + if (this.context.options.dir === 'rtl') { + parts.reverse(); + } + return '' + parts.join('') + ''; + }; + return DayBgRow; + }()); + function renderCellHtml(date, dateProfile, context, otherAttrs) { + var dateEnv = context.dateEnv, theme = context.theme; + var isDateValid = core.rangeContainsMarker(dateProfile.activeRange, date); // TODO: called too frequently. cache somehow. + var classes = core.getDayClasses(date, dateProfile, context); + classes.unshift('fc-day', theme.getClass('widgetContent')); + return ''; + } + + var DAY_NUM_FORMAT = core.createFormatter({ day: 'numeric' }); + var WEEK_NUM_FORMAT = core.createFormatter({ week: 'numeric' }); + var DayGrid = /** @class */ (function (_super) { + __extends(DayGrid, _super); + function DayGrid(context, el, renderProps) { + var _this = _super.call(this, context, el) || this; + _this.bottomCoordPadding = 0; // hack for extending the hit area for the last row of the coordinate grid + _this.isCellSizesDirty = false; + var eventRenderer = _this.eventRenderer = new DayGridEventRenderer(_this); + var fillRenderer = _this.fillRenderer = new DayGridFillRenderer(_this); + _this.mirrorRenderer = new DayGridMirrorRenderer(_this); + var renderCells = _this.renderCells = core.memoizeRendering(_this._renderCells, _this._unrenderCells); + _this.renderBusinessHours = core.memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'businessHours'), fillRenderer.unrender.bind(fillRenderer, 'businessHours'), [renderCells]); + _this.renderDateSelection = core.memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'highlight'), fillRenderer.unrender.bind(fillRenderer, 'highlight'), [renderCells]); + _this.renderBgEvents = core.memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'bgEvent'), fillRenderer.unrender.bind(fillRenderer, 'bgEvent'), [renderCells]); + _this.renderFgEvents = core.memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer), [renderCells]); + _this.renderEventSelection = core.memoizeRendering(eventRenderer.selectByInstanceId.bind(eventRenderer), eventRenderer.unselectByInstanceId.bind(eventRenderer), [_this.renderFgEvents]); + _this.renderEventDrag = core.memoizeRendering(_this._renderEventDrag, _this._unrenderEventDrag, [renderCells]); + _this.renderEventResize = core.memoizeRendering(_this._renderEventResize, _this._unrenderEventResize, [renderCells]); + _this.renderProps = renderProps; + return _this; + } + DayGrid.prototype.render = function (props) { + var cells = props.cells; + this.rowCnt = cells.length; + this.colCnt = cells[0].length; + this.renderCells(cells, props.isRigid); + this.renderBusinessHours(props.businessHourSegs); + this.renderDateSelection(props.dateSelectionSegs); + this.renderBgEvents(props.bgEventSegs); + this.renderFgEvents(props.fgEventSegs); + this.renderEventSelection(props.eventSelection); + this.renderEventDrag(props.eventDrag); + this.renderEventResize(props.eventResize); + if (this.segPopoverTile) { + this.updateSegPopoverTile(); + } + }; + DayGrid.prototype.destroy = function () { + _super.prototype.destroy.call(this); + this.renderCells.unrender(); // will unrender everything else + }; + DayGrid.prototype.getCellRange = function (row, col) { + var start = this.props.cells[row][col].date; + var end = core.addDays(start, 1); + return { start: start, end: end }; + }; + DayGrid.prototype.updateSegPopoverTile = function (date, segs) { + var ownProps = this.props; + this.segPopoverTile.receiveProps({ + date: date || this.segPopoverTile.props.date, + fgSegs: segs || this.segPopoverTile.props.fgSegs, + eventSelection: ownProps.eventSelection, + eventDragInstances: ownProps.eventDrag ? ownProps.eventDrag.affectedInstances : null, + eventResizeInstances: ownProps.eventResize ? ownProps.eventResize.affectedInstances : null + }); + }; + /* Date Rendering + ------------------------------------------------------------------------------------------------------------------*/ + DayGrid.prototype._renderCells = function (cells, isRigid) { + var _a = this, view = _a.view, dateEnv = _a.dateEnv; + var _b = this, rowCnt = _b.rowCnt, colCnt = _b.colCnt; + var html = ''; + var row; + var col; + for (row = 0; row < rowCnt; row++) { + html += this.renderDayRowHtml(row, isRigid); + } + this.el.innerHTML = html; + this.rowEls = core.findElements(this.el, '.fc-row'); + this.cellEls = core.findElements(this.el, '.fc-day, .fc-disabled-day'); + if (this.isRtl) { + this.cellEls.reverse(); + } + this.rowPositions = new core.PositionCache(this.el, this.rowEls, false, true // vertical + ); + this.colPositions = new core.PositionCache(this.el, this.cellEls.slice(0, colCnt), // only the first row + true, false // horizontal + ); + // trigger dayRender with each cell's element + for (row = 0; row < rowCnt; row++) { + for (col = 0; col < colCnt; col++) { + this.publiclyTrigger('dayRender', [ + { + date: dateEnv.toDate(cells[row][col].date), + el: this.getCellEl(row, col), + view: view + } + ]); + } + } + this.isCellSizesDirty = true; + }; + DayGrid.prototype._unrenderCells = function () { + this.removeSegPopover(); + }; + // Generates the HTML for a single row, which is a div that wraps a table. + // `row` is the row number. + DayGrid.prototype.renderDayRowHtml = function (row, isRigid) { + var theme = this.theme; + var classes = ['fc-row', 'fc-week', theme.getClass('dayRow')]; + if (isRigid) { + classes.push('fc-rigid'); + } + var bgRow = new DayBgRow(this.context); + return '' + + '
' + + '
' + + '' + + bgRow.renderHtml({ + cells: this.props.cells[row], + dateProfile: this.props.dateProfile, + renderIntroHtml: this.renderProps.renderBgIntroHtml + }) + + '
' + + '
' + + '
' + + '' + + (this.getIsNumbersVisible() ? + '' + + this.renderNumberTrHtml(row) + + '' : + '') + + '
' + + '
' + + '
'; + }; + DayGrid.prototype.getIsNumbersVisible = function () { + return this.getIsDayNumbersVisible() || + this.renderProps.cellWeekNumbersVisible || + this.renderProps.colWeekNumbersVisible; + }; + DayGrid.prototype.getIsDayNumbersVisible = function () { + return this.rowCnt > 1; + }; + /* Grid Number Rendering + ------------------------------------------------------------------------------------------------------------------*/ + DayGrid.prototype.renderNumberTrHtml = function (row) { + var intro = this.renderProps.renderNumberIntroHtml(row, this); + return '' + + '' + + (this.isRtl ? '' : intro) + + this.renderNumberCellsHtml(row) + + (this.isRtl ? intro : '') + + ''; + }; + DayGrid.prototype.renderNumberCellsHtml = function (row) { + var htmls = []; + var col; + var date; + for (col = 0; col < this.colCnt; col++) { + date = this.props.cells[row][col].date; + htmls.push(this.renderNumberCellHtml(date)); + } + if (this.isRtl) { + htmls.reverse(); + } + return htmls.join(''); + }; + // Generates the HTML for the s of the "number" row in the DayGrid's content skeleton. + // The number row will only exist if either day numbers or week numbers are turned on. + DayGrid.prototype.renderNumberCellHtml = function (date) { + var _a = this, view = _a.view, dateEnv = _a.dateEnv; + var html = ''; + var isDateValid = core.rangeContainsMarker(this.props.dateProfile.activeRange, date); // TODO: called too frequently. cache somehow. + var isDayNumberVisible = this.getIsDayNumbersVisible() && isDateValid; + var classes; + var weekCalcFirstDow; + if (!isDayNumberVisible && !this.renderProps.cellWeekNumbersVisible) { + // no numbers in day cell (week number must be along the side) + return ''; // will create an empty space above events :( + } + classes = core.getDayClasses(date, this.props.dateProfile, this.context); + classes.unshift('fc-day-top'); + if (this.renderProps.cellWeekNumbersVisible) { + weekCalcFirstDow = dateEnv.weekDow; + } + html += ''; + if (this.renderProps.cellWeekNumbersVisible && (date.getUTCDay() === weekCalcFirstDow)) { + html += core.buildGotoAnchorHtml(view, { date: date, type: 'week' }, { 'class': 'fc-week-number' }, dateEnv.format(date, WEEK_NUM_FORMAT) // inner HTML + ); + } + if (isDayNumberVisible) { + html += core.buildGotoAnchorHtml(view, date, { 'class': 'fc-day-number' }, dateEnv.format(date, DAY_NUM_FORMAT) // inner HTML + ); + } + html += ''; + return html; + }; + /* Sizing + ------------------------------------------------------------------------------------------------------------------*/ + DayGrid.prototype.updateSize = function (isResize) { + var _a = this, fillRenderer = _a.fillRenderer, eventRenderer = _a.eventRenderer, mirrorRenderer = _a.mirrorRenderer; + if (isResize || + this.isCellSizesDirty || + this.view.calendar.isEventsUpdated // hack + ) { + this.buildPositionCaches(); + this.isCellSizesDirty = false; + } + fillRenderer.computeSizes(isResize); + eventRenderer.computeSizes(isResize); + mirrorRenderer.computeSizes(isResize); + fillRenderer.assignSizes(isResize); + eventRenderer.assignSizes(isResize); + mirrorRenderer.assignSizes(isResize); + }; + DayGrid.prototype.buildPositionCaches = function () { + this.buildColPositions(); + this.buildRowPositions(); + }; + DayGrid.prototype.buildColPositions = function () { + this.colPositions.build(); + }; + DayGrid.prototype.buildRowPositions = function () { + this.rowPositions.build(); + this.rowPositions.bottoms[this.rowCnt - 1] += this.bottomCoordPadding; // hack + }; + /* Hit System + ------------------------------------------------------------------------------------------------------------------*/ + DayGrid.prototype.positionToHit = function (leftPosition, topPosition) { + var _a = this, colPositions = _a.colPositions, rowPositions = _a.rowPositions; + var col = colPositions.leftToIndex(leftPosition); + var row = rowPositions.topToIndex(topPosition); + if (row != null && col != null) { + return { + row: row, + col: col, + dateSpan: { + range: this.getCellRange(row, col), + allDay: true + }, + dayEl: this.getCellEl(row, col), + relativeRect: { + left: colPositions.lefts[col], + right: colPositions.rights[col], + top: rowPositions.tops[row], + bottom: rowPositions.bottoms[row] + } + }; + } + }; + /* Cell System + ------------------------------------------------------------------------------------------------------------------*/ + // FYI: the first column is the leftmost column, regardless of date + DayGrid.prototype.getCellEl = function (row, col) { + return this.cellEls[row * this.colCnt + col]; + }; + /* Event Drag Visualization + ------------------------------------------------------------------------------------------------------------------*/ + DayGrid.prototype._renderEventDrag = function (state) { + if (state) { + this.eventRenderer.hideByHash(state.affectedInstances); + this.fillRenderer.renderSegs('highlight', state.segs); + } + }; + DayGrid.prototype._unrenderEventDrag = function (state) { + if (state) { + this.eventRenderer.showByHash(state.affectedInstances); + this.fillRenderer.unrender('highlight'); + } + }; + /* Event Resize Visualization + ------------------------------------------------------------------------------------------------------------------*/ + DayGrid.prototype._renderEventResize = function (state) { + if (state) { + this.eventRenderer.hideByHash(state.affectedInstances); + this.fillRenderer.renderSegs('highlight', state.segs); + this.mirrorRenderer.renderSegs(state.segs, { isResizing: true, sourceSeg: state.sourceSeg }); + } + }; + DayGrid.prototype._unrenderEventResize = function (state) { + if (state) { + this.eventRenderer.showByHash(state.affectedInstances); + this.fillRenderer.unrender('highlight'); + this.mirrorRenderer.unrender(state.segs, { isResizing: true, sourceSeg: state.sourceSeg }); + } + }; + /* More+ Link Popover + ------------------------------------------------------------------------------------------------------------------*/ + DayGrid.prototype.removeSegPopover = function () { + if (this.segPopover) { + this.segPopover.hide(); // in handler, will call segPopover's removeElement + } + }; + // Limits the number of "levels" (vertically stacking layers of events) for each row of the grid. + // `levelLimit` can be false (don't limit), a number, or true (should be computed). + DayGrid.prototype.limitRows = function (levelLimit) { + var rowStructs = this.eventRenderer.rowStructs || []; + var row; // row # + var rowLevelLimit; + for (row = 0; row < rowStructs.length; row++) { + this.unlimitRow(row); + if (!levelLimit) { + rowLevelLimit = false; + } + else if (typeof levelLimit === 'number') { + rowLevelLimit = levelLimit; + } + else { + rowLevelLimit = this.computeRowLevelLimit(row); + } + if (rowLevelLimit !== false) { + this.limitRow(row, rowLevelLimit); + } + } + }; + // Computes the number of levels a row will accomodate without going outside its bounds. + // Assumes the row is "rigid" (maintains a constant height regardless of what is inside). + // `row` is the row number. + DayGrid.prototype.computeRowLevelLimit = function (row) { + var rowEl = this.rowEls[row]; // the containing "fake" row div + var rowBottom = rowEl.getBoundingClientRect().bottom; // relative to viewport! + var trEls = core.findChildren(this.eventRenderer.rowStructs[row].tbodyEl); + var i; + var trEl; + // Reveal one level at a time and stop when we find one out of bounds + for (i = 0; i < trEls.length; i++) { + trEl = trEls[i]; + trEl.classList.remove('fc-limited'); // reset to original state (reveal) + if (trEl.getBoundingClientRect().bottom > rowBottom) { + return i; + } + } + return false; // should not limit at all + }; + // Limits the given grid row to the maximum number of levels and injects "more" links if necessary. + // `row` is the row number. + // `levelLimit` is a number for the maximum (inclusive) number of levels allowed. + DayGrid.prototype.limitRow = function (row, levelLimit) { + var _this = this; + var _a = this, colCnt = _a.colCnt, isRtl = _a.isRtl; + var rowStruct = this.eventRenderer.rowStructs[row]; + var moreNodes = []; // array of "more" links and DOM nodes + var col = 0; // col #, left-to-right (not chronologically) + var levelSegs; // array of segment objects in the last allowable level, ordered left-to-right + var cellMatrix; // a matrix (by level, then column) of all elements in the row + var limitedNodes; // array of temporarily hidden level and segment DOM nodes + var i; + var seg; + var segsBelow; // array of segment objects below `seg` in the current `col` + var totalSegsBelow; // total number of segments below `seg` in any of the columns `seg` occupies + var colSegsBelow; // array of segment arrays, below seg, one for each column (offset from segs's first column) + var td; + var rowSpan; + var segMoreNodes; // array of "more" cells that will stand-in for the current seg's cell + var j; + var moreTd; + var moreWrap; + var moreLink; + // Iterates through empty level cells and places "more" links inside if need be + var emptyCellsUntil = function (endCol) { + while (col < endCol) { + segsBelow = _this.getCellSegs(row, col, levelLimit); + if (segsBelow.length) { + td = cellMatrix[levelLimit - 1][col]; + moreLink = _this.renderMoreLink(row, col, segsBelow); + moreWrap = core.createElement('div', null, moreLink); + td.appendChild(moreWrap); + moreNodes.push(moreWrap); + } + col++; + } + }; + if (levelLimit && levelLimit < rowStruct.segLevels.length) { // is it actually over the limit? + levelSegs = rowStruct.segLevels[levelLimit - 1]; + cellMatrix = rowStruct.cellMatrix; + limitedNodes = core.findChildren(rowStruct.tbodyEl).slice(levelLimit); // get level elements past the limit + limitedNodes.forEach(function (node) { + node.classList.add('fc-limited'); // hide elements and get a simple DOM-nodes array + }); + // iterate though segments in the last allowable level + for (i = 0; i < levelSegs.length; i++) { + seg = levelSegs[i]; + var leftCol = isRtl ? (colCnt - 1 - seg.lastCol) : seg.firstCol; + var rightCol = isRtl ? (colCnt - 1 - seg.firstCol) : seg.lastCol; + emptyCellsUntil(leftCol); // process empty cells before the segment + // determine *all* segments below `seg` that occupy the same columns + colSegsBelow = []; + totalSegsBelow = 0; + while (col <= rightCol) { + segsBelow = this.getCellSegs(row, col, levelLimit); + colSegsBelow.push(segsBelow); + totalSegsBelow += segsBelow.length; + col++; + } + if (totalSegsBelow) { // do we need to replace this segment with one or many "more" links? + td = cellMatrix[levelLimit - 1][leftCol]; // the segment's parent cell + rowSpan = td.rowSpan || 1; + segMoreNodes = []; + // make a replacement for each column the segment occupies. will be one for each colspan + for (j = 0; j < colSegsBelow.length; j++) { + moreTd = core.createElement('td', { className: 'fc-more-cell', rowSpan: rowSpan }); + segsBelow = colSegsBelow[j]; + moreLink = this.renderMoreLink(row, leftCol + j, [seg].concat(segsBelow) // count seg as hidden too + ); + moreWrap = core.createElement('div', null, moreLink); + moreTd.appendChild(moreWrap); + segMoreNodes.push(moreTd); + moreNodes.push(moreTd); + } + td.classList.add('fc-limited'); + core.insertAfterElement(td, segMoreNodes); + limitedNodes.push(td); + } + } + emptyCellsUntil(this.colCnt); // finish off the level + rowStruct.moreEls = moreNodes; // for easy undoing later + rowStruct.limitedEls = limitedNodes; // for easy undoing later + } + }; + // Reveals all levels and removes all "more"-related elements for a grid's row. + // `row` is a row number. + DayGrid.prototype.unlimitRow = function (row) { + var rowStruct = this.eventRenderer.rowStructs[row]; + if (rowStruct.moreEls) { + rowStruct.moreEls.forEach(core.removeElement); + rowStruct.moreEls = null; + } + if (rowStruct.limitedEls) { + rowStruct.limitedEls.forEach(function (limitedEl) { + limitedEl.classList.remove('fc-limited'); + }); + rowStruct.limitedEls = null; + } + }; + // Renders an element that represents hidden event element for a cell. + // Responsible for attaching click handler as well. + DayGrid.prototype.renderMoreLink = function (row, col, hiddenSegs) { + var _this = this; + var _a = this, view = _a.view, dateEnv = _a.dateEnv; + var a = core.createElement('a', { className: 'fc-more' }); + a.innerText = this.getMoreLinkText(hiddenSegs.length); + a.addEventListener('click', function (ev) { + var clickOption = _this.opt('eventLimitClick'); + var _col = _this.isRtl ? _this.colCnt - col - 1 : col; // HACK: props.cells has different dir system? + var date = _this.props.cells[row][_col].date; + var moreEl = ev.currentTarget; + var dayEl = _this.getCellEl(row, col); + var allSegs = _this.getCellSegs(row, col); + // rescope the segments to be within the cell's date + var reslicedAllSegs = _this.resliceDaySegs(allSegs, date); + var reslicedHiddenSegs = _this.resliceDaySegs(hiddenSegs, date); + if (typeof clickOption === 'function') { + // the returned value can be an atomic option + clickOption = _this.publiclyTrigger('eventLimitClick', [ + { + date: dateEnv.toDate(date), + allDay: true, + dayEl: dayEl, + moreEl: moreEl, + segs: reslicedAllSegs, + hiddenSegs: reslicedHiddenSegs, + jsEvent: ev, + view: view + } + ]); + } + if (clickOption === 'popover') { + _this.showSegPopover(row, col, moreEl, reslicedAllSegs); + } + else if (typeof clickOption === 'string') { // a view name + view.calendar.zoomTo(date, clickOption); + } + }); + return a; + }; + // Reveals the popover that displays all events within a cell + DayGrid.prototype.showSegPopover = function (row, col, moreLink, segs) { + var _this = this; + var _a = this, calendar = _a.calendar, view = _a.view, theme = _a.theme; + var _col = this.isRtl ? this.colCnt - col - 1 : col; // HACK: props.cells has different dir system? + var moreWrap = moreLink.parentNode; // the
wrapper around the + var topEl; // the element we want to match the top coordinate of + var options; + if (this.rowCnt === 1) { + topEl = view.el; // will cause the popover to cover any sort of header + } + else { + topEl = this.rowEls[row]; // will align with top of row + } + options = { + className: 'fc-more-popover ' + theme.getClass('popover'), + parentEl: view.el, + top: core.computeRect(topEl).top, + autoHide: true, + content: function (el) { + _this.segPopoverTile = new DayTile(_this.context, el); + _this.updateSegPopoverTile(_this.props.cells[row][_col].date, segs); + }, + hide: function () { + _this.segPopoverTile.destroy(); + _this.segPopoverTile = null; + _this.segPopover.destroy(); + _this.segPopover = null; + } + }; + // Determine horizontal coordinate. + // We use the moreWrap instead of the to avoid border confusion. + if (this.isRtl) { + options.right = core.computeRect(moreWrap).right + 1; // +1 to be over cell border + } + else { + options.left = core.computeRect(moreWrap).left - 1; // -1 to be over cell border + } + this.segPopover = new Popover(options); + this.segPopover.show(); + calendar.releaseAfterSizingTriggers(); // hack for eventPositioned + }; + // Given the events within an array of segment objects, reslice them to be in a single day + DayGrid.prototype.resliceDaySegs = function (segs, dayDate) { + var dayStart = dayDate; + var dayEnd = core.addDays(dayStart, 1); + var dayRange = { start: dayStart, end: dayEnd }; + var newSegs = []; + for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) { + var seg = segs_1[_i]; + var eventRange = seg.eventRange; + var origRange = eventRange.range; + var slicedRange = core.intersectRanges(origRange, dayRange); + if (slicedRange) { + newSegs.push(__assign({}, seg, { eventRange: { + def: eventRange.def, + ui: __assign({}, eventRange.ui, { durationEditable: false }), + instance: eventRange.instance, + range: slicedRange + }, isStart: seg.isStart && slicedRange.start.valueOf() === origRange.start.valueOf(), isEnd: seg.isEnd && slicedRange.end.valueOf() === origRange.end.valueOf() })); + } + } + return newSegs; + }; + // Generates the text that should be inside a "more" link, given the number of events it represents + DayGrid.prototype.getMoreLinkText = function (num) { + var opt = this.opt('eventLimitText'); + if (typeof opt === 'function') { + return opt(num); + } + else { + return '+' + num + ' ' + opt; + } + }; + // Returns segments within a given cell. + // If `startLevel` is specified, returns only events including and below that level. Otherwise returns all segs. + DayGrid.prototype.getCellSegs = function (row, col, startLevel) { + var segMatrix = this.eventRenderer.rowStructs[row].segMatrix; + var level = startLevel || 0; + var segs = []; + var seg; + while (level < segMatrix.length) { + seg = segMatrix[level][col]; + if (seg) { + segs.push(seg); + } + level++; + } + return segs; + }; + return DayGrid; + }(core.DateComponent)); + + var WEEK_NUM_FORMAT$1 = core.createFormatter({ week: 'numeric' }); + /* An abstract class for the daygrid views, as well as month view. Renders one or more rows of day cells. + ----------------------------------------------------------------------------------------------------------------------*/ + // It is a manager for a DayGrid subcomponent, which does most of the heavy lifting. + // It is responsible for managing width/height. + var DayGridView = /** @class */ (function (_super) { + __extends(DayGridView, _super); + function DayGridView(context, viewSpec, dateProfileGenerator, parentEl) { + var _this = _super.call(this, context, viewSpec, dateProfileGenerator, parentEl) || this; + /* Header Rendering + ------------------------------------------------------------------------------------------------------------------*/ + // Generates the HTML that will go before the day-of week header cells + _this.renderHeadIntroHtml = function () { + var theme = _this.theme; + if (_this.colWeekNumbersVisible) { + return '' + + '' + + '' + // needed for matchCellWidths + core.htmlEscape(_this.opt('weekLabel')) + + '' + + ''; + } + return ''; + }; + /* Day Grid Rendering + ------------------------------------------------------------------------------------------------------------------*/ + // Generates the HTML that will go before content-skeleton cells that display the day/week numbers + _this.renderDayGridNumberIntroHtml = function (row, dayGrid) { + var dateEnv = _this.dateEnv; + var weekStart = dayGrid.props.cells[row][0].date; + if (_this.colWeekNumbersVisible) { + return '' + + '' + + core.buildGotoAnchorHtml(// aside from link, important for matchCellWidths + _this, { date: weekStart, type: 'week', forceOff: dayGrid.colCnt === 1 }, dateEnv.format(weekStart, WEEK_NUM_FORMAT$1) // inner HTML + ) + + ''; + } + return ''; + }; + // Generates the HTML that goes before the day bg cells for each day-row + _this.renderDayGridBgIntroHtml = function () { + var theme = _this.theme; + if (_this.colWeekNumbersVisible) { + return ''; + } + return ''; + }; + // Generates the HTML that goes before every other type of row generated by DayGrid. + // Affects mirror-skeleton and highlight-skeleton rows. + _this.renderDayGridIntroHtml = function () { + if (_this.colWeekNumbersVisible) { + return ''; + } + return ''; + }; + _this.el.classList.add('fc-dayGrid-view'); + _this.el.innerHTML = _this.renderSkeletonHtml(); + _this.scroller = new core.ScrollComponent('hidden', // overflow x + 'auto' // overflow y + ); + var dayGridContainerEl = _this.scroller.el; + _this.el.querySelector('.fc-body > tr > td').appendChild(dayGridContainerEl); + dayGridContainerEl.classList.add('fc-day-grid-container'); + var dayGridEl = core.createElement('div', { className: 'fc-day-grid' }); + dayGridContainerEl.appendChild(dayGridEl); + var cellWeekNumbersVisible; + if (_this.opt('weekNumbers')) { + if (_this.opt('weekNumbersWithinDays')) { + cellWeekNumbersVisible = true; + _this.colWeekNumbersVisible = false; + } + else { + cellWeekNumbersVisible = false; + _this.colWeekNumbersVisible = true; + } + } + else { + _this.colWeekNumbersVisible = false; + cellWeekNumbersVisible = false; + } + _this.dayGrid = new DayGrid(_this.context, dayGridEl, { + renderNumberIntroHtml: _this.renderDayGridNumberIntroHtml, + renderBgIntroHtml: _this.renderDayGridBgIntroHtml, + renderIntroHtml: _this.renderDayGridIntroHtml, + colWeekNumbersVisible: _this.colWeekNumbersVisible, + cellWeekNumbersVisible: cellWeekNumbersVisible + }); + return _this; + } + DayGridView.prototype.destroy = function () { + _super.prototype.destroy.call(this); + this.dayGrid.destroy(); + this.scroller.destroy(); + }; + // Builds the HTML skeleton for the view. + // The day-grid component will render inside of a container defined by this HTML. + DayGridView.prototype.renderSkeletonHtml = function () { + var theme = this.theme; + return '' + + '' + + (this.opt('columnHeader') ? + '' + + '' + + '' + + '' + + '' : + '') + + '' + + '' + + '' + + '' + + '' + + '
 
'; + }; + // Generates an HTML attribute string for setting the width of the week number column, if it is known + DayGridView.prototype.weekNumberStyleAttr = function () { + if (this.weekNumberWidth != null) { + return 'style="width:' + this.weekNumberWidth + 'px"'; + } + return ''; + }; + // Determines whether each row should have a constant height + DayGridView.prototype.hasRigidRows = function () { + var eventLimit = this.opt('eventLimit'); + return eventLimit && typeof eventLimit !== 'number'; + }; + /* Dimensions + ------------------------------------------------------------------------------------------------------------------*/ + DayGridView.prototype.updateSize = function (isResize, viewHeight, isAuto) { + _super.prototype.updateSize.call(this, isResize, viewHeight, isAuto); // will call updateBaseSize. important that executes first + this.dayGrid.updateSize(isResize); + }; + // Refreshes the horizontal dimensions of the view + DayGridView.prototype.updateBaseSize = function (isResize, viewHeight, isAuto) { + var dayGrid = this.dayGrid; + var eventLimit = this.opt('eventLimit'); + var headRowEl = this.header ? this.header.el : null; // HACK + var scrollerHeight; + var scrollbarWidths; + // hack to give the view some height prior to dayGrid's columns being rendered + // TODO: separate setting height from scroller VS dayGrid. + if (!dayGrid.rowEls) { + if (!isAuto) { + scrollerHeight = this.computeScrollerHeight(viewHeight); + this.scroller.setHeight(scrollerHeight); + } + return; + } + if (this.colWeekNumbersVisible) { + // Make sure all week number cells running down the side have the same width. + this.weekNumberWidth = core.matchCellWidths(core.findElements(this.el, '.fc-week-number')); + } + // reset all heights to be natural + this.scroller.clear(); + if (headRowEl) { + core.uncompensateScroll(headRowEl); + } + dayGrid.removeSegPopover(); // kill the "more" popover if displayed + // is the event limit a constant level number? + if (eventLimit && typeof eventLimit === 'number') { + dayGrid.limitRows(eventLimit); // limit the levels first so the height can redistribute after + } + // distribute the height to the rows + // (viewHeight is a "recommended" value if isAuto) + scrollerHeight = this.computeScrollerHeight(viewHeight); + this.setGridHeight(scrollerHeight, isAuto); + // is the event limit dynamically calculated? + if (eventLimit && typeof eventLimit !== 'number') { + dayGrid.limitRows(eventLimit); // limit the levels after the grid's row heights have been set + } + if (!isAuto) { // should we force dimensions of the scroll container? + this.scroller.setHeight(scrollerHeight); + scrollbarWidths = this.scroller.getScrollbarWidths(); + if (scrollbarWidths.left || scrollbarWidths.right) { // using scrollbars? + if (headRowEl) { + core.compensateScroll(headRowEl, scrollbarWidths); + } + // doing the scrollbar compensation might have created text overflow which created more height. redo + scrollerHeight = this.computeScrollerHeight(viewHeight); + this.scroller.setHeight(scrollerHeight); + } + // guarantees the same scrollbar widths + this.scroller.lockOverflow(scrollbarWidths); + } + }; + // given a desired total height of the view, returns what the height of the scroller should be + DayGridView.prototype.computeScrollerHeight = function (viewHeight) { + return viewHeight - + core.subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller + }; + // Sets the height of just the DayGrid component in this view + DayGridView.prototype.setGridHeight = function (height, isAuto) { + if (this.opt('monthMode')) { + // if auto, make the height of each row the height that it would be if there were 6 weeks + if (isAuto) { + height *= this.dayGrid.rowCnt / 6; + } + core.distributeHeight(this.dayGrid.rowEls, height, !isAuto); // if auto, don't compensate for height-hogging rows + } + else { + if (isAuto) { + core.undistributeHeight(this.dayGrid.rowEls); // let the rows be their natural height with no expanding + } + else { + core.distributeHeight(this.dayGrid.rowEls, height, true); // true = compensate for height-hogging rows + } + } + }; + /* Scroll + ------------------------------------------------------------------------------------------------------------------*/ + DayGridView.prototype.computeDateScroll = function (duration) { + return { top: 0 }; + }; + DayGridView.prototype.queryDateScroll = function () { + return { top: this.scroller.getScrollTop() }; + }; + DayGridView.prototype.applyDateScroll = function (scroll) { + if (scroll.top !== undefined) { + this.scroller.setScrollTop(scroll.top); + } + }; + return DayGridView; + }(core.View)); + DayGridView.prototype.dateProfileGeneratorClass = DayGridDateProfileGenerator; + + var SimpleDayGrid = /** @class */ (function (_super) { + __extends(SimpleDayGrid, _super); + function SimpleDayGrid(context, dayGrid) { + var _this = _super.call(this, context, dayGrid.el) || this; + _this.slicer = new DayGridSlicer(); + _this.dayGrid = dayGrid; + context.calendar.registerInteractiveComponent(_this, { el: _this.dayGrid.el }); + return _this; + } + SimpleDayGrid.prototype.destroy = function () { + _super.prototype.destroy.call(this); + this.calendar.unregisterInteractiveComponent(this); + }; + SimpleDayGrid.prototype.render = function (props) { + var dayGrid = this.dayGrid; + var dateProfile = props.dateProfile, dayTable = props.dayTable; + dayGrid.receiveProps(__assign({}, this.slicer.sliceProps(props, dateProfile, props.nextDayThreshold, dayGrid, dayTable), { dateProfile: dateProfile, cells: dayTable.cells, isRigid: props.isRigid })); + }; + SimpleDayGrid.prototype.buildPositionCaches = function () { + this.dayGrid.buildPositionCaches(); + }; + SimpleDayGrid.prototype.queryHit = function (positionLeft, positionTop) { + var rawHit = this.dayGrid.positionToHit(positionLeft, positionTop); + if (rawHit) { + return { + component: this.dayGrid, + dateSpan: rawHit.dateSpan, + dayEl: rawHit.dayEl, + rect: { + left: rawHit.relativeRect.left, + right: rawHit.relativeRect.right, + top: rawHit.relativeRect.top, + bottom: rawHit.relativeRect.bottom + }, + layer: 0 + }; + } + }; + return SimpleDayGrid; + }(core.DateComponent)); + var DayGridSlicer = /** @class */ (function (_super) { + __extends(DayGridSlicer, _super); + function DayGridSlicer() { + return _super !== null && _super.apply(this, arguments) || this; + } + DayGridSlicer.prototype.sliceRange = function (dateRange, dayTable) { + return dayTable.sliceRange(dateRange); + }; + return DayGridSlicer; + }(core.Slicer)); + + var DayGridView$1 = /** @class */ (function (_super) { + __extends(DayGridView, _super); + function DayGridView(_context, viewSpec, dateProfileGenerator, parentEl) { + var _this = _super.call(this, _context, viewSpec, dateProfileGenerator, parentEl) || this; + _this.buildDayTable = core.memoize(buildDayTable); + if (_this.opt('columnHeader')) { + _this.header = new core.DayHeader(_this.context, _this.el.querySelector('.fc-head-container')); + } + _this.simpleDayGrid = new SimpleDayGrid(_this.context, _this.dayGrid); + return _this; + } + DayGridView.prototype.destroy = function () { + _super.prototype.destroy.call(this); + if (this.header) { + this.header.destroy(); + } + this.simpleDayGrid.destroy(); + }; + DayGridView.prototype.render = function (props) { + _super.prototype.render.call(this, props); + var dateProfile = this.props.dateProfile; + var dayTable = this.dayTable = + this.buildDayTable(dateProfile, this.dateProfileGenerator); + if (this.header) { + this.header.receiveProps({ + dateProfile: dateProfile, + dates: dayTable.headerDates, + datesRepDistinctDays: dayTable.rowCnt === 1, + renderIntroHtml: this.renderHeadIntroHtml + }); + } + this.simpleDayGrid.receiveProps({ + dateProfile: dateProfile, + dayTable: dayTable, + businessHours: props.businessHours, + dateSelection: props.dateSelection, + eventStore: props.eventStore, + eventUiBases: props.eventUiBases, + eventSelection: props.eventSelection, + eventDrag: props.eventDrag, + eventResize: props.eventResize, + isRigid: this.hasRigidRows(), + nextDayThreshold: this.nextDayThreshold + }); + }; + return DayGridView; + }(DayGridView)); + function buildDayTable(dateProfile, dateProfileGenerator) { + var daySeries = new core.DaySeries(dateProfile.renderRange, dateProfileGenerator); + return new core.DayTable(daySeries, /year|month|week/.test(dateProfile.currentRangeUnit)); + } + + var main = core.createPlugin({ + defaultView: 'dayGridMonth', + views: { + dayGrid: DayGridView$1, + dayGridDay: { + type: 'dayGrid', + duration: { days: 1 } + }, + dayGridWeek: { + type: 'dayGrid', + duration: { weeks: 1 } + }, + dayGridMonth: { + type: 'dayGrid', + duration: { months: 1 }, + monthMode: true, + fixedWeekCount: true + } + } + }); + + exports.AbstractDayGridView = DayGridView; + exports.DayBgRow = DayBgRow; + exports.DayGrid = DayGrid; + exports.DayGridSlicer = DayGridSlicer; + exports.DayGridView = DayGridView$1; + exports.SimpleDayGrid = SimpleDayGrid; + exports.buildBasicDayTable = buildDayTable; + exports.default = main; + + Object.defineProperty(exports, '__esModule', { value: true }); + +})); diff --git a/static/assets/libs/fullcalendar/daygrid/main.min.css b/static/assets/libs/fullcalendar/daygrid/main.min.css new file mode 100644 index 0000000..55a5724 --- /dev/null +++ b/static/assets/libs/fullcalendar/daygrid/main.min.css @@ -0,0 +1 @@ +.fc-dayGridDay-view .fc-content-skeleton,.fc-dayGridWeek-view .fc-content-skeleton{padding-bottom:1em}.fc-dayGrid-view .fc-body .fc-row{min-height:4em}.fc-row.fc-rigid{overflow:hidden}.fc-row.fc-rigid .fc-content-skeleton{position:absolute;top:0;left:0;right:0}.fc-day-top.fc-other-month{opacity:.3}.fc-dayGrid-view .fc-day-number,.fc-dayGrid-view .fc-week-number{padding:2px}.fc-dayGrid-view th.fc-day-number,.fc-dayGrid-view th.fc-week-number{padding:0 2px}.fc-ltr .fc-dayGrid-view .fc-day-top .fc-day-number{float:right}.fc-rtl .fc-dayGrid-view .fc-day-top .fc-day-number{float:left}.fc-ltr .fc-dayGrid-view .fc-day-top .fc-week-number{float:left;border-radius:0 0 3px}.fc-rtl .fc-dayGrid-view .fc-day-top .fc-week-number{float:right;border-radius:0 0 0 3px}.fc-dayGrid-view .fc-day-top .fc-week-number{min-width:1.5em;text-align:center;background-color:#f2f2f2;color:grey}.fc-dayGrid-view td.fc-week-number{text-align:center}.fc-dayGrid-view td.fc-week-number>*{display:inline-block;min-width:1.25em} \ No newline at end of file diff --git a/static/assets/libs/fullcalendar/daygrid/main.min.js b/static/assets/libs/fullcalendar/daygrid/main.min.js new file mode 100644 index 0000000..2f4c0d4 --- /dev/null +++ b/static/assets/libs/fullcalendar/daygrid/main.min.js @@ -0,0 +1,6 @@ +/*! +FullCalendar Day Grid Plugin v4.3.0 +Docs & License: https://fullcalendar.io/ +(c) 2019 Adam Shaw +*/ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@fullcalendar/core")):"function"==typeof define&&define.amd?define(["exports","@fullcalendar/core"],t):t((e=e||self).FullCalendarDayGrid={},e.FullCalendar)}(this,function(e,t){"use strict";var r=function(e,t){return(r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])})(e,t)};function n(e,t){function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var i=function(){return(i=Object.assign||function(e){for(var t,r=1,n=arguments.length;r'+t.htmlEscape(n)+""),i=''+(t.htmlEscape(d.title||"")||" ")+"",'
'+("rtl"===l.dir?i+" "+y:y+" "+i)+"
"+(u?'
':"")+(f?'
':"")+"
"},r.prototype.computeEventTimeFormat=function(){return{hour:"numeric",minute:"2-digit",omitZeroMinute:!0,meridiem:"narrow"}},r.prototype.computeDisplayEventEnd=function(){return!1},r}(t.FgEventRenderer),a=function(e){function r(t){var r=e.call(this,t.context)||this;return r.dayGrid=t,r}return n(r,e),r.prototype.attachSegs=function(e,t){var r=this.rowStructs=this.renderSegRows(e);this.dayGrid.rowEls.forEach(function(e,t){e.querySelector(".fc-content-skeleton > table").appendChild(r[t].tbodyEl)}),t||this.dayGrid.removeSegPopover()},r.prototype.detachSegs=function(){for(var e,r=this.rowStructs||[];e=r.pop();)t.removeElement(e.tbodyEl);this.rowStructs=null},r.prototype.renderSegRows=function(e){var t,r,n=[];for(t=this.groupSegRows(e),r=0;r=e.firstCol)return!0;return!1}function c(e,t){return e.leftCol-t.leftCol}var h=function(e){function r(){return null!==e&&e.apply(this,arguments)||this}return n(r,e),r.prototype.attachSegs=function(e,r){var n=r.sourceSeg,i=this.rowStructs=this.renderSegRows(e);this.dayGrid.rowEls.forEach(function(e,r){var o,s,l=t.htmlToElement('
');n&&n.row===r?o=n.el:(o=e.querySelector(".fc-content-skeleton tbody"))||(o=e.querySelector(".fc-content-skeleton table")),s=o.getBoundingClientRect().top-e.getBoundingClientRect().top,l.style.top=s+"px",l.querySelector("table").appendChild(i[r].tbodyEl),e.appendChild(l)})},r}(a),p=function(e){function r(t){var r=e.call(this,t.context)||this;return r.fillSegTag="td",r.dayGrid=t,r}return n(r,e),r.prototype.renderSegs=function(t,r){"bgEvent"===t&&(r=r.filter(function(e){return e.eventRange.def.allDay})),e.prototype.renderSegs.call(this,t,r)},r.prototype.attachSegs=function(e,t){var r,n,i,o=[];for(r=0;r
')).getElementsByTagName("tr")[0],d>0&&t.appendToElement(o,new Array(d+1).join('')),r.el.colSpan=c-d,o.appendChild(r.el),c'));var h=s.renderProps.renderIntroHtml();return h&&(s.isRtl?t.appendToElement(o,h):t.prependToElement(o,h)),i},r}(t.FillRenderer),u=function(e){function r(r,n){var i=e.call(this,r,n)||this,o=i.eventRenderer=new f(i),s=i.renderFrame=t.memoizeRendering(i._renderFrame);return i.renderFgEvents=t.memoizeRendering(o.renderSegs.bind(o),o.unrender.bind(o),[s]),i.renderEventSelection=t.memoizeRendering(o.selectByInstanceId.bind(o),o.unselectByInstanceId.bind(o),[i.renderFgEvents]),i.renderEventDrag=t.memoizeRendering(o.hideByHash.bind(o),o.showByHash.bind(o),[s]),i.renderEventResize=t.memoizeRendering(o.hideByHash.bind(o),o.showByHash.bind(o),[s]),r.calendar.registerInteractiveComponent(i,{el:i.el,useEventCenter:!1}),i}return n(r,e),r.prototype.render=function(e){this.renderFrame(e.date),this.renderFgEvents(e.fgSegs),this.renderEventSelection(e.eventSelection),this.renderEventDrag(e.eventDragInstances),this.renderEventResize(e.eventResizeInstances)},r.prototype.destroy=function(){e.prototype.destroy.call(this),this.renderFrame.unrender(),this.calendar.unregisterInteractiveComponent(this)},r.prototype._renderFrame=function(e){var r=this.theme,n=this.dateEnv.format(e,t.createFormatter(this.opt("dayPopoverFormat")));this.el.innerHTML='
'+t.htmlEscape(n)+'
',this.segContainerEl=this.el.querySelector(".fc-event-container")},r.prototype.queryHit=function(e,r,n,i){var o=this.props.date;if(e'),"rtl"===this.context.options.dir&&t.reverse(),""+t.join("")+""},e}();function m(e,r,n,i){var o=n.dateEnv,s=n.theme,l=t.rangeContainsMarker(r.activeRange,e),a=t.getDayClasses(e,r,n);return a.unshift("fc-day",s.getClass("widgetContent")),'"}var y=t.createFormatter({day:"numeric"}),v=t.createFormatter({week:"numeric"}),b=function(e){function r(r,n,i){var o=e.call(this,r,n)||this;o.bottomCoordPadding=0,o.isCellSizesDirty=!1;var s=o.eventRenderer=new a(o),l=o.fillRenderer=new p(o);o.mirrorRenderer=new h(o);var d=o.renderCells=t.memoizeRendering(o._renderCells,o._unrenderCells);return o.renderBusinessHours=t.memoizeRendering(l.renderSegs.bind(l,"businessHours"),l.unrender.bind(l,"businessHours"),[d]),o.renderDateSelection=t.memoizeRendering(l.renderSegs.bind(l,"highlight"),l.unrender.bind(l,"highlight"),[d]),o.renderBgEvents=t.memoizeRendering(l.renderSegs.bind(l,"bgEvent"),l.unrender.bind(l,"bgEvent"),[d]),o.renderFgEvents=t.memoizeRendering(s.renderSegs.bind(s),s.unrender.bind(s),[d]),o.renderEventSelection=t.memoizeRendering(s.selectByInstanceId.bind(s),s.unselectByInstanceId.bind(s),[o.renderFgEvents]),o.renderEventDrag=t.memoizeRendering(o._renderEventDrag,o._unrenderEventDrag,[d]),o.renderEventResize=t.memoizeRendering(o._renderEventResize,o._unrenderEventResize,[d]),o.renderProps=i,o}return n(r,e),r.prototype.render=function(e){var t=e.cells;this.rowCnt=t.length,this.colCnt=t[0].length,this.renderCells(t,e.isRigid),this.renderBusinessHours(e.businessHourSegs),this.renderDateSelection(e.dateSelectionSegs),this.renderBgEvents(e.bgEventSegs),this.renderFgEvents(e.fgEventSegs),this.renderEventSelection(e.eventSelection),this.renderEventDrag(e.eventDrag),this.renderEventResize(e.eventResize),this.segPopoverTile&&this.updateSegPopoverTile()},r.prototype.destroy=function(){e.prototype.destroy.call(this),this.renderCells.unrender()},r.prototype.getCellRange=function(e,r){var n=this.props.cells[e][r].date;return{start:n,end:t.addDays(n,1)}},r.prototype.updateSegPopoverTile=function(e,t){var r=this.props;this.segPopoverTile.receiveProps({date:e||this.segPopoverTile.props.date,fgSegs:t||this.segPopoverTile.props.fgSegs,eventSelection:r.eventSelection,eventDragInstances:r.eventDrag?r.eventDrag.affectedInstances:null,eventResizeInstances:r.eventResize?r.eventResize.affectedInstances:null})},r.prototype._renderCells=function(e,r){var n,i,o=this.view,s=this.dateEnv,l=this.rowCnt,a=this.colCnt,d="";for(n=0;n
'+i.renderHtml({cells:this.props.cells[e],dateProfile:this.props.dateProfile,renderIntroHtml:this.renderProps.renderBgIntroHtml})+'
'+(this.getIsNumbersVisible()?""+this.renderNumberTrHtml(e)+"":"")+"
"},r.prototype.getIsNumbersVisible=function(){return this.getIsDayNumbersVisible()||this.renderProps.cellWeekNumbersVisible||this.renderProps.colWeekNumbersVisible},r.prototype.getIsDayNumbersVisible=function(){return this.rowCnt>1},r.prototype.renderNumberTrHtml=function(e){var t=this.renderProps.renderNumberIntroHtml(e,this);return""+(this.isRtl?"":t)+this.renderNumberCellsHtml(e)+(this.isRtl?t:"")+""},r.prototype.renderNumberCellsHtml=function(e){var t,r,n=[];for(t=0;t",this.renderProps.cellWeekNumbersVisible&&e.getUTCDay()===n&&(s+=t.buildGotoAnchorHtml(i,{date:e,type:"week"},{class:"fc-week-number"},o.format(e,v))),a&&(s+=t.buildGotoAnchorHtml(i,e,{class:"fc-day-number"},o.format(e,y))),s+=""):""},r.prototype.updateSize=function(e){var t=this.fillRenderer,r=this.eventRenderer,n=this.mirrorRenderer;(e||this.isCellSizesDirty||this.view.calendar.isEventsUpdated)&&(this.buildPositionCaches(),this.isCellSizesDirty=!1),t.computeSizes(e),r.computeSizes(e),n.computeSizes(e),t.assignSizes(e),r.assignSizes(e),n.assignSizes(e)},r.prototype.buildPositionCaches=function(){this.buildColPositions(),this.buildRowPositions()},r.prototype.buildColPositions=function(){this.colPositions.build()},r.prototype.buildRowPositions=function(){this.rowPositions.build(),this.rowPositions.bottoms[this.rowCnt-1]+=this.bottomCoordPadding},r.prototype.positionToHit=function(e,t){var r=this.colPositions,n=this.rowPositions,i=r.leftToIndex(e),o=n.topToIndex(t);if(null!=o&&null!=i)return{row:o,col:i,dateSpan:{range:this.getCellRange(o,i),allDay:!0},dayEl:this.getCellEl(o,i),relativeRect:{left:r.lefts[i],right:r.rights[i],top:n.tops[o],bottom:n.bottoms[o]}}},r.prototype.getCellEl=function(e,t){return this.cellEls[e*this.colCnt+t]},r.prototype._renderEventDrag=function(e){e&&(this.eventRenderer.hideByHash(e.affectedInstances),this.fillRenderer.renderSegs("highlight",e.segs))},r.prototype._unrenderEventDrag=function(e){e&&(this.eventRenderer.showByHash(e.affectedInstances),this.fillRenderer.unrender("highlight"))},r.prototype._renderEventResize=function(e){e&&(this.eventRenderer.hideByHash(e.affectedInstances),this.fillRenderer.renderSegs("highlight",e.segs),this.mirrorRenderer.renderSegs(e.segs,{isResizing:!0,sourceSeg:e.sourceSeg}))},r.prototype._unrenderEventResize=function(e){e&&(this.eventRenderer.showByHash(e.affectedInstances),this.fillRenderer.unrender("highlight"),this.mirrorRenderer.unrender(e.segs,{isResizing:!0,sourceSeg:e.sourceSeg}))},r.prototype.removeSegPopover=function(){this.segPopover&&this.segPopover.hide()},r.prototype.limitRows=function(e){var t,r,n=this.eventRenderer.rowStructs||[];for(t=0;ti)return r;return!1},r.prototype.limitRow=function(e,r){var n,i,o,s,l,a,d,c,h,p,u,f,g,m,y,v=this,b=this.colCnt,w=this.isRtl,S=this.eventRenderer.rowStructs[e],C=[],E=0,R=function(n){for(;E"+t.htmlEscape(s.opt("weekLabel"))+"":""},s.renderDayGridNumberIntroHtml=function(e,r){var n=s.dateEnv,i=r.props.cells[e][0].date;return s.colWeekNumbersVisible?'"+t.buildGotoAnchorHtml(s,{date:i,type:"week",forceOff:1===r.colCnt},n.format(i,w))+"":""},s.renderDayGridBgIntroHtml=function(){var e=s.theme;return s.colWeekNumbersVisible?'":""},s.renderDayGridIntroHtml=function(){return s.colWeekNumbersVisible?'":""},s.el.classList.add("fc-dayGrid-view"),s.el.innerHTML=s.renderSkeletonHtml(),s.scroller=new t.ScrollComponent("hidden","auto");var l=s.scroller.el;s.el.querySelector(".fc-body > tr > td").appendChild(l),l.classList.add("fc-day-grid-container");var a,d=t.createElement("div",{className:"fc-day-grid"});return l.appendChild(d),s.opt("weekNumbers")?s.opt("weekNumbersWithinDays")?(a=!0,s.colWeekNumbersVisible=!1):(a=!1,s.colWeekNumbersVisible=!0):(s.colWeekNumbersVisible=!1,a=!1),s.dayGrid=new b(s.context,d,{renderNumberIntroHtml:s.renderDayGridNumberIntroHtml,renderBgIntroHtml:s.renderDayGridBgIntroHtml,renderIntroHtml:s.renderDayGridIntroHtml,colWeekNumbersVisible:s.colWeekNumbersVisible,cellWeekNumbersVisible:a}),s}return n(r,e),r.prototype.destroy=function(){e.prototype.destroy.call(this),this.dayGrid.destroy(),this.scroller.destroy()},r.prototype.renderSkeletonHtml=function(){var e=this.theme;return''+(this.opt("columnHeader")?'':"")+'
 
'},r.prototype.weekNumberStyleAttr=function(){return null!=this.weekNumberWidth?'style="width:'+this.weekNumberWidth+'px"':""},r.prototype.hasRigidRows=function(){var e=this.opt("eventLimit");return e&&"number"!=typeof e},r.prototype.updateSize=function(t,r,n){e.prototype.updateSize.call(this,t,r,n),this.dayGrid.updateSize(t)},r.prototype.updateBaseSize=function(e,r,n){var i,o,s=this.dayGrid,l=this.opt("eventLimit"),a=this.header?this.header.el:null;s.rowEls?(this.colWeekNumbersVisible&&(this.weekNumberWidth=t.matchCellWidths(t.findElements(this.el,".fc-week-number"))),this.scroller.clear(),a&&t.uncompensateScroll(a),s.removeSegPopover(),l&&"number"==typeof l&&s.limitRows(l),i=this.computeScrollerHeight(r),this.setGridHeight(i,n),l&&"number"!=typeof l&&s.limitRows(l),n||(this.scroller.setHeight(i),((o=this.scroller.getScrollbarWidths()).left||o.right)&&(a&&t.compensateScroll(a,o),i=this.computeScrollerHeight(r),this.scroller.setHeight(i)),this.scroller.lockOverflow(o))):n||(i=this.computeScrollerHeight(r),this.scroller.setHeight(i))},r.prototype.computeScrollerHeight=function(e){return e-t.subtractInnerElHeight(this.el,this.scroller.el)},r.prototype.setGridHeight=function(e,r){this.opt("monthMode")?(r&&(e*=this.dayGrid.rowCnt/6),t.distributeHeight(this.dayGrid.rowEls,e,!r)):r?t.undistributeHeight(this.dayGrid.rowEls):t.distributeHeight(this.dayGrid.rowEls,e,!0)},r.prototype.computeDateScroll=function(e){return{top:0}},r.prototype.queryDateScroll=function(){return{top:this.scroller.getScrollTop()}},r.prototype.applyDateScroll=function(e){void 0!==e.top&&this.scroller.setScrollTop(e.top)},r}(t.View);S.prototype.dateProfileGeneratorClass=o;var C=function(e){function t(t,r){var n=e.call(this,t,r.el)||this;return n.slicer=new E,n.dayGrid=r,t.calendar.registerInteractiveComponent(n,{el:n.dayGrid.el}),n}return n(t,e),t.prototype.destroy=function(){e.prototype.destroy.call(this),this.calendar.unregisterInteractiveComponent(this)},t.prototype.render=function(e){var t=this.dayGrid,r=e.dateProfile,n=e.dayTable;t.receiveProps(i({},this.slicer.sliceProps(e,r,e.nextDayThreshold,t,n),{dateProfile:r,cells:n.cells,isRigid:e.isRigid}))},t.prototype.buildPositionCaches=function(){this.dayGrid.buildPositionCaches()},t.prototype.queryHit=function(e,t){var r=this.dayGrid.positionToHit(e,t);if(r)return{component:this.dayGrid,dateSpan:r.dateSpan,dayEl:r.dayEl,rect:{left:r.relativeRect.left,right:r.relativeRect.right,top:r.relativeRect.top,bottom:r.relativeRect.bottom},layer:0}},t}(t.DateComponent),E=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return n(t,e),t.prototype.sliceRange=function(e,t){return t.sliceRange(e)},t}(t.Slicer),R=function(e){function r(r,n,i,o){var s=e.call(this,r,n,i,o)||this;return s.buildDayTable=t.memoize(H),s.opt("columnHeader")&&(s.header=new t.DayHeader(s.context,s.el.querySelector(".fc-head-container"))),s.simpleDayGrid=new C(s.context,s.dayGrid),s}return n(r,e),r.prototype.destroy=function(){e.prototype.destroy.call(this),this.header&&this.header.destroy(),this.simpleDayGrid.destroy()},r.prototype.render=function(t){e.prototype.render.call(this,t);var r=this.props.dateProfile,n=this.dayTable=this.buildDayTable(r,this.dateProfileGenerator);this.header&&this.header.receiveProps({dateProfile:r,dates:n.headerDates,datesRepDistinctDays:1===n.rowCnt,renderIntroHtml:this.renderHeadIntroHtml}),this.simpleDayGrid.receiveProps({dateProfile:r,dayTable:n,businessHours:t.businessHours,dateSelection:t.dateSelection,eventStore:t.eventStore,eventUiBases:t.eventUiBases,eventSelection:t.eventSelection,eventDrag:t.eventDrag,eventResize:t.eventResize,isRigid:this.hasRigidRows(),nextDayThreshold:this.nextDayThreshold})},r}(S);function H(e,r){var n=new t.DaySeries(e.renderRange,r);return new t.DayTable(n,/year|month|week/.test(e.currentRangeUnit))}var D=t.createPlugin({defaultView:"dayGridMonth",views:{dayGrid:R,dayGridDay:{type:"dayGrid",duration:{days:1}},dayGridWeek:{type:"dayGrid",duration:{weeks:1}},dayGridMonth:{type:"dayGrid",duration:{months:1},monthMode:!0,fixedWeekCount:!0}}});e.AbstractDayGridView=S,e.DayBgRow=g,e.DayGrid=b,e.DayGridSlicer=E,e.DayGridView=R,e.SimpleDayGrid=C,e.buildBasicDayTable=H,e.default=D,Object.defineProperty(e,"__esModule",{value:!0})}); \ No newline at end of file diff --git a/static/assets/libs/fullcalendar/google-calendar/main.js b/static/assets/libs/fullcalendar/google-calendar/main.js new file mode 100644 index 0000000..fca85d4 --- /dev/null +++ b/static/assets/libs/fullcalendar/google-calendar/main.js @@ -0,0 +1,170 @@ +/*! +FullCalendar Google Calendar Plugin v4.3.0 +Docs & License: https://fullcalendar.io/ +(c) 2019 Adam Shaw +*/ + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core')) : + typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core'], factory) : + (global = global || self, factory(global.FullCalendarGoogleCalendar = {}, global.FullCalendar)); +}(this, function (exports, core) { 'use strict'; + + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use + this file except in compliance with the License. You may obtain a copy of the + License at http://www.apache.org/licenses/LICENSE-2.0 + + THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED + WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, + MERCHANTABLITY OR NON-INFRINGEMENT. + + See the Apache Version 2.0 License for specific language governing permissions + and limitations under the License. + ***************************************************************************** */ + + var __assign = function() { + __assign = Object.assign || function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); + }; + + // TODO: expose somehow + var API_BASE = 'https://www.googleapis.com/calendar/v3/calendars'; + var STANDARD_PROPS = { + url: String, + googleCalendarApiKey: String, + googleCalendarId: String, + data: null + }; + var eventSourceDef = { + parseMeta: function (raw) { + if (typeof raw === 'string') { + raw = { url: raw }; + } + if (typeof raw === 'object') { + var standardProps = core.refineProps(raw, STANDARD_PROPS); + if (!standardProps.googleCalendarId && standardProps.url) { + standardProps.googleCalendarId = parseGoogleCalendarId(standardProps.url); + } + delete standardProps.url; + if (standardProps.googleCalendarId) { + return standardProps; + } + } + return null; + }, + fetch: function (arg, onSuccess, onFailure) { + var calendar = arg.calendar; + var meta = arg.eventSource.meta; + var apiKey = meta.googleCalendarApiKey || calendar.opt('googleCalendarApiKey'); + if (!apiKey) { + onFailure({ + message: 'Specify a googleCalendarApiKey. See http://fullcalendar.io/docs/google_calendar/' + }); + } + else { + var url = buildUrl(meta); + var requestParams_1 = buildRequestParams(arg.range, apiKey, meta.data, calendar.dateEnv); + core.requestJson('GET', url, requestParams_1, function (body, xhr) { + if (body.error) { + onFailure({ + message: 'Google Calendar API: ' + body.error.message, + errors: body.error.errors, + xhr: xhr + }); + } + else { + onSuccess({ + rawEvents: gcalItemsToRawEventDefs(body.items, requestParams_1.timeZone), + xhr: xhr + }); + } + }, function (message, xhr) { + onFailure({ message: message, xhr: xhr }); + }); + } + } + }; + function parseGoogleCalendarId(url) { + var match; + // detect if the ID was specified as a single string. + // will match calendars like "asdf1234@calendar.google.com" in addition to person email calendars. + if (/^[^\/]+@([^\/\.]+\.)*(google|googlemail|gmail)\.com$/.test(url)) { + return url; + } + else if ((match = /^https:\/\/www.googleapis.com\/calendar\/v3\/calendars\/([^\/]*)/.exec(url)) || + (match = /^https?:\/\/www.google.com\/calendar\/feeds\/([^\/]*)/.exec(url))) { + return decodeURIComponent(match[1]); + } + } + function buildUrl(meta) { + return API_BASE + '/' + encodeURIComponent(meta.googleCalendarId) + '/events'; + } + function buildRequestParams(range, apiKey, extraParams, dateEnv) { + var params; + var startStr; + var endStr; + if (dateEnv.canComputeOffset) { + // strings will naturally have offsets, which GCal needs + startStr = dateEnv.formatIso(range.start); + endStr = dateEnv.formatIso(range.end); + } + else { + // when timezone isn't known, we don't know what the UTC offset should be, so ask for +/- 1 day + // from the UTC day-start to guarantee we're getting all the events + // (start/end will be UTC-coerced dates, so toISOString is okay) + startStr = core.addDays(range.start, -1).toISOString(); + endStr = core.addDays(range.end, 1).toISOString(); + } + params = __assign({}, (extraParams || {}), { key: apiKey, timeMin: startStr, timeMax: endStr, singleEvents: true, maxResults: 9999 }); + if (dateEnv.timeZone !== 'local') { + params.timeZone = dateEnv.timeZone; + } + return params; + } + function gcalItemsToRawEventDefs(items, gcalTimezone) { + return items.map(function (item) { + return gcalItemToRawEventDef(item, gcalTimezone); + }); + } + function gcalItemToRawEventDef(item, gcalTimezone) { + var url = item.htmlLink || null; + // make the URLs for each event show times in the correct timezone + if (url && gcalTimezone) { + url = injectQsComponent(url, 'ctz=' + gcalTimezone); + } + return { + id: item.id, + title: item.summary, + start: item.start.dateTime || item.start.date, + end: item.end.dateTime || item.end.date, + url: url, + location: item.location, + description: item.description + }; + } + // Injects a string like "arg=value" into the querystring of a URL + // TODO: move to a general util file? + function injectQsComponent(url, component) { + // inject it after the querystring but before the fragment + return url.replace(/(\?.*?)?(#|$)/, function (whole, qs, hash) { + return (qs ? qs + '&' : '?') + component + hash; + }); + } + var main = core.createPlugin({ + eventSourceDefs: [eventSourceDef] + }); + + exports.default = main; + + Object.defineProperty(exports, '__esModule', { value: true }); + +})); diff --git a/static/assets/libs/fullcalendar/google-calendar/main.min.js b/static/assets/libs/fullcalendar/google-calendar/main.min.js new file mode 100644 index 0000000..e762612 --- /dev/null +++ b/static/assets/libs/fullcalendar/google-calendar/main.min.js @@ -0,0 +1,6 @@ +/*! +FullCalendar Google Calendar Plugin v4.3.0 +Docs & License: https://fullcalendar.io/ +(c) 2019 Adam Shaw +*/ +!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("@fullcalendar/core")):"function"==typeof define&&define.amd?define(["exports","@fullcalendar/core"],r):r((e=e||self).FullCalendarGoogleCalendar={},e.FullCalendar)}(this,function(e,r){"use strict";var t=function(){return(t=Object.assign||function(e){for(var r,t=1,n=arguments.length;t 0) { + this.everMovedDown = true; + } + if (xDelta < 0) { + this.everMovedLeft = true; + } + else if (xDelta > 0) { + this.everMovedRight = true; + } + this.pointerScreenX = pointerScreenX; + this.pointerScreenY = pointerScreenY; + if (!this.isAnimating) { + this.isAnimating = true; + this.requestAnimation(getTime()); + } + } + }; + AutoScroller.prototype.stop = function () { + if (this.isEnabled) { + this.isAnimating = false; // will stop animation + for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) { + var scrollCache = _a[_i]; + scrollCache.destroy(); + } + this.scrollCaches = null; + } + }; + AutoScroller.prototype.requestAnimation = function (now) { + this.msSinceRequest = now; + requestAnimationFrame(this.animate); + }; + AutoScroller.prototype.handleSide = function (edge, seconds) { + var scrollCache = edge.scrollCache; + var edgeThreshold = this.edgeThreshold; + var invDistance = edgeThreshold - edge.distance; + var velocity = // the closer to the edge, the faster we scroll + (invDistance * invDistance) / (edgeThreshold * edgeThreshold) * // quadratic + this.maxVelocity * seconds; + var sign = 1; + switch (edge.name) { + case 'left': + sign = -1; + // falls through + case 'right': + scrollCache.setScrollLeft(scrollCache.getScrollLeft() + velocity * sign); + break; + case 'top': + sign = -1; + // falls through + case 'bottom': + scrollCache.setScrollTop(scrollCache.getScrollTop() + velocity * sign); + break; + } + }; + // left/top are relative to document topleft + AutoScroller.prototype.computeBestEdge = function (left, top) { + var edgeThreshold = this.edgeThreshold; + var bestSide = null; + for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) { + var scrollCache = _a[_i]; + var rect = scrollCache.clientRect; + var leftDist = left - rect.left; + var rightDist = rect.right - left; + var topDist = top - rect.top; + var bottomDist = rect.bottom - top; + // completely within the rect? + if (leftDist >= 0 && rightDist >= 0 && topDist >= 0 && bottomDist >= 0) { + if (topDist <= edgeThreshold && this.everMovedUp && scrollCache.canScrollUp() && + (!bestSide || bestSide.distance > topDist)) { + bestSide = { scrollCache: scrollCache, name: 'top', distance: topDist }; + } + if (bottomDist <= edgeThreshold && this.everMovedDown && scrollCache.canScrollDown() && + (!bestSide || bestSide.distance > bottomDist)) { + bestSide = { scrollCache: scrollCache, name: 'bottom', distance: bottomDist }; + } + if (leftDist <= edgeThreshold && this.everMovedLeft && scrollCache.canScrollLeft() && + (!bestSide || bestSide.distance > leftDist)) { + bestSide = { scrollCache: scrollCache, name: 'left', distance: leftDist }; + } + if (rightDist <= edgeThreshold && this.everMovedRight && scrollCache.canScrollRight() && + (!bestSide || bestSide.distance > rightDist)) { + bestSide = { scrollCache: scrollCache, name: 'right', distance: rightDist }; + } + } + } + return bestSide; + }; + AutoScroller.prototype.buildCaches = function () { + return this.queryScrollEls().map(function (el) { + if (el === window) { + return new WindowScrollGeomCache(false); // false = don't listen to user-generated scrolls + } + else { + return new ElementScrollGeomCache(el, false); // false = don't listen to user-generated scrolls + } + }); + }; + AutoScroller.prototype.queryScrollEls = function () { + var els = []; + for (var _i = 0, _a = this.scrollQuery; _i < _a.length; _i++) { + var query = _a[_i]; + if (typeof query === 'object') { + els.push(query); + } + else { + els.push.apply(els, Array.prototype.slice.call(document.querySelectorAll(query))); + } + } + return els; + }; + return AutoScroller; + }()); + + /* + Monitors dragging on an element. Has a number of high-level features: + - minimum distance required before dragging + - minimum wait time ("delay") before dragging + - a mirror element that follows the pointer + */ + var FeaturefulElementDragging = /** @class */ (function (_super) { + __extends(FeaturefulElementDragging, _super); + function FeaturefulElementDragging(containerEl) { + var _this = _super.call(this, containerEl) || this; + // options that can be directly set by caller + // the caller can also set the PointerDragging's options as well + _this.delay = null; + _this.minDistance = 0; + _this.touchScrollAllowed = true; // prevents drag from starting and blocks scrolling during drag + _this.mirrorNeedsRevert = false; + _this.isInteracting = false; // is the user validly moving the pointer? lasts until pointerup + _this.isDragging = false; // is it INTENTFULLY dragging? lasts until after revert animation + _this.isDelayEnded = false; + _this.isDistanceSurpassed = false; + _this.delayTimeoutId = null; + _this.onPointerDown = function (ev) { + if (!_this.isDragging) { // so new drag doesn't happen while revert animation is going + _this.isInteracting = true; + _this.isDelayEnded = false; + _this.isDistanceSurpassed = false; + core.preventSelection(document.body); + core.preventContextMenu(document.body); + // prevent links from being visited if there's an eventual drag. + // also prevents selection in older browsers (maybe?). + // not necessary for touch, besides, browser would complain about passiveness. + if (!ev.isTouch) { + ev.origEvent.preventDefault(); + } + _this.emitter.trigger('pointerdown', ev); + if (!_this.pointer.shouldIgnoreMove) { + // actions related to initiating dragstart+dragmove+dragend... + _this.mirror.setIsVisible(false); // reset. caller must set-visible + _this.mirror.start(ev.subjectEl, ev.pageX, ev.pageY); // must happen on first pointer down + _this.startDelay(ev); + if (!_this.minDistance) { + _this.handleDistanceSurpassed(ev); + } + } + } + }; + _this.onPointerMove = function (ev) { + if (_this.isInteracting) { // if false, still waiting for previous drag's revert + _this.emitter.trigger('pointermove', ev); + if (!_this.isDistanceSurpassed) { + var minDistance = _this.minDistance; + var distanceSq = void 0; // current distance from the origin, squared + var deltaX = ev.deltaX, deltaY = ev.deltaY; + distanceSq = deltaX * deltaX + deltaY * deltaY; + if (distanceSq >= minDistance * minDistance) { // use pythagorean theorem + _this.handleDistanceSurpassed(ev); + } + } + if (_this.isDragging) { + // a real pointer move? (not one simulated by scrolling) + if (ev.origEvent.type !== 'scroll') { + _this.mirror.handleMove(ev.pageX, ev.pageY); + _this.autoScroller.handleMove(ev.pageX, ev.pageY); + } + _this.emitter.trigger('dragmove', ev); + } + } + }; + _this.onPointerUp = function (ev) { + if (_this.isInteracting) { // if false, still waiting for previous drag's revert + _this.isInteracting = false; + core.allowSelection(document.body); + core.allowContextMenu(document.body); + _this.emitter.trigger('pointerup', ev); // can potentially set mirrorNeedsRevert + if (_this.isDragging) { + _this.autoScroller.stop(); + _this.tryStopDrag(ev); // which will stop the mirror + } + if (_this.delayTimeoutId) { + clearTimeout(_this.delayTimeoutId); + _this.delayTimeoutId = null; + } + } + }; + var pointer = _this.pointer = new PointerDragging(containerEl); + pointer.emitter.on('pointerdown', _this.onPointerDown); + pointer.emitter.on('pointermove', _this.onPointerMove); + pointer.emitter.on('pointerup', _this.onPointerUp); + _this.mirror = new ElementMirror(); + _this.autoScroller = new AutoScroller(); + return _this; + } + FeaturefulElementDragging.prototype.destroy = function () { + this.pointer.destroy(); + }; + FeaturefulElementDragging.prototype.startDelay = function (ev) { + var _this = this; + if (typeof this.delay === 'number') { + this.delayTimeoutId = setTimeout(function () { + _this.delayTimeoutId = null; + _this.handleDelayEnd(ev); + }, this.delay); // not assignable to number! + } + else { + this.handleDelayEnd(ev); + } + }; + FeaturefulElementDragging.prototype.handleDelayEnd = function (ev) { + this.isDelayEnded = true; + this.tryStartDrag(ev); + }; + FeaturefulElementDragging.prototype.handleDistanceSurpassed = function (ev) { + this.isDistanceSurpassed = true; + this.tryStartDrag(ev); + }; + FeaturefulElementDragging.prototype.tryStartDrag = function (ev) { + if (this.isDelayEnded && this.isDistanceSurpassed) { + if (!this.pointer.wasTouchScroll || this.touchScrollAllowed) { + this.isDragging = true; + this.mirrorNeedsRevert = false; + this.autoScroller.start(ev.pageX, ev.pageY); + this.emitter.trigger('dragstart', ev); + if (this.touchScrollAllowed === false) { + this.pointer.cancelTouchScroll(); + } + } + } + }; + FeaturefulElementDragging.prototype.tryStopDrag = function (ev) { + // .stop() is ALWAYS asynchronous, which we NEED because we want all pointerup events + // that come from the document to fire beforehand. much more convenient this way. + this.mirror.stop(this.mirrorNeedsRevert, this.stopDrag.bind(this, ev) // bound with args + ); + }; + FeaturefulElementDragging.prototype.stopDrag = function (ev) { + this.isDragging = false; + this.emitter.trigger('dragend', ev); + }; + // fill in the implementations... + FeaturefulElementDragging.prototype.setIgnoreMove = function (bool) { + this.pointer.shouldIgnoreMove = bool; + }; + FeaturefulElementDragging.prototype.setMirrorIsVisible = function (bool) { + this.mirror.setIsVisible(bool); + }; + FeaturefulElementDragging.prototype.setMirrorNeedsRevert = function (bool) { + this.mirrorNeedsRevert = bool; + }; + FeaturefulElementDragging.prototype.setAutoScrollEnabled = function (bool) { + this.autoScroller.isEnabled = bool; + }; + return FeaturefulElementDragging; + }(core.ElementDragging)); + + /* + When this class is instantiated, it records the offset of an element (relative to the document topleft), + and continues to monitor scrolling, updating the cached coordinates if it needs to. + Does not access the DOM after instantiation, so highly performant. + + Also keeps track of all scrolling/overflow:hidden containers that are parents of the given element + and an determine if a given point is inside the combined clipping rectangle. + */ + var OffsetTracker = /** @class */ (function () { + function OffsetTracker(el) { + this.origRect = core.computeRect(el); + // will work fine for divs that have overflow:hidden + this.scrollCaches = core.getClippingParents(el).map(function (el) { + return new ElementScrollGeomCache(el, true); // listen=true + }); + } + OffsetTracker.prototype.destroy = function () { + for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) { + var scrollCache = _a[_i]; + scrollCache.destroy(); + } + }; + OffsetTracker.prototype.computeLeft = function () { + var left = this.origRect.left; + for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) { + var scrollCache = _a[_i]; + left += scrollCache.origScrollLeft - scrollCache.getScrollLeft(); + } + return left; + }; + OffsetTracker.prototype.computeTop = function () { + var top = this.origRect.top; + for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) { + var scrollCache = _a[_i]; + top += scrollCache.origScrollTop - scrollCache.getScrollTop(); + } + return top; + }; + OffsetTracker.prototype.isWithinClipping = function (pageX, pageY) { + var point = { left: pageX, top: pageY }; + for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) { + var scrollCache = _a[_i]; + if (!isIgnoredClipping(scrollCache.getEventTarget()) && + !core.pointInsideRect(point, scrollCache.clientRect)) { + return false; + } + } + return true; + }; + return OffsetTracker; + }()); + // certain clipping containers should never constrain interactions, like and + // https://github.com/fullcalendar/fullcalendar/issues/3615 + function isIgnoredClipping(node) { + var tagName = node.tagName; + return tagName === 'HTML' || tagName === 'BODY'; + } + + /* + Tracks movement over multiple droppable areas (aka "hits") + that exist in one or more DateComponents. + Relies on an existing draggable. + + emits: + - pointerdown + - dragstart + - hitchange - fires initially, even if not over a hit + - pointerup + - (hitchange - again, to null, if ended over a hit) + - dragend + */ + var HitDragging = /** @class */ (function () { + function HitDragging(dragging, droppableStore) { + var _this = this; + // options that can be set by caller + this.useSubjectCenter = false; + this.requireInitial = true; // if doesn't start out on a hit, won't emit any events + this.initialHit = null; + this.movingHit = null; + this.finalHit = null; // won't ever be populated if shouldIgnoreMove + this.handlePointerDown = function (ev) { + var dragging = _this.dragging; + _this.initialHit = null; + _this.movingHit = null; + _this.finalHit = null; + _this.prepareHits(); + _this.processFirstCoord(ev); + if (_this.initialHit || !_this.requireInitial) { + dragging.setIgnoreMove(false); + _this.emitter.trigger('pointerdown', ev); // TODO: fire this before computing processFirstCoord, so listeners can cancel. this gets fired by almost every handler :( + } + else { + dragging.setIgnoreMove(true); + } + }; + this.handleDragStart = function (ev) { + _this.emitter.trigger('dragstart', ev); + _this.handleMove(ev, true); // force = fire even if initially null + }; + this.handleDragMove = function (ev) { + _this.emitter.trigger('dragmove', ev); + _this.handleMove(ev); + }; + this.handlePointerUp = function (ev) { + _this.releaseHits(); + _this.emitter.trigger('pointerup', ev); + }; + this.handleDragEnd = function (ev) { + if (_this.movingHit) { + _this.emitter.trigger('hitupdate', null, true, ev); + } + _this.finalHit = _this.movingHit; + _this.movingHit = null; + _this.emitter.trigger('dragend', ev); + }; + this.droppableStore = droppableStore; + dragging.emitter.on('pointerdown', this.handlePointerDown); + dragging.emitter.on('dragstart', this.handleDragStart); + dragging.emitter.on('dragmove', this.handleDragMove); + dragging.emitter.on('pointerup', this.handlePointerUp); + dragging.emitter.on('dragend', this.handleDragEnd); + this.dragging = dragging; + this.emitter = new core.EmitterMixin(); + } + // sets initialHit + // sets coordAdjust + HitDragging.prototype.processFirstCoord = function (ev) { + var origPoint = { left: ev.pageX, top: ev.pageY }; + var adjustedPoint = origPoint; + var subjectEl = ev.subjectEl; + var subjectRect; + if (subjectEl !== document) { + subjectRect = core.computeRect(subjectEl); + adjustedPoint = core.constrainPoint(adjustedPoint, subjectRect); + } + var initialHit = this.initialHit = this.queryHitForOffset(adjustedPoint.left, adjustedPoint.top); + if (initialHit) { + if (this.useSubjectCenter && subjectRect) { + var slicedSubjectRect = core.intersectRects(subjectRect, initialHit.rect); + if (slicedSubjectRect) { + adjustedPoint = core.getRectCenter(slicedSubjectRect); + } + } + this.coordAdjust = core.diffPoints(adjustedPoint, origPoint); + } + else { + this.coordAdjust = { left: 0, top: 0 }; + } + }; + HitDragging.prototype.handleMove = function (ev, forceHandle) { + var hit = this.queryHitForOffset(ev.pageX + this.coordAdjust.left, ev.pageY + this.coordAdjust.top); + if (forceHandle || !isHitsEqual(this.movingHit, hit)) { + this.movingHit = hit; + this.emitter.trigger('hitupdate', hit, false, ev); + } + }; + HitDragging.prototype.prepareHits = function () { + this.offsetTrackers = core.mapHash(this.droppableStore, function (interactionSettings) { + interactionSettings.component.buildPositionCaches(); + return new OffsetTracker(interactionSettings.el); + }); + }; + HitDragging.prototype.releaseHits = function () { + var offsetTrackers = this.offsetTrackers; + for (var id in offsetTrackers) { + offsetTrackers[id].destroy(); + } + this.offsetTrackers = {}; + }; + HitDragging.prototype.queryHitForOffset = function (offsetLeft, offsetTop) { + var _a = this, droppableStore = _a.droppableStore, offsetTrackers = _a.offsetTrackers; + var bestHit = null; + for (var id in droppableStore) { + var component = droppableStore[id].component; + var offsetTracker = offsetTrackers[id]; + if (offsetTracker.isWithinClipping(offsetLeft, offsetTop)) { + var originLeft = offsetTracker.computeLeft(); + var originTop = offsetTracker.computeTop(); + var positionLeft = offsetLeft - originLeft; + var positionTop = offsetTop - originTop; + var origRect = offsetTracker.origRect; + var width = origRect.right - origRect.left; + var height = origRect.bottom - origRect.top; + if ( + // must be within the element's bounds + positionLeft >= 0 && positionLeft < width && + positionTop >= 0 && positionTop < height) { + var hit = component.queryHit(positionLeft, positionTop, width, height); + if (hit && + ( + // make sure the hit is within activeRange, meaning it's not a deal cell + !component.props.dateProfile || // hack for DayTile + core.rangeContainsRange(component.props.dateProfile.activeRange, hit.dateSpan.range)) && + (!bestHit || hit.layer > bestHit.layer)) { + // TODO: better way to re-orient rectangle + hit.rect.left += originLeft; + hit.rect.right += originLeft; + hit.rect.top += originTop; + hit.rect.bottom += originTop; + bestHit = hit; + } + } + } + } + return bestHit; + }; + return HitDragging; + }()); + function isHitsEqual(hit0, hit1) { + if (!hit0 && !hit1) { + return true; + } + if (Boolean(hit0) !== Boolean(hit1)) { + return false; + } + return core.isDateSpansEqual(hit0.dateSpan, hit1.dateSpan); + } + + /* + Monitors when the user clicks on a specific date/time of a component. + A pointerdown+pointerup on the same "hit" constitutes a click. + */ + var DateClicking = /** @class */ (function (_super) { + __extends(DateClicking, _super); + function DateClicking(settings) { + var _this = _super.call(this, settings) || this; + _this.handlePointerDown = function (ev) { + var dragging = _this.dragging; + // do this in pointerdown (not dragend) because DOM might be mutated by the time dragend is fired + dragging.setIgnoreMove(!_this.component.isValidDateDownEl(dragging.pointer.downEl)); + }; + // won't even fire if moving was ignored + _this.handleDragEnd = function (ev) { + var component = _this.component; + var pointer = _this.dragging.pointer; + if (!pointer.wasTouchScroll) { + var _a = _this.hitDragging, initialHit = _a.initialHit, finalHit = _a.finalHit; + if (initialHit && finalHit && isHitsEqual(initialHit, finalHit)) { + component.calendar.triggerDateClick(initialHit.dateSpan, initialHit.dayEl, component.view, ev.origEvent); + } + } + }; + var component = settings.component; + // we DO want to watch pointer moves because otherwise finalHit won't get populated + _this.dragging = new FeaturefulElementDragging(component.el); + _this.dragging.autoScroller.isEnabled = false; + var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, core.interactionSettingsToStore(settings)); + hitDragging.emitter.on('pointerdown', _this.handlePointerDown); + hitDragging.emitter.on('dragend', _this.handleDragEnd); + return _this; + } + DateClicking.prototype.destroy = function () { + this.dragging.destroy(); + }; + return DateClicking; + }(core.Interaction)); + + /* + Tracks when the user selects a portion of time of a component, + constituted by a drag over date cells, with a possible delay at the beginning of the drag. + */ + var DateSelecting = /** @class */ (function (_super) { + __extends(DateSelecting, _super); + function DateSelecting(settings) { + var _this = _super.call(this, settings) || this; + _this.dragSelection = null; + _this.handlePointerDown = function (ev) { + var _a = _this, component = _a.component, dragging = _a.dragging; + var canSelect = component.opt('selectable') && + component.isValidDateDownEl(ev.origEvent.target); + // don't bother to watch expensive moves if component won't do selection + dragging.setIgnoreMove(!canSelect); + // if touch, require user to hold down + dragging.delay = ev.isTouch ? getComponentTouchDelay(component) : null; + }; + _this.handleDragStart = function (ev) { + _this.component.calendar.unselect(ev); // unselect previous selections + }; + _this.handleHitUpdate = function (hit, isFinal) { + var calendar = _this.component.calendar; + var dragSelection = null; + var isInvalid = false; + if (hit) { + dragSelection = joinHitsIntoSelection(_this.hitDragging.initialHit, hit, calendar.pluginSystem.hooks.dateSelectionTransformers); + if (!dragSelection || !_this.component.isDateSelectionValid(dragSelection)) { + isInvalid = true; + dragSelection = null; + } + } + if (dragSelection) { + calendar.dispatch({ type: 'SELECT_DATES', selection: dragSelection }); + } + else if (!isFinal) { // only unselect if moved away while dragging + calendar.dispatch({ type: 'UNSELECT_DATES' }); + } + if (!isInvalid) { + core.enableCursor(); + } + else { + core.disableCursor(); + } + if (!isFinal) { + _this.dragSelection = dragSelection; // only clear if moved away from all hits while dragging + } + }; + _this.handlePointerUp = function (pev) { + if (_this.dragSelection) { + // selection is already rendered, so just need to report selection + _this.component.calendar.triggerDateSelect(_this.dragSelection, pev); + _this.dragSelection = null; + } + }; + var component = settings.component; + var dragging = _this.dragging = new FeaturefulElementDragging(component.el); + dragging.touchScrollAllowed = false; + dragging.minDistance = component.opt('selectMinDistance') || 0; + dragging.autoScroller.isEnabled = component.opt('dragScroll'); + var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, core.interactionSettingsToStore(settings)); + hitDragging.emitter.on('pointerdown', _this.handlePointerDown); + hitDragging.emitter.on('dragstart', _this.handleDragStart); + hitDragging.emitter.on('hitupdate', _this.handleHitUpdate); + hitDragging.emitter.on('pointerup', _this.handlePointerUp); + return _this; + } + DateSelecting.prototype.destroy = function () { + this.dragging.destroy(); + }; + return DateSelecting; + }(core.Interaction)); + function getComponentTouchDelay(component) { + var delay = component.opt('selectLongPressDelay'); + if (delay == null) { + delay = component.opt('longPressDelay'); + } + return delay; + } + function joinHitsIntoSelection(hit0, hit1, dateSelectionTransformers) { + var dateSpan0 = hit0.dateSpan; + var dateSpan1 = hit1.dateSpan; + var ms = [ + dateSpan0.range.start, + dateSpan0.range.end, + dateSpan1.range.start, + dateSpan1.range.end + ]; + ms.sort(core.compareNumbers); + var props = {}; + for (var _i = 0, dateSelectionTransformers_1 = dateSelectionTransformers; _i < dateSelectionTransformers_1.length; _i++) { + var transformer = dateSelectionTransformers_1[_i]; + var res = transformer(hit0, hit1); + if (res === false) { + return null; + } + else if (res) { + __assign(props, res); + } + } + props.range = { start: ms[0], end: ms[3] }; + props.allDay = dateSpan0.allDay; + return props; + } + + var EventDragging = /** @class */ (function (_super) { + __extends(EventDragging, _super); + function EventDragging(settings) { + var _this = _super.call(this, settings) || this; + // internal state + _this.subjectSeg = null; // the seg being selected/dragged + _this.isDragging = false; + _this.eventRange = null; + _this.relevantEvents = null; // the events being dragged + _this.receivingCalendar = null; + _this.validMutation = null; + _this.mutatedRelevantEvents = null; + _this.handlePointerDown = function (ev) { + var origTarget = ev.origEvent.target; + var _a = _this, component = _a.component, dragging = _a.dragging; + var mirror = dragging.mirror; + var initialCalendar = component.calendar; + var subjectSeg = _this.subjectSeg = core.getElSeg(ev.subjectEl); + var eventRange = _this.eventRange = subjectSeg.eventRange; + var eventInstanceId = eventRange.instance.instanceId; + _this.relevantEvents = core.getRelevantEvents(initialCalendar.state.eventStore, eventInstanceId); + dragging.minDistance = ev.isTouch ? 0 : component.opt('eventDragMinDistance'); + dragging.delay = + // only do a touch delay if touch and this event hasn't been selected yet + (ev.isTouch && eventInstanceId !== component.props.eventSelection) ? + getComponentTouchDelay$1(component) : + null; + mirror.parentNode = initialCalendar.el; + mirror.revertDuration = component.opt('dragRevertDuration'); + var isValid = component.isValidSegDownEl(origTarget) && + !core.elementClosest(origTarget, '.fc-resizer'); // NOT on a resizer + dragging.setIgnoreMove(!isValid); + // disable dragging for elements that are resizable (ie, selectable) + // but are not draggable + _this.isDragging = isValid && + ev.subjectEl.classList.contains('fc-draggable'); + }; + _this.handleDragStart = function (ev) { + var initialCalendar = _this.component.calendar; + var eventRange = _this.eventRange; + var eventInstanceId = eventRange.instance.instanceId; + if (ev.isTouch) { + // need to select a different event? + if (eventInstanceId !== _this.component.props.eventSelection) { + initialCalendar.dispatch({ type: 'SELECT_EVENT', eventInstanceId: eventInstanceId }); + } + } + else { + // if now using mouse, but was previous touch interaction, clear selected event + initialCalendar.dispatch({ type: 'UNSELECT_EVENT' }); + } + if (_this.isDragging) { + initialCalendar.unselect(ev); // unselect *date* selection + initialCalendar.publiclyTrigger('eventDragStart', [ + { + el: _this.subjectSeg.el, + event: new core.EventApi(initialCalendar, eventRange.def, eventRange.instance), + jsEvent: ev.origEvent, + view: _this.component.view + } + ]); + } + }; + _this.handleHitUpdate = function (hit, isFinal) { + if (!_this.isDragging) { + return; + } + var relevantEvents = _this.relevantEvents; + var initialHit = _this.hitDragging.initialHit; + var initialCalendar = _this.component.calendar; + // states based on new hit + var receivingCalendar = null; + var mutation = null; + var mutatedRelevantEvents = null; + var isInvalid = false; + var interaction = { + affectedEvents: relevantEvents, + mutatedEvents: core.createEmptyEventStore(), + isEvent: true, + origSeg: _this.subjectSeg + }; + if (hit) { + var receivingComponent = hit.component; + receivingCalendar = receivingComponent.calendar; + if (initialCalendar === receivingCalendar || + receivingComponent.opt('editable') && receivingComponent.opt('droppable')) { + mutation = computeEventMutation(initialHit, hit, receivingCalendar.pluginSystem.hooks.eventDragMutationMassagers); + if (mutation) { + mutatedRelevantEvents = core.applyMutationToEventStore(relevantEvents, receivingCalendar.eventUiBases, mutation, receivingCalendar); + interaction.mutatedEvents = mutatedRelevantEvents; + if (!receivingComponent.isInteractionValid(interaction)) { + isInvalid = true; + mutation = null; + mutatedRelevantEvents = null; + interaction.mutatedEvents = core.createEmptyEventStore(); + } + } + } + else { + receivingCalendar = null; + } + } + _this.displayDrag(receivingCalendar, interaction); + if (!isInvalid) { + core.enableCursor(); + } + else { + core.disableCursor(); + } + if (!isFinal) { + if (initialCalendar === receivingCalendar && // TODO: write test for this + isHitsEqual(initialHit, hit)) { + mutation = null; + } + _this.dragging.setMirrorNeedsRevert(!mutation); + // render the mirror if no already-rendered mirror + // TODO: wish we could somehow wait for dispatch to guarantee render + _this.dragging.setMirrorIsVisible(!hit || !document.querySelector('.fc-mirror')); + // assign states based on new hit + _this.receivingCalendar = receivingCalendar; + _this.validMutation = mutation; + _this.mutatedRelevantEvents = mutatedRelevantEvents; + } + }; + _this.handlePointerUp = function () { + if (!_this.isDragging) { + _this.cleanup(); // because handleDragEnd won't fire + } + }; + _this.handleDragEnd = function (ev) { + if (_this.isDragging) { + var initialCalendar_1 = _this.component.calendar; + var initialView = _this.component.view; + var _a = _this, receivingCalendar = _a.receivingCalendar, validMutation = _a.validMutation; + var eventDef = _this.eventRange.def; + var eventInstance = _this.eventRange.instance; + var eventApi = new core.EventApi(initialCalendar_1, eventDef, eventInstance); + var relevantEvents_1 = _this.relevantEvents; + var mutatedRelevantEvents = _this.mutatedRelevantEvents; + var finalHit = _this.hitDragging.finalHit; + _this.clearDrag(); // must happen after revert animation + initialCalendar_1.publiclyTrigger('eventDragStop', [ + { + el: _this.subjectSeg.el, + event: eventApi, + jsEvent: ev.origEvent, + view: initialView + } + ]); + if (validMutation) { + // dropped within same calendar + if (receivingCalendar === initialCalendar_1) { + initialCalendar_1.dispatch({ + type: 'MERGE_EVENTS', + eventStore: mutatedRelevantEvents + }); + var transformed = {}; + for (var _i = 0, _b = initialCalendar_1.pluginSystem.hooks.eventDropTransformers; _i < _b.length; _i++) { + var transformer = _b[_i]; + __assign(transformed, transformer(validMutation, initialCalendar_1)); + } + var eventDropArg = __assign({}, transformed, { el: ev.subjectEl, delta: validMutation.datesDelta, oldEvent: eventApi, event: new core.EventApi(// the data AFTER the mutation + initialCalendar_1, mutatedRelevantEvents.defs[eventDef.defId], eventInstance ? mutatedRelevantEvents.instances[eventInstance.instanceId] : null), revert: function () { + initialCalendar_1.dispatch({ + type: 'MERGE_EVENTS', + eventStore: relevantEvents_1 + }); + }, jsEvent: ev.origEvent, view: initialView }); + initialCalendar_1.publiclyTrigger('eventDrop', [eventDropArg]); + // dropped in different calendar + } + else if (receivingCalendar) { + initialCalendar_1.publiclyTrigger('eventLeave', [ + { + draggedEl: ev.subjectEl, + event: eventApi, + view: initialView + } + ]); + initialCalendar_1.dispatch({ + type: 'REMOVE_EVENT_INSTANCES', + instances: _this.mutatedRelevantEvents.instances + }); + receivingCalendar.dispatch({ + type: 'MERGE_EVENTS', + eventStore: _this.mutatedRelevantEvents + }); + if (ev.isTouch) { + receivingCalendar.dispatch({ + type: 'SELECT_EVENT', + eventInstanceId: eventInstance.instanceId + }); + } + var dropArg = __assign({}, receivingCalendar.buildDatePointApi(finalHit.dateSpan), { draggedEl: ev.subjectEl, jsEvent: ev.origEvent, view: finalHit.component // should this be finalHit.component.view? See #4644 + }); + receivingCalendar.publiclyTrigger('drop', [dropArg]); + receivingCalendar.publiclyTrigger('eventReceive', [ + { + draggedEl: ev.subjectEl, + event: new core.EventApi(// the data AFTER the mutation + receivingCalendar, mutatedRelevantEvents.defs[eventDef.defId], mutatedRelevantEvents.instances[eventInstance.instanceId]), + view: finalHit.component // should this be finalHit.component.view? See #4644 + } + ]); + } + } + else { + initialCalendar_1.publiclyTrigger('_noEventDrop'); + } + } + _this.cleanup(); + }; + var component = _this.component; + var dragging = _this.dragging = new FeaturefulElementDragging(component.el); + dragging.pointer.selector = EventDragging.SELECTOR; + dragging.touchScrollAllowed = false; + dragging.autoScroller.isEnabled = component.opt('dragScroll'); + var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, core.interactionSettingsStore); + hitDragging.useSubjectCenter = settings.useEventCenter; + hitDragging.emitter.on('pointerdown', _this.handlePointerDown); + hitDragging.emitter.on('dragstart', _this.handleDragStart); + hitDragging.emitter.on('hitupdate', _this.handleHitUpdate); + hitDragging.emitter.on('pointerup', _this.handlePointerUp); + hitDragging.emitter.on('dragend', _this.handleDragEnd); + return _this; + } + EventDragging.prototype.destroy = function () { + this.dragging.destroy(); + }; + // render a drag state on the next receivingCalendar + EventDragging.prototype.displayDrag = function (nextCalendar, state) { + var initialCalendar = this.component.calendar; + var prevCalendar = this.receivingCalendar; + // does the previous calendar need to be cleared? + if (prevCalendar && prevCalendar !== nextCalendar) { + // does the initial calendar need to be cleared? + // if so, don't clear all the way. we still need to to hide the affectedEvents + if (prevCalendar === initialCalendar) { + prevCalendar.dispatch({ + type: 'SET_EVENT_DRAG', + state: { + affectedEvents: state.affectedEvents, + mutatedEvents: core.createEmptyEventStore(), + isEvent: true, + origSeg: state.origSeg + } + }); + // completely clear the old calendar if it wasn't the initial + } + else { + prevCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' }); + } + } + if (nextCalendar) { + nextCalendar.dispatch({ type: 'SET_EVENT_DRAG', state: state }); + } + }; + EventDragging.prototype.clearDrag = function () { + var initialCalendar = this.component.calendar; + var receivingCalendar = this.receivingCalendar; + if (receivingCalendar) { + receivingCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' }); + } + // the initial calendar might have an dummy drag state from displayDrag + if (initialCalendar !== receivingCalendar) { + initialCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' }); + } + }; + EventDragging.prototype.cleanup = function () { + this.subjectSeg = null; + this.isDragging = false; + this.eventRange = null; + this.relevantEvents = null; + this.receivingCalendar = null; + this.validMutation = null; + this.mutatedRelevantEvents = null; + }; + EventDragging.SELECTOR = '.fc-draggable, .fc-resizable'; // TODO: test this in IE11 + return EventDragging; + }(core.Interaction)); + function computeEventMutation(hit0, hit1, massagers) { + var dateSpan0 = hit0.dateSpan; + var dateSpan1 = hit1.dateSpan; + var date0 = dateSpan0.range.start; + var date1 = dateSpan1.range.start; + var standardProps = {}; + if (dateSpan0.allDay !== dateSpan1.allDay) { + standardProps.allDay = dateSpan1.allDay; + standardProps.hasEnd = hit1.component.opt('allDayMaintainDuration'); + if (dateSpan1.allDay) { + // means date1 is already start-of-day, + // but date0 needs to be converted + date0 = core.startOfDay(date0); + } + } + var delta = core.diffDates(date0, date1, hit0.component.dateEnv, hit0.component === hit1.component ? + hit0.component.largeUnit : + null); + if (delta.milliseconds) { // has hours/minutes/seconds + standardProps.allDay = false; + } + var mutation = { + datesDelta: delta, + standardProps: standardProps + }; + for (var _i = 0, massagers_1 = massagers; _i < massagers_1.length; _i++) { + var massager = massagers_1[_i]; + massager(mutation, hit0, hit1); + } + return mutation; + } + function getComponentTouchDelay$1(component) { + var delay = component.opt('eventLongPressDelay'); + if (delay == null) { + delay = component.opt('longPressDelay'); + } + return delay; + } + + var EventDragging$1 = /** @class */ (function (_super) { + __extends(EventDragging, _super); + function EventDragging(settings) { + var _this = _super.call(this, settings) || this; + // internal state + _this.draggingSeg = null; // TODO: rename to resizingSeg? subjectSeg? + _this.eventRange = null; + _this.relevantEvents = null; + _this.validMutation = null; + _this.mutatedRelevantEvents = null; + _this.handlePointerDown = function (ev) { + var component = _this.component; + var seg = _this.querySeg(ev); + var eventRange = _this.eventRange = seg.eventRange; + _this.dragging.minDistance = component.opt('eventDragMinDistance'); + // if touch, need to be working with a selected event + _this.dragging.setIgnoreMove(!_this.component.isValidSegDownEl(ev.origEvent.target) || + (ev.isTouch && _this.component.props.eventSelection !== eventRange.instance.instanceId)); + }; + _this.handleDragStart = function (ev) { + var calendar = _this.component.calendar; + var eventRange = _this.eventRange; + _this.relevantEvents = core.getRelevantEvents(calendar.state.eventStore, _this.eventRange.instance.instanceId); + _this.draggingSeg = _this.querySeg(ev); + calendar.unselect(); + calendar.publiclyTrigger('eventResizeStart', [ + { + el: _this.draggingSeg.el, + event: new core.EventApi(calendar, eventRange.def, eventRange.instance), + jsEvent: ev.origEvent, + view: _this.component.view + } + ]); + }; + _this.handleHitUpdate = function (hit, isFinal, ev) { + var calendar = _this.component.calendar; + var relevantEvents = _this.relevantEvents; + var initialHit = _this.hitDragging.initialHit; + var eventInstance = _this.eventRange.instance; + var mutation = null; + var mutatedRelevantEvents = null; + var isInvalid = false; + var interaction = { + affectedEvents: relevantEvents, + mutatedEvents: core.createEmptyEventStore(), + isEvent: true, + origSeg: _this.draggingSeg + }; + if (hit) { + mutation = computeMutation(initialHit, hit, ev.subjectEl.classList.contains('fc-start-resizer'), eventInstance.range, calendar.pluginSystem.hooks.eventResizeJoinTransforms); + } + if (mutation) { + mutatedRelevantEvents = core.applyMutationToEventStore(relevantEvents, calendar.eventUiBases, mutation, calendar); + interaction.mutatedEvents = mutatedRelevantEvents; + if (!_this.component.isInteractionValid(interaction)) { + isInvalid = true; + mutation = null; + mutatedRelevantEvents = null; + interaction.mutatedEvents = null; + } + } + if (mutatedRelevantEvents) { + calendar.dispatch({ + type: 'SET_EVENT_RESIZE', + state: interaction + }); + } + else { + calendar.dispatch({ type: 'UNSET_EVENT_RESIZE' }); + } + if (!isInvalid) { + core.enableCursor(); + } + else { + core.disableCursor(); + } + if (!isFinal) { + if (mutation && isHitsEqual(initialHit, hit)) { + mutation = null; + } + _this.validMutation = mutation; + _this.mutatedRelevantEvents = mutatedRelevantEvents; + } + }; + _this.handleDragEnd = function (ev) { + var calendar = _this.component.calendar; + var view = _this.component.view; + var eventDef = _this.eventRange.def; + var eventInstance = _this.eventRange.instance; + var eventApi = new core.EventApi(calendar, eventDef, eventInstance); + var relevantEvents = _this.relevantEvents; + var mutatedRelevantEvents = _this.mutatedRelevantEvents; + calendar.publiclyTrigger('eventResizeStop', [ + { + el: _this.draggingSeg.el, + event: eventApi, + jsEvent: ev.origEvent, + view: view + } + ]); + if (_this.validMutation) { + calendar.dispatch({ + type: 'MERGE_EVENTS', + eventStore: mutatedRelevantEvents + }); + calendar.publiclyTrigger('eventResize', [ + { + el: _this.draggingSeg.el, + startDelta: _this.validMutation.startDelta || core.createDuration(0), + endDelta: _this.validMutation.endDelta || core.createDuration(0), + prevEvent: eventApi, + event: new core.EventApi(// the data AFTER the mutation + calendar, mutatedRelevantEvents.defs[eventDef.defId], eventInstance ? mutatedRelevantEvents.instances[eventInstance.instanceId] : null), + revert: function () { + calendar.dispatch({ + type: 'MERGE_EVENTS', + eventStore: relevantEvents + }); + }, + jsEvent: ev.origEvent, + view: view + } + ]); + } + else { + calendar.publiclyTrigger('_noEventResize'); + } + // reset all internal state + _this.draggingSeg = null; + _this.relevantEvents = null; + _this.validMutation = null; + // okay to keep eventInstance around. useful to set it in handlePointerDown + }; + var component = settings.component; + var dragging = _this.dragging = new FeaturefulElementDragging(component.el); + dragging.pointer.selector = '.fc-resizer'; + dragging.touchScrollAllowed = false; + dragging.autoScroller.isEnabled = component.opt('dragScroll'); + var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, core.interactionSettingsToStore(settings)); + hitDragging.emitter.on('pointerdown', _this.handlePointerDown); + hitDragging.emitter.on('dragstart', _this.handleDragStart); + hitDragging.emitter.on('hitupdate', _this.handleHitUpdate); + hitDragging.emitter.on('dragend', _this.handleDragEnd); + return _this; + } + EventDragging.prototype.destroy = function () { + this.dragging.destroy(); + }; + EventDragging.prototype.querySeg = function (ev) { + return core.getElSeg(core.elementClosest(ev.subjectEl, this.component.fgSegSelector)); + }; + return EventDragging; + }(core.Interaction)); + function computeMutation(hit0, hit1, isFromStart, instanceRange, transforms) { + var dateEnv = hit0.component.dateEnv; + var date0 = hit0.dateSpan.range.start; + var date1 = hit1.dateSpan.range.start; + var delta = core.diffDates(date0, date1, dateEnv, hit0.component.largeUnit); + var props = {}; + for (var _i = 0, transforms_1 = transforms; _i < transforms_1.length; _i++) { + var transform = transforms_1[_i]; + var res = transform(hit0, hit1); + if (res === false) { + return null; + } + else if (res) { + __assign(props, res); + } + } + if (isFromStart) { + if (dateEnv.add(instanceRange.start, delta) < instanceRange.end) { + props.startDelta = delta; + return props; + } + } + else { + if (dateEnv.add(instanceRange.end, delta) > instanceRange.start) { + props.endDelta = delta; + return props; + } + } + return null; + } + + var UnselectAuto = /** @class */ (function () { + function UnselectAuto(calendar) { + var _this = this; + this.isRecentPointerDateSelect = false; // wish we could use a selector to detect date selection, but uses hit system + this.onSelect = function (selectInfo) { + if (selectInfo.jsEvent) { + _this.isRecentPointerDateSelect = true; + } + }; + this.onDocumentPointerUp = function (pev) { + var _a = _this, calendar = _a.calendar, documentPointer = _a.documentPointer; + var state = calendar.state; + // touch-scrolling should never unfocus any type of selection + if (!documentPointer.wasTouchScroll) { + if (state.dateSelection && // an existing date selection? + !_this.isRecentPointerDateSelect // a new pointer-initiated date selection since last onDocumentPointerUp? + ) { + var unselectAuto = calendar.viewOpt('unselectAuto'); + var unselectCancel = calendar.viewOpt('unselectCancel'); + if (unselectAuto && (!unselectAuto || !core.elementClosest(documentPointer.downEl, unselectCancel))) { + calendar.unselect(pev); + } + } + if (state.eventSelection && // an existing event selected? + !core.elementClosest(documentPointer.downEl, EventDragging.SELECTOR) // interaction DIDN'T start on an event + ) { + calendar.dispatch({ type: 'UNSELECT_EVENT' }); + } + } + _this.isRecentPointerDateSelect = false; + }; + this.calendar = calendar; + var documentPointer = this.documentPointer = new PointerDragging(document); + documentPointer.shouldIgnoreMove = true; + documentPointer.shouldWatchScroll = false; + documentPointer.emitter.on('pointerup', this.onDocumentPointerUp); + /* + TODO: better way to know about whether there was a selection with the pointer + */ + calendar.on('select', this.onSelect); + } + UnselectAuto.prototype.destroy = function () { + this.calendar.off('select', this.onSelect); + this.documentPointer.destroy(); + }; + return UnselectAuto; + }()); + + /* + Given an already instantiated draggable object for one-or-more elements, + Interprets any dragging as an attempt to drag an events that lives outside + of a calendar onto a calendar. + */ + var ExternalElementDragging = /** @class */ (function () { + function ExternalElementDragging(dragging, suppliedDragMeta) { + var _this = this; + this.receivingCalendar = null; + this.droppableEvent = null; // will exist for all drags, even if create:false + this.suppliedDragMeta = null; + this.dragMeta = null; + this.handleDragStart = function (ev) { + _this.dragMeta = _this.buildDragMeta(ev.subjectEl); + }; + this.handleHitUpdate = function (hit, isFinal, ev) { + var dragging = _this.hitDragging.dragging; + var receivingCalendar = null; + var droppableEvent = null; + var isInvalid = false; + var interaction = { + affectedEvents: core.createEmptyEventStore(), + mutatedEvents: core.createEmptyEventStore(), + isEvent: _this.dragMeta.create, + origSeg: null + }; + if (hit) { + receivingCalendar = hit.component.calendar; + if (_this.canDropElOnCalendar(ev.subjectEl, receivingCalendar)) { + droppableEvent = computeEventForDateSpan(hit.dateSpan, _this.dragMeta, receivingCalendar); + interaction.mutatedEvents = core.eventTupleToStore(droppableEvent); + isInvalid = !core.isInteractionValid(interaction, receivingCalendar); + if (isInvalid) { + interaction.mutatedEvents = core.createEmptyEventStore(); + droppableEvent = null; + } + } + } + _this.displayDrag(receivingCalendar, interaction); + // show mirror if no already-rendered mirror element OR if we are shutting down the mirror (?) + // TODO: wish we could somehow wait for dispatch to guarantee render + dragging.setMirrorIsVisible(isFinal || !droppableEvent || !document.querySelector('.fc-mirror')); + if (!isInvalid) { + core.enableCursor(); + } + else { + core.disableCursor(); + } + if (!isFinal) { + dragging.setMirrorNeedsRevert(!droppableEvent); + _this.receivingCalendar = receivingCalendar; + _this.droppableEvent = droppableEvent; + } + }; + this.handleDragEnd = function (pev) { + var _a = _this, receivingCalendar = _a.receivingCalendar, droppableEvent = _a.droppableEvent; + _this.clearDrag(); + if (receivingCalendar && droppableEvent) { + var finalHit = _this.hitDragging.finalHit; + var finalView = finalHit.component.view; + var dragMeta = _this.dragMeta; + var arg = __assign({}, receivingCalendar.buildDatePointApi(finalHit.dateSpan), { draggedEl: pev.subjectEl, jsEvent: pev.origEvent, view: finalView }); + receivingCalendar.publiclyTrigger('drop', [arg]); + if (dragMeta.create) { + receivingCalendar.dispatch({ + type: 'MERGE_EVENTS', + eventStore: core.eventTupleToStore(droppableEvent) + }); + if (pev.isTouch) { + receivingCalendar.dispatch({ + type: 'SELECT_EVENT', + eventInstanceId: droppableEvent.instance.instanceId + }); + } + // signal that an external event landed + receivingCalendar.publiclyTrigger('eventReceive', [ + { + draggedEl: pev.subjectEl, + event: new core.EventApi(receivingCalendar, droppableEvent.def, droppableEvent.instance), + view: finalView + } + ]); + } + } + _this.receivingCalendar = null; + _this.droppableEvent = null; + }; + var hitDragging = this.hitDragging = new HitDragging(dragging, core.interactionSettingsStore); + hitDragging.requireInitial = false; // will start outside of a component + hitDragging.emitter.on('dragstart', this.handleDragStart); + hitDragging.emitter.on('hitupdate', this.handleHitUpdate); + hitDragging.emitter.on('dragend', this.handleDragEnd); + this.suppliedDragMeta = suppliedDragMeta; + } + ExternalElementDragging.prototype.buildDragMeta = function (subjectEl) { + if (typeof this.suppliedDragMeta === 'object') { + return core.parseDragMeta(this.suppliedDragMeta); + } + else if (typeof this.suppliedDragMeta === 'function') { + return core.parseDragMeta(this.suppliedDragMeta(subjectEl)); + } + else { + return getDragMetaFromEl(subjectEl); + } + }; + ExternalElementDragging.prototype.displayDrag = function (nextCalendar, state) { + var prevCalendar = this.receivingCalendar; + if (prevCalendar && prevCalendar !== nextCalendar) { + prevCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' }); + } + if (nextCalendar) { + nextCalendar.dispatch({ type: 'SET_EVENT_DRAG', state: state }); + } + }; + ExternalElementDragging.prototype.clearDrag = function () { + if (this.receivingCalendar) { + this.receivingCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' }); + } + }; + ExternalElementDragging.prototype.canDropElOnCalendar = function (el, receivingCalendar) { + var dropAccept = receivingCalendar.opt('dropAccept'); + if (typeof dropAccept === 'function') { + return dropAccept(el); + } + else if (typeof dropAccept === 'string' && dropAccept) { + return Boolean(core.elementMatches(el, dropAccept)); + } + return true; + }; + return ExternalElementDragging; + }()); + // Utils for computing event store from the DragMeta + // ---------------------------------------------------------------------------------------------------- + function computeEventForDateSpan(dateSpan, dragMeta, calendar) { + var defProps = __assign({}, dragMeta.leftoverProps); + for (var _i = 0, _a = calendar.pluginSystem.hooks.externalDefTransforms; _i < _a.length; _i++) { + var transform = _a[_i]; + __assign(defProps, transform(dateSpan, dragMeta)); + } + var def = core.parseEventDef(defProps, dragMeta.sourceId, dateSpan.allDay, calendar.opt('forceEventDuration') || Boolean(dragMeta.duration), // hasEnd + calendar); + var start = dateSpan.range.start; + // only rely on time info if drop zone is all-day, + // otherwise, we already know the time + if (dateSpan.allDay && dragMeta.startTime) { + start = calendar.dateEnv.add(start, dragMeta.startTime); + } + var end = dragMeta.duration ? + calendar.dateEnv.add(start, dragMeta.duration) : + calendar.getDefaultEventEnd(dateSpan.allDay, start); + var instance = core.createEventInstance(def.defId, { start: start, end: end }); + return { def: def, instance: instance }; + } + // Utils for extracting data from element + // ---------------------------------------------------------------------------------------------------- + function getDragMetaFromEl(el) { + var str = getEmbeddedElData(el, 'event'); + var obj = str ? + JSON.parse(str) : + { create: false }; // if no embedded data, assume no event creation + return core.parseDragMeta(obj); + } + core.config.dataAttrPrefix = ''; + function getEmbeddedElData(el, name) { + var prefix = core.config.dataAttrPrefix; + var prefixedName = (prefix ? prefix + '-' : '') + name; + return el.getAttribute('data-' + prefixedName) || ''; + } + + /* + Makes an element (that is *external* to any calendar) draggable. + Can pass in data that determines how an event will be created when dropped onto a calendar. + Leverages FullCalendar's internal drag-n-drop functionality WITHOUT a third-party drag system. + */ + var ExternalDraggable = /** @class */ (function () { + function ExternalDraggable(el, settings) { + var _this = this; + if (settings === void 0) { settings = {}; } + this.handlePointerDown = function (ev) { + var dragging = _this.dragging; + var _a = _this.settings, minDistance = _a.minDistance, longPressDelay = _a.longPressDelay; + dragging.minDistance = + minDistance != null ? + minDistance : + (ev.isTouch ? 0 : core.globalDefaults.eventDragMinDistance); + dragging.delay = + ev.isTouch ? // TODO: eventually read eventLongPressDelay instead vvv + (longPressDelay != null ? longPressDelay : core.globalDefaults.longPressDelay) : + 0; + }; + this.handleDragStart = function (ev) { + if (ev.isTouch && + _this.dragging.delay && + ev.subjectEl.classList.contains('fc-event')) { + _this.dragging.mirror.getMirrorEl().classList.add('fc-selected'); + } + }; + this.settings = settings; + var dragging = this.dragging = new FeaturefulElementDragging(el); + dragging.touchScrollAllowed = false; + if (settings.itemSelector != null) { + dragging.pointer.selector = settings.itemSelector; + } + if (settings.appendTo != null) { + dragging.mirror.parentNode = settings.appendTo; // TODO: write tests + } + dragging.emitter.on('pointerdown', this.handlePointerDown); + dragging.emitter.on('dragstart', this.handleDragStart); + new ExternalElementDragging(dragging, settings.eventData); + } + ExternalDraggable.prototype.destroy = function () { + this.dragging.destroy(); + }; + return ExternalDraggable; + }()); + + /* + Detects when a *THIRD-PARTY* drag-n-drop system interacts with elements. + The third-party system is responsible for drawing the visuals effects of the drag. + This class simply monitors for pointer movements and fires events. + It also has the ability to hide the moving element (the "mirror") during the drag. + */ + var InferredElementDragging = /** @class */ (function (_super) { + __extends(InferredElementDragging, _super); + function InferredElementDragging(containerEl) { + var _this = _super.call(this, containerEl) || this; + _this.shouldIgnoreMove = false; + _this.mirrorSelector = ''; + _this.currentMirrorEl = null; + _this.handlePointerDown = function (ev) { + _this.emitter.trigger('pointerdown', ev); + if (!_this.shouldIgnoreMove) { + // fire dragstart right away. does not support delay or min-distance + _this.emitter.trigger('dragstart', ev); + } + }; + _this.handlePointerMove = function (ev) { + if (!_this.shouldIgnoreMove) { + _this.emitter.trigger('dragmove', ev); + } + }; + _this.handlePointerUp = function (ev) { + _this.emitter.trigger('pointerup', ev); + if (!_this.shouldIgnoreMove) { + // fire dragend right away. does not support a revert animation + _this.emitter.trigger('dragend', ev); + } + }; + var pointer = _this.pointer = new PointerDragging(containerEl); + pointer.emitter.on('pointerdown', _this.handlePointerDown); + pointer.emitter.on('pointermove', _this.handlePointerMove); + pointer.emitter.on('pointerup', _this.handlePointerUp); + return _this; + } + InferredElementDragging.prototype.destroy = function () { + this.pointer.destroy(); + }; + InferredElementDragging.prototype.setIgnoreMove = function (bool) { + this.shouldIgnoreMove = bool; + }; + InferredElementDragging.prototype.setMirrorIsVisible = function (bool) { + if (bool) { + // restore a previously hidden element. + // use the reference in case the selector class has already been removed. + if (this.currentMirrorEl) { + this.currentMirrorEl.style.visibility = ''; + this.currentMirrorEl = null; + } + } + else { + var mirrorEl = this.mirrorSelector ? + document.querySelector(this.mirrorSelector) : + null; + if (mirrorEl) { + this.currentMirrorEl = mirrorEl; + mirrorEl.style.visibility = 'hidden'; + } + } + }; + return InferredElementDragging; + }(core.ElementDragging)); + + /* + Bridges third-party drag-n-drop systems with FullCalendar. + Must be instantiated and destroyed by caller. + */ + var ThirdPartyDraggable = /** @class */ (function () { + function ThirdPartyDraggable(containerOrSettings, settings) { + var containerEl = document; + if ( + // wish we could just test instanceof EventTarget, but doesn't work in IE11 + containerOrSettings === document || + containerOrSettings instanceof Element) { + containerEl = containerOrSettings; + settings = settings || {}; + } + else { + settings = (containerOrSettings || {}); + } + var dragging = this.dragging = new InferredElementDragging(containerEl); + if (typeof settings.itemSelector === 'string') { + dragging.pointer.selector = settings.itemSelector; + } + else if (containerEl === document) { + dragging.pointer.selector = '[data-event]'; + } + if (typeof settings.mirrorSelector === 'string') { + dragging.mirrorSelector = settings.mirrorSelector; + } + new ExternalElementDragging(dragging, settings.eventData); + } + ThirdPartyDraggable.prototype.destroy = function () { + this.dragging.destroy(); + }; + return ThirdPartyDraggable; + }()); + + var main = core.createPlugin({ + componentInteractions: [DateClicking, DateSelecting, EventDragging, EventDragging$1], + calendarInteractions: [UnselectAuto], + elementDraggingImpl: FeaturefulElementDragging + }); + + exports.Draggable = ExternalDraggable; + exports.FeaturefulElementDragging = FeaturefulElementDragging; + exports.PointerDragging = PointerDragging; + exports.ThirdPartyDraggable = ThirdPartyDraggable; + exports.default = main; + + Object.defineProperty(exports, '__esModule', { value: true }); + +})); diff --git a/static/assets/libs/fullcalendar/interaction/main.min.js b/static/assets/libs/fullcalendar/interaction/main.min.js new file mode 100644 index 0000000..7a36731 --- /dev/null +++ b/static/assets/libs/fullcalendar/interaction/main.min.js @@ -0,0 +1,6 @@ +/*! +FullCalendar Interaction Plugin v4.3.0 +Docs & License: https://fullcalendar.io/ +(c) 2019 Adam Shaw +*/ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@fullcalendar/core")):"function"==typeof define&&define.amd?define(["exports","@fullcalendar/core"],t):t((e=e||self).FullCalendarInteraction={},e.FullCalendar)}(this,function(e,t){"use strict";var n=function(e,t){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)};function r(e,t){function r(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}var i=function(){return(i=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&(this.everMovedDown=!0),o<0?this.everMovedLeft=!0:o>0&&(this.everMovedRight=!0),this.pointerScreenX=n,this.pointerScreenY=r,this.isAnimating||(this.isAnimating=!0,this.requestAnimation(p()))}},e.prototype.stop=function(){if(this.isEnabled){this.isAnimating=!1;for(var e=0,t=this.scrollCaches;e=0&&c>=0&&d>=0&&g>=0&&(d<=n&&this.everMovedUp&&a.canScrollUp()&&(!r||r.distance>d)&&(r={scrollCache:a,name:"top",distance:d}),g<=n&&this.everMovedDown&&a.canScrollDown()&&(!r||r.distance>g)&&(r={scrollCache:a,name:"bottom",distance:g}),s<=n&&this.everMovedLeft&&a.canScrollLeft()&&(!r||r.distance>s)&&(r={scrollCache:a,name:"left",distance:s}),c<=n&&this.everMovedRight&&a.canScrollRight()&&(!r||r.distance>c)&&(r={scrollCache:a,name:"right",distance:c}))}return r},e.prototype.buildCaches=function(){return this.queryScrollEls().map(function(e){return e===window?new h(!1):new u(e,!1)})},e.prototype.queryScrollEls=function(){for(var e=[],t=0,n=this.scrollQuery;t=t*t&&r.handleDistanceSurpassed(e)}r.isDragging&&("scroll"!==e.origEvent.type&&(r.mirror.handleMove(e.pageX,e.pageY),r.autoScroller.handleMove(e.pageX,e.pageY)),r.emitter.trigger("dragmove",e))}},r.onPointerUp=function(e){r.isInteracting&&(r.isInteracting=!1,t.allowSelection(document.body),t.allowContextMenu(document.body),r.emitter.trigger("pointerup",e),r.isDragging&&(r.autoScroller.stop(),r.tryStopDrag(e)),r.delayTimeoutId&&(clearTimeout(r.delayTimeoutId),r.delayTimeoutId=null))};var i=r.pointer=new s(n);return i.emitter.on("pointerdown",r.onPointerDown),i.emitter.on("pointermove",r.onPointerMove),i.emitter.on("pointerup",r.onPointerUp),r.mirror=new d,r.autoScroller=new v,r}return r(n,e),n.prototype.destroy=function(){this.pointer.destroy()},n.prototype.startDelay=function(e){var t=this;"number"==typeof this.delay?this.delayTimeoutId=setTimeout(function(){t.delayTimeoutId=null,t.handleDelayEnd(e)},this.delay):this.handleDelayEnd(e)},n.prototype.handleDelayEnd=function(e){this.isDelayEnded=!0,this.tryStartDrag(e)},n.prototype.handleDistanceSurpassed=function(e){this.isDistanceSurpassed=!0,this.tryStartDrag(e)},n.prototype.tryStartDrag=function(e){this.isDelayEnded&&this.isDistanceSurpassed&&(this.pointer.wasTouchScroll&&!this.touchScrollAllowed||(this.isDragging=!0,this.mirrorNeedsRevert=!1,this.autoScroller.start(e.pageX,e.pageY),this.emitter.trigger("dragstart",e),!1===this.touchScrollAllowed&&this.pointer.cancelTouchScroll()))},n.prototype.tryStopDrag=function(e){this.mirror.stop(this.mirrorNeedsRevert,this.stopDrag.bind(this,e))},n.prototype.stopDrag=function(e){this.isDragging=!1,this.emitter.trigger("dragend",e)},n.prototype.setIgnoreMove=function(e){this.pointer.shouldIgnoreMove=e},n.prototype.setMirrorIsVisible=function(e){this.mirror.setIsVisible(e)},n.prototype.setMirrorNeedsRevert=function(e){this.mirrorNeedsRevert=e},n.prototype.setAutoScrollEnabled=function(e){this.autoScroller.isEnabled=e},n}(t.ElementDragging),E=function(){function e(e){this.origRect=t.computeRect(e),this.scrollCaches=t.getClippingParents(e).map(function(e){return new u(e,!0)})}return e.prototype.destroy=function(){for(var e=0,t=this.scrollCaches;e=0&&g=0&&uo.layer)||(f.rect.left+=c,f.rect.right+=c,f.rect.top+=d,f.rect.bottom+=d,o=f)}}}return o},e}();function S(e,n){return!e&&!n||Boolean(e)===Boolean(n)&&t.isDateSpansEqual(e.dateSpan,n.dateSpan)}var y=function(e){function n(n){var r=e.call(this,n)||this;r.handlePointerDown=function(e){var t=r.dragging;t.setIgnoreMove(!r.component.isValidDateDownEl(t.pointer.downEl))},r.handleDragEnd=function(e){var t=r.component;if(!r.dragging.pointer.wasTouchScroll){var n=r.hitDragging,i=n.initialHit,o=n.finalHit;i&&o&&S(i,o)&&t.calendar.triggerDateClick(i.dateSpan,i.dayEl,t.view,e.origEvent)}};var i=n.component;r.dragging=new f(i.el),r.dragging.autoScroller.isEnabled=!1;var o=r.hitDragging=new m(r.dragging,t.interactionSettingsToStore(n));return o.emitter.on("pointerdown",r.handlePointerDown),o.emitter.on("dragend",r.handleDragEnd),r}return r(n,e),n.prototype.destroy=function(){this.dragging.destroy()},n}(t.Interaction),D=function(e){function n(n){var r=e.call(this,n)||this;r.dragSelection=null,r.handlePointerDown=function(e){var t=r,n=t.component,i=t.dragging,o=n.opt("selectable")&&n.isValidDateDownEl(e.origEvent.target);i.setIgnoreMove(!o),i.delay=e.isTouch?function(e){var t=e.opt("selectLongPressDelay");null==t&&(t=e.opt("longPressDelay"));return t}(n):null},r.handleDragStart=function(e){r.component.calendar.unselect(e)},r.handleHitUpdate=function(e,n){var o=r.component.calendar,a=null,l=!1;e&&((a=function(e,n,r){var o=e.dateSpan,a=n.dateSpan,l=[o.range.start,o.range.end,a.range.start,a.range.end];l.sort(t.compareNumbers);for(var s={},c=0,d=r;co.start)return g.endDelta=d,g;return null}(s,e,o.subjectEl.classList.contains("fc-start-resizer"),c.range,a.pluginSystem.hooks.eventResizeJoinTransforms)),d&&(g=t.applyMutationToEventStore(l,a.eventUiBases,d,a),h.mutatedEvents=g,r.component.isInteractionValid(h)||(u=!0,d=null,g=null,h.mutatedEvents=null)),g?a.dispatch({type:"SET_EVENT_RESIZE",state:h}):a.dispatch({type:"UNSET_EVENT_RESIZE"}),u?t.disableCursor():t.enableCursor(),n||(d&&S(s,e)&&(d=null),r.validMutation=d,r.mutatedRelevantEvents=g)},r.handleDragEnd=function(e){var n=r.component.calendar,i=r.component.view,o=r.eventRange.def,a=r.eventRange.instance,l=new t.EventApi(n,o,a),s=r.relevantEvents,c=r.mutatedRelevantEvents;n.publiclyTrigger("eventResizeStop",[{el:r.draggingSeg.el,event:l,jsEvent:e.origEvent,view:i}]),r.validMutation?(n.dispatch({type:"MERGE_EVENTS",eventStore:c}),n.publiclyTrigger("eventResize",[{el:r.draggingSeg.el,startDelta:r.validMutation.startDelta||t.createDuration(0),endDelta:r.validMutation.endDelta||t.createDuration(0),prevEvent:l,event:new t.EventApi(n,c.defs[o.defId],a?c.instances[a.instanceId]:null),revert:function(){n.dispatch({type:"MERGE_EVENTS",eventStore:s})},jsEvent:e.origEvent,view:i}])):n.publiclyTrigger("_noEventResize"),r.draggingSeg=null,r.relevantEvents=null,r.validMutation=null};var o=n.component,a=r.dragging=new f(o.el);a.pointer.selector=".fc-resizer",a.touchScrollAllowed=!1,a.autoScroller.isEnabled=o.opt("dragScroll");var l=r.hitDragging=new m(r.dragging,t.interactionSettingsToStore(n));return l.emitter.on("pointerdown",r.handlePointerDown),l.emitter.on("dragstart",r.handleDragStart),l.emitter.on("hitupdate",r.handleHitUpdate),l.emitter.on("dragend",r.handleDragEnd),r}return r(n,e),n.prototype.destroy=function(){this.dragging.destroy()},n.prototype.querySeg=function(e){return t.getElSeg(t.elementClosest(e.subjectEl,this.component.fgSegSelector))},n}(t.Interaction);var M=function(){function e(e){var n=this;this.isRecentPointerDateSelect=!1,this.onSelect=function(e){e.jsEvent&&(n.isRecentPointerDateSelect=!0)},this.onDocumentPointerUp=function(e){var r=n,i=r.calendar,o=r.documentPointer,a=i.state;if(!o.wasTouchScroll){if(a.dateSelection&&!n.isRecentPointerDateSelect){var l=i.viewOpt("unselectAuto"),s=i.viewOpt("unselectCancel");!l||l&&t.elementClosest(o.downEl,s)||i.unselect(e)}a.eventSelection&&!t.elementClosest(o.downEl,w.SELECTOR)&&i.dispatch({type:"UNSELECT_EVENT"})}n.isRecentPointerDateSelect=!1},this.calendar=e;var r=this.documentPointer=new s(document);r.shouldIgnoreMove=!0,r.shouldWatchScroll=!1,r.emitter.on("pointerup",this.onDocumentPointerUp),e.on("select",this.onSelect)}return e.prototype.destroy=function(){this.calendar.off("select",this.onSelect),this.documentPointer.destroy()},e}(),b=function(){function e(e,n){var r=this;this.receivingCalendar=null,this.droppableEvent=null,this.suppliedDragMeta=null,this.dragMeta=null,this.handleDragStart=function(e){r.dragMeta=r.buildDragMeta(e.subjectEl)},this.handleHitUpdate=function(e,n,o){var a=r.hitDragging.dragging,l=null,s=null,c=!1,d={affectedEvents:t.createEmptyEventStore(),mutatedEvents:t.createEmptyEventStore(),isEvent:r.dragMeta.create,origSeg:null};e&&(l=e.component.calendar,r.canDropElOnCalendar(o.subjectEl,l)&&(s=function(e,n,r){for(var o=i({},n.leftoverProps),a=0,l=r.pluginSystem.hooks.externalDefTransforms;a tag */ + text-decoration: none; + color: inherit; +} + +.fc-list-item-title a[href]:hover { + /* hover effect only on titles with hrefs */ + text-decoration: underline; +} + +/* message when no events */ +.fc-list-empty-wrap2 { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.fc-list-empty-wrap1 { + width: 100%; + height: 100%; + display: table; +} + +.fc-list-empty { + display: table-cell; + vertical-align: middle; + text-align: center; +} + +.fc-unthemed .fc-list-empty { + /* theme will provide own background */ + background-color: #eee; +} diff --git a/static/assets/libs/fullcalendar/list/main.js b/static/assets/libs/fullcalendar/list/main.js new file mode 100644 index 0000000..b855e4e --- /dev/null +++ b/static/assets/libs/fullcalendar/list/main.js @@ -0,0 +1,343 @@ +/*! +FullCalendar List View Plugin v4.3.0 +Docs & License: https://fullcalendar.io/ +(c) 2019 Adam Shaw +*/ + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core')) : + typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core'], factory) : + (global = global || self, factory(global.FullCalendarList = {}, global.FullCalendar)); +}(this, function (exports, core) { 'use strict'; + + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use + this file except in compliance with the License. You may obtain a copy of the + License at http://www.apache.org/licenses/LICENSE-2.0 + + THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED + WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, + MERCHANTABLITY OR NON-INFRINGEMENT. + + See the Apache Version 2.0 License for specific language governing permissions + and limitations under the License. + ***************************************************************************** */ + /* global Reflect, Promise */ + + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + + function __extends(d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + } + + var ListEventRenderer = /** @class */ (function (_super) { + __extends(ListEventRenderer, _super); + function ListEventRenderer(listView) { + var _this = _super.call(this, listView.context) || this; + _this.listView = listView; + return _this; + } + ListEventRenderer.prototype.attachSegs = function (segs) { + if (!segs.length) { + this.listView.renderEmptyMessage(); + } + else { + this.listView.renderSegList(segs); + } + }; + ListEventRenderer.prototype.detachSegs = function () { + }; + // generates the HTML for a single event row + ListEventRenderer.prototype.renderSegHtml = function (seg) { + var _a = this.context, view = _a.view, theme = _a.theme; + var eventRange = seg.eventRange; + var eventDef = eventRange.def; + var eventInstance = eventRange.instance; + var eventUi = eventRange.ui; + var url = eventDef.url; + var classes = ['fc-list-item'].concat(eventUi.classNames); + var bgColor = eventUi.backgroundColor; + var timeHtml; + if (eventDef.allDay) { + timeHtml = core.getAllDayHtml(view); + } + else if (core.isMultiDayRange(eventRange.range)) { + if (seg.isStart) { + timeHtml = core.htmlEscape(this._getTimeText(eventInstance.range.start, seg.end, false // allDay + )); + } + else if (seg.isEnd) { + timeHtml = core.htmlEscape(this._getTimeText(seg.start, eventInstance.range.end, false // allDay + )); + } + else { // inner segment that lasts the whole day + timeHtml = core.getAllDayHtml(view); + } + } + else { + // Display the normal time text for the *event's* times + timeHtml = core.htmlEscape(this.getTimeText(eventRange)); + } + if (url) { + classes.push('fc-has-url'); + } + return '' + + (this.displayEventTime ? + '' + + (timeHtml || '') + + '' : + '') + + '' + + '' + + '' + + '' + + '' + + core.htmlEscape(eventDef.title || '') + + '' + + '' + + ''; + }; + // like "4:00am" + ListEventRenderer.prototype.computeEventTimeFormat = function () { + return { + hour: 'numeric', + minute: '2-digit', + meridiem: 'short' + }; + }; + return ListEventRenderer; + }(core.FgEventRenderer)); + + /* + Responsible for the scroller, and forwarding event-related actions into the "grid". + */ + var ListView = /** @class */ (function (_super) { + __extends(ListView, _super); + function ListView(context, viewSpec, dateProfileGenerator, parentEl) { + var _this = _super.call(this, context, viewSpec, dateProfileGenerator, parentEl) || this; + _this.computeDateVars = core.memoize(computeDateVars); + _this.eventStoreToSegs = core.memoize(_this._eventStoreToSegs); + var eventRenderer = _this.eventRenderer = new ListEventRenderer(_this); + _this.renderContent = core.memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer)); + _this.el.classList.add('fc-list-view'); + var listViewClassNames = (_this.theme.getClass('listView') || '').split(' '); // wish we didn't have to do this + for (var _i = 0, listViewClassNames_1 = listViewClassNames; _i < listViewClassNames_1.length; _i++) { + var listViewClassName = listViewClassNames_1[_i]; + if (listViewClassName) { // in case input was empty string + _this.el.classList.add(listViewClassName); + } + } + _this.scroller = new core.ScrollComponent('hidden', // overflow x + 'auto' // overflow y + ); + _this.el.appendChild(_this.scroller.el); + _this.contentEl = _this.scroller.el; // shortcut + context.calendar.registerInteractiveComponent(_this, { + el: _this.el + // TODO: make aware that it doesn't do Hits + }); + return _this; + } + ListView.prototype.render = function (props) { + var _a = this.computeDateVars(props.dateProfile), dayDates = _a.dayDates, dayRanges = _a.dayRanges; + this.dayDates = dayDates; + this.renderContent(this.eventStoreToSegs(props.eventStore, props.eventUiBases, dayRanges)); + }; + ListView.prototype.destroy = function () { + _super.prototype.destroy.call(this); + this.renderContent.unrender(); + this.scroller.destroy(); // will remove the Grid too + this.calendar.unregisterInteractiveComponent(this); + }; + ListView.prototype.updateSize = function (isResize, viewHeight, isAuto) { + _super.prototype.updateSize.call(this, isResize, viewHeight, isAuto); + this.eventRenderer.computeSizes(isResize); + this.eventRenderer.assignSizes(isResize); + this.scroller.clear(); // sets height to 'auto' and clears overflow + if (!isAuto) { + this.scroller.setHeight(this.computeScrollerHeight(viewHeight)); + } + }; + ListView.prototype.computeScrollerHeight = function (viewHeight) { + return viewHeight - + core.subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller + }; + ListView.prototype._eventStoreToSegs = function (eventStore, eventUiBases, dayRanges) { + return this.eventRangesToSegs(core.sliceEventStore(eventStore, eventUiBases, this.props.dateProfile.activeRange, this.nextDayThreshold).fg, dayRanges); + }; + ListView.prototype.eventRangesToSegs = function (eventRanges, dayRanges) { + var segs = []; + for (var _i = 0, eventRanges_1 = eventRanges; _i < eventRanges_1.length; _i++) { + var eventRange = eventRanges_1[_i]; + segs.push.apply(segs, this.eventRangeToSegs(eventRange, dayRanges)); + } + return segs; + }; + ListView.prototype.eventRangeToSegs = function (eventRange, dayRanges) { + var _a = this, dateEnv = _a.dateEnv, nextDayThreshold = _a.nextDayThreshold; + var range = eventRange.range; + var allDay = eventRange.def.allDay; + var dayIndex; + var segRange; + var seg; + var segs = []; + for (dayIndex = 0; dayIndex < dayRanges.length; dayIndex++) { + segRange = core.intersectRanges(range, dayRanges[dayIndex]); + if (segRange) { + seg = { + component: this, + eventRange: eventRange, + start: segRange.start, + end: segRange.end, + isStart: eventRange.isStart && segRange.start.valueOf() === range.start.valueOf(), + isEnd: eventRange.isEnd && segRange.end.valueOf() === range.end.valueOf(), + dayIndex: dayIndex + }; + segs.push(seg); + // detect when range won't go fully into the next day, + // and mutate the latest seg to the be the end. + if (!seg.isEnd && !allDay && + dayIndex + 1 < dayRanges.length && + range.end < + dateEnv.add(dayRanges[dayIndex + 1].start, nextDayThreshold)) { + seg.end = range.end; + seg.isEnd = true; + break; + } + } + } + return segs; + }; + ListView.prototype.renderEmptyMessage = function () { + this.contentEl.innerHTML = + '
' + // TODO: try less wraps + '
' + + '
' + + core.htmlEscape(this.opt('noEventsMessage')) + + '
' + + '
' + + '
'; + }; + // called by ListEventRenderer + ListView.prototype.renderSegList = function (allSegs) { + var segsByDay = this.groupSegsByDay(allSegs); // sparse array + var dayIndex; + var daySegs; + var i; + var tableEl = core.htmlToElement('
'); + var tbodyEl = tableEl.querySelector('tbody'); + for (dayIndex = 0; dayIndex < segsByDay.length; dayIndex++) { + daySegs = segsByDay[dayIndex]; + if (daySegs) { // sparse array, so might be undefined + // append a day header + tbodyEl.appendChild(this.buildDayHeaderRow(this.dayDates[dayIndex])); + daySegs = this.eventRenderer.sortEventSegs(daySegs); + for (i = 0; i < daySegs.length; i++) { + tbodyEl.appendChild(daySegs[i].el); // append event row + } + } + } + this.contentEl.innerHTML = ''; + this.contentEl.appendChild(tableEl); + }; + // Returns a sparse array of arrays, segs grouped by their dayIndex + ListView.prototype.groupSegsByDay = function (segs) { + var segsByDay = []; // sparse array + var i; + var seg; + for (i = 0; i < segs.length; i++) { + seg = segs[i]; + (segsByDay[seg.dayIndex] || (segsByDay[seg.dayIndex] = [])) + .push(seg); + } + return segsByDay; + }; + // generates the HTML for the day headers that live amongst the event rows + ListView.prototype.buildDayHeaderRow = function (dayDate) { + var dateEnv = this.dateEnv; + var mainFormat = core.createFormatter(this.opt('listDayFormat')); // TODO: cache + var altFormat = core.createFormatter(this.opt('listDayAltFormat')); // TODO: cache + return core.createElement('tr', { + className: 'fc-list-heading', + 'data-date': dateEnv.formatIso(dayDate, { omitTime: true }) + }, '' + + (mainFormat ? + core.buildGotoAnchorHtml(this, dayDate, { 'class': 'fc-list-heading-main' }, core.htmlEscape(dateEnv.format(dayDate, mainFormat)) // inner HTML + ) : + '') + + (altFormat ? + core.buildGotoAnchorHtml(this, dayDate, { 'class': 'fc-list-heading-alt' }, core.htmlEscape(dateEnv.format(dayDate, altFormat)) // inner HTML + ) : + '') + + ''); + }; + return ListView; + }(core.View)); + ListView.prototype.fgSegSelector = '.fc-list-item'; // which elements accept event actions + function computeDateVars(dateProfile) { + var dayStart = core.startOfDay(dateProfile.renderRange.start); + var viewEnd = dateProfile.renderRange.end; + var dayDates = []; + var dayRanges = []; + while (dayStart < viewEnd) { + dayDates.push(dayStart); + dayRanges.push({ + start: dayStart, + end: core.addDays(dayStart, 1) + }); + dayStart = core.addDays(dayStart, 1); + } + return { dayDates: dayDates, dayRanges: dayRanges }; + } + + var main = core.createPlugin({ + views: { + list: { + class: ListView, + buttonTextKey: 'list', + listDayFormat: { month: 'long', day: 'numeric', year: 'numeric' } // like "January 1, 2016" + }, + listDay: { + type: 'list', + duration: { days: 1 }, + listDayFormat: { weekday: 'long' } // day-of-week is all we need. full date is probably in header + }, + listWeek: { + type: 'list', + duration: { weeks: 1 }, + listDayFormat: { weekday: 'long' }, + listDayAltFormat: { month: 'long', day: 'numeric', year: 'numeric' } + }, + listMonth: { + type: 'list', + duration: { month: 1 }, + listDayAltFormat: { weekday: 'long' } // day-of-week is nice-to-have + }, + listYear: { + type: 'list', + duration: { year: 1 }, + listDayAltFormat: { weekday: 'long' } // day-of-week is nice-to-have + } + } + }); + + exports.ListView = ListView; + exports.default = main; + + Object.defineProperty(exports, '__esModule', { value: true }); + +})); diff --git a/static/assets/libs/fullcalendar/list/main.min.css b/static/assets/libs/fullcalendar/list/main.min.css new file mode 100644 index 0000000..18446f4 --- /dev/null +++ b/static/assets/libs/fullcalendar/list/main.min.css @@ -0,0 +1 @@ +.fc-event-dot{display:inline-block;width:10px;height:10px;border-radius:5px}.fc-rtl .fc-list-view{direction:rtl}.fc-list-view{border-width:1px;border-style:solid}.fc .fc-list-table{table-layout:auto}.fc-list-table td{border-width:1px 0 0;padding:8px 14px}.fc-list-table tr:first-child td{border-top-width:0}.fc-list-heading{border-bottom-width:1px}.fc-list-heading td{font-weight:700}.fc-ltr .fc-list-heading-main{float:left}.fc-ltr .fc-list-heading-alt,.fc-rtl .fc-list-heading-main{float:right}.fc-rtl .fc-list-heading-alt{float:left}.fc-list-item.fc-has-url{cursor:pointer}.fc-list-item-marker,.fc-list-item-time{white-space:nowrap;width:1px}.fc-ltr .fc-list-item-marker{padding-right:0}.fc-rtl .fc-list-item-marker{padding-left:0}.fc-list-item-title a{text-decoration:none;color:inherit}.fc-list-item-title a[href]:hover{text-decoration:underline}.fc-list-empty-wrap2{position:absolute;top:0;left:0;right:0;bottom:0}.fc-list-empty-wrap1{width:100%;height:100%;display:table}.fc-list-empty{display:table-cell;vertical-align:middle;text-align:center}.fc-unthemed .fc-list-empty{background-color:#eee} \ No newline at end of file diff --git a/static/assets/libs/fullcalendar/list/main.min.js b/static/assets/libs/fullcalendar/list/main.min.js new file mode 100644 index 0000000..f954aac --- /dev/null +++ b/static/assets/libs/fullcalendar/list/main.min.js @@ -0,0 +1,6 @@ +/*! +FullCalendar List View Plugin v4.3.0 +Docs & License: https://fullcalendar.io/ +(c) 2019 Adam Shaw +*/ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@fullcalendar/core")):"function"==typeof define&&define.amd?define(["exports","@fullcalendar/core"],t):t((e=e||self).FullCalendarList={},e.FullCalendar)}(this,function(e,t){"use strict";var n=function(e,t){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)};function r(e,t){function r(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}var s=function(e){function n(t){var n=e.call(this,t.context)||this;return n.listView=t,n}return r(n,e),n.prototype.attachSegs=function(e){e.length?this.listView.renderSegList(e):this.listView.renderEmptyMessage()},n.prototype.detachSegs=function(){},n.prototype.renderSegHtml=function(e){var n,r=this.context,s=r.view,a=r.theme,i=e.eventRange,o=i.def,l=i.instance,d=i.ui,c=o.url,p=["fc-list-item"].concat(d.classNames),h=d.backgroundColor;return n=o.allDay?t.getAllDayHtml(s):t.isMultiDayRange(i.range)?e.isStart?t.htmlEscape(this._getTimeText(l.range.start,e.end,!1)):e.isEnd?t.htmlEscape(this._getTimeText(e.start,l.range.end,!1)):t.getAllDayHtml(s):t.htmlEscape(this.getTimeText(i)),c&&p.push("fc-has-url"),''+(this.displayEventTime?''+(n||"")+"":"")+'"+t.htmlEscape(o.title||"")+""},n.prototype.computeEventTimeFormat=function(){return{hour:"numeric",minute:"2-digit",meridiem:"short"}},n}(t.FgEventRenderer),a=function(e){function n(n,r,a,o){var l=e.call(this,n,r,a,o)||this;l.computeDateVars=t.memoize(i),l.eventStoreToSegs=t.memoize(l._eventStoreToSegs);var d=l.eventRenderer=new s(l);l.renderContent=t.memoizeRendering(d.renderSegs.bind(d),d.unrender.bind(d)),l.el.classList.add("fc-list-view");for(var c=0,p=(l.theme.getClass("listView")||"").split(" ");c
'+t.htmlEscape(this.opt("noEventsMessage"))+"
"},n.prototype.renderSegList=function(e){var n,r,s,a=this.groupSegsByDay(e),i=t.htmlToElement('
'),o=i.querySelector("tbody");for(n=0;n'+(r?t.buildGotoAnchorHtml(this,e,{class:"fc-list-heading-main"},t.htmlEscape(n.format(e,r))):"")+(s?t.buildGotoAnchorHtml(this,e,{class:"fc-list-heading-alt"},t.htmlEscape(n.format(e,s))):"")+"")},n}(t.View);function i(e){for(var n=t.startOfDay(e.renderRange.start),r=e.renderRange.end,s=[],a=[];n