diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml deleted file mode 100644 index 0fb0295..0000000 --- a/.github/workflows/pages.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Pages - -on: - push: - branches: - - develop # default branch - -jobs: - pages: - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - uses: actions/checkout@v2 - - name: Use Node.js 16.x - uses: actions/setup-node@v2 - with: - node-version: "16" - - name: Cache NPM dependencies - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.OS }}-npm-cache - restore-keys: | - ${{ runner.OS }}-npm-cache - - name: Install Dependencies - run: npm install - - name: Build - run: npm run build - - name: Deploy - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./public - publish_branch: master diff --git a/.gitignore b/.gitignore deleted file mode 100644 index c6bba59..0000000 --- a/.gitignore +++ /dev/null @@ -1,130 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variable files -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# Next.js build output -.next -out - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# vuepress v2.x temp and cache directory -.temp -.cache - -# Docusaurus cache and generated files -.docusaurus - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# yarn v2 -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* diff --git a/.gitmodules b/.nojekyll similarity index 100% rename from .gitmodules rename to .nojekyll diff --git a/2019/12/09/hot-to-install-nodejs-nginx-mongodb-on-ubuntu/index.html b/2019/12/09/hot-to-install-nodejs-nginx-mongodb-on-ubuntu/index.html new file mode 100644 index 0000000..6f99b0d --- /dev/null +++ b/2019/12/09/hot-to-install-nodejs-nginx-mongodb-on-ubuntu/index.html @@ -0,0 +1,115 @@ + +在ubuntu上从零搭建node.js + nginx + mongodb环境 - 西门闲话

在ubuntu上从零搭建node.js + nginx + mongodb环境

说到后端开发环境,最有名的莫过于LAMP和LNMP,最近由于node.js的强势崛起,越来越多的后端开发也开始试水node.js了。我最近也因为各种原因,前前后后总够构建了好几台node.js + nginx + mongodb的Linux服务器。

+

首先关于Linux服务器,比起CentOS来说,我更加喜欢ubuntu一点。所以无论是阿里云还是一些海外的vps服务器上,我也倾向选用ubuntu服务器,本贴也是基于ubuntu服务器里说明的。

+

1.开始前的一些准备

首先还是需要刷新一下ubuntu的包索引并安装build-essential和libssl-dev这2个包以及curl这个工具。

+
1
2
3
sudo apt-get update
sudo apt-get install build-essential libssl-dev
sudo apt-get isntall curl
+ +

2.安装node.js

关于安装node.js这一点,我不是很推荐使用apt-get 来安装node.js的环境。主要是因为node.js和io.js合并以后,版本迭代速度相当频繁(主要还是因为更多ES6的特性得到了支持)。今后很有可能会有在一台服务器上使用不同版本的node.js的需求。

+

这里推荐一个管理不同版本node.js的工具:nvm,官网: https://github.com/creationix/nvm  。安装nvm,如果前面你安装了curl的话可以

+
1
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
+

如果没有按照curl的话,也可以使用wget来进行安装

+
1
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
+ +

然后nvm就会自动安装到home目录下面的.nvm目录里,并会在.bashrc里自动添加nvm的环境变量。为了让环境变量生效,最简单的方法就是通过ssh或是telnet重新连接你的服务器。

+

安装完nvm后,就可以通过nvm来安装指定版本的node.js了。

+
1
2
3
4
5
# 列出可以安装的node版本号
nvm ls-remote

# 安装指定版本的node (当前最新版本为v5.7.1, LTS版是v4.3.2)
nvm install v4.3.2
+ +

3.安装nginx

由于ubuntu源(尤其是阿里云的源)上的nginx经常不是最新的,如果需要安装最新版本nginx的时候需要手动添加nginx的源。

+
1
2
3
4
5
6
7
8
9
10
11
# 添加nginx的mainline仓库
cd /tmp/ && wget http://nginx.org/keys/nginx_signing.key
sudo apt-key add nginx_signing.key

# 编辑/etc/apt/sources.list.d/nginx.list 添加下面2行内容,井号不需要
# deb http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
# deb-src http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
sudo vi /etc/apt/sources.list.d/nginx.list

# 更新源,并安装nginx
sudo apt-get update && sudo apt-get install nginx
+ +

在编辑/etc/apt/sources.list.d/nginx.list的时候需要注意,“ubuntu代号”需要根据ubuntu服务器的版本不同手动调整的,比如14.04是trusty。通过下面的命令可以获取ubuntu的代号。

+
1
lsb_release -cs
+ +

4.安装mongodb

同样和nginx有同样的问题,要安装最新3.2版本的mongodb也需要手动添加ubuntu的源。

+
1
2
3
4
5
6
7
8
9
10
11
# 导入mongodb的public key
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927

# 生成mongodb的源list
echo "deb http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list

# 更新源
sudo apt-get update

# 安装最新版本的mongodb
sudo apt-get install -y mongodb-org
+ +

以上一台node.js + nginx + mongodb的ubuntu服务器就完成了。

+
作者

Sebastian Qu

发布于

2019-12-09

更新于

2023-07-04

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git "a/2020/04/01/\351\230\277\351\207\214\344\272\221Kubernetes\344\270\212\347\272\277\350\270\251\345\235\221\350\256\260/index.html" "b/2020/04/01/\351\230\277\351\207\214\344\272\221Kubernetes\344\270\212\347\272\277\350\270\251\345\235\221\350\256\260/index.html" new file mode 100644 index 0000000..c8826c3 --- /dev/null +++ "b/2020/04/01/\351\230\277\351\207\214\344\272\221Kubernetes\344\270\212\347\272\277\350\270\251\345\235\221\350\256\260/index.html" @@ -0,0 +1,105 @@ + +阿里云Kubernetes上线踩坑记 - 西门闲话

阿里云Kubernetes上线踩坑记

1
2
Update:
2020-04-08 增加istio-ingressgateway高可用的设置
+ +

最近公司因为项目需要,在阿里云上部署了一个Kubernetes集群。虽然阿里云的文档说的还算细致,但是还是有些没有明确说明的细节。

+

1. 购买篇

申请项目预算的时候,只考虑到Worker节点,1个SLB节点以及域名和证书的预算。但是实际购买的时候发现还有许多额外的开销。

+

1.1 SNAT

这个和EIP一并购买,可以方便通过公网使用kubectl访问集群。关于SNAT网关至今不是很明白需要购买这个服务的意义何在,只是为了一个EIP来访问集群吗?

+

1.2 Ingress

这个选上了后,阿里云会给你买个SLB而且还是带公网访问的,如果你后期考虑使用Istio的话,建议你集群创建后,直接停止这个SLB,以免产生额外的费用。

+

1.3 日志服务

通过阿里云的日志服务来收集应用的的日志,挺好用的。但是另外收费,如果有能力的自建日志服务的可不购买。

+

2. Istio

阿里云的Kubernetes集群完美集成了Istio,根据向导就能很简单的部署成功。

+

2.1 额外的SLB

Istio的Gateway 需要绑定一个新的SLB,和Ingress的SLB不能是同一个,又是一笔额外的开销

+

2.2 集群外访问

这个在阿里云的Istio FAQ中有提到,按照指导很容易解决

+

2.2 SLB的443监听

为了方便443端口的证书绑定,我们直接删除了SLB上原有的443监听(TCP协议), 重新建了一个443监听(HTTPS协议),指向和80端口同样的虚拟服务器组。但是设置健康检查时一直出错,经过排查发现SLB健康检查发送的请求协议是HTTP 1.0的,Istio的envoy直接反悔了426(Upgrade Required)这个状态码,所以我们无奈只能把健康检查的检查返回状态改为http_4xx,这样就能通过SLB的健康检查了。

+

2.3 istio-ingressgateway的高可用

istio-ingressgateway要达成高可用,只需要增加通过伸缩POD就可以实现,于istio-ingressgateway对应的SLB中的虚拟服务器组也会自动增加,完全不需要进行额外的手动设定。

+

由于istio-ingressgateway中挂载了HPAHorizontalPodAutoscaler(简称HPA),通常三节点的集群中最小POD数只有1台,在3节点的集群中,要实现高可用,需要手动修改HPA,增加最小POD数。

+
+

基本上现在遇到了这些坑,再有在总结吧。

+
作者

Sebastian Qu

发布于

2020-04-01

更新于

2023-07-04

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git "a/2020/05/09/lagrange\351\241\271\347\233\256\345\233\236\351\241\276/index.html" "b/2020/05/09/lagrange\351\241\271\347\233\256\345\233\236\351\241\276/index.html" new file mode 100644 index 0000000..4e1ba05 --- /dev/null +++ "b/2020/05/09/lagrange\351\241\271\347\233\256\345\233\236\351\241\276/index.html" @@ -0,0 +1,124 @@ + +Largrange项目架构与设计回顾 (一) - 西门闲话

Largrange项目架构与设计回顾 (一)

+

项目背景

+
+

从去年年底开始一个老客户希望在他们的一个传统的机械设备(后面略称 E机关 )上外装一个Android设备。 Android设备和 E机关 之间通过串口或是RJ45接口进行数据交互,主要是Android设备获取 E机关 内部的数据,并不会通过接口来控制 E机关 。 Android设备则通过4G 来和平台交互,上报Android设备的状态数据,同时接受平台的控制。以此来实现让传统机械设备也能拥抱物联网的概念。

+

最后围绕着客户的需求分成了3个项目来并行推进

+
    +
  1. Android设备的硬件设备的设计、选材、样机制作与量产规划
  2. +
  3. 在Android设备上,进行与E机关以及平台进行交互的APP开发
  4. +
  5. 用于Android设备交互的平台的架构、设计与开发
  6. +
+

我们平台Team就负责 3.用于Android设备交互的平台 并命名Lagrange (拉格朗日) 。

+

设计&架构

整个平台这块不仅需要向Android设备的APP提供数据交互接口,还需要有一个供相关运营人员使用的前端Web应用,以及与云平台(主要是Aliyun) 交互的功能。所以考虑到多方面使用微服务的架构来实现整个平台端。
由于E机关的工作工况不能保证长期较的稳定的连接到4G网络,所以我们并不考虑使用Socket长连接的方式来做APP和平台之间的数据交互,要实现平台对Android设备进行反控的话,只能实现推送方式来把控制命令下发给Android设备,所以还需要有一个第三方推送服务商交互的服务。同时控制推送也有实时和非实时以及定期推送的需求,所以可能还需要一个任务调度的服务。

+

根据对上述业务进行梳理,我们将项目分成几个服务

+
    +
  • 提供Android设备的交互接口的 App Service
  • +
  • 提供运营人员使用前端Web应用 Platform Web Service
  • +
  • 前端Web应用使用到的一些接口 Platform Service
  • +
  • 负责第三方推送服务商交互的 Push Service
  • +
  • 提供云平台鉴权用的 Auth Service
  • +
  • 用来管理任务调度的 Cron Service
  • +
+

由于前一个项目实施的时候没有使用容器部署,每当访问量峰值的时候,我们这些码农兼运维就各种加班,所以这次项目决定直接将服务容器化,同时选择了Kubernetes来管理容器。由于客观原因线上的Kubernetes直接购买了Aliyun的托管版Kubernetes服务。 内部的开发测试环境则使用了 Rancher 2.0 来构建Kubernetes集群。

+

技术选型

a. 后端服务选型

由于不考虑长连接的原因,所以在后端服务在技术选型上基本就不考虑Netty了,直接上Spring大礼包。

+
    +
  • Spring Boot 开发接口
  • +
  • Spring Data 配合 JPA 来进行数据的持久化
  • +
  • Spring Cloud Kubenetes 来做数据的Config的autoreload
  • +
  • Quartz 负责处理任务调度
  • +
+

服务之间调用都使用HTTP服务, 服务发现也有Kubernetes的DNS机制支持。服务网格则选择了比较成熟的Istio,主要还是Aliyun的Kubernetes可以集成Istio,部署和使用都相当方便。

+

b. 前端Web应用选型

因为前端Web应用主要是给运营人员使用,所以我们考虑使用Single Page Application来做个前后端分离的Web应用。框架这块由于团队成员基本上没有什么前端开发经验,基本都是后台写Java的码农,所以框架选择有点随性,直接就点名了vue.js。前端控件库则用的是阿里系的Antd。

+

c. 数据持久层

主数据库选择了mysql,缓存用的redis。这些都是团队比较熟悉的。由于一些特殊的业务需求和使用场景,我们还加了一个mongodb来做为一个副数据库,主要存放一些特殊业务使用的数据。

+

d. DevOps

用了相当传统的GitLab CE 加上Jenkins的组合,实现前后端代码的自动编译,推送到私有的镜像仓库(使用Aliyun的镜像仓库服务)

+

最后

以上基本是项目最早做设计时候的各种考量。暂时先写这么多,过两天再回顾一下当初设计上有哪些觉得不足的地方。

+
作者

Sebastian Qu

发布于

2020-05-09

更新于

2023-07-04

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git "a/2020/05/21/Lagrange\351\241\271\347\233\256\345\233\236\351\241\2762/index.html" "b/2020/05/21/Lagrange\351\241\271\347\233\256\345\233\236\351\241\2762/index.html" new file mode 100644 index 0000000..032ae04 --- /dev/null +++ "b/2020/05/21/Lagrange\351\241\271\347\233\256\345\233\236\351\241\2762/index.html" @@ -0,0 +1,104 @@ + +Largrange项目架构与设计回顾 (二) - 西门闲话

Largrange项目架构与设计回顾 (二)

Largrange项目架构与设计回顾 (一) 里面我讲了一下项目开始架构和技术选型的一些内容。这一章来聊聊业务设计上的这点事。

+

总体来说项目设计的时候,我们对业务模块的划分和拆解总体上来说都是遵循着高内聚,低耦合的原则来进行划分的。大部分和业务相关的服务,都还是能很好的进行功能划分的。但是有一些和具体业务关联性并不是很高的服务在界定与实现时出现了一些问题。

+

任务调度服务

由于平台方面会有一些控制命令下发给Android设备,而且下发的命令并不一定都是实时的,大部分都是指定一个时间来下发,所以在设计的时候就考虑到需要一个任务调度服务(以下略称cron),来处理这些下发指令以及未来可能会有的定时批处理任务的业务需求。技术选型的时候考虑到整个服务是构建在Kubernetes上的一个分布式的微服务架构,所以就没有选择Spring Scheduler,而是使用了Quarter来支撑整个cron

+

从技术选型上来说cron没有什么问题,但是在设计如何使用cron上还是有点问题的, 我们先来看看已推送服务为例,整个平台中cron的处理流程是怎样的。

+
    +
  1. Platform 调用 cron的创建定时任务Job的接口(接口的参数为推送时使用的相关参数)
  2. +
  3. cron 创建Quartz的Job和Trigger,并将Job和Trigger的Name(Quartz中Job和Trigger的唯一标识符)返回个Platform
  4. +
  5. cron 在指定时间触发推送的Job,即调用Push服务的推送接口
  6. +
  7. Push的推送接口调用第三方服务商的推送服务,并将第三方推送服务的调用结果返回给cron
  8. +
  9. cron通过调用Platform预留的推送服务回调地址将第三方推送服务调用接口返还给Platform
  10. +
  11. Platform 将第三方推送服务的调用结果留档保存,并继续业务处理
  12. +
+

设计之初考虑到不想在cron中牵扯到具体的业务, 所以设计了一个Platform的回调接口来处理推送后的具体业务处理。虽然保证了cron尽量减少了和业务逻辑接触与数据库的访问。但是前前后后要访问集群内部的其他微服务2次,额外增加了网络开销,以及因为网络通信造成额外的通信失败风险。而且针对每种不同的定时任务,都需要额外开发一个QuartzJobBean,很难形成统一的QuartzJobBean来进行处理。今后如果还有相似项目还是需要重新考量一下如何设计一个更加完善的定时任务。

+

总结

暂时就想到了这些东西,今后想到啥还会继续在这里补存。

+
作者

Sebastian Qu

发布于

2020-05-21

更新于

2023-07-04

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git "a/2020/12/29/2020\345\271\264\351\230\205\350\257\273\346\200\273\347\273\223/index.html" "b/2020/12/29/2020\345\271\264\351\230\205\350\257\273\346\200\273\347\273\223/index.html" new file mode 100644 index 0000000..0fdf3b7 --- /dev/null +++ "b/2020/12/29/2020\345\271\264\351\230\205\350\257\273\346\200\273\347\273\223/index.html" @@ -0,0 +1,127 @@ + +2020年阅读总结 - 西门闲话

2020年阅读总结

多灾多难的2020年已经没有几天了,整个2020年要说还真是像一记刹车,去年很多想法,计划都有因为疫情原因搁置了。平时的生活更是从宅家变成了重度宅家的状态,自然就多出了许多大段大段的连续时间,可以用来好好玩玩大作,看看小说。仔细盘点一下今年总共看了10本小说外加2本科普书籍( 2020年阅读书单
),算是自2018年以来读书最多的一个年份了。其中不乏拖了近3年的那本《西方文化中的数学》。接着就来絮叨絮叨今年看的这12本书吧。

+
+
+

《莱博维茨的赞歌》

+
+

第一次被安利《莱博维茨的赞歌》的机核的电台节目辐射-视觉、音乐以及文学, 当时听完节目就相当好奇,可以被一部载入史册的CRPG游戏誉为精神文本的小说到底是个什么样子的。但是当时新星版的译本几乎就是绝本了,某鱼的二手价也要已经飙到了3位数。直到今年年初在核市节目上听到了中信版译本的消息,才有机会买来拜读。

+

全书通过3个故事讲述了核战灭世后莱博维茨修道院从保卫文明,到重建,然后看着文明再度毁灭的故事。各种无不透露着辐射那股 War, War never changed的味道。

+
+

《占星术杀人事件》《屋顶上的小丑》《亿男》

+
+

这3本书都是2019年上海书展上买的。前两本都是岛田庄司的推理名篇,其中《占星术杀人事件》在本格派推理史上有着非凡的地位,各种被抄袭从未被超越过。《屋顶上的小丑》要是最吸引人的话就是多线叙事,每条线都掐在恰如其分的地方。《亿男》则是挂着悬疑羊头卖着人生哲理。

+
+

《神经漫游者》

+
+

威廉吉布森的蔓生三部曲之一,开创了赛博朋克的科幻流派,说实话这本书我看的很痛苦,很多地方实在读不懂,到现在我还是不明白那些无法读懂的地方到底是原著如此,还是翻译的不行,亦或者是Kindle版排版太烂了。

+
+

《神们自己》,《神的九十亿个名字》

+
+

阿瑟克拉克的中篇小说和短篇小说集,这两本书阅读体验奇佳,尤其是在读完《神经漫游者》后,虽然三本书被Amazon打在同一个包里来卖的,但是光从排版上就比《神经漫游者》高出不少,所以才会有我对后者排版问题的质疑。

+

《神们自己》三线叙事,讲述了2个平行宇宙中的2个物种在能源,种族存续,家庭伦理上的各种讨论和故事。 《神的九十亿个名字》作为短篇小说集收录了好几篇风格题材迥异的小说,花上十几分钟读上一个故事是相当惬意的。

+
+

《佐伊的战争》 《人类决裂》 《万物的终结》

+
+

约翰斯卡尔齐的《老人的战争》系列的后3部,前三部大概是3,4年前读的吧,在这个宇宙观里最吸引人的设定就绿皮的人类防卫军战士,以及跨种族间的政治斗争。系列的六本书相当于在这个大设定下讲的六个故事。每个故事虽说都是独立的故事,但是前后皆有关联,而且最重要的就是每本的长度都算不上很长,相当适合于一口气读完。

+
+

《西方文化中的数学》

+
+

这本书大概是从2017年开始读的,当时意气奋发觉得对于理科生出身的我,书中内容应该很好理解,没想到最后不光是文化还是数学读的都是一知半解。只能说自大了。

+
+

《一想到还有95%的问题留给人类,我就放心了》

+
+

大概是18年还是19年前后对量子物理和宇宙物理开始有了兴趣,虽然各中理论并不是很了解,但是仍然很好奇的买了这本书,全书插画相当有趣,同时使用浅显易懂的语言来解释各种复杂的物理现象,以及当今人类物理知识的边界。强烈推荐给想假装成学霸的朋友。

+
+
+

2021年读什么

+
+

具体读啥还没想好基本上还是以科幻和悬疑为主,但是肯定会读的应该有海明威的《丧钟为谁而鸣》和小林泰三的《醉步男》吧。

+
作者

Sebastian Qu

发布于

2020-12-29

更新于

2023-07-04

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git "a/2021/03/15/\351\207\215\345\255\246Java-\344\270\200-\346\263\233\345\236\213/index.html" "b/2021/03/15/\351\207\215\345\255\246Java-\344\270\200-\346\263\233\345\236\213/index.html" new file mode 100644 index 0000000..4ce282b --- /dev/null +++ "b/2021/03/15/\351\207\215\345\255\246Java-\344\270\200-\346\263\233\345\236\213/index.html" @@ -0,0 +1,137 @@ + +重学Java (一) 泛型 - 西门闲话

重学Java (一) 泛型

1. 前言

泛型编程自从 Java 5.0 中引入后已经超过15个年头了。对于现在的 Java 码农来说熟练使用泛型编程已经是家常便饭的事情了。所以本文就在不对泛型的基础使用在做说明了。 如果你还不会使用泛型的话,可以参考下面两个链接

+ +

这篇文章就简答聊一下,我实际在开发工作中很少用的到泛型方法这个知识点,以及在实际项目中有哪些东西会使用到泛型。

+

2. 泛型方法

在阅读代码的时候我们经常会看到下面这样的方法 (这段代码摘自 java.util.AbstractCollection)

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 public <T> T[] toArray(T[] a) {
// Estimate size of array; be prepared to see more or fewer elements
int size = size();
T[] r = a.length >= size ? a :
(T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
Iterator<E> it = iterator();

for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) { // fewer elements than expected
if (a == r) {
r[i] = null; // null-terminate
} else if (a.length < i) {
return Arrays.copyOf(r, i);
} else {
System.arraycopy(r, 0, a, 0, i);
if (a.length > i) {
a[i] = null;
}
}
return a;
}
r[i] = (T)it.next();
}
// more elements than expected
return it.hasNext() ? finishToArray(r, it) : r;
}
+ +

那么 pulic 关键字后面的那个 <T> 就是用来标记这个方法是一个泛型方法。 那什么是泛型方法呢。

+

官方的解释是这样的

+
1
Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.
+ +

通俗点来将就是将一个方法泛型化,让一个普通的类的某一个方法具有泛型功能。 如果在一个泛型类中增加一个泛型方法,那这个泛型方法就可以有一套独立于这个类的泛型类型。

+

通过一个简单的例子, 我们来看看

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* GenericClass 这个泛型类是一个简单的套皮的 HashMap
*/
public class GenericClass<K, V> {

private Map<K, V> map = new HashMap<>();

public V put(K key, V value) {
return map.put(key, value);
}

public V get(K key) {
return map.get(key);
}

// 泛型方法 genericMethod 可以接受一个全新的、作用域只限本函数的泛型类型T
public <T> T genericMethod(T t) {
return t;
}

}
+ +

实际使用起来

+
1
2
3
4
5
6
GenericClass<String, Integer> map = new GenericClass<>();
// put 和 get 方法的参数必须使用定义时指定的 String 和 Integer
System.out.println(map.put("One", 1));
System.out.println(map.get("One"));
// 泛型方法 genericMethod 就可以接受一个 String 和 Integer 以外的类型
System.out.println(map.genericMethod(new Double(1.0)).getClass());
+ +

我们再来看看 JDK 中使用到泛型方法的例子。我们最常使用的泛型容器 ArrayList 中有个 toArray 方法。JDK 在它的实现中就提供了两个版本,其中一个就是泛型方法的版本

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
// 这是一个普通版本,返回一个Object的数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}

// 这是一个泛型方法的版本,将容器里存储的元素输出到 T[] 数组中。 其中 T 必须是 E 的父类,否则 System.arraycopy 会抛出 ArrayStoreException 异常
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
}
+ +

泛型方法总体上来说就是可以给与现有的方法实现上,增加一个更加灵活的实现可能。

+

3. 实战应用

在实际的项目中,对于泛型的使用,除了像倾倒垃圾一样往泛型容易里塞各种 java bean 和其他泛型对象。还能怎么使用泛型呢?

+

我们在实际的一些项目中,会对数据库中的一些表(多数时候是全部)先实现 CRUD (Create, Read, Update, Delete)的操作,并从这些操作中延伸出一些简单的 REST 风格的 WebAPI 接口,然后才会根据实际业务需要实现一些更复杂的业务接口。

+

大体上会是下面这个样子。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

// 这是一个简单的 Entity 对象
// 通常现在的 Java 应用都会使用到 Lombok 和 Spring Boot
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Entity
@Table(name = "user")
public class User {
@Id
private Long id;
private String username;
private String password;
}

// 然后这个是 DAO 接口继承自 spring-data 的 JpaRepository
public interface UserDao extends JpaRepository<User, Long> {
}

// 在来是一个访问 User 资源的 Service 和他的实现
public interface UserService {
List<User> findAll();
Optional<User> findById(Long id);
User save (User user)
void deleteById(Long id);
}

@Service
public class UserSerivceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}

@Override
public List<User> findAll() {
return this.dao.findAll();
}

@Override
public Optional<User> findById(Long id) {
return this.dao.findById(id);
}

@Override
public User save(User user) {
return this.dao.save(user);
}

@Override
public void deleteById(Long id) {
this.dao.deleteById(id);
}
}

// 最后就是 WebAPI 的接口了
@RestController
@RequestMapping("/user/")
public class UserController{
private UserService userService;
public UserController(userService userService) {
this.userService = userService;
}

@GetMapping
@ResponseBody
public List<User> fetch() {
return this.userService.findAll();
}

@GetMapping("{id}")
@ResponseBody
public User get(@PathVariable("id") Long id) {
// 由于是示例这里就不考虑没有数据的情况了
return this.userService.findById(id).get();
}

@PostMapping
@ResponseBody
public User create(@RequestBody User user) {
return this.userService.save(user);
}

@PutMapping("{id}")
@ResponseBody
public User update(@RequestBody User user) {
return this.userService.save(user);
}

@DeleteMapping("{id}")
@ResponseBody
public User delete(@PathVariable("id") Long id) {
User user = this.userService.findById(id);
this.userService.deleteById(id);
return user;
}
}
+ +

大致一个表的一套相关接口就是这个样子的。如果你的数据库中有大量表的话,而且每个表都需要提供 REST 风格的 WebAPI 接口的话,那么这将是一个相当枯燥的而又及其容易出错的工作。

+

为了不让这项枯燥而又容易犯错的工作占去我们宝贵的私人时间,我们可以通过泛型和继承的技巧来重构从 Service 层到 Controller 的这段代码(感谢 spring-data 提供了 JpaRepository, 让我们不至于从 DAO 层重构)

+

3.1 Service 层的重构

首先是 Service 接口的重构,我们 Service 层接口就是定义了一组 CRUD 的操作,我们可以将这组 CRUD 操作抽象到一个父接口,然后所有 Service 层的接口都将继承自这个父接口。而接口中出现的 Entity 和主键的类型(上例中 User 的主键 id 的类型是 Long)就可以用泛型来展现。

+
1
2
3
4
5
6
7
8
9
10
11
// 这里泛型表示 E 来指代 Entity, ID 用来指代 Entity 主键的类型
public interface ICrudService<E, ID> {
List<E> findAll();
Optional<E> findById(ID id);
E save(E e);
void deleteById(ID id);
}

// 然后 Service 层的接口,就可以简化成这样
public interface UserService extends ICrudService<User, Long> {
}
+ +

同样 Service 层的实现也可以使用相似的方法具体实现可以抽象到一个基类中。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 相比 ICrudService 这里有多了一个泛型 T 来代表 Entity 对应的 DAO, 我们的每一个 DAO 都继承自
// spring-data 的 JpaRepository 所以,这里可以使用到泛型的边界
public abstract class AbstractCrudService<T extends JpaRepository<E, ID>, E, ID> {
private T dao;
public AbstractCrudService(T dao) {
this.dao = dao;
}

public List<E> findAll() {
return this.dao.findAll();
}

public Optional<E> findById(ID id) {
return this.dao.findById(id);
}

public E save(E e) {
return this.dao.save(e);
}

public void deleteById(ID id) {
this.dao.deleteById(id);
}
}

// 那 Service 的实现类可以简化成这样
@Service
public class UserServiceImpl extends AbstractCrudService<UserDao, User, Long> implements UserService {
public UserServiceImpl(UserDao dao) {
supper(dao);
}
}
+ +

同样我们可以通过相同的方法来对 Controller 层进行重构

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Controller 层的基类
public abstract class AbstractCrudController<T extends ICrudService<E, ID>, E, ID> {
private T service;
public AbstractCrudController(T service) {
this.service = service;
}

@GetMapping
@ResponseBody
public List<E> fetch() {
return this.service.findAll();
}

@GetMapping("{id}")
@ResponseBody
public E get(@PathVariable("id") ID id) {
// 由于是示例这里就不考虑没有数据的情况了
return this.service.findById(id).get();
}

@PostMapping
@ResponseBody
public E create(@RequestBody E e) {
return this.service.save(e);
}

@PutMapping("{id}")
@ResponseBody
public E update(@RequestBody E e) {
return this.service.save(e);
}

@DeleteMapping("{id}")
@ResponseBody
public E delete(@PathVariable("id") ID id) {
E e = this.service.findById(id).get();
this.service.deleteById(id);
return e;
}
}

// 具体的 WebAPI
@RestController
@RequestMapping("/user/")
public class UserController extends AbstractCrudController<UserService, User, Long> {
public UserController(UserService service) {
super(service);
}
}
+ +

经过重构可以消减掉 Servcie 和 Controller 中的大量重复代码,使代码更容易维护了。

+

4. 结尾

关于泛型就简单的说这些了,泛型作为 Java 日常开发中一个常用的知识点,其实还有很多知识点可以供我们挖掘,奈何本人才疏学浅,这么多年工作下来,只积累出来这么点内容。

+

文末放上示例代码的代码库:

+ +
作者

Sebastian Qu

发布于

2021-03-15

更新于

2023-07-04

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git "a/2021/09/29/Spring-Cloud-Kubernetes\347\216\257\345\242\203\344\270\213\344\275\277\347\224\250Jasypt/index.html" "b/2021/09/29/Spring-Cloud-Kubernetes\347\216\257\345\242\203\344\270\213\344\275\277\347\224\250Jasypt/index.html" new file mode 100644 index 0000000..eaee61a --- /dev/null +++ "b/2021/09/29/Spring-Cloud-Kubernetes\347\216\257\345\242\203\344\270\213\344\275\277\347\224\250Jasypt/index.html" @@ -0,0 +1,135 @@ + +Spring Cloud Kubernetes环境下使用Jasypt - 西门闲话

Spring Cloud Kubernetes环境下使用Jasypt

前言

最近半年着手开始做了基于微服务的中台项目,整个项目的技术栈采用的是Java + Spring Cloud + Kubernetes + Istio

+

业务开放上还是相当顺利的。但是在安全审核上,运维组提出了一个简易。现在项目一些敏感配置,例如MySQL用户的密码,Redis的密码等现在都是明文保存在Kubernetes的ConfigMap中的(是的,我们并没有Nacos作为微服务的配置中心)。这样可能存在安全隐患。

+

首次尝试

既然有问题,那就解决问题。要给配置文件中的属性项目加密很简单,稍微Google一下,就有现成的方案了。

+

现在比较常用的解决方案就是集成Jasypt,然后通过jasypt-spring-boot-starter来融合进Spring。

+

POM包加入jasypt-spring-boot-starter

1
2
3
4
5
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
+ +

Dockerfile中增加java参数

1
2
...
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar app.jar --jasypt.encryptor.password=helloworld $PARAMS"]
+ +

在ConfigMap中添加加密属性

1
2
3
4
5
6
7
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
data:
application.yaml: |-
test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
+ +

利用actuator接口测试

management.endpoints.web.exposure.include属性中增加env,这样我们就可以通过调用/actuator/env来查看一下env接口返回的整个Spring 容器中所有的PropertySource。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
...
"propertySources": [
{
"name": "bootstrapProperties-configmap.demo.default",
"properties": {
"test2": {
"value": "Hello,world"
}
}
}
...
]
}
+ +

OK, 这下配置项已经加密了。问题解决了。

+

但是…

+

新的问题

自从项目集成了Jayspt以后,出现了一个奇怪的问题。每次项目试图通过修改ConfigMap的配置文件,然后试图通过spring-cloud-starter-kubernetes-fabric8-config来做自动Reload,都失败了。然而查阅应用日志,并没有出现任何异常。无奈只能打开spring-cloudjasypt-spring-bootDEBUG日志。

+

进过几天对日志和两边源代码的分析。终于找到了原因

+

原因

在Spring Boot启动时jasypt-spring-boot会将下面6种配置(并不仅限与这6种配置文件)

+
    +
  • Classpath下的application.yaml
  • +
  • Classpath下的bootstrap.yaml
  • +
  • 集群里名称为${spring.cloud.kubernetes.config.name}的ConfigMap
  • +
  • 集群里名称为${spring.cloud.kubernetes.config.name}-kubernetes的ConfigMap
  • +
  • Java启动参数
  • +
  • 环境变量
  • +
+

转换成jasypt-spring-boot自己的PropertySource实现类EncryptableMapPropertySourceWrapper

+

但是如果使用Kubernetes的ConfigMap来作微服务配置中心的时候,Spring Cloud会在ConfigurationChangeDetector中查找配置类org.springframework.cloud.bootstrap.config.BootstrapPropertySource, 并依据BootstrapPropertySource的类型来判断容器内的配置与集群中ConfigMap里的配置是否有差异,来触发配置reload。

+

由于jasypt-spring-boot已经将所有的配置文件转型成了EncryptableMapPropertySourceWrapper, 所以ConfigurationChangeDetector无法找到BootstrapPropertySource所以会一直任务ConfigMap的里的配置没有变化,导致整个Reload失效(无论是使用polling还是event方式)

+

解决问题

为了保证ConfigMap变化后自动Reload的功能,所以jasypt-spring-boot不能把BootstrapPropertySource转换成EncryptableMapPropertySourceWrapper

+

所以我们需要设置jasypt.encryptor.skip-property-sources配置项, Classpath中的application.yaml需要增加配置

+
1
2
3
jasypt:
encryptor:
skip-property-sources: org.springframework.cloud.bootstrap.config.BootstrapPropertySource
+ +

skip-property-sources配置项配置后,加密项目就不能配置在ConfigMap里了,毕竟已经被我们忽略了。那么我们只能另外找一个PropertySource来存放加密项目了。

+

Classpath中的两个Yaml由于编译时会被Maven打包进Jar文件,会牵涉多个CI/CD多个流程显然不合适,启动参数配置项的也要影响到Docker镜像制作这个流程。所以判断下来最适合的PropertySource就是环境变量了。

+

环境变量增加加密项

在Kubernetes的部署Yaml中,添加加密数据项application.test.str

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: demo
name: demo
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- env:
- name: TZ
value: Asia/Shanghai
- name: application.test.str
value: >-
ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
....
+ +

如果需要更加严密的加密方针的话,我们可以把环境变量的内容放进Kubernetes的Secrets中。

+

在ConfigMap中引用application.test.str

1
2
3
4
5
6
7
8
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
data:
application.yaml: |-
test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
test3: ${application.test.str}
+ +

通过actuator接口来测试

通过actuator\env接口来测试一下

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
...
"propertySources": [
{
"name": "bootstrapProperties-configmap.demo.default",
"properties": {
"test2": {
"value": "ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)"
},
"test3": {
"value": "Hello,world"
}
}
}
...
]
}
+ +

这样ConfigMap中的配置项test3就可以通过环境变量引用并使用加密配置项了。同时修改ConfigMap依然可以触发auto reload了。这下终于算是解决了。

+
作者

Sebastian Qu

发布于

2021-09-29

更新于

2023-07-04

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git "a/2022/09/23/AKS\344\270\255\351\207\215\345\206\231\350\247\204\345\210\231\350\270\251\345\235\221\345\260\217\350\256\260\345\275\225/index.html" "b/2022/09/23/AKS\344\270\255\351\207\215\345\206\231\350\247\204\345\210\231\350\270\251\345\235\221\345\260\217\350\256\260\345\275\225/index.html" new file mode 100644 index 0000000..812b324 --- /dev/null +++ "b/2022/09/23/AKS\344\270\255\351\207\215\345\206\231\350\247\204\345\210\231\350\270\251\345\235\221\345\260\217\350\256\260\345\275\225/index.html" @@ -0,0 +1,133 @@ + +AKS中重写规则踩坑小记录 - 西门闲话

AKS中重写规则踩坑小记录

前言

最近在做标准产品在不同云平台中的部署验证,有幸体验了一下微软的Azure。负责采购的运维部门这次采用了Application Gateway来搭配AKS(Azure Kubernetes Service)对外暴露服务,正好借着这个机会来体验一下Application Gateway

+

应用场景

    +
  1. 域名api.demo.com指向Application Gateway的IP地址
  2. +
  3. AKS内部2个Service, gateway-servicebackend-service分别需要通过Application Gateway对外暴露。
  4. +
  5. /gateway/指向gateway-service, 然后/backend/指向backend-service。而且两个Service都没有context-path,所以需要做一个Rewrite重写URI到Service的根目录上。
  6. +
+

定义重写集

打开AKS对应的应用程序网关设置 > 重写。选择添加重写集。在1. 名称和关联这个Tab上只需要填写名称这项即可(名称后面在做ingress时需要使用), 关联的传递规则不需要选择。2. 重写规则配置里添加一个重写规则,然后填上重写规则的名称,并添加条件(默认新建重写规则时,只会生成操作,不会生成条件)

+

条件做如下设置

    +
  • 要检查的变量类型 : 服务器变量
  • +
  • 服务器变量: request_uri
  • +
  • 区分大小写:
  • +
  • 运算符: 等号(=)
  • +
  • 要匹配的模式: /(gateway|backend)/?(.*)
  • +
+

操作做如下设置

    +
  • 重写类型: URL
  • +
  • 操作类型: 设置
  • +
  • 组件: URL路径和URL查询字符串
  • +
  • URL路径值: /{var_request_uri_2}
  • +
  • 重新计算路径映射: 不选中
  • +
  • URL查询字符串值: 留空不设值
  • +
+

特殊说明

操作里的URL路径值不能使用正则表达式GROUP替换组,例如$1$2之类的。Azure自己定义了一套对应的替换组命名规则。具体可以参考这个网页使用应用程序网关重写 HTTP 标头和 URL

+

另外一个需要注意一点,如果在条件里选择了服务器变量request_uri的时候,注意这个request_uri是完整的原始请求URI(携带了查询参数)。例如: 在请求http://api.demo.com/gateway/search?foo=bar&hello=world中,request_uri的值将为/gateway/search?foo=bar&hello=world。由于request_uri里包含了查询参数,所以在操作组件中建议勾选URL路径和URL查询字符串。如果只选择URL路径的情况下可能出现无法预期的错误。以我们上述的配置来说明。

+

对象URL: http://api.demo.com/gateway/search?foo=bar&hello=world

+ + + + + + + + + + + + + +
组件URL路径和URL查询字符串URL路径
结果/search?foo=bar&hello=world/search?foo=bar&hello=world?foo=bar&hello=world
+

ACK的Ingress设置

当选择了Application Gateway作为对外暴露Service的方式时,Kubernetes集群里(kube-system命名空间里)多一个Application Gateway Ingress Controller(Azure工单时通常会简称为agic)的Deployment,所以对外暴露服务时可以像传统nginx ingress controller一样添加一个Ingress对象即可(甚至配置也和ngic大致相同,只是多了2个annotations)

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
# 这里指定重写规则集(不是重写规则的名字)
appgw.ingress.kubernetes.io/rewrite-rule-set: rule-backend
# 指定说明你这里ingress的类型是agic
kubernetes.io/ingress.class: azure/application-gateway
name: backend-ingress
namespace: default
spec:
rules:
- host: api.demo.com
http:
paths:
- backend:
service:
name: gateway-service
port:
number: 8080
path: /gateway/
pathType: Prefix
- backend:
service:
name: backend-service
port:
number: 8080
path: /backend/
pathType: Prefix
+ +

总结

由于微软云这块文档有部分缺失,导致在配置这块花了一点时间去排查,甚至开了工单。总结下来Ingress的配置主要是根据请求路径路由到对应的Service,重写规则集才是实际负责根据正则来进行匹配重写。

+
作者

Sebastian Qu

发布于

2022-09-23

更新于

2023-07-04

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git "a/2023/07/10/H\351\241\271\347\233\256\345\277\203\350\267\257\345\216\206\347\250\213\350\256\260-1/index.html" "b/2023/07/10/H\351\241\271\347\233\256\345\277\203\350\267\257\345\216\206\347\250\213\350\256\260-1/index.html" new file mode 100644 index 0000000..abf47e7 --- /dev/null +++ "b/2023/07/10/H\351\241\271\347\233\256\345\277\203\350\267\257\345\216\206\347\250\213\350\256\260-1/index.html" @@ -0,0 +1,108 @@ + +H项目心路历程记 (一) - 西门闲话

H项目心路历程记 (一)

前言

2021年年中从前一家公司离职,经过朋友介绍到了现在公司参与了Halfling项目(以下简称H项目),并从零开始的设计开发到第一个客户交付的全部流程。

+

H项目是一个面向零售行业在电商时代的智能一体化中台产品。项目最早是从2021年中开始立项投入,期间经过数个版本迭代,以及2022年初的魔都封城等诸多困难。终于在2023年Q2季度完成了针对首个客户的上线。

+

作为从头开始全程参与项目的技术人员,经过2年多的产品研发期间有不少想吐槽和想说的。

+

业务设计

最初H项目是以打造一个面向零售行业业务中台为目的进行项目立项的,其中一个很重要的点就是希望除核心组件以外都可以独立售卖。所以在架构设计的使用当下最流行的微服务架构(基于Spring Boot + Kubernetes集群的解决方案), 再做业务拆分到服务的时候,进行了更细颗粒度的拆分。所以前前后后一共拆分了近20个微服务。

+

实际上到了2022年Q2前后,两个核心服务订单管理和库存服务实现以后发现,各个业务服务中间的耦合程度比想象中的还要精密,单个服务拆开单独售卖几乎不可能(应该说是完全不可能)。 然而由于服务拆分的过细,导致一个服务需要频繁访问其他服务获取大量数据。尤其类似订单关联数据比如商品的主档数据,会穿越好几个服务。而每个服务都会重复去获取主档数据,造成了大量的额外请求。 虽然最后在所有服务底部兜底使用同一个Redis作为缓存来减少额外的请求调用。

+

随着时间来到2023年产品组负责人离职后,整个H项目的方向有从行业的业务中台降级成中小企业的订单管理系统(Order Management System, OMS)。前期做的很多设计比如用户权限体系(当初需求是要精确到每个页面的每个按钮,如果下拉列表框有外调请求也需要做权限控制)等就显的很重,导致用户在使用以及配置都很不方便。而有些设计就显得稍显欠缺。 所以整个2022年Q4到2023年都是在对业务层上反复横跳,比如库存模型和商品模型的调整。这点是相当影响团队士气,同时也加剧了研发组和产品组之间的矛盾。

+

然后还有一个很重要的问题是多租户的态度上。早起项目是以服务中大体量客户为目的,所以设计之初并没有考虑多租户的问题。但实际项的进展过程中发现部分中大体量的客户也需要类似多租户的功能。这类客户需求与其说是需求多租户,不如说是希望有一个多租户级别的数据隔离,每个业务部门只能操作自己的业务部门的数据(数据包含商品,品牌,属性,平台店铺,订单,售后单据等),而总部依据可以查看一些汇总信息(例如销售对账单据等)。而随着2022年疫情状况严峻,很多中大客户对于内部平台升级和替换都变的更加谨慎和保守。所以当项目方向变成中小企业OMS时,多租户SaaS化的需求就更加迫切了。所以后期如何在原有系统上在扩展一套多租户SaaS化功能也是个课题。

+

中间件

H项目使用的中间件中比较特别的有分布式任务管理系统xxl-job,消息队列RocketMQ,这两个中间件都是第一次使用。关于这两个中间件也有不是可以谈谈的。

+

分布式任务管理系统 XXL-JOB

作为一个开源的分布式任务管理系统,xxl-job足够简单且相当容易上手使用,无论是基于Spring,还是原生Java做开发都是相当简单的。

+

而不足之处也很明显,最近1年版本迭代速度明显变慢,有些功能和BUG都有待处理的。比如想实动态创建定时任务这类功能就必须自己动手去对xxl-job-admin(调度中心)进行修改。

+

对于任务执行日志这块,现在xxl-job也是文件格式在执行器所在服务器上进行输出的,当执行器容器化或是运行在Kubernetes集群中的时候,当容器或是Pod重启后,就会丢失日志。这点在后期运维以及调试上还是有点麻烦的。

+

消息队列 RocketMQ

阿里出品的消息队列如果基于云产品来使用的话问题不大(阿里云卖的价钱也是有点黑的),但是在运维上还是有点问题的比如Topic,Group这些东西都只能人工去添加,阿里云并没有提供导入服务。全系统如果使用消息队列的场景多的话,维护这些Topic和Group还是很酸爽的。还有一点RocketMQ重复消息这一点是无法保证的。用阿里云的原话

+
+

绝大多数情况下,消息是不重复的。作为一款分布式消息中间件,在网络抖动、应用处理超时等异常情况下,无法保证消息不重复,但是能保证消息不丢失。

+
+

实际运用上,在大数据量并发的情况下,我们实际上是遇到了消息重复消费的问题。这点还是需要通过代码干预(例如增加一个唯一MsgId,对MsgId做分布式锁来解决)。

+

还有一点就是在旧版本SDK的默认配置下消费者的负载是有点小问题,总是有一个节点会比其他节点更容易消费到大量消息。据阿里云自己说明在新版本SDK的默认负载配置也已经修复这个问题了。

+

(未完待续)

+
作者

Sebastian Qu

发布于

2023-07-10

更新于

2023-07-10

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git "a/2023/07/12/H\351\241\271\347\233\256\345\277\203\350\267\257\345\216\206\347\250\213\350\256\260-2/index.html" "b/2023/07/12/H\351\241\271\347\233\256\345\277\203\350\267\257\345\216\206\347\250\213\350\256\260-2/index.html" new file mode 100644 index 0000000..4a73032 --- /dev/null +++ "b/2023/07/12/H\351\241\271\347\233\256\345\277\203\350\267\257\345\216\206\347\250\213\350\256\260-2/index.html" @@ -0,0 +1,121 @@ + +H项目心路历程记 (二) - 西门闲话

H项目心路历程记 (二)

(书接上文)

+

前文已经吐槽了产品设计以及中间件上的相关问题。接下来讨论一下技术上的一些问题。

+

关于配置

对于一个Spring的应用来说,应用配置怎么存取其实并不能说是一个技术问题。更应该算是设计或是架构问题。哪些配置应该进入配置文件,通过微服务配置中心(例如Nacos, Apollo,Kubernetes ConfigMap等)来进行在线配置,哪些配置应该通过其他方式来配置,亦或者哪些配置项目看似是配置项其实本质上就是常量。实际上截止现在H项目的确在有些微服务上是完全没有厘清三者之间的区别,以及界限。以至于现在堆积在配置文件中的内容实在太多。多个运行环境需要统一维护的难度实在太大。

+

对于配置来说,理想的状态应该是下面这样的。

+
    +
  1. 通过微服务配置中心来影响Spring的配置文件来实现动态配置的方案,通常会伴随Spring容器的重启(有些情况下甚至需要整个Docker容器),所以存在这里的配置项应该是随着运行环境改变才会需要改动的配置。例如:

    +
      +
    • 外部系统的链接信息(比如MySQL, Redis的连接信息)
    • +
    • 外部系统的认证凭证(数据库用户密码,第三方系统的密钥等)
    • +
    • 因为运行环境不同导致的不同变量 (例如消息队列的Topic,Group需要根据运行环境使用不同的名称)
    • +
    +
  2. +
  3. 通过数据库或是Redis存储的配置信息,这类配置主要是应该以业务级别的配置,可以通过页面进行修改,瞬间或是几分钟内生效,且不会造成微服务重启(无论是Spring或是Docker容器的重启)。对于这类配置有几个问题还是需要注意的。

    +
      +
    • 配置是否需要集中管理,每个微服务,都有自己独特的业务配置,这些业务配置是否需要集中到一个微服务统一管理,还是分散到各自的微服务进行单独管理。
    • +
    • 全局性统一管理的业务配置对外保留接口应该利用缓存有效的减少数据库访问的频次。甚至可以只使用内存而不是Redis来缓存这部分内容。如果使用内存的话则需要考虑失效时效和重新装载的机制。
    • +
    +
  4. +
  5. 无效配置,实际上很多配置说是可以修改,其实自从开始使用的那天开始就没有修改过。比如用来做分布式锁的键值。这部分需要梳理出来直接以常量的形式做在代码里。

    +
  6. +
+

数据清理以及归档

对于电商行业的零售业务来说每天产生的单据比如订单少做200,300条,多的是上千条。加上明细数据。几个月以后这些数据就会给MySQL的库表增加检索压力。整个H项目在这块是相当薄弱的。只依靠云平台的能力,对一些不需要留档的数据进行清理。远远没有能定期归档。

+

对于数据清理这块大致可以分为这几类

+
    +
  • 定期可以无脑物理删除的数据,比如一些日志类数据。
  • +
  • 定期直接归档的数据,归档后即可清理的数据。
  • +
  • 业务必须处理完成后才能归档并清理的数据。例如订单数据等。
  • +
+

对于数据归档的时候,还需要考虑一些相关联单据数据一并归档。比如订单归档时,同时需要归档相关联的退换货单,发货单,以及操作日志等等。 归档格式基本上就是考虑单条数据以JSON格式存储,然后一个时间段的数据做成压缩包存储在OSS上,然后提供给客户下载。

+

而对于归档数据的检索功能,客户选择需要归档的数据,我们可以将归档的数据(每个压缩包内的所有数据)导入到一个主数据库以外的,临时文档数据库(例如MongoDB),然后针对这个临时的文档数据库进行检索。当然提供的检索项目也会远少于热数据的检索项目。这个临时文档数据库基本上保持一两天数据,定期就全部回收。

+

未完待续

+
作者

Sebastian Qu

发布于

2023-07-12

更新于

2023-07-12

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git "a/2023/09/21/H\351\241\271\347\233\256\345\277\203\350\267\257\345\216\206\347\250\213\350\256\260-3/index.html" "b/2023/09/21/H\351\241\271\347\233\256\345\277\203\350\267\257\345\216\206\347\250\213\350\256\260-3/index.html" new file mode 100644 index 0000000..b5eae47 --- /dev/null +++ "b/2023/09/21/H\351\241\271\347\233\256\345\277\203\350\267\257\345\216\206\347\250\213\350\256\260-3/index.html" @@ -0,0 +1,112 @@ + +H项目心路历程记(三) - 西门闲话

H项目心路历程记(三)

现状

对于一个基于微服务的中台系统来说,需要以什么样的颗粒度来拆分系统。这一直是基于微服务架构的系统中绕不过去的问题。

+

已H项目来说,当初考量是这么划分微服务的,其中

+
    +
  • 基础服务: 作用主要是负责用户,角色权限,登陆鉴权,以及一些基础主档数据的日常维护功能
  • +
  • 商品服务: 物料,销售商品,款式,平台商品等和实际商品相关的主档数据
  • +
  • 库存服务:负责管理各个库存层级库存(总库存,冻结库存,在途库存等),以及展示各种出入库单据,并向其他微服务提供出入库服务接口。
  • +
  • 订单服务: 负责接收各个公域平台的ToC订单,售后单(含发货前退款,退货,换货单等),并根据配置策略进行单据路由(分仓,分物流,Hold单,加送赠品,通知仓库发货,通知平台发货等)
  • +
  • 接口服务: 主要承揽中台服务和第三方平台间的交互处理(主要是第三方平台发起的主调通知以及回调)
  • +
+

上述五个服务为核心服务,是保证整个中台系统基础业务功能运行的重要组成部分。除此之外还有,

+
    +
  • 采购服务:负责ToB业务线的处理
  • +
  • 财务服务:满足一些企业的财务对账以及开具电子发票的功能
  • +
  • 报表服务: 每天,每周定期从其他服务(主要是订单)
  • +
  • 会员服务:管理各类会员
  • +
+

等7,8个非核心服务,组成非核心业务。来构成一个完整的中台系统。

+

拆分策略以及缺点

当时主要考虑到整个中台系统是可以拆分出售,客户可以只选择购买自身需要的服务,而不需要的服务则无需购买服务,所以当初在拆分服务上在业务领域层面上需要更加低颗粒度;同时考虑到再业务高峰期(下午到晚上,就是各位疯狂刷淘宝,京东的时候)各个服务的负载是各不相同的,如果在高峰期对于特定服务需要进行精准扩容的话,拆分的颗粒度也需要更加细致,所以结果上导致整个系统满血运行起来要至少要十几个服务,外加一些没有云平台的中间件。整个Kubernetes环境中,至少要有十五六个Deployment,考虑到系统稳定性,一个Deployment也至少要有2到3个Pod,那整个系统至少就是50个Pod。

+

对于一个中大型的电商企业再服务器成本上可以负担这样一个能支持50个Pod的Kubernentes集群(考虑到需要一些中间件的云产品,当时估算最小的服务器成本都要到70K/年,一个中大型企业能面对双十一流量的服务器成本都要到300K/年以上)。所以整个系统对于中小型企业来说前期的投入负担相当大(尤其是在后疫情时代的2022年,2023年),以至于早起的好几个客户都是需要我们自己贴补服务器费用的。

+

复盘

现在重新复盘当初服务的拆分策略,个人觉得对于核心业务来说,相似领域的一些服务应该可以适当的聚合。比如上述的五大核心服务上中的商品服务,订单服务,库存服务三个核心服务应该可以合并在一起。首先这3个核心服务是H项目的最重要的竞争力,几乎不可能出现把这3个服务拆分出售的可能性。

+

而且在业务高峰期有订单服务有新订单处理时,势必会产生库存的变化,因此会调用到库存服务。三个服务之间的调用是相当频繁的,尤其是负责处理商品主档数据的商品服务,无论是通过HTTP或是其他方式的远程调用的话,都会有许多浪费的带宽。如果把三个服务整合,那么之间的调用基本上就是线程间的交互,无论是效率和可靠性上都会有更加显著的提升。

+

结尾

怎么拆分服务一直是个难题。虽然事后复盘的时候我的很多想法也不一定是正确,但是什么样的规模的客户需要使用什么样的拆分颗粒度,都是需要仔细考虑的。绝对不是一尘不变的。

+
作者

Sebastian Qu

发布于

2023-09-21

更新于

2023-09-21

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 2f244ac..0000000 --- a/LICENSE +++ /dev/null @@ -1,395 +0,0 @@ -Attribution 4.0 International - -======================================================================= - -Creative Commons Corporation ("Creative Commons") is not a law firm and -does not provide legal services or legal advice. Distribution of -Creative Commons public licenses does not create a lawyer-client or -other relationship. Creative Commons makes its licenses and related -information available on an "as-is" basis. Creative Commons gives no -warranties regarding its licenses, any material licensed under their -terms and conditions, or any related information. Creative Commons -disclaims all liability for damages resulting from their use to the -fullest extent possible. - -Using Creative Commons Public Licenses - -Creative Commons public licenses provide a standard set of terms and -conditions that creators and other rights holders may use to share -original works of authorship and other material subject to copyright -and certain other rights specified in the public license below. The -following considerations are for informational purposes only, are not -exhaustive, and do not form part of our licenses. - - Considerations for licensors: Our public licenses are - intended for use by those authorized to give the public - permission to use material in ways otherwise restricted by - copyright and certain other rights. Our licenses are - irrevocable. Licensors should read and understand the terms - and conditions of the license they choose before applying it. - Licensors should also secure all rights necessary before - applying our licenses so that the public can reuse the - material as expected. Licensors should clearly mark any - material not subject to the license. This includes other CC- - licensed material, or material used under an exception or - limitation to copyright. More considerations for licensors: - wiki.creativecommons.org/Considerations_for_licensors - - Considerations for the public: By using one of our public - licenses, a licensor grants the public permission to use the - licensed material under specified terms and conditions. If - the licensor's permission is not necessary for any reason--for - example, because of any applicable exception or limitation to - copyright--then that use is not regulated by the license. Our - licenses grant only permissions under copyright and certain - other rights that a licensor has authority to grant. Use of - the licensed material may still be restricted for other - reasons, including because others have copyright or other - rights in the material. A licensor may make special requests, - such as asking that all changes be marked or described. - Although not required by our licenses, you are encouraged to - respect those requests where reasonable. More_considerations - for the public: - wiki.creativecommons.org/Considerations_for_licensees - -======================================================================= - -Creative Commons Attribution 4.0 International Public License - -By exercising the Licensed Rights (defined below), You accept and agree -to be bound by the terms and conditions of this Creative Commons -Attribution 4.0 International Public License ("Public License"). To the -extent this Public License may be interpreted as a contract, You are -granted the Licensed Rights in consideration of Your acceptance of -these terms and conditions, and the Licensor grants You such rights in -consideration of benefits the Licensor receives from making the -Licensed Material available under these terms and conditions. - - -Section 1 -- Definitions. - - a. Adapted Material means material subject to Copyright and Similar - Rights that is derived from or based upon the Licensed Material - and in which the Licensed Material is translated, altered, - arranged, transformed, or otherwise modified in a manner requiring - permission under the Copyright and Similar Rights held by the - Licensor. For purposes of this Public License, where the Licensed - Material is a musical work, performance, or sound recording, - Adapted Material is always produced where the Licensed Material is - synched in timed relation with a moving image. - - b. Adapter's License means the license You apply to Your Copyright - and Similar Rights in Your contributions to Adapted Material in - accordance with the terms and conditions of this Public License. - - c. Copyright and Similar Rights means copyright and/or similar rights - closely related to copyright including, without limitation, - performance, broadcast, sound recording, and Sui Generis Database - Rights, without regard to how the rights are labeled or - categorized. For purposes of this Public License, the rights - specified in Section 2(b)(1)-(2) are not Copyright and Similar - Rights. - - d. Effective Technological Measures means those measures that, in the - absence of proper authority, may not be circumvented under laws - fulfilling obligations under Article 11 of the WIPO Copyright - Treaty adopted on December 20, 1996, and/or similar international - agreements. - - e. Exceptions and Limitations means fair use, fair dealing, and/or - any other exception or limitation to Copyright and Similar Rights - that applies to Your use of the Licensed Material. - - f. Licensed Material means the artistic or literary work, database, - or other material to which the Licensor applied this Public - License. - - g. Licensed Rights means the rights granted to You subject to the - terms and conditions of this Public License, which are limited to - all Copyright and Similar Rights that apply to Your use of the - Licensed Material and that the Licensor has authority to license. - - h. Licensor means the individual(s) or entity(ies) granting rights - under this Public License. - - i. Share means to provide material to the public by any means or - process that requires permission under the Licensed Rights, such - as reproduction, public display, public performance, distribution, - dissemination, communication, or importation, and to make material - available to the public including in ways that members of the - public may access the material from a place and at a time - individually chosen by them. - - j. Sui Generis Database Rights means rights other than copyright - resulting from Directive 96/9/EC of the European Parliament and of - the Council of 11 March 1996 on the legal protection of databases, - as amended and/or succeeded, as well as other essentially - equivalent rights anywhere in the world. - - k. You means the individual or entity exercising the Licensed Rights - under this Public License. Your has a corresponding meaning. - - -Section 2 -- Scope. - - a. License grant. - - 1. Subject to the terms and conditions of this Public License, - the Licensor hereby grants You a worldwide, royalty-free, - non-sublicensable, non-exclusive, irrevocable license to - exercise the Licensed Rights in the Licensed Material to: - - a. reproduce and Share the Licensed Material, in whole or - in part; and - - b. produce, reproduce, and Share Adapted Material. - - 2. Exceptions and Limitations. For the avoidance of doubt, where - Exceptions and Limitations apply to Your use, this Public - License does not apply, and You do not need to comply with - its terms and conditions. - - 3. Term. The term of this Public License is specified in Section - 6(a). - - 4. Media and formats; technical modifications allowed. The - Licensor authorizes You to exercise the Licensed Rights in - all media and formats whether now known or hereafter created, - and to make technical modifications necessary to do so. The - Licensor waives and/or agrees not to assert any right or - authority to forbid You from making technical modifications - necessary to exercise the Licensed Rights, including - technical modifications necessary to circumvent Effective - Technological Measures. For purposes of this Public License, - simply making modifications authorized by this Section 2(a) - (4) never produces Adapted Material. - - 5. Downstream recipients. - - a. Offer from the Licensor -- Licensed Material. Every - recipient of the Licensed Material automatically - receives an offer from the Licensor to exercise the - Licensed Rights under the terms and conditions of this - Public License. - - b. No downstream restrictions. You may not offer or impose - any additional or different terms or conditions on, or - apply any Effective Technological Measures to, the - Licensed Material if doing so restricts exercise of the - Licensed Rights by any recipient of the Licensed - Material. - - 6. No endorsement. Nothing in this Public License constitutes or - may be construed as permission to assert or imply that You - are, or that Your use of the Licensed Material is, connected - with, or sponsored, endorsed, or granted official status by, - the Licensor or others designated to receive attribution as - provided in Section 3(a)(1)(A)(i). - - b. Other rights. - - 1. Moral rights, such as the right of integrity, are not - licensed under this Public License, nor are publicity, - privacy, and/or other similar personality rights; however, to - the extent possible, the Licensor waives and/or agrees not to - assert any such rights held by the Licensor to the limited - extent necessary to allow You to exercise the Licensed - Rights, but not otherwise. - - 2. Patent and trademark rights are not licensed under this - Public License. - - 3. To the extent possible, the Licensor waives any right to - collect royalties from You for the exercise of the Licensed - Rights, whether directly or through a collecting society - under any voluntary or waivable statutory or compulsory - licensing scheme. In all other cases the Licensor expressly - reserves any right to collect such royalties. - - -Section 3 -- License Conditions. - -Your exercise of the Licensed Rights is expressly made subject to the -following conditions. - - a. Attribution. - - 1. If You Share the Licensed Material (including in modified - form), You must: - - a. retain the following if it is supplied by the Licensor - with the Licensed Material: - - i. identification of the creator(s) of the Licensed - Material and any others designated to receive - attribution, in any reasonable manner requested by - the Licensor (including by pseudonym if - designated); - - ii. a copyright notice; - - iii. a notice that refers to this Public License; - - iv. a notice that refers to the disclaimer of - warranties; - - v. a URI or hyperlink to the Licensed Material to the - extent reasonably practicable; - - b. indicate if You modified the Licensed Material and - retain an indication of any previous modifications; and - - c. indicate the Licensed Material is licensed under this - Public License, and include the text of, or the URI or - hyperlink to, this Public License. - - 2. You may satisfy the conditions in Section 3(a)(1) in any - reasonable manner based on the medium, means, and context in - which You Share the Licensed Material. For example, it may be - reasonable to satisfy the conditions by providing a URI or - hyperlink to a resource that includes the required - information. - - 3. If requested by the Licensor, You must remove any of the - information required by Section 3(a)(1)(A) to the extent - reasonably practicable. - - 4. If You Share Adapted Material You produce, the Adapter's - License You apply must not prevent recipients of the Adapted - Material from complying with this Public License. - - -Section 4 -- Sui Generis Database Rights. - -Where the Licensed Rights include Sui Generis Database Rights that -apply to Your use of the Licensed Material: - - a. for the avoidance of doubt, Section 2(a)(1) grants You the right - to extract, reuse, reproduce, and Share all or a substantial - portion of the contents of the database; - - b. if You include all or a substantial portion of the database - contents in a database in which You have Sui Generis Database - Rights, then the database in which You have Sui Generis Database - Rights (but not its individual contents) is Adapted Material; and - - c. You must comply with the conditions in Section 3(a) if You Share - all or a substantial portion of the contents of the database. - -For the avoidance of doubt, this Section 4 supplements and does not -replace Your obligations under this Public License where the Licensed -Rights include other Copyright and Similar Rights. - - -Section 5 -- Disclaimer of Warranties and Limitation of Liability. - - a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE - EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS - AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF - ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, - IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, - WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR - PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, - ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT - KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT - ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. - - b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE - TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, - NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, - INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, - COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR - USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN - ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR - DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR - IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. - - c. The disclaimer of warranties and limitation of liability provided - above shall be interpreted in a manner that, to the extent - possible, most closely approximates an absolute disclaimer and - waiver of all liability. - - -Section 6 -- Term and Termination. - - a. This Public License applies for the term of the Copyright and - Similar Rights licensed here. However, if You fail to comply with - this Public License, then Your rights under this Public License - terminate automatically. - - b. Where Your right to use the Licensed Material has terminated under - Section 6(a), it reinstates: - - 1. automatically as of the date the violation is cured, provided - it is cured within 30 days of Your discovery of the - violation; or - - 2. upon express reinstatement by the Licensor. - - For the avoidance of doubt, this Section 6(b) does not affect any - right the Licensor may have to seek remedies for Your violations - of this Public License. - - c. For the avoidance of doubt, the Licensor may also offer the - Licensed Material under separate terms or conditions or stop - distributing the Licensed Material at any time; however, doing so - will not terminate this Public License. - - d. Sections 1, 5, 6, 7, and 8 survive termination of this Public - License. - - -Section 7 -- Other Terms and Conditions. - - a. The Licensor shall not be bound by any additional or different - terms or conditions communicated by You unless expressly agreed. - - b. Any arrangements, understandings, or agreements regarding the - Licensed Material not stated herein are separate from and - independent of the terms and conditions of this Public License. - - -Section 8 -- Interpretation. - - a. For the avoidance of doubt, this Public License does not, and - shall not be interpreted to, reduce, limit, restrict, or impose - conditions on any use of the Licensed Material that could lawfully - be made without permission under this Public License. - - b. To the extent possible, if any provision of this Public License is - deemed unenforceable, it shall be automatically reformed to the - minimum extent necessary to make it enforceable. If the provision - cannot be reformed, it shall be severed from this Public License - without affecting the enforceability of the remaining terms and - conditions. - - c. No term or condition of this Public License will be waived and no - failure to comply consented to unless expressly agreed to by the - Licensor. - - d. Nothing in this Public License constitutes or may be interpreted - as a limitation upon, or waiver of, any privileges and immunities - that apply to the Licensor or You, including from the legal - processes of any jurisdiction or authority. - - -======================================================================= - -Creative Commons is not a party to its public -licenses. Notwithstanding, Creative Commons may elect to apply one of -its public licenses to material it publishes and in those instances -will be considered the “Licensor.” The text of the Creative Commons -public licenses is dedicated to the public domain under the CC0 Public -Domain Dedication. Except for the limited purpose of indicating that -material is shared under a Creative Commons public license or as -otherwise permitted by the Creative Commons policies published at -creativecommons.org/policies, Creative Commons does not authorize the -use of the trademark "Creative Commons" or any other trademark or logo -of Creative Commons without its prior written consent including, -without limitation, in connection with any unauthorized modifications -to any of its public licenses or any other arrangements, -understandings, or agreements concerning use of licensed material. For -the avoidance of doubt, this paragraph does not form part of the -public licenses. - -Creative Commons may be contacted at creativecommons.org. diff --git a/README.md b/README.md deleted file mode 100644 index 4f3d30c..0000000 --- a/README.md +++ /dev/null @@ -1,15 +0,0 @@ -西门闲话 -================================ - -[![Build Status](https://github.com/hashmaparraylist/hashmaparraylist.github.io/actions/workflows/pages.yml/badge.svg?branch=develop&event=push)](https://github.com/hashmaparraylist/hashmaparraylist.github.io/actions/workflows/pages.yml) - -个人BLOG站点 - -License -------- - -[![Creative Commons License](https://i.creativecommons.org/l/by/4.0/88x31.png)](http://creativecommons.org/licenses/by/4.0/) - -This work is licensed under a [Creative Commons Attribution 4.0 International License.](http://creativecommons.org/licenses/by/4.0/). - -本作品采用[知识共享署名 4.0 国际许可协议 CC-BY-4.0](http://creativecommons.org/licenses/by/4.0/).进行许可。 diff --git a/_config.icarus.yml b/_config.icarus.yml deleted file mode 100644 index 9f93371..0000000 --- a/_config.icarus.yml +++ /dev/null @@ -1,396 +0,0 @@ -# Version of the configuration file -version: 4.0.0 -# Icarus theme variant, can be "default" or "cyberpunk" -variant: default -# Path or URL to the website's logo -logo: /img/logo.svg -# Page metadata configurations -head: - # URL or path to the website's icon - favicon: /img/favicon.svg - # Web application manifests configuration - # https://developer.mozilla.org/en-US/docs/Web/Manifest - manifest: - # Name of the web application (default to the site title) - name: - # The displayed name of the web application - # when there is not enough space to display full name - short_name: - # The start URL of the web application - start_url: - # The default theme color for the application - theme_color: - # A placeholder background color for the application page to display - # before its stylesheet is loaded - background_color: - # The preferred display mode for the website - display: standalone - # Image files that can serve as application icons for different contexts - icons: - - - # The path to the image file - src: '' - # A string containing space-separated image dimensions - sizes: '' - # A hint as to the media type of the image - type: - # Open Graph metadata - # https://hexo.io/docs/helpers.html#open-graph - open_graph: - # Page title (og:title) (optional) - # You should leave this blank for most of the time - title: - # Page type (og:type) (optional) - # You should leave this blank for most of the time - type: blog - # Page URL (og:url) (optional) - # You should leave this blank for most of the time - url: - # Page cover (og:image) (optional) - # You should leave this blank for most of the time - image: - # Site name (og:site_name) (optional) - # You should leave this blank for most of the time - site_name: - # Page author (article:author) (optional) - # You should leave this blank for most of the time - author: - # Page description (og:description) (optional) - # You should leave this blank for most of the time - description: - # Twitter card type (twitter:card) - twitter_card: - # Twitter ID (twitter:creator) - twitter_id: - # Twitter Site (twitter:site) - twitter_site: - # Google+ profile link (deprecated) - google_plus: - # Facebook admin ID - fb_admins: - # Facebook App ID - fb_app_id: - # Structured data of the page - # https://developers.google.com/search/docs/guides/intro-structured-data - structured_data: - # Page title (optional) - # You should leave this blank for most of the time - title: - # Page description (optional) - # You should leave this blank for most of the time - description: - # Page URL (optional) - # You should leave this blank for most of the time - url: - # Page author (article:author) (optional) - # You should leave this blank for most of the time - author: - # Page images (optional) - # You should leave this blank for most of the time - image: - # Additional HTML meta tags in an array - meta: - # Meta tag specified in = style - # E.g., name=theme-color;content=#123456 => - - '' - # URL or path to the website's RSS atom.xml - rss: /atom.xml -# Page top navigation bar configurations -navbar: - # Navigation menu items - menu: - 首页: / - 文章: /archives - 分类: /categories - 标签: /tags - # About: /about - # Links to be shown on the right of the navigation bar - links: - Download on GitHub: - icon: fab fa-github - url: 'https://github.com/hashmaparraylist/hashmaparraylist.github.io' -# Page footer configurations -footer: - # Links to be shown on the right of the footer section - links: - Creative Commons: - icon: fab fa-creative-commons - url: 'https://creativecommons.org/' - Attribution 4.0 International: - icon: fab fa-creative-commons-by - url: 'https://creativecommons.org/licenses/by/4.0/' -# Article related configurations -article: - # Code highlight settings - highlight: - # Code highlight themes - # https://github.com/highlightjs/highlight.js/tree/master/src/styles - theme: atom-one-light - # Show copy code button - clipboard: true - # Default folding status of the code blocks. Can be "", "folded", "unfolded" - fold: unfolded - # Whether to show estimated article reading time - readtime: true - # Article licensing block - licenses: - Creative Commons: - icon: fab fa-creative-commons - url: 'https://creativecommons.org/' - Attribution: - icon: fab fa-creative-commons-by - url: 'https://creativecommons.org/licenses/by/4.0/' - Noncommercial: - icon: fab fa-creative-commons-nc - url: 'https://creativecommons.org/licenses/by-nc/4.0/' -# Search plugin configurations -# https://ppoffice.github.io/hexo-theme-icarus/categories/Plugins/Search/ -search: - type: insight -# Comment plugin configurations -# https://ppoffice.github.io/hexo-theme-icarus/categories/Plugins/Comment/ -comment: - type: gitalk - client_id: 7adb5c968ecf6738f14e - client_secret: b2f23fc0a2ecca7a3ac2b2e10079a3331e2ee0c7 - repo: hashmaparraylist.github.io - owner: hashmaparraylist - admin: - - hashmaparraylist - proxy: 'https://cors-anywhere.azm.workers.dev/https://github.com/login/oauth/access_token' - enable_hotkey: true # 可选填 - language: zh-CN # 可选填 -# Donate plugin configurations -# https://ppoffice.github.io/hexo-theme-icarus/categories/Plugins/Donation/ -#donates: -# # "Afdian.net" donate button configurations -# - -# type: afdian -# # URL to the "Afdian.net" personal page -# url: '' -# # Alipay donate button configurations -# - -# type: alipay -# # Alipay qrcode image URL -# qrcode: '' -# # "Buy me a coffee" donate button configurations -# - -# type: buymeacoffee -# # URL to the "Buy me a coffee" page -# url: '' -# # Patreon donate button configurations -# - -# type: patreon -# # URL to the Patreon page -# url: '' -# # Paypal donate button configurations -# - -# type: paypal -# # Paypal business ID or email address -# business: '' -# # Currency code -# currency_code: USD -# # Wechat donate button configurations -# - -# type: wechat -# # Wechat qrcode image URL -# qrcode: '' -## Share plugin configurations -## https://ppoffice.github.io/hexo-theme-icarus/categories/Plugins/Share/ -share: - type: addthis - # URL to the ShareThis share plugin script - install_url: //s7.addthis.com/js/300/addthis_widget.js#pubid=ra-60505094cbbbd07d -# Sidebar configurations. -# Please be noted that a sidebar is only visible when it has at least one widget -sidebar: - # Left sidebar configurations - left: - # Whether the sidebar sticks to the top when page scrolls - sticky: false - # Right sidebar configurations - right: - # Whether the sidebar sticks to the top when page scrolls - sticky: false -# Sidebar widget configurations -# http://ppoffice.github.io/hexo-theme-icarus/categories/Widgets/ -widgets: - # Profile widget configurations - - - # Where should the widget be placed, left sidebar or right sidebar - position: left - type: profile - # Author name - author: 西门闲话 - # Author title - author_title: 旧居魔都西门外,年岁已长仍爱吹牛,故曰西门闲话 - # Author's current location - location: Shanghai - # URL or path to the avatar image - avatar: /images/avatar.jpeg - # Whether show the rounded avatar image - avatar_rounded: false - # Email address for the Gravatar - gravatar: - # URL or path for the follow button - follow_link: 'https://github.com/hashmaparraylist' - # Links to be shown on the bottom of the profile widget - social_links: - Github: - icon: fab fa-github - url: 'https://github.com/hashmaparraylist' - #Facebook: - # icon: fab fa-facebook - # url: 'https://facebook.com' - #Twitter: - # icon: fab fa-twitter - # url: 'https://twitter.com' - #Dribbble: - # icon: fab fa-dribbble - # url: 'https://dribbble.com' - RSS: - icon: fas fa-rss - url: /atom.xml - # Table of contents widget configurations - - - # Where should the widget be placed, left sidebar or right sidebar - position: left - type: toc - # Whether to show the index of each heading - index: true - # Whether to collapse sub-headings when they are out-of-view - collapsed: true - # Maximum level of headings to show (1-6) - depth: 3 - # Recommendation links widget configurations - - - # Where should the widget be placed, left sidebar or right sidebar - position: left - type: links - # Names and URLs of the sites - links: - Hexo: 'https://hexo.io' - Bulma: 'https://bulma.io' - PPOffice: 'https://github.com/ppoffice' - # Categories widget configurations - - - # Where should the widget be placed, left sidebar or right sidebar - position: left - type: categories - # Recent posts widget configurations - - - # Where should the widget be placed, left sidebar or right sidebar - position: right - type: recent_posts - # Archives widget configurations - - - # Where should the widget be placed, left sidebar or right sidebar - position: right - type: archives - # Tags widget configurations - - - # Where should the widget be placed, left sidebar or right sidebar - position: right - type: tags - # Google FeedBurner email subscription widget configurations - - - # Where should the widget be placed, left sidebar or right sidebar - position: left - type: subscribe_email - # Hint text under the email input - description: - # Feedburner ID - feedburner_id: '' - # Google AdSense unit configurations -# - -# # Where should the widget be placed, left sidebar or right sidebar -# position: left -# type: adsense -# # AdSense client ID -# client_id: '' -# # AdSense AD unit ID -# slot_id: '' -# Plugin configurations -# https://ppoffice.github.io/hexo-theme-icarus/categories/Plugins/ -plugins: - # Enable page startup animations - animejs: true - # Show the "back to top" button - back_to_top: true - # Baidu Analytics plugin settings - # https://tongji.baidu.com - baidu_analytics: - # Baidu Analytics tracking ID - tracking_id: e204b736e83e085afaa171efcb954f86 - # Bing Webmaster Tools plugin settings - # https://www.bing.com/toolbox/webmaster/ - bing_webmaster: - # Bing Webmaster Tools tracking ID in the tag - tracking_id: - # BuSuanZi site/page view counter - # https://busuanzi.ibruce.info - busuanzi: false - # CNZZ statistics - # https://www.umeng.com/web - cnzz: - # CNZZ tracker id - id: - # CNZZ website id - web_id: - # Alerting users about the use of cookies - # https://www.osano.com/cookieconsent/ - cookie_consent: - # The compliance type. Can be "info", "opt-in", or "opt-out" - type: info - # Theme of the popup. Can be "block", "edgeless", or "classic" - theme: edgeless - # Whether the popup should stay static regardless of the page scrolls - static: false - # Where on the screen the consent popup should display - position: bottom-left - # URL to your site's cookie policy - policyLink: 'https://www.cookiesandyou.com/' - # Enable the lightGallery and Justified Gallery plugins - gallery: true - # Google Analytics plugin settings - # https://analytics.google.com - google_analytics: - # Google Analytics tracking ID - tracking_id: - # Hotjar user feedback plugin - # https://www.hotjar.com/ - hotjar: - # Hotjar site id - site_id: - # Enable the KaTeX math typesetting support - # https://katex.org/ - katex: false - # Enable the MathJax math typesetting support - # https://www.mathjax.org/ - mathjax: true - # Enable the Outdated Browser plugin - # http://outdatedbrowser.com/ - outdated_browser: true - # Show a progress bar at top of the page on page loading - progressbar: true - # Statcounter statistics - # https://statcounter.com/ - statcounter: - # Statcounter project id - project: - # Statcounter project security code - security: - # Twitter conversion tracking plugin settings - # https://business.twitter.com/en/help/campaign-measurement-and-analytics/conversion-tracking-for-websites.html - twitter_conversion_tracking: - # Twitter Pixel ID - pixel_id: -# CDN provider settings -# https://ppoffice.github.io/hexo-theme-icarus/Configuration/Theme/speed-up-your-site-with-custom-cdn/ -providers: - # Name or URL template of the JavaScript and/or stylesheet CDN provider - cdn: jsdelivr - # Name or URL template of the webfont CDN provider - fontcdn: google - # Name or URL of the fontawesome icon font CDN provider - iconcdn: https://cdn.bootcdn.net/ajax/libs/font-awesome/5.14.0/css/all.min.css diff --git a/_config.yml b/_config.yml deleted file mode 100644 index fa8e432..0000000 --- a/_config.yml +++ /dev/null @@ -1,125 +0,0 @@ -# Hexo Configuration -## Docs: https://hexo.io/docs/configuration.html -## Source: https://github.com/hexojs/hexo/ - -# Site -title: 西门闲话 -subtitle: 旧居魔都西门外,年岁已长仍爱吹牛,故曰西门闲话 -description: '' -keywords: keyword -author: Sebastian Qu -language: zh-CN -timezone: Asia/Shanghai - -# URL -## If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/' -url: https://hashmaparraylist.github.io/ -root: / -permalink: :year/:month/:day/:title/ -permalink_defaults: -pretty_urls: - trailing_index: true # Set to false to remove trailing index.html from permalinks - -# Directory -source_dir: source -public_dir: public -tag_dir: tags -archive_dir: archives -category_dir: categories -code_dir: downloads/code -i18n_dir: :lang -skip_render: - -# Writing -new_post_name: :title.md # File name of new posts -default_layout: post -titlecase: false # Transform title into titlecase -external_link: - enable: true # Open external links in new tab - field: site # Apply to the whole site - exclude: '' -filename_case: 0 -render_drafts: false -post_asset_folder: false -relative_link: false -future: true -highlight: - enable: true - line_number: true - auto_detect: false - tab_replace: '' - wrap: true - hljs: false - -# Home page setting -# path: Root path for your blogs index page. (default = '') -# per_page: Posts displayed per page. (0 = disable pagination) -# order_by: Posts order. (Order by date descending by default) -index_generator: - path: '' - per_page: 10 - order_by: -date - -# Category & Tag -default_category: uncategorized -category_map: -tag_map: - -# Metadata elements -## https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta -meta_generator: true - -# Date / Time format -## Hexo uses Moment.js to parse and display date -## You can customize the date format as defined in -## http://momentjs.com/docs/#/displaying/format/ -date_format: YYYY-MM-DD -time_format: HH:mm:ss -## updated_option supports 'mtime', 'date', 'empty' -updated_option: 'mtime' - -# Pagination -## Set per_page to 0 to disable pagination -per_page: 10 -pagination_dir: page - -# Include / Exclude file(s) -## include:/exclude: options only apply to the 'source/' folder -include: -exclude: -ignore: - -# Extensions -## Plugins: https://hexo.io/plugins/ -## Themes: https://hexo.io/themes/ -#theme: landscape -theme: icarus - -# Deployment -## Docs: https://hexo.io/docs/deployment.html -deploy: - type: git - repo: - branch: - -# mermaid chart -mermaid: ## mermaid url https://github.com/knsv/mermaid - enable: true # default true - version: "8.6.0" # default v7.1.2 - options: # find more api options from https://github.com/knsv/mermaid/blob/master/src/mermaidAPI.js - #startOnload: true // default true - -feed: - enable: true - type: - - rss2 - - atom - path: - - rss2.xml - - atom.xml - limit: 20 - hub: - content: true - content_limit: 140 - order_by: -date - autodiscovery: true diff --git a/archives/2019/12/index.html b/archives/2019/12/index.html new file mode 100644 index 0000000..ac6ba6e --- /dev/null +++ b/archives/2019/12/index.html @@ -0,0 +1,74 @@ + +归档: 2019/12 - 西门闲话

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/archives/2019/index.html b/archives/2019/index.html new file mode 100644 index 0000000..fb4f6d4 --- /dev/null +++ b/archives/2019/index.html @@ -0,0 +1,74 @@ + +归档: 2019 - 西门闲话

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/archives/2020/04/index.html b/archives/2020/04/index.html new file mode 100644 index 0000000..86a815e --- /dev/null +++ b/archives/2020/04/index.html @@ -0,0 +1,74 @@ + +归档: 2020/4 - 西门闲话

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/archives/2020/05/index.html b/archives/2020/05/index.html new file mode 100644 index 0000000..756fba0 --- /dev/null +++ b/archives/2020/05/index.html @@ -0,0 +1,74 @@ + +归档: 2020/5 - 西门闲话

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/archives/2020/12/index.html b/archives/2020/12/index.html new file mode 100644 index 0000000..202b174 --- /dev/null +++ b/archives/2020/12/index.html @@ -0,0 +1,74 @@ + +归档: 2020/12 - 西门闲话

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/archives/2020/index.html b/archives/2020/index.html new file mode 100644 index 0000000..3598b61 --- /dev/null +++ b/archives/2020/index.html @@ -0,0 +1,74 @@ + +归档: 2020 - 西门闲话

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/archives/2021/03/index.html b/archives/2021/03/index.html new file mode 100644 index 0000000..f7dc4b5 --- /dev/null +++ b/archives/2021/03/index.html @@ -0,0 +1,74 @@ + +归档: 2021/3 - 西门闲话

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/archives/2021/09/index.html b/archives/2021/09/index.html new file mode 100644 index 0000000..708218a --- /dev/null +++ b/archives/2021/09/index.html @@ -0,0 +1,74 @@ + +归档: 2021/9 - 西门闲话

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/archives/2021/index.html b/archives/2021/index.html new file mode 100644 index 0000000..69a456c --- /dev/null +++ b/archives/2021/index.html @@ -0,0 +1,74 @@ + +归档: 2021 - 西门闲话

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/archives/2022/09/index.html b/archives/2022/09/index.html new file mode 100644 index 0000000..e950de5 --- /dev/null +++ b/archives/2022/09/index.html @@ -0,0 +1,74 @@ + +归档: 2022/9 - 西门闲话

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/archives/2022/index.html b/archives/2022/index.html new file mode 100644 index 0000000..577d764 --- /dev/null +++ b/archives/2022/index.html @@ -0,0 +1,74 @@ + +归档: 2022 - 西门闲话

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/archives/2023/07/index.html b/archives/2023/07/index.html new file mode 100644 index 0000000..cd0e228 --- /dev/null +++ b/archives/2023/07/index.html @@ -0,0 +1,74 @@ + +归档: 2023/7 - 西门闲话

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/archives/2023/09/index.html b/archives/2023/09/index.html new file mode 100644 index 0000000..598d366 --- /dev/null +++ b/archives/2023/09/index.html @@ -0,0 +1,74 @@ + +归档: 2023/9 - 西门闲话

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/archives/2023/index.html b/archives/2023/index.html new file mode 100644 index 0000000..66215bb --- /dev/null +++ b/archives/2023/index.html @@ -0,0 +1,74 @@ + +归档: 2023 - 西门闲话

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/archives/index.html b/archives/index.html new file mode 100644 index 0000000..a132c99 --- /dev/null +++ b/archives/index.html @@ -0,0 +1,74 @@ + +归档 - 西门闲话

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/archives/page/2/index.html b/archives/page/2/index.html new file mode 100644 index 0000000..d295f37 --- /dev/null +++ b/archives/page/2/index.html @@ -0,0 +1,74 @@ + +归档 - 西门闲话

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/atom.xml b/atom.xml new file mode 100644 index 0000000..5e58c5d --- /dev/null +++ b/atom.xml @@ -0,0 +1,357 @@ + + + + 西门闲话 + https://hashmaparraylist.github.io/ + + + + + Thu, 21 Sep 2023 10:00:16 GMT + http://hexo.io/ + + + H项目心路历程记(三) + https://hashmaparraylist.github.io/2023/09/21/H%E9%A1%B9%E7%9B%AE%E5%BF%83%E8%B7%AF%E5%8E%86%E7%A8%8B%E8%AE%B0-3/ + https://hashmaparraylist.github.io/2023/09/21/H%E9%A1%B9%E7%9B%AE%E5%BF%83%E8%B7%AF%E5%8E%86%E7%A8%8B%E8%AE%B0-3/ + Thu, 21 Sep 2023 09:58:33 GMT + + + + <h1 id="现状"><a href="#现状" class="headerlink" title="现状"></a>现状</h1><p>对于一个基于微服务的中台系统来说,需要以什么样的颗粒度来拆分系统。这一直是基于微服务架构的系统中绕不过去的问题。</p> +<p>已H项目来说 + + + + + 现状

对于一个基于微服务的中台系统来说,需要以什么样的颗粒度来拆分系统。这一直是基于微服务架构的系统中绕不过去的问题。

已H项目来说,当初考量是这么划分微服务的,其中

  • 基础服务: 作用主要是负责用户,角色权限,登陆鉴权,以及一些基础主档数据的日常维护功能
  • 商品服务: 物料,销售商品,款式,平台商品等和实际商品相关的主档数据
  • 库存服务:负责管理各个库存层级库存(总库存,冻结库存,在途库存等),以及展示各种出入库单据,并向其他微服务提供出入库服务接口。
  • 订单服务: 负责接收各个公域平台的ToC订单,售后单(含发货前退款,退货,换货单等),并根据配置策略进行单据路由(分仓,分物流,Hold单,加送赠品,通知仓库发货,通知平台发货等)
  • 接口服务: 主要承揽中台服务和第三方平台间的交互处理(主要是第三方平台发起的主调通知以及回调)

上述五个服务为核心服务,是保证整个中台系统基础业务功能运行的重要组成部分。除此之外还有,

  • 采购服务:负责ToB业务线的处理
  • 财务服务:满足一些企业的财务对账以及开具电子发票的功能
  • 报表服务: 每天,每周定期从其他服务(主要是订单)
  • 会员服务:管理各类会员

等7,8个非核心服务,组成非核心业务。来构成一个完整的中台系统。

拆分策略以及缺点

当时主要考虑到整个中台系统是可以拆分出售,客户可以只选择购买自身需要的服务,而不需要的服务则无需购买服务,所以当初在拆分服务上在业务领域层面上需要更加低颗粒度;同时考虑到再业务高峰期(下午到晚上,就是各位疯狂刷淘宝,京东的时候)各个服务的负载是各不相同的,如果在高峰期对于特定服务需要进行精准扩容的话,拆分的颗粒度也需要更加细致,所以结果上导致整个系统满血运行起来要至少要十几个服务,外加一些没有云平台的中间件。整个Kubernetes环境中,至少要有十五六个Deployment,考虑到系统稳定性,一个Deployment也至少要有2到3个Pod,那整个系统至少就是50个Pod。

对于一个中大型的电商企业再服务器成本上可以负担这样一个能支持50个Pod的Kubernentes集群(考虑到需要一些中间件的云产品,当时估算最小的服务器成本都要到70K/年,一个中大型企业能面对双十一流量的服务器成本都要到300K/年以上)。所以整个系统对于中小型企业来说前期的投入负担相当大(尤其是在后疫情时代的2022年,2023年),以至于早起的好几个客户都是需要我们自己贴补服务器费用的。

复盘

现在重新复盘当初服务的拆分策略,个人觉得对于核心业务来说,相似领域的一些服务应该可以适当的聚合。比如上述的五大核心服务上中的商品服务,订单服务,库存服务三个核心服务应该可以合并在一起。首先这3个核心服务是H项目的最重要的竞争力,几乎不可能出现把这3个服务拆分出售的可能性。

而且在业务高峰期有订单服务有新订单处理时,势必会产生库存的变化,因此会调用到库存服务。三个服务之间的调用是相当频繁的,尤其是负责处理商品主档数据的商品服务,无论是通过HTTP或是其他方式的远程调用的话,都会有许多浪费的带宽。如果把三个服务整合,那么之间的调用基本上就是线程间的交互,无论是效率和可靠性上都会有更加显著的提升。

结尾

怎么拆分服务一直是个难题。虽然事后复盘的时候我的很多想法也不一定是正确,但是什么样的规模的客户需要使用什么样的拆分颗粒度,都是需要仔细考虑的。绝对不是一尘不变的。

]]>
+ + + 设计 + + + Aliyun + + Kubernetes + + 架构 + + Java + + + https://hashmaparraylist.github.io/2023/09/21/H%E9%A1%B9%E7%9B%AE%E5%BF%83%E8%B7%AF%E5%8E%86%E7%A8%8B%E8%AE%B0-3/#disqus_thread + +
+ + + H项目心路历程记 (二) + https://hashmaparraylist.github.io/2023/07/12/H%E9%A1%B9%E7%9B%AE%E5%BF%83%E8%B7%AF%E5%8E%86%E7%A8%8B%E8%AE%B0-2/ + https://hashmaparraylist.github.io/2023/07/12/H%E9%A1%B9%E7%9B%AE%E5%BF%83%E8%B7%AF%E5%8E%86%E7%A8%8B%E8%AE%B0-2/ + Wed, 12 Jul 2023 09:29:36 GMT + + + + <p><strong>(书接上文)</strong></p> +<p>前文已经吐槽了产品设计以及中间件上的相关问题。接下来讨论一下技术上的一些问题。</p> +<h1 id="关于配置"><a href="#关于配置" class="headerlink" title="关于配置"> + + + + + (书接上文)

前文已经吐槽了产品设计以及中间件上的相关问题。接下来讨论一下技术上的一些问题。

关于配置

对于一个Spring的应用来说,应用配置怎么存取其实并不能说是一个技术问题。更应该算是设计或是架构问题。哪些配置应该进入配置文件,通过微服务配置中心(例如Nacos, Apollo,Kubernetes ConfigMap等)来进行在线配置,哪些配置应该通过其他方式来配置,亦或者哪些配置项目看似是配置项其实本质上就是常量。实际上截止现在H项目的确在有些微服务上是完全没有厘清三者之间的区别,以及界限。以至于现在堆积在配置文件中的内容实在太多。多个运行环境需要统一维护的难度实在太大。

对于配置来说,理想的状态应该是下面这样的。

  1. 通过微服务配置中心来影响Spring的配置文件来实现动态配置的方案,通常会伴随Spring容器的重启(有些情况下甚至需要整个Docker容器),所以存在这里的配置项应该是随着运行环境改变才会需要改动的配置。例如:

    • 外部系统的链接信息(比如MySQL, Redis的连接信息)
    • 外部系统的认证凭证(数据库用户密码,第三方系统的密钥等)
    • 因为运行环境不同导致的不同变量 (例如消息队列的Topic,Group需要根据运行环境使用不同的名称)
  2. 通过数据库或是Redis存储的配置信息,这类配置主要是应该以业务级别的配置,可以通过页面进行修改,瞬间或是几分钟内生效,且不会造成微服务重启(无论是Spring或是Docker容器的重启)。对于这类配置有几个问题还是需要注意的。

    • 配置是否需要集中管理,每个微服务,都有自己独特的业务配置,这些业务配置是否需要集中到一个微服务统一管理,还是分散到各自的微服务进行单独管理。
    • 全局性统一管理的业务配置对外保留接口应该利用缓存有效的减少数据库访问的频次。甚至可以只使用内存而不是Redis来缓存这部分内容。如果使用内存的话则需要考虑失效时效和重新装载的机制。
  3. 无效配置,实际上很多配置说是可以修改,其实自从开始使用的那天开始就没有修改过。比如用来做分布式锁的键值。这部分需要梳理出来直接以常量的形式做在代码里。

数据清理以及归档

对于电商行业的零售业务来说每天产生的单据比如订单少做200,300条,多的是上千条。加上明细数据。几个月以后这些数据就会给MySQL的库表增加检索压力。整个H项目在这块是相当薄弱的。只依靠云平台的能力,对一些不需要留档的数据进行清理。远远没有能定期归档。

对于数据清理这块大致可以分为这几类

  • 定期可以无脑物理删除的数据,比如一些日志类数据。
  • 定期直接归档的数据,归档后即可清理的数据。
  • 业务必须处理完成后才能归档并清理的数据。例如订单数据等。

对于数据归档的时候,还需要考虑一些相关联单据数据一并归档。比如订单归档时,同时需要归档相关联的退换货单,发货单,以及操作日志等等。 归档格式基本上就是考虑单条数据以JSON格式存储,然后一个时间段的数据做成压缩包存储在OSS上,然后提供给客户下载。

而对于归档数据的检索功能,客户选择需要归档的数据,我们可以将归档的数据(每个压缩包内的所有数据)导入到一个主数据库以外的,临时文档数据库(例如MongoDB),然后针对这个临时的文档数据库进行检索。当然提供的检索项目也会远少于热数据的检索项目。这个临时文档数据库基本上保持一两天数据,定期就全部回收。

未完待续

]]>
+ + + 设计 + + + Aliyun + + Kubernetes + + 架构 + + Java + + + https://hashmaparraylist.github.io/2023/07/12/H%E9%A1%B9%E7%9B%AE%E5%BF%83%E8%B7%AF%E5%8E%86%E7%A8%8B%E8%AE%B0-2/#disqus_thread + +
+ + + H项目心路历程记 (一) + https://hashmaparraylist.github.io/2023/07/10/H%E9%A1%B9%E7%9B%AE%E5%BF%83%E8%B7%AF%E5%8E%86%E7%A8%8B%E8%AE%B0-1/ + https://hashmaparraylist.github.io/2023/07/10/H%E9%A1%B9%E7%9B%AE%E5%BF%83%E8%B7%AF%E5%8E%86%E7%A8%8B%E8%AE%B0-1/ + Mon, 10 Jul 2023 03:19:00 GMT + + + + <h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>2021年年中从前一家公司离职,经过朋友介绍到了现在公司参与了<code>Halfling</code>项目(以下简称<code>H项目</ + + + + + 前言

2021年年中从前一家公司离职,经过朋友介绍到了现在公司参与了Halfling项目(以下简称H项目),并从零开始的设计开发到第一个客户交付的全部流程。

H项目是一个面向零售行业在电商时代的智能一体化中台产品。项目最早是从2021年中开始立项投入,期间经过数个版本迭代,以及2022年初的魔都封城等诸多困难。终于在2023年Q2季度完成了针对首个客户的上线。

作为从头开始全程参与项目的技术人员,经过2年多的产品研发期间有不少想吐槽和想说的。

业务设计

最初H项目是以打造一个面向零售行业业务中台为目的进行项目立项的,其中一个很重要的点就是希望除核心组件以外都可以独立售卖。所以在架构设计的使用当下最流行的微服务架构(基于Spring Boot + Kubernetes集群的解决方案), 再做业务拆分到服务的时候,进行了更细颗粒度的拆分。所以前前后后一共拆分了近20个微服务。

实际上到了2022年Q2前后,两个核心服务订单管理和库存服务实现以后发现,各个业务服务中间的耦合程度比想象中的还要精密,单个服务拆开单独售卖几乎不可能(应该说是完全不可能)。 然而由于服务拆分的过细,导致一个服务需要频繁访问其他服务获取大量数据。尤其类似订单关联数据比如商品的主档数据,会穿越好几个服务。而每个服务都会重复去获取主档数据,造成了大量的额外请求。 虽然最后在所有服务底部兜底使用同一个Redis作为缓存来减少额外的请求调用。

随着时间来到2023年产品组负责人离职后,整个H项目的方向有从行业的业务中台降级成中小企业的订单管理系统(Order Management System, OMS)。前期做的很多设计比如用户权限体系(当初需求是要精确到每个页面的每个按钮,如果下拉列表框有外调请求也需要做权限控制)等就显的很重,导致用户在使用以及配置都很不方便。而有些设计就显得稍显欠缺。 所以整个2022年Q4到2023年都是在对业务层上反复横跳,比如库存模型和商品模型的调整。这点是相当影响团队士气,同时也加剧了研发组和产品组之间的矛盾。

然后还有一个很重要的问题是多租户的态度上。早起项目是以服务中大体量客户为目的,所以设计之初并没有考虑多租户的问题。但实际项的进展过程中发现部分中大体量的客户也需要类似多租户的功能。这类客户需求与其说是需求多租户,不如说是希望有一个多租户级别的数据隔离,每个业务部门只能操作自己的业务部门的数据(数据包含商品,品牌,属性,平台店铺,订单,售后单据等),而总部依据可以查看一些汇总信息(例如销售对账单据等)。而随着2022年疫情状况严峻,很多中大客户对于内部平台升级和替换都变的更加谨慎和保守。所以当项目方向变成中小企业OMS时,多租户SaaS化的需求就更加迫切了。所以后期如何在原有系统上在扩展一套多租户SaaS化功能也是个课题。

中间件

H项目使用的中间件中比较特别的有分布式任务管理系统xxl-job,消息队列RocketMQ,这两个中间件都是第一次使用。关于这两个中间件也有不是可以谈谈的。

分布式任务管理系统 XXL-JOB

作为一个开源的分布式任务管理系统,xxl-job足够简单且相当容易上手使用,无论是基于Spring,还是原生Java做开发都是相当简单的。

而不足之处也很明显,最近1年版本迭代速度明显变慢,有些功能和BUG都有待处理的。比如想实动态创建定时任务这类功能就必须自己动手去对xxl-job-admin(调度中心)进行修改。

对于任务执行日志这块,现在xxl-job也是文件格式在执行器所在服务器上进行输出的,当执行器容器化或是运行在Kubernetes集群中的时候,当容器或是Pod重启后,就会丢失日志。这点在后期运维以及调试上还是有点麻烦的。

消息队列 RocketMQ

阿里出品的消息队列如果基于云产品来使用的话问题不大(阿里云卖的价钱也是有点黑的),但是在运维上还是有点问题的比如Topic,Group这些东西都只能人工去添加,阿里云并没有提供导入服务。全系统如果使用消息队列的场景多的话,维护这些Topic和Group还是很酸爽的。还有一点RocketMQ重复消息这一点是无法保证的。用阿里云的原话

绝大多数情况下,消息是不重复的。作为一款分布式消息中间件,在网络抖动、应用处理超时等异常情况下,无法保证消息不重复,但是能保证消息不丢失。

实际运用上,在大数据量并发的情况下,我们实际上是遇到了消息重复消费的问题。这点还是需要通过代码干预(例如增加一个唯一MsgId,对MsgId做分布式锁来解决)。

还有一点就是在旧版本SDK的默认配置下消费者的负载是有点小问题,总是有一个节点会比其他节点更容易消费到大量消息。据阿里云自己说明在新版本SDK的默认负载配置也已经修复这个问题了。

(未完待续)

]]>
+ + + 设计 + + + Aliyun + + Kubernetes + + 架构 + + Java + + + https://hashmaparraylist.github.io/2023/07/10/H%E9%A1%B9%E7%9B%AE%E5%BF%83%E8%B7%AF%E5%8E%86%E7%A8%8B%E8%AE%B0-1/#disqus_thread + +
+ + + AKS中重写规则踩坑小记录 + https://hashmaparraylist.github.io/2022/09/23/AKS%E4%B8%AD%E9%87%8D%E5%86%99%E8%A7%84%E5%88%99%E8%B8%A9%E5%9D%91%E5%B0%8F%E8%AE%B0%E5%BD%95/ + https://hashmaparraylist.github.io/2022/09/23/AKS%E4%B8%AD%E9%87%8D%E5%86%99%E8%A7%84%E5%88%99%E8%B8%A9%E5%9D%91%E5%B0%8F%E8%AE%B0%E5%BD%95/ + Fri, 23 Sep 2022 05:51:49 GMT + + + + <h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>最近在做标准产品在不同云平台中的部署验证,有幸体验了一下微软的Azure。负责采购的运维部门这次采用了<code>Application G + + + + + 前言

最近在做标准产品在不同云平台中的部署验证,有幸体验了一下微软的Azure。负责采购的运维部门这次采用了Application Gateway来搭配AKS(Azure Kubernetes Service)对外暴露服务,正好借着这个机会来体验一下Application Gateway

应用场景

  1. 域名api.demo.com指向Application Gateway的IP地址
  2. AKS内部2个Service, gateway-servicebackend-service分别需要通过Application Gateway对外暴露。
  3. /gateway/指向gateway-service, 然后/backend/指向backend-service。而且两个Service都没有context-path,所以需要做一个Rewrite重写URI到Service的根目录上。

定义重写集

打开AKS对应的应用程序网关设置 > 重写。选择添加重写集。在1. 名称和关联这个Tab上只需要填写名称这项即可(名称后面在做ingress时需要使用), 关联的传递规则不需要选择。2. 重写规则配置里添加一个重写规则,然后填上重写规则的名称,并添加条件(默认新建重写规则时,只会生成操作,不会生成条件)

条件做如下设置

  • 要检查的变量类型 : 服务器变量
  • 服务器变量: request_uri
  • 区分大小写:
  • 运算符: 等号(=)
  • 要匹配的模式: /(gateway|backend)/?(.*)

操作做如下设置

  • 重写类型: URL
  • 操作类型: 设置
  • 组件: URL路径和URL查询字符串
  • URL路径值: /{var_request_uri_2}
  • 重新计算路径映射: 不选中
  • URL查询字符串值: 留空不设值

特殊说明

操作里的URL路径值不能使用正则表达式GROUP替换组,例如$1$2之类的。Azure自己定义了一套对应的替换组命名规则。具体可以参考这个网页使用应用程序网关重写 HTTP 标头和 URL

另外一个需要注意一点,如果在条件里选择了服务器变量request_uri的时候,注意这个request_uri是完整的原始请求URI(携带了查询参数)。例如: 在请求http://api.demo.com/gateway/search?foo=bar&hello=world中,request_uri的值将为/gateway/search?foo=bar&hello=world。由于request_uri里包含了查询参数,所以在操作组件中建议勾选URL路径和URL查询字符串。如果只选择URL路径的情况下可能出现无法预期的错误。以我们上述的配置来说明。

对象URL: http://api.demo.com/gateway/search?foo=bar&hello=world

组件URL路径和URL查询字符串URL路径
结果/search?foo=bar&hello=world/search?foo=bar&hello=world?foo=bar&hello=world

ACK的Ingress设置

当选择了Application Gateway作为对外暴露Service的方式时,Kubernetes集群里(kube-system命名空间里)多一个Application Gateway Ingress Controller(Azure工单时通常会简称为agic)的Deployment,所以对外暴露服务时可以像传统nginx ingress controller一样添加一个Ingress对象即可(甚至配置也和ngic大致相同,只是多了2个annotations)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
# 这里指定重写规则集(不是重写规则的名字)
appgw.ingress.kubernetes.io/rewrite-rule-set: rule-backend
# 指定说明你这里ingress的类型是agic
kubernetes.io/ingress.class: azure/application-gateway
name: backend-ingress
namespace: default
spec:
rules:
- host: api.demo.com
http:
paths:
- backend:
service:
name: gateway-service
port:
number: 8080
path: /gateway/
pathType: Prefix
- backend:
service:
name: backend-service
port:
number: 8080
path: /backend/
pathType: Prefix

总结

由于微软云这块文档有部分缺失,导致在配置这块花了一点时间去排查,甚至开了工单。总结下来Ingress的配置主要是根据请求路径路由到对应的Service,重写规则集才是实际负责根据正则来进行匹配重写。

]]>
+ + + 后端 + + Cloud + + + Aliyun + + Kubernetes + + + https://hashmaparraylist.github.io/2022/09/23/AKS%E4%B8%AD%E9%87%8D%E5%86%99%E8%A7%84%E5%88%99%E8%B8%A9%E5%9D%91%E5%B0%8F%E8%AE%B0%E5%BD%95/#disqus_thread + +
+ + + Spring Cloud Kubernetes环境下使用Jasypt + https://hashmaparraylist.github.io/2021/09/29/Spring-Cloud-Kubernetes%E7%8E%AF%E5%A2%83%E4%B8%8B%E4%BD%BF%E7%94%A8Jasypt/ + https://hashmaparraylist.github.io/2021/09/29/Spring-Cloud-Kubernetes%E7%8E%AF%E5%A2%83%E4%B8%8B%E4%BD%BF%E7%94%A8Jasypt/ + Wed, 29 Sep 2021 03:28:11 GMT + + + + <h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>最近半年着手开始做了基于微服务的中台项目,整个项目的技术栈采用的是<code>Java</code> + <code>Spring Clou + + + + + 前言

最近半年着手开始做了基于微服务的中台项目,整个项目的技术栈采用的是Java + Spring Cloud + Kubernetes + Istio

业务开放上还是相当顺利的。但是在安全审核上,运维组提出了一个简易。现在项目一些敏感配置,例如MySQL用户的密码,Redis的密码等现在都是明文保存在Kubernetes的ConfigMap中的(是的,我们并没有Nacos作为微服务的配置中心)。这样可能存在安全隐患。

首次尝试

既然有问题,那就解决问题。要给配置文件中的属性项目加密很简单,稍微Google一下,就有现成的方案了。

现在比较常用的解决方案就是集成Jasypt,然后通过jasypt-spring-boot-starter来融合进Spring。

POM包加入jasypt-spring-boot-starter

1
2
3
4
5
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>

Dockerfile中增加java参数

1
2
...
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar app.jar --jasypt.encryptor.password=helloworld $PARAMS"]

在ConfigMap中添加加密属性

1
2
3
4
5
6
7
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
data:
application.yaml: |-
test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)

利用actuator接口测试

management.endpoints.web.exposure.include属性中增加env,这样我们就可以通过调用/actuator/env来查看一下env接口返回的整个Spring 容器中所有的PropertySource。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
...
"propertySources": [
{
"name": "bootstrapProperties-configmap.demo.default",
"properties": {
"test2": {
"value": "Hello,world"
}
}
}
...
]
}

OK, 这下配置项已经加密了。问题解决了。

但是…

新的问题

自从项目集成了Jayspt以后,出现了一个奇怪的问题。每次项目试图通过修改ConfigMap的配置文件,然后试图通过spring-cloud-starter-kubernetes-fabric8-config来做自动Reload,都失败了。然而查阅应用日志,并没有出现任何异常。无奈只能打开spring-cloudjasypt-spring-bootDEBUG日志。

进过几天对日志和两边源代码的分析。终于找到了原因

原因

在Spring Boot启动时jasypt-spring-boot会将下面6种配置(并不仅限与这6种配置文件)

  • Classpath下的application.yaml
  • Classpath下的bootstrap.yaml
  • 集群里名称为${spring.cloud.kubernetes.config.name}的ConfigMap
  • 集群里名称为${spring.cloud.kubernetes.config.name}-kubernetes的ConfigMap
  • Java启动参数
  • 环境变量

转换成jasypt-spring-boot自己的PropertySource实现类EncryptableMapPropertySourceWrapper

但是如果使用Kubernetes的ConfigMap来作微服务配置中心的时候,Spring Cloud会在ConfigurationChangeDetector中查找配置类org.springframework.cloud.bootstrap.config.BootstrapPropertySource, 并依据BootstrapPropertySource的类型来判断容器内的配置与集群中ConfigMap里的配置是否有差异,来触发配置reload。

由于jasypt-spring-boot已经将所有的配置文件转型成了EncryptableMapPropertySourceWrapper, 所以ConfigurationChangeDetector无法找到BootstrapPropertySource所以会一直任务ConfigMap的里的配置没有变化,导致整个Reload失效(无论是使用polling还是event方式)

解决问题

为了保证ConfigMap变化后自动Reload的功能,所以jasypt-spring-boot不能把BootstrapPropertySource转换成EncryptableMapPropertySourceWrapper

所以我们需要设置jasypt.encryptor.skip-property-sources配置项, Classpath中的application.yaml需要增加配置

1
2
3
jasypt:
encryptor:
skip-property-sources: org.springframework.cloud.bootstrap.config.BootstrapPropertySource

skip-property-sources配置项配置后,加密项目就不能配置在ConfigMap里了,毕竟已经被我们忽略了。那么我们只能另外找一个PropertySource来存放加密项目了。

Classpath中的两个Yaml由于编译时会被Maven打包进Jar文件,会牵涉多个CI/CD多个流程显然不合适,启动参数配置项的也要影响到Docker镜像制作这个流程。所以判断下来最适合的PropertySource就是环境变量了。

环境变量增加加密项

在Kubernetes的部署Yaml中,添加加密数据项application.test.str

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: demo
name: demo
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- env:
- name: TZ
value: Asia/Shanghai
- name: application.test.str
value: >-
ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
....

如果需要更加严密的加密方针的话,我们可以把环境变量的内容放进Kubernetes的Secrets中。

在ConfigMap中引用application.test.str

1
2
3
4
5
6
7
8
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
data:
application.yaml: |-
test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
test3: ${application.test.str}

通过actuator接口来测试

通过actuator\env接口来测试一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
...
"propertySources": [
{
"name": "bootstrapProperties-configmap.demo.default",
"properties": {
"test2": {
"value": "ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)"
},
"test3": {
"value": "Hello,world"
}
}
}
...
]
}

这样ConfigMap中的配置项test3就可以通过环境变量引用并使用加密配置项了。同时修改ConfigMap依然可以触发auto reload了。这下终于算是解决了。

]]>
+ + + 后端 + + + Java + + Spring + + + https://hashmaparraylist.github.io/2021/09/29/Spring-Cloud-Kubernetes%E7%8E%AF%E5%A2%83%E4%B8%8B%E4%BD%BF%E7%94%A8Jasypt/#disqus_thread + +
+ + + 重学Java (一) 泛型 + https://hashmaparraylist.github.io/2021/03/15/%E9%87%8D%E5%AD%A6Java-%E4%B8%80-%E6%B3%9B%E5%9E%8B/ + https://hashmaparraylist.github.io/2021/03/15/%E9%87%8D%E5%AD%A6Java-%E4%B8%80-%E6%B3%9B%E5%9E%8B/ + Mon, 15 Mar 2021 08:01:46 GMT + + + + <h2 id="1-前言"><a href="#1-前言" class="headerlink" title="1. 前言"></a>1. 前言</h2><p>泛型编程自从 Java 5.0 中引入后已经超过15个年头了。对于现在的 Java 码农来说熟练使用泛型编程已经是家常便 + + + + + 1. 前言

泛型编程自从 Java 5.0 中引入后已经超过15个年头了。对于现在的 Java 码农来说熟练使用泛型编程已经是家常便饭的事情了。所以本文就在不对泛型的基础使用在做说明了。 如果你还不会使用泛型的话,可以参考下面两个链接

这篇文章就简答聊一下,我实际在开发工作中很少用的到泛型方法这个知识点,以及在实际项目中有哪些东西会使用到泛型。

2. 泛型方法

在阅读代码的时候我们经常会看到下面这样的方法 (这段代码摘自 java.util.AbstractCollection)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 public <T> T[] toArray(T[] a) {
// Estimate size of array; be prepared to see more or fewer elements
int size = size();
T[] r = a.length >= size ? a :
(T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
Iterator<E> it = iterator();

for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) { // fewer elements than expected
if (a == r) {
r[i] = null; // null-terminate
} else if (a.length < i) {
return Arrays.copyOf(r, i);
} else {
System.arraycopy(r, 0, a, 0, i);
if (a.length > i) {
a[i] = null;
}
}
return a;
}
r[i] = (T)it.next();
}
// more elements than expected
return it.hasNext() ? finishToArray(r, it) : r;
}

那么 pulic 关键字后面的那个 <T> 就是用来标记这个方法是一个泛型方法。 那什么是泛型方法呢。

官方的解释是这样的

1
Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.

通俗点来将就是将一个方法泛型化,让一个普通的类的某一个方法具有泛型功能。 如果在一个泛型类中增加一个泛型方法,那这个泛型方法就可以有一套独立于这个类的泛型类型。

通过一个简单的例子, 我们来看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* GenericClass 这个泛型类是一个简单的套皮的 HashMap
*/
public class GenericClass<K, V> {

private Map<K, V> map = new HashMap<>();

public V put(K key, V value) {
return map.put(key, value);
}

public V get(K key) {
return map.get(key);
}

// 泛型方法 genericMethod 可以接受一个全新的、作用域只限本函数的泛型类型T
public <T> T genericMethod(T t) {
return t;
}

}

实际使用起来

1
2
3
4
5
6
GenericClass<String, Integer> map = new GenericClass<>();
// put 和 get 方法的参数必须使用定义时指定的 String 和 Integer
System.out.println(map.put("One", 1));
System.out.println(map.get("One"));
// 泛型方法 genericMethod 就可以接受一个 String 和 Integer 以外的类型
System.out.println(map.genericMethod(new Double(1.0)).getClass());

我们再来看看 JDK 中使用到泛型方法的例子。我们最常使用的泛型容器 ArrayList 中有个 toArray 方法。JDK 在它的实现中就提供了两个版本,其中一个就是泛型方法的版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
// 这是一个普通版本,返回一个Object的数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}

// 这是一个泛型方法的版本,将容器里存储的元素输出到 T[] 数组中。 其中 T 必须是 E 的父类,否则 System.arraycopy 会抛出 ArrayStoreException 异常
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
}

泛型方法总体上来说就是可以给与现有的方法实现上,增加一个更加灵活的实现可能。

3. 实战应用

在实际的项目中,对于泛型的使用,除了像倾倒垃圾一样往泛型容易里塞各种 java bean 和其他泛型对象。还能怎么使用泛型呢?

我们在实际的一些项目中,会对数据库中的一些表(多数时候是全部)先实现 CRUD (Create, Read, Update, Delete)的操作,并从这些操作中延伸出一些简单的 REST 风格的 WebAPI 接口,然后才会根据实际业务需要实现一些更复杂的业务接口。

大体上会是下面这个样子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

// 这是一个简单的 Entity 对象
// 通常现在的 Java 应用都会使用到 Lombok 和 Spring Boot
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Entity
@Table(name = "user")
public class User {
@Id
private Long id;
private String username;
private String password;
}

// 然后这个是 DAO 接口继承自 spring-data 的 JpaRepository
public interface UserDao extends JpaRepository<User, Long> {
}

// 在来是一个访问 User 资源的 Service 和他的实现
public interface UserService {
List<User> findAll();
Optional<User> findById(Long id);
User save (User user)
void deleteById(Long id);
}

@Service
public class UserSerivceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}

@Override
public List<User> findAll() {
return this.dao.findAll();
}

@Override
public Optional<User> findById(Long id) {
return this.dao.findById(id);
}

@Override
public User save(User user) {
return this.dao.save(user);
}

@Override
public void deleteById(Long id) {
this.dao.deleteById(id);
}
}

// 最后就是 WebAPI 的接口了
@RestController
@RequestMapping("/user/")
public class UserController{
private UserService userService;
public UserController(userService userService) {
this.userService = userService;
}

@GetMapping
@ResponseBody
public List<User> fetch() {
return this.userService.findAll();
}

@GetMapping("{id}")
@ResponseBody
public User get(@PathVariable("id") Long id) {
// 由于是示例这里就不考虑没有数据的情况了
return this.userService.findById(id).get();
}

@PostMapping
@ResponseBody
public User create(@RequestBody User user) {
return this.userService.save(user);
}

@PutMapping("{id}")
@ResponseBody
public User update(@RequestBody User user) {
return this.userService.save(user);
}

@DeleteMapping("{id}")
@ResponseBody
public User delete(@PathVariable("id") Long id) {
User user = this.userService.findById(id);
this.userService.deleteById(id);
return user;
}
}

大致一个表的一套相关接口就是这个样子的。如果你的数据库中有大量表的话,而且每个表都需要提供 REST 风格的 WebAPI 接口的话,那么这将是一个相当枯燥的而又及其容易出错的工作。

为了不让这项枯燥而又容易犯错的工作占去我们宝贵的私人时间,我们可以通过泛型和继承的技巧来重构从 Service 层到 Controller 的这段代码(感谢 spring-data 提供了 JpaRepository, 让我们不至于从 DAO 层重构)

3.1 Service 层的重构

首先是 Service 接口的重构,我们 Service 层接口就是定义了一组 CRUD 的操作,我们可以将这组 CRUD 操作抽象到一个父接口,然后所有 Service 层的接口都将继承自这个父接口。而接口中出现的 Entity 和主键的类型(上例中 User 的主键 id 的类型是 Long)就可以用泛型来展现。

1
2
3
4
5
6
7
8
9
10
11
// 这里泛型表示 E 来指代 Entity, ID 用来指代 Entity 主键的类型
public interface ICrudService<E, ID> {
List<E> findAll();
Optional<E> findById(ID id);
E save(E e);
void deleteById(ID id);
}

// 然后 Service 层的接口,就可以简化成这样
public interface UserService extends ICrudService<User, Long> {
}

同样 Service 层的实现也可以使用相似的方法具体实现可以抽象到一个基类中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 相比 ICrudService 这里有多了一个泛型 T 来代表 Entity 对应的 DAO, 我们的每一个 DAO 都继承自
// spring-data 的 JpaRepository 所以,这里可以使用到泛型的边界
public abstract class AbstractCrudService<T extends JpaRepository<E, ID>, E, ID> {
private T dao;
public AbstractCrudService(T dao) {
this.dao = dao;
}

public List<E> findAll() {
return this.dao.findAll();
}

public Optional<E> findById(ID id) {
return this.dao.findById(id);
}

public E save(E e) {
return this.dao.save(e);
}

public void deleteById(ID id) {
this.dao.deleteById(id);
}
}

// 那 Service 的实现类可以简化成这样
@Service
public class UserServiceImpl extends AbstractCrudService<UserDao, User, Long> implements UserService {
public UserServiceImpl(UserDao dao) {
supper(dao);
}
}

同样我们可以通过相同的方法来对 Controller 层进行重构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Controller 层的基类
public abstract class AbstractCrudController<T extends ICrudService<E, ID>, E, ID> {
private T service;
public AbstractCrudController(T service) {
this.service = service;
}

@GetMapping
@ResponseBody
public List<E> fetch() {
return this.service.findAll();
}

@GetMapping("{id}")
@ResponseBody
public E get(@PathVariable("id") ID id) {
// 由于是示例这里就不考虑没有数据的情况了
return this.service.findById(id).get();
}

@PostMapping
@ResponseBody
public E create(@RequestBody E e) {
return this.service.save(e);
}

@PutMapping("{id}")
@ResponseBody
public E update(@RequestBody E e) {
return this.service.save(e);
}

@DeleteMapping("{id}")
@ResponseBody
public E delete(@PathVariable("id") ID id) {
E e = this.service.findById(id).get();
this.service.deleteById(id);
return e;
}
}

// 具体的 WebAPI
@RestController
@RequestMapping("/user/")
public class UserController extends AbstractCrudController<UserService, User, Long> {
public UserController(UserService service) {
super(service);
}
}

经过重构可以消减掉 Servcie 和 Controller 中的大量重复代码,使代码更容易维护了。

4. 结尾

关于泛型就简单的说这些了,泛型作为 Java 日常开发中一个常用的知识点,其实还有很多知识点可以供我们挖掘,奈何本人才疏学浅,这么多年工作下来,只积累出来这么点内容。

文末放上示例代码的代码库:

]]>
+ + + 后端 + + + java + + generic + + + https://hashmaparraylist.github.io/2021/03/15/%E9%87%8D%E5%AD%A6Java-%E4%B8%80-%E6%B3%9B%E5%9E%8B/#disqus_thread + +
+ + + 2020年阅读总结 + https://hashmaparraylist.github.io/2020/12/29/2020%E5%B9%B4%E9%98%85%E8%AF%BB%E6%80%BB%E7%BB%93/ + https://hashmaparraylist.github.io/2020/12/29/2020%E5%B9%B4%E9%98%85%E8%AF%BB%E6%80%BB%E7%BB%93/ + Tue, 29 Dec 2020 02:45:41 GMT + + + + <p>多灾多难的2020年已经没有几天了,整个2020年要说还真是像一记刹车,去年很多想法,计划都有因为疫情原因搁置了。平时的生活更是从宅家变成了重度宅家的状态,自然就多出了许多大段大段的连续时间,可以用来好好玩玩大作,看看小说。仔细盘点一下今年总共看了10本小说外加2本科普书籍 + + + + + 多灾多难的2020年已经没有几天了,整个2020年要说还真是像一记刹车,去年很多想法,计划都有因为疫情原因搁置了。平时的生活更是从宅家变成了重度宅家的状态,自然就多出了许多大段大段的连续时间,可以用来好好玩玩大作,看看小说。仔细盘点一下今年总共看了10本小说外加2本科普书籍( 2020年阅读书单
),算是自2018年以来读书最多的一个年份了。其中不乏拖了近3年的那本《西方文化中的数学》。接着就来絮叨絮叨今年看的这12本书吧。


《莱博维茨的赞歌》

第一次被安利《莱博维茨的赞歌》的机核的电台节目辐射-视觉、音乐以及文学, 当时听完节目就相当好奇,可以被一部载入史册的CRPG游戏誉为精神文本的小说到底是个什么样子的。但是当时新星版的译本几乎就是绝本了,某鱼的二手价也要已经飙到了3位数。直到今年年初在核市节目上听到了中信版译本的消息,才有机会买来拜读。

全书通过3个故事讲述了核战灭世后莱博维茨修道院从保卫文明,到重建,然后看着文明再度毁灭的故事。各种无不透露着辐射那股 War, War never changed的味道。

《占星术杀人事件》《屋顶上的小丑》《亿男》

这3本书都是2019年上海书展上买的。前两本都是岛田庄司的推理名篇,其中《占星术杀人事件》在本格派推理史上有着非凡的地位,各种被抄袭从未被超越过。《屋顶上的小丑》要是最吸引人的话就是多线叙事,每条线都掐在恰如其分的地方。《亿男》则是挂着悬疑羊头卖着人生哲理。

《神经漫游者》

威廉吉布森的蔓生三部曲之一,开创了赛博朋克的科幻流派,说实话这本书我看的很痛苦,很多地方实在读不懂,到现在我还是不明白那些无法读懂的地方到底是原著如此,还是翻译的不行,亦或者是Kindle版排版太烂了。

《神们自己》,《神的九十亿个名字》

阿瑟克拉克的中篇小说和短篇小说集,这两本书阅读体验奇佳,尤其是在读完《神经漫游者》后,虽然三本书被Amazon打在同一个包里来卖的,但是光从排版上就比《神经漫游者》高出不少,所以才会有我对后者排版问题的质疑。

《神们自己》三线叙事,讲述了2个平行宇宙中的2个物种在能源,种族存续,家庭伦理上的各种讨论和故事。 《神的九十亿个名字》作为短篇小说集收录了好几篇风格题材迥异的小说,花上十几分钟读上一个故事是相当惬意的。

《佐伊的战争》 《人类决裂》 《万物的终结》

约翰斯卡尔齐的《老人的战争》系列的后3部,前三部大概是3,4年前读的吧,在这个宇宙观里最吸引人的设定就绿皮的人类防卫军战士,以及跨种族间的政治斗争。系列的六本书相当于在这个大设定下讲的六个故事。每个故事虽说都是独立的故事,但是前后皆有关联,而且最重要的就是每本的长度都算不上很长,相当适合于一口气读完。

《西方文化中的数学》

这本书大概是从2017年开始读的,当时意气奋发觉得对于理科生出身的我,书中内容应该很好理解,没想到最后不光是文化还是数学读的都是一知半解。只能说自大了。

《一想到还有95%的问题留给人类,我就放心了》

大概是18年还是19年前后对量子物理和宇宙物理开始有了兴趣,虽然各中理论并不是很了解,但是仍然很好奇的买了这本书,全书插画相当有趣,同时使用浅显易懂的语言来解释各种复杂的物理现象,以及当今人类物理知识的边界。强烈推荐给想假装成学霸的朋友。


2021年读什么

具体读啥还没想好基本上还是以科幻和悬疑为主,但是肯定会读的应该有海明威的《丧钟为谁而鸣》和小林泰三的《醉步男》吧。

]]>
+ + + 杂记 + + + 阅读 + + + https://hashmaparraylist.github.io/2020/12/29/2020%E5%B9%B4%E9%98%85%E8%AF%BB%E6%80%BB%E7%BB%93/#disqus_thread + +
+ + + Largrange项目架构与设计回顾 (二) + https://hashmaparraylist.github.io/2020/05/21/Lagrange%E9%A1%B9%E7%9B%AE%E5%9B%9E%E9%A1%BE2/ + https://hashmaparraylist.github.io/2020/05/21/Lagrange%E9%A1%B9%E7%9B%AE%E5%9B%9E%E9%A1%BE2/ + Thu, 21 May 2020 02:49:41 GMT + + + + <p>在<a href="/2020/05/09/lagrange%E9%A1%B9%E7%9B%AE%E5%9B%9E%E9%A1%BE/">Largrange项目架构与设计回顾 (一)</a> 里面我讲了一下项目开始架构和技术选型的一些内容。这一章来聊聊业务设计上的这点事。< + + + + + Largrange项目架构与设计回顾 (一) 里面我讲了一下项目开始架构和技术选型的一些内容。这一章来聊聊业务设计上的这点事。

总体来说项目设计的时候,我们对业务模块的划分和拆解总体上来说都是遵循着高内聚,低耦合的原则来进行划分的。大部分和业务相关的服务,都还是能很好的进行功能划分的。但是有一些和具体业务关联性并不是很高的服务在界定与实现时出现了一些问题。

任务调度服务

由于平台方面会有一些控制命令下发给Android设备,而且下发的命令并不一定都是实时的,大部分都是指定一个时间来下发,所以在设计的时候就考虑到需要一个任务调度服务(以下略称cron),来处理这些下发指令以及未来可能会有的定时批处理任务的业务需求。技术选型的时候考虑到整个服务是构建在Kubernetes上的一个分布式的微服务架构,所以就没有选择Spring Scheduler,而是使用了Quarter来支撑整个cron

从技术选型上来说cron没有什么问题,但是在设计如何使用cron上还是有点问题的, 我们先来看看已推送服务为例,整个平台中cron的处理流程是怎样的。

  1. Platform 调用 cron的创建定时任务Job的接口(接口的参数为推送时使用的相关参数)
  2. cron 创建Quartz的Job和Trigger,并将Job和Trigger的Name(Quartz中Job和Trigger的唯一标识符)返回个Platform
  3. cron 在指定时间触发推送的Job,即调用Push服务的推送接口
  4. Push的推送接口调用第三方服务商的推送服务,并将第三方推送服务的调用结果返回给cron
  5. cron通过调用Platform预留的推送服务回调地址将第三方推送服务调用接口返还给Platform
  6. Platform 将第三方推送服务的调用结果留档保存,并继续业务处理

设计之初考虑到不想在cron中牵扯到具体的业务, 所以设计了一个Platform的回调接口来处理推送后的具体业务处理。虽然保证了cron尽量减少了和业务逻辑接触与数据库的访问。但是前前后后要访问集群内部的其他微服务2次,额外增加了网络开销,以及因为网络通信造成额外的通信失败风险。而且针对每种不同的定时任务,都需要额外开发一个QuartzJobBean,很难形成统一的QuartzJobBean来进行处理。今后如果还有相似项目还是需要重新考量一下如何设计一个更加完善的定时任务。

总结

暂时就想到了这些东西,今后想到啥还会继续在这里补存。

]]>
+ + + 设计 + + + Aliyun + + Kubernetes + + 架构 + + Java + + + https://hashmaparraylist.github.io/2020/05/21/Lagrange%E9%A1%B9%E7%9B%AE%E5%9B%9E%E9%A1%BE2/#disqus_thread + +
+ + + Largrange项目架构与设计回顾 (一) + https://hashmaparraylist.github.io/2020/05/09/lagrange%E9%A1%B9%E7%9B%AE%E5%9B%9E%E9%A1%BE/ + https://hashmaparraylist.github.io/2020/05/09/lagrange%E9%A1%B9%E7%9B%AE%E5%9B%9E%E9%A1%BE/ + Sat, 09 May 2020 02:34:47 GMT + + + + <blockquote> +<p>项目背景</p> +</blockquote> +<p>从去年年底开始一个老客户希望在他们的一个传统的机械设备(后面略称 <code>E机关</code> )上外装一个Android设备。 Android设备和 <code>E机关</code> 之间通 + + + + +

项目背景

从去年年底开始一个老客户希望在他们的一个传统的机械设备(后面略称 E机关 )上外装一个Android设备。 Android设备和 E机关 之间通过串口或是RJ45接口进行数据交互,主要是Android设备获取 E机关 内部的数据,并不会通过接口来控制 E机关 。 Android设备则通过4G 来和平台交互,上报Android设备的状态数据,同时接受平台的控制。以此来实现让传统机械设备也能拥抱物联网的概念。

最后围绕着客户的需求分成了3个项目来并行推进

  1. Android设备的硬件设备的设计、选材、样机制作与量产规划
  2. 在Android设备上,进行与E机关以及平台进行交互的APP开发
  3. 用于Android设备交互的平台的架构、设计与开发

我们平台Team就负责 3.用于Android设备交互的平台 并命名Lagrange (拉格朗日) 。

设计&架构

整个平台这块不仅需要向Android设备的APP提供数据交互接口,还需要有一个供相关运营人员使用的前端Web应用,以及与云平台(主要是Aliyun) 交互的功能。所以考虑到多方面使用微服务的架构来实现整个平台端。
由于E机关的工作工况不能保证长期较的稳定的连接到4G网络,所以我们并不考虑使用Socket长连接的方式来做APP和平台之间的数据交互,要实现平台对Android设备进行反控的话,只能实现推送方式来把控制命令下发给Android设备,所以还需要有一个第三方推送服务商交互的服务。同时控制推送也有实时和非实时以及定期推送的需求,所以可能还需要一个任务调度的服务。

根据对上述业务进行梳理,我们将项目分成几个服务

  • 提供Android设备的交互接口的 App Service
  • 提供运营人员使用前端Web应用 Platform Web Service
  • 前端Web应用使用到的一些接口 Platform Service
  • 负责第三方推送服务商交互的 Push Service
  • 提供云平台鉴权用的 Auth Service
  • 用来管理任务调度的 Cron Service

由于前一个项目实施的时候没有使用容器部署,每当访问量峰值的时候,我们这些码农兼运维就各种加班,所以这次项目决定直接将服务容器化,同时选择了Kubernetes来管理容器。由于客观原因线上的Kubernetes直接购买了Aliyun的托管版Kubernetes服务。 内部的开发测试环境则使用了 Rancher 2.0 来构建Kubernetes集群。

技术选型

a. 后端服务选型

由于不考虑长连接的原因,所以在后端服务在技术选型上基本就不考虑Netty了,直接上Spring大礼包。

  • Spring Boot 开发接口
  • Spring Data 配合 JPA 来进行数据的持久化
  • Spring Cloud Kubenetes 来做数据的Config的autoreload
  • Quartz 负责处理任务调度

服务之间调用都使用HTTP服务, 服务发现也有Kubernetes的DNS机制支持。服务网格则选择了比较成熟的Istio,主要还是Aliyun的Kubernetes可以集成Istio,部署和使用都相当方便。

b. 前端Web应用选型

因为前端Web应用主要是给运营人员使用,所以我们考虑使用Single Page Application来做个前后端分离的Web应用。框架这块由于团队成员基本上没有什么前端开发经验,基本都是后台写Java的码农,所以框架选择有点随性,直接就点名了vue.js。前端控件库则用的是阿里系的Antd。

c. 数据持久层

主数据库选择了mysql,缓存用的redis。这些都是团队比较熟悉的。由于一些特殊的业务需求和使用场景,我们还加了一个mongodb来做为一个副数据库,主要存放一些特殊业务使用的数据。

d. DevOps

用了相当传统的GitLab CE 加上Jenkins的组合,实现前后端代码的自动编译,推送到私有的镜像仓库(使用Aliyun的镜像仓库服务)

最后

以上基本是项目最早做设计时候的各种考量。暂时先写这么多,过两天再回顾一下当初设计上有哪些觉得不足的地方。

]]>
+ + + 设计 + + + Aliyun + + Kubernetes + + 架构 + + Java + + + https://hashmaparraylist.github.io/2020/05/09/lagrange%E9%A1%B9%E7%9B%AE%E5%9B%9E%E9%A1%BE/#disqus_thread + +
+ + + 阿里云Kubernetes上线踩坑记 + https://hashmaparraylist.github.io/2020/04/01/%E9%98%BF%E9%87%8C%E4%BA%91Kubernetes%E4%B8%8A%E7%BA%BF%E8%B8%A9%E5%9D%91%E8%AE%B0/ + https://hashmaparraylist.github.io/2020/04/01/%E9%98%BF%E9%87%8C%E4%BA%91Kubernetes%E4%B8%8A%E7%BA%BF%E8%B8%A9%E5%9D%91%E8%AE%B0/ + Wed, 01 Apr 2020 01:47:38 GMT + + + + <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre>< + + + + +
1
2
Update:
2020-04-08 增加istio-ingressgateway高可用的设置

最近公司因为项目需要,在阿里云上部署了一个Kubernetes集群。虽然阿里云的文档说的还算细致,但是还是有些没有明确说明的细节。

1. 购买篇

申请项目预算的时候,只考虑到Worker节点,1个SLB节点以及域名和证书的预算。但是实际购买的时候发现还有许多额外的开销。

1.1 SNAT

这个和EIP一并购买,可以方便通过公网使用kubectl访问集群。关于SNAT网关至今不是很明白需要购买这个服务的意义何在,只是为了一个EIP来访问集群吗?

1.2 Ingress

这个选上了后,阿里云会给你买个SLB而且还是带公网访问的,如果你后期考虑使用Istio的话,建议你集群创建后,直接停止这个SLB,以免产生额外的费用。

1.3 日志服务

通过阿里云的日志服务来收集应用的的日志,挺好用的。但是另外收费,如果有能力的自建日志服务的可不购买。

2. Istio

阿里云的Kubernetes集群完美集成了Istio,根据向导就能很简单的部署成功。

2.1 额外的SLB

Istio的Gateway 需要绑定一个新的SLB,和Ingress的SLB不能是同一个,又是一笔额外的开销

2.2 集群外访问

这个在阿里云的Istio FAQ中有提到,按照指导很容易解决

2.2 SLB的443监听

为了方便443端口的证书绑定,我们直接删除了SLB上原有的443监听(TCP协议), 重新建了一个443监听(HTTPS协议),指向和80端口同样的虚拟服务器组。但是设置健康检查时一直出错,经过排查发现SLB健康检查发送的请求协议是HTTP 1.0的,Istio的envoy直接反悔了426(Upgrade Required)这个状态码,所以我们无奈只能把健康检查的检查返回状态改为http_4xx,这样就能通过SLB的健康检查了。

2.3 istio-ingressgateway的高可用

istio-ingressgateway要达成高可用,只需要增加通过伸缩POD就可以实现,于istio-ingressgateway对应的SLB中的虚拟服务器组也会自动增加,完全不需要进行额外的手动设定。

由于istio-ingressgateway中挂载了HPAHorizontalPodAutoscaler(简称HPA),通常三节点的集群中最小POD数只有1台,在3节点的集群中,要实现高可用,需要手动修改HPA,增加最小POD数。


基本上现在遇到了这些坑,再有在总结吧。

]]>
+ + + 后端 + + Cloud + + + Aliyun + + Kubernetes + + + https://hashmaparraylist.github.io/2020/04/01/%E9%98%BF%E9%87%8C%E4%BA%91Kubernetes%E4%B8%8A%E7%BA%BF%E8%B8%A9%E5%9D%91%E8%AE%B0/#disqus_thread + +
+ + + 在ubuntu上从零搭建node.js + nginx + mongodb环境 + https://hashmaparraylist.github.io/2019/12/09/hot-to-install-nodejs-nginx-mongodb-on-ubuntu/ + https://hashmaparraylist.github.io/2019/12/09/hot-to-install-nodejs-nginx-mongodb-on-ubuntu/ + Mon, 09 Dec 2019 02:40:05 GMT + + + + <p>说到后端开发环境,最有名的莫过于LAMP和LNMP,最近由于node.js的强势崛起,越来越多的后端开发也开始试水node.js了。我最近也因为各种原因,前前后后总够构建了好几台node.js + nginx + mongodb的Linux服务器。</p> +<p>首先关于L + + + + + 说到后端开发环境,最有名的莫过于LAMP和LNMP,最近由于node.js的强势崛起,越来越多的后端开发也开始试水node.js了。我最近也因为各种原因,前前后后总够构建了好几台node.js + nginx + mongodb的Linux服务器。

首先关于Linux服务器,比起CentOS来说,我更加喜欢ubuntu一点。所以无论是阿里云还是一些海外的vps服务器上,我也倾向选用ubuntu服务器,本贴也是基于ubuntu服务器里说明的。

1.开始前的一些准备

首先还是需要刷新一下ubuntu的包索引并安装build-essential和libssl-dev这2个包以及curl这个工具。

1
2
3
sudo apt-get update
sudo apt-get install build-essential libssl-dev
sudo apt-get isntall curl

2.安装node.js

关于安装node.js这一点,我不是很推荐使用apt-get 来安装node.js的环境。主要是因为node.js和io.js合并以后,版本迭代速度相当频繁(主要还是因为更多ES6的特性得到了支持)。今后很有可能会有在一台服务器上使用不同版本的node.js的需求。

这里推荐一个管理不同版本node.js的工具:nvm,官网: https://github.com/creationix/nvm  。安装nvm,如果前面你安装了curl的话可以

1
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash

如果没有按照curl的话,也可以使用wget来进行安装

1
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash

然后nvm就会自动安装到home目录下面的.nvm目录里,并会在.bashrc里自动添加nvm的环境变量。为了让环境变量生效,最简单的方法就是通过ssh或是telnet重新连接你的服务器。

安装完nvm后,就可以通过nvm来安装指定版本的node.js了。

1
2
3
4
5
# 列出可以安装的node版本号
nvm ls-remote

# 安装指定版本的node (当前最新版本为v5.7.1, LTS版是v4.3.2)
nvm install v4.3.2

3.安装nginx

由于ubuntu源(尤其是阿里云的源)上的nginx经常不是最新的,如果需要安装最新版本nginx的时候需要手动添加nginx的源。

1
2
3
4
5
6
7
8
9
10
11
# 添加nginx的mainline仓库
cd /tmp/ && wget http://nginx.org/keys/nginx_signing.key
sudo apt-key add nginx_signing.key

# 编辑/etc/apt/sources.list.d/nginx.list 添加下面2行内容,井号不需要
# deb http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
# deb-src http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
sudo vi /etc/apt/sources.list.d/nginx.list

# 更新源,并安装nginx
sudo apt-get update && sudo apt-get install nginx

在编辑/etc/apt/sources.list.d/nginx.list的时候需要注意,“ubuntu代号”需要根据ubuntu服务器的版本不同手动调整的,比如14.04是trusty。通过下面的命令可以获取ubuntu的代号。

1
lsb_release -cs

4.安装mongodb

同样和nginx有同样的问题,要安装最新3.2版本的mongodb也需要手动添加ubuntu的源。

1
2
3
4
5
6
7
8
9
10
11
# 导入mongodb的public key
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927

# 生成mongodb的源list
echo "deb http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list

# 更新源
sudo apt-get update

# 安装最新版本的mongodb
sudo apt-get install -y mongodb-org

以上一台node.js + nginx + mongodb的ubuntu服务器就完成了。

]]>
+ + + 后端 + + + ubuntu + + node.js + + nginx + + mongodb + + + https://hashmaparraylist.github.io/2019/12/09/hot-to-install-nodejs-nginx-mongodb-on-ubuntu/#disqus_thread + +
+ +
+
diff --git a/categories/index.html b/categories/index.html new file mode 100644 index 0000000..c34ccaa --- /dev/null +++ b/categories/index.html @@ -0,0 +1,74 @@ + +分类 - 西门闲话

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git "a/categories/\345\220\216\347\253\257/Cloud/index.html" "b/categories/\345\220\216\347\253\257/Cloud/index.html" new file mode 100644 index 0000000..31860e4 --- /dev/null +++ "b/categories/\345\220\216\347\253\257/Cloud/index.html" @@ -0,0 +1,132 @@ + +分类: Cloud - 西门闲话

AKS中重写规则踩坑小记录

前言

最近在做标准产品在不同云平台中的部署验证,有幸体验了一下微软的Azure。负责采购的运维部门这次采用了Application Gateway来搭配AKS(Azure Kubernetes Service)对外暴露服务,正好借着这个机会来体验一下Application Gateway

+

应用场景

    +
  1. 域名api.demo.com指向Application Gateway的IP地址
  2. +
  3. AKS内部2个Service, gateway-servicebackend-service分别需要通过Application Gateway对外暴露。
  4. +
  5. /gateway/指向gateway-service, 然后/backend/指向backend-service。而且两个Service都没有context-path,所以需要做一个Rewrite重写URI到Service的根目录上。
  6. +
+

定义重写集

打开AKS对应的应用程序网关设置 > 重写。选择添加重写集。在1. 名称和关联这个Tab上只需要填写名称这项即可(名称后面在做ingress时需要使用), 关联的传递规则不需要选择。2. 重写规则配置里添加一个重写规则,然后填上重写规则的名称,并添加条件(默认新建重写规则时,只会生成操作,不会生成条件)

+

条件做如下设置

    +
  • 要检查的变量类型 : 服务器变量
  • +
  • 服务器变量: request_uri
  • +
  • 区分大小写:
  • +
  • 运算符: 等号(=)
  • +
  • 要匹配的模式: /(gateway|backend)/?(.*)
  • +
+

操作做如下设置

    +
  • 重写类型: URL
  • +
  • 操作类型: 设置
  • +
  • 组件: URL路径和URL查询字符串
  • +
  • URL路径值: /{var_request_uri_2}
  • +
  • 重新计算路径映射: 不选中
  • +
  • URL查询字符串值: 留空不设值
  • +
+

特殊说明

操作里的URL路径值不能使用正则表达式GROUP替换组,例如$1$2之类的。Azure自己定义了一套对应的替换组命名规则。具体可以参考这个网页使用应用程序网关重写 HTTP 标头和 URL

+

另外一个需要注意一点,如果在条件里选择了服务器变量request_uri的时候,注意这个request_uri是完整的原始请求URI(携带了查询参数)。例如: 在请求http://api.demo.com/gateway/search?foo=bar&hello=world中,request_uri的值将为/gateway/search?foo=bar&hello=world。由于request_uri里包含了查询参数,所以在操作组件中建议勾选URL路径和URL查询字符串。如果只选择URL路径的情况下可能出现无法预期的错误。以我们上述的配置来说明。

+

对象URL: http://api.demo.com/gateway/search?foo=bar&hello=world

+ + + + + + + + + + + + + +
组件URL路径和URL查询字符串URL路径
结果/search?foo=bar&hello=world/search?foo=bar&hello=world?foo=bar&hello=world
+

ACK的Ingress设置

当选择了Application Gateway作为对外暴露Service的方式时,Kubernetes集群里(kube-system命名空间里)多一个Application Gateway Ingress Controller(Azure工单时通常会简称为agic)的Deployment,所以对外暴露服务时可以像传统nginx ingress controller一样添加一个Ingress对象即可(甚至配置也和ngic大致相同,只是多了2个annotations)

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
# 这里指定重写规则集(不是重写规则的名字)
appgw.ingress.kubernetes.io/rewrite-rule-set: rule-backend
# 指定说明你这里ingress的类型是agic
kubernetes.io/ingress.class: azure/application-gateway
name: backend-ingress
namespace: default
spec:
rules:
- host: api.demo.com
http:
paths:
- backend:
service:
name: gateway-service
port:
number: 8080
path: /gateway/
pathType: Prefix
- backend:
service:
name: backend-service
port:
number: 8080
path: /backend/
pathType: Prefix
+ +

总结

由于微软云这块文档有部分缺失,导致在配置这块花了一点时间去排查,甚至开了工单。总结下来Ingress的配置主要是根据请求路径路由到对应的Service,重写规则集才是实际负责根据正则来进行匹配重写。

+

阿里云Kubernetes上线踩坑记

1
2
Update:
2020-04-08 增加istio-ingressgateway高可用的设置
+ +

最近公司因为项目需要,在阿里云上部署了一个Kubernetes集群。虽然阿里云的文档说的还算细致,但是还是有些没有明确说明的细节。

+

1. 购买篇

申请项目预算的时候,只考虑到Worker节点,1个SLB节点以及域名和证书的预算。但是实际购买的时候发现还有许多额外的开销。

+

1.1 SNAT

这个和EIP一并购买,可以方便通过公网使用kubectl访问集群。关于SNAT网关至今不是很明白需要购买这个服务的意义何在,只是为了一个EIP来访问集群吗?

+

1.2 Ingress

这个选上了后,阿里云会给你买个SLB而且还是带公网访问的,如果你后期考虑使用Istio的话,建议你集群创建后,直接停止这个SLB,以免产生额外的费用。

+

1.3 日志服务

通过阿里云的日志服务来收集应用的的日志,挺好用的。但是另外收费,如果有能力的自建日志服务的可不购买。

+

2. Istio

阿里云的Kubernetes集群完美集成了Istio,根据向导就能很简单的部署成功。

+

2.1 额外的SLB

Istio的Gateway 需要绑定一个新的SLB,和Ingress的SLB不能是同一个,又是一笔额外的开销

+

2.2 集群外访问

这个在阿里云的Istio FAQ中有提到,按照指导很容易解决

+

2.2 SLB的443监听

为了方便443端口的证书绑定,我们直接删除了SLB上原有的443监听(TCP协议), 重新建了一个443监听(HTTPS协议),指向和80端口同样的虚拟服务器组。但是设置健康检查时一直出错,经过排查发现SLB健康检查发送的请求协议是HTTP 1.0的,Istio的envoy直接反悔了426(Upgrade Required)这个状态码,所以我们无奈只能把健康检查的检查返回状态改为http_4xx,这样就能通过SLB的健康检查了。

+

2.3 istio-ingressgateway的高可用

istio-ingressgateway要达成高可用,只需要增加通过伸缩POD就可以实现,于istio-ingressgateway对应的SLB中的虚拟服务器组也会自动增加,完全不需要进行额外的手动设定。

+

由于istio-ingressgateway中挂载了HPAHorizontalPodAutoscaler(简称HPA),通常三节点的集群中最小POD数只有1台,在3节点的集群中,要实现高可用,需要手动修改HPA,增加最小POD数。

+
+

基本上现在遇到了这些坑,再有在总结吧。

+

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git "a/categories/\345\220\216\347\253\257/index.html" "b/categories/\345\220\216\347\253\257/index.html" new file mode 100644 index 0000000..63ad59d --- /dev/null +++ "b/categories/\345\220\216\347\253\257/index.html" @@ -0,0 +1,249 @@ + +分类: 后端 - 西门闲话

AKS中重写规则踩坑小记录

前言

最近在做标准产品在不同云平台中的部署验证,有幸体验了一下微软的Azure。负责采购的运维部门这次采用了Application Gateway来搭配AKS(Azure Kubernetes Service)对外暴露服务,正好借着这个机会来体验一下Application Gateway

+

应用场景

    +
  1. 域名api.demo.com指向Application Gateway的IP地址
  2. +
  3. AKS内部2个Service, gateway-servicebackend-service分别需要通过Application Gateway对外暴露。
  4. +
  5. /gateway/指向gateway-service, 然后/backend/指向backend-service。而且两个Service都没有context-path,所以需要做一个Rewrite重写URI到Service的根目录上。
  6. +
+

定义重写集

打开AKS对应的应用程序网关设置 > 重写。选择添加重写集。在1. 名称和关联这个Tab上只需要填写名称这项即可(名称后面在做ingress时需要使用), 关联的传递规则不需要选择。2. 重写规则配置里添加一个重写规则,然后填上重写规则的名称,并添加条件(默认新建重写规则时,只会生成操作,不会生成条件)

+

条件做如下设置

    +
  • 要检查的变量类型 : 服务器变量
  • +
  • 服务器变量: request_uri
  • +
  • 区分大小写:
  • +
  • 运算符: 等号(=)
  • +
  • 要匹配的模式: /(gateway|backend)/?(.*)
  • +
+

操作做如下设置

    +
  • 重写类型: URL
  • +
  • 操作类型: 设置
  • +
  • 组件: URL路径和URL查询字符串
  • +
  • URL路径值: /{var_request_uri_2}
  • +
  • 重新计算路径映射: 不选中
  • +
  • URL查询字符串值: 留空不设值
  • +
+

特殊说明

操作里的URL路径值不能使用正则表达式GROUP替换组,例如$1$2之类的。Azure自己定义了一套对应的替换组命名规则。具体可以参考这个网页使用应用程序网关重写 HTTP 标头和 URL

+

另外一个需要注意一点,如果在条件里选择了服务器变量request_uri的时候,注意这个request_uri是完整的原始请求URI(携带了查询参数)。例如: 在请求http://api.demo.com/gateway/search?foo=bar&hello=world中,request_uri的值将为/gateway/search?foo=bar&hello=world。由于request_uri里包含了查询参数,所以在操作组件中建议勾选URL路径和URL查询字符串。如果只选择URL路径的情况下可能出现无法预期的错误。以我们上述的配置来说明。

+

对象URL: http://api.demo.com/gateway/search?foo=bar&hello=world

+ + + + + + + + + + + + + +
组件URL路径和URL查询字符串URL路径
结果/search?foo=bar&hello=world/search?foo=bar&hello=world?foo=bar&hello=world
+

ACK的Ingress设置

当选择了Application Gateway作为对外暴露Service的方式时,Kubernetes集群里(kube-system命名空间里)多一个Application Gateway Ingress Controller(Azure工单时通常会简称为agic)的Deployment,所以对外暴露服务时可以像传统nginx ingress controller一样添加一个Ingress对象即可(甚至配置也和ngic大致相同,只是多了2个annotations)

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
# 这里指定重写规则集(不是重写规则的名字)
appgw.ingress.kubernetes.io/rewrite-rule-set: rule-backend
# 指定说明你这里ingress的类型是agic
kubernetes.io/ingress.class: azure/application-gateway
name: backend-ingress
namespace: default
spec:
rules:
- host: api.demo.com
http:
paths:
- backend:
service:
name: gateway-service
port:
number: 8080
path: /gateway/
pathType: Prefix
- backend:
service:
name: backend-service
port:
number: 8080
path: /backend/
pathType: Prefix
+ +

总结

由于微软云这块文档有部分缺失,导致在配置这块花了一点时间去排查,甚至开了工单。总结下来Ingress的配置主要是根据请求路径路由到对应的Service,重写规则集才是实际负责根据正则来进行匹配重写。

+

Spring Cloud Kubernetes环境下使用Jasypt

前言

最近半年着手开始做了基于微服务的中台项目,整个项目的技术栈采用的是Java + Spring Cloud + Kubernetes + Istio

+

业务开放上还是相当顺利的。但是在安全审核上,运维组提出了一个简易。现在项目一些敏感配置,例如MySQL用户的密码,Redis的密码等现在都是明文保存在Kubernetes的ConfigMap中的(是的,我们并没有Nacos作为微服务的配置中心)。这样可能存在安全隐患。

+

首次尝试

既然有问题,那就解决问题。要给配置文件中的属性项目加密很简单,稍微Google一下,就有现成的方案了。

+

现在比较常用的解决方案就是集成Jasypt,然后通过jasypt-spring-boot-starter来融合进Spring。

+

POM包加入jasypt-spring-boot-starter

1
2
3
4
5
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
+ +

Dockerfile中增加java参数

1
2
...
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar app.jar --jasypt.encryptor.password=helloworld $PARAMS"]
+ +

在ConfigMap中添加加密属性

1
2
3
4
5
6
7
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
data:
application.yaml: |-
test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
+ +

利用actuator接口测试

management.endpoints.web.exposure.include属性中增加env,这样我们就可以通过调用/actuator/env来查看一下env接口返回的整个Spring 容器中所有的PropertySource。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
...
"propertySources": [
{
"name": "bootstrapProperties-configmap.demo.default",
"properties": {
"test2": {
"value": "Hello,world"
}
}
}
...
]
}
+ +

OK, 这下配置项已经加密了。问题解决了。

+

但是…

+

新的问题

自从项目集成了Jayspt以后,出现了一个奇怪的问题。每次项目试图通过修改ConfigMap的配置文件,然后试图通过spring-cloud-starter-kubernetes-fabric8-config来做自动Reload,都失败了。然而查阅应用日志,并没有出现任何异常。无奈只能打开spring-cloudjasypt-spring-bootDEBUG日志。

+

进过几天对日志和两边源代码的分析。终于找到了原因

+

原因

在Spring Boot启动时jasypt-spring-boot会将下面6种配置(并不仅限与这6种配置文件)

+
    +
  • Classpath下的application.yaml
  • +
  • Classpath下的bootstrap.yaml
  • +
  • 集群里名称为${spring.cloud.kubernetes.config.name}的ConfigMap
  • +
  • 集群里名称为${spring.cloud.kubernetes.config.name}-kubernetes的ConfigMap
  • +
  • Java启动参数
  • +
  • 环境变量
  • +
+

转换成jasypt-spring-boot自己的PropertySource实现类EncryptableMapPropertySourceWrapper

+

但是如果使用Kubernetes的ConfigMap来作微服务配置中心的时候,Spring Cloud会在ConfigurationChangeDetector中查找配置类org.springframework.cloud.bootstrap.config.BootstrapPropertySource, 并依据BootstrapPropertySource的类型来判断容器内的配置与集群中ConfigMap里的配置是否有差异,来触发配置reload。

+

由于jasypt-spring-boot已经将所有的配置文件转型成了EncryptableMapPropertySourceWrapper, 所以ConfigurationChangeDetector无法找到BootstrapPropertySource所以会一直任务ConfigMap的里的配置没有变化,导致整个Reload失效(无论是使用polling还是event方式)

+

解决问题

为了保证ConfigMap变化后自动Reload的功能,所以jasypt-spring-boot不能把BootstrapPropertySource转换成EncryptableMapPropertySourceWrapper

+

所以我们需要设置jasypt.encryptor.skip-property-sources配置项, Classpath中的application.yaml需要增加配置

+
1
2
3
jasypt:
encryptor:
skip-property-sources: org.springframework.cloud.bootstrap.config.BootstrapPropertySource
+ +

skip-property-sources配置项配置后,加密项目就不能配置在ConfigMap里了,毕竟已经被我们忽略了。那么我们只能另外找一个PropertySource来存放加密项目了。

+

Classpath中的两个Yaml由于编译时会被Maven打包进Jar文件,会牵涉多个CI/CD多个流程显然不合适,启动参数配置项的也要影响到Docker镜像制作这个流程。所以判断下来最适合的PropertySource就是环境变量了。

+

环境变量增加加密项

在Kubernetes的部署Yaml中,添加加密数据项application.test.str

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: demo
name: demo
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- env:
- name: TZ
value: Asia/Shanghai
- name: application.test.str
value: >-
ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
....
+ +

如果需要更加严密的加密方针的话,我们可以把环境变量的内容放进Kubernetes的Secrets中。

+

在ConfigMap中引用application.test.str

1
2
3
4
5
6
7
8
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
data:
application.yaml: |-
test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
test3: ${application.test.str}
+ +

通过actuator接口来测试

通过actuator\env接口来测试一下

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
...
"propertySources": [
{
"name": "bootstrapProperties-configmap.demo.default",
"properties": {
"test2": {
"value": "ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)"
},
"test3": {
"value": "Hello,world"
}
}
}
...
]
}
+ +

这样ConfigMap中的配置项test3就可以通过环境变量引用并使用加密配置项了。同时修改ConfigMap依然可以触发auto reload了。这下终于算是解决了。

+

重学Java (一) 泛型

1. 前言

泛型编程自从 Java 5.0 中引入后已经超过15个年头了。对于现在的 Java 码农来说熟练使用泛型编程已经是家常便饭的事情了。所以本文就在不对泛型的基础使用在做说明了。 如果你还不会使用泛型的话,可以参考下面两个链接

+ +

这篇文章就简答聊一下,我实际在开发工作中很少用的到泛型方法这个知识点,以及在实际项目中有哪些东西会使用到泛型。

+

2. 泛型方法

在阅读代码的时候我们经常会看到下面这样的方法 (这段代码摘自 java.util.AbstractCollection)

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 public <T> T[] toArray(T[] a) {
// Estimate size of array; be prepared to see more or fewer elements
int size = size();
T[] r = a.length >= size ? a :
(T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
Iterator<E> it = iterator();

for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) { // fewer elements than expected
if (a == r) {
r[i] = null; // null-terminate
} else if (a.length < i) {
return Arrays.copyOf(r, i);
} else {
System.arraycopy(r, 0, a, 0, i);
if (a.length > i) {
a[i] = null;
}
}
return a;
}
r[i] = (T)it.next();
}
// more elements than expected
return it.hasNext() ? finishToArray(r, it) : r;
}
+ +

那么 pulic 关键字后面的那个 <T> 就是用来标记这个方法是一个泛型方法。 那什么是泛型方法呢。

+

官方的解释是这样的

+
1
Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.
+ +

通俗点来将就是将一个方法泛型化,让一个普通的类的某一个方法具有泛型功能。 如果在一个泛型类中增加一个泛型方法,那这个泛型方法就可以有一套独立于这个类的泛型类型。

+

通过一个简单的例子, 我们来看看

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* GenericClass 这个泛型类是一个简单的套皮的 HashMap
*/
public class GenericClass<K, V> {

private Map<K, V> map = new HashMap<>();

public V put(K key, V value) {
return map.put(key, value);
}

public V get(K key) {
return map.get(key);
}

// 泛型方法 genericMethod 可以接受一个全新的、作用域只限本函数的泛型类型T
public <T> T genericMethod(T t) {
return t;
}

}
+ +

实际使用起来

+
1
2
3
4
5
6
GenericClass<String, Integer> map = new GenericClass<>();
// put 和 get 方法的参数必须使用定义时指定的 String 和 Integer
System.out.println(map.put("One", 1));
System.out.println(map.get("One"));
// 泛型方法 genericMethod 就可以接受一个 String 和 Integer 以外的类型
System.out.println(map.genericMethod(new Double(1.0)).getClass());
+ +

我们再来看看 JDK 中使用到泛型方法的例子。我们最常使用的泛型容器 ArrayList 中有个 toArray 方法。JDK 在它的实现中就提供了两个版本,其中一个就是泛型方法的版本

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
// 这是一个普通版本,返回一个Object的数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}

// 这是一个泛型方法的版本,将容器里存储的元素输出到 T[] 数组中。 其中 T 必须是 E 的父类,否则 System.arraycopy 会抛出 ArrayStoreException 异常
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
}
+ +

泛型方法总体上来说就是可以给与现有的方法实现上,增加一个更加灵活的实现可能。

+

3. 实战应用

在实际的项目中,对于泛型的使用,除了像倾倒垃圾一样往泛型容易里塞各种 java bean 和其他泛型对象。还能怎么使用泛型呢?

+

我们在实际的一些项目中,会对数据库中的一些表(多数时候是全部)先实现 CRUD (Create, Read, Update, Delete)的操作,并从这些操作中延伸出一些简单的 REST 风格的 WebAPI 接口,然后才会根据实际业务需要实现一些更复杂的业务接口。

+

大体上会是下面这个样子。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

// 这是一个简单的 Entity 对象
// 通常现在的 Java 应用都会使用到 Lombok 和 Spring Boot
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Entity
@Table(name = "user")
public class User {
@Id
private Long id;
private String username;
private String password;
}

// 然后这个是 DAO 接口继承自 spring-data 的 JpaRepository
public interface UserDao extends JpaRepository<User, Long> {
}

// 在来是一个访问 User 资源的 Service 和他的实现
public interface UserService {
List<User> findAll();
Optional<User> findById(Long id);
User save (User user)
void deleteById(Long id);
}

@Service
public class UserSerivceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}

@Override
public List<User> findAll() {
return this.dao.findAll();
}

@Override
public Optional<User> findById(Long id) {
return this.dao.findById(id);
}

@Override
public User save(User user) {
return this.dao.save(user);
}

@Override
public void deleteById(Long id) {
this.dao.deleteById(id);
}
}

// 最后就是 WebAPI 的接口了
@RestController
@RequestMapping("/user/")
public class UserController{
private UserService userService;
public UserController(userService userService) {
this.userService = userService;
}

@GetMapping
@ResponseBody
public List<User> fetch() {
return this.userService.findAll();
}

@GetMapping("{id}")
@ResponseBody
public User get(@PathVariable("id") Long id) {
// 由于是示例这里就不考虑没有数据的情况了
return this.userService.findById(id).get();
}

@PostMapping
@ResponseBody
public User create(@RequestBody User user) {
return this.userService.save(user);
}

@PutMapping("{id}")
@ResponseBody
public User update(@RequestBody User user) {
return this.userService.save(user);
}

@DeleteMapping("{id}")
@ResponseBody
public User delete(@PathVariable("id") Long id) {
User user = this.userService.findById(id);
this.userService.deleteById(id);
return user;
}
}
+ +

大致一个表的一套相关接口就是这个样子的。如果你的数据库中有大量表的话,而且每个表都需要提供 REST 风格的 WebAPI 接口的话,那么这将是一个相当枯燥的而又及其容易出错的工作。

+

为了不让这项枯燥而又容易犯错的工作占去我们宝贵的私人时间,我们可以通过泛型和继承的技巧来重构从 Service 层到 Controller 的这段代码(感谢 spring-data 提供了 JpaRepository, 让我们不至于从 DAO 层重构)

+

3.1 Service 层的重构

首先是 Service 接口的重构,我们 Service 层接口就是定义了一组 CRUD 的操作,我们可以将这组 CRUD 操作抽象到一个父接口,然后所有 Service 层的接口都将继承自这个父接口。而接口中出现的 Entity 和主键的类型(上例中 User 的主键 id 的类型是 Long)就可以用泛型来展现。

+
1
2
3
4
5
6
7
8
9
10
11
// 这里泛型表示 E 来指代 Entity, ID 用来指代 Entity 主键的类型
public interface ICrudService<E, ID> {
List<E> findAll();
Optional<E> findById(ID id);
E save(E e);
void deleteById(ID id);
}

// 然后 Service 层的接口,就可以简化成这样
public interface UserService extends ICrudService<User, Long> {
}
+ +

同样 Service 层的实现也可以使用相似的方法具体实现可以抽象到一个基类中。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 相比 ICrudService 这里有多了一个泛型 T 来代表 Entity 对应的 DAO, 我们的每一个 DAO 都继承自
// spring-data 的 JpaRepository 所以,这里可以使用到泛型的边界
public abstract class AbstractCrudService<T extends JpaRepository<E, ID>, E, ID> {
private T dao;
public AbstractCrudService(T dao) {
this.dao = dao;
}

public List<E> findAll() {
return this.dao.findAll();
}

public Optional<E> findById(ID id) {
return this.dao.findById(id);
}

public E save(E e) {
return this.dao.save(e);
}

public void deleteById(ID id) {
this.dao.deleteById(id);
}
}

// 那 Service 的实现类可以简化成这样
@Service
public class UserServiceImpl extends AbstractCrudService<UserDao, User, Long> implements UserService {
public UserServiceImpl(UserDao dao) {
supper(dao);
}
}
+ +

同样我们可以通过相同的方法来对 Controller 层进行重构

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Controller 层的基类
public abstract class AbstractCrudController<T extends ICrudService<E, ID>, E, ID> {
private T service;
public AbstractCrudController(T service) {
this.service = service;
}

@GetMapping
@ResponseBody
public List<E> fetch() {
return this.service.findAll();
}

@GetMapping("{id}")
@ResponseBody
public E get(@PathVariable("id") ID id) {
// 由于是示例这里就不考虑没有数据的情况了
return this.service.findById(id).get();
}

@PostMapping
@ResponseBody
public E create(@RequestBody E e) {
return this.service.save(e);
}

@PutMapping("{id}")
@ResponseBody
public E update(@RequestBody E e) {
return this.service.save(e);
}

@DeleteMapping("{id}")
@ResponseBody
public E delete(@PathVariable("id") ID id) {
E e = this.service.findById(id).get();
this.service.deleteById(id);
return e;
}
}

// 具体的 WebAPI
@RestController
@RequestMapping("/user/")
public class UserController extends AbstractCrudController<UserService, User, Long> {
public UserController(UserService service) {
super(service);
}
}
+ +

经过重构可以消减掉 Servcie 和 Controller 中的大量重复代码,使代码更容易维护了。

+

4. 结尾

关于泛型就简单的说这些了,泛型作为 Java 日常开发中一个常用的知识点,其实还有很多知识点可以供我们挖掘,奈何本人才疏学浅,这么多年工作下来,只积累出来这么点内容。

+

文末放上示例代码的代码库:

+ +

阿里云Kubernetes上线踩坑记

1
2
Update:
2020-04-08 增加istio-ingressgateway高可用的设置
+ +

最近公司因为项目需要,在阿里云上部署了一个Kubernetes集群。虽然阿里云的文档说的还算细致,但是还是有些没有明确说明的细节。

+

1. 购买篇

申请项目预算的时候,只考虑到Worker节点,1个SLB节点以及域名和证书的预算。但是实际购买的时候发现还有许多额外的开销。

+

1.1 SNAT

这个和EIP一并购买,可以方便通过公网使用kubectl访问集群。关于SNAT网关至今不是很明白需要购买这个服务的意义何在,只是为了一个EIP来访问集群吗?

+

1.2 Ingress

这个选上了后,阿里云会给你买个SLB而且还是带公网访问的,如果你后期考虑使用Istio的话,建议你集群创建后,直接停止这个SLB,以免产生额外的费用。

+

1.3 日志服务

通过阿里云的日志服务来收集应用的的日志,挺好用的。但是另外收费,如果有能力的自建日志服务的可不购买。

+

2. Istio

阿里云的Kubernetes集群完美集成了Istio,根据向导就能很简单的部署成功。

+

2.1 额外的SLB

Istio的Gateway 需要绑定一个新的SLB,和Ingress的SLB不能是同一个,又是一笔额外的开销

+

2.2 集群外访问

这个在阿里云的Istio FAQ中有提到,按照指导很容易解决

+

2.2 SLB的443监听

为了方便443端口的证书绑定,我们直接删除了SLB上原有的443监听(TCP协议), 重新建了一个443监听(HTTPS协议),指向和80端口同样的虚拟服务器组。但是设置健康检查时一直出错,经过排查发现SLB健康检查发送的请求协议是HTTP 1.0的,Istio的envoy直接反悔了426(Upgrade Required)这个状态码,所以我们无奈只能把健康检查的检查返回状态改为http_4xx,这样就能通过SLB的健康检查了。

+

2.3 istio-ingressgateway的高可用

istio-ingressgateway要达成高可用,只需要增加通过伸缩POD就可以实现,于istio-ingressgateway对应的SLB中的虚拟服务器组也会自动增加,完全不需要进行额外的手动设定。

+

由于istio-ingressgateway中挂载了HPAHorizontalPodAutoscaler(简称HPA),通常三节点的集群中最小POD数只有1台,在3节点的集群中,要实现高可用,需要手动修改HPA,增加最小POD数。

+
+

基本上现在遇到了这些坑,再有在总结吧。

+

在ubuntu上从零搭建node.js + nginx + mongodb环境

说到后端开发环境,最有名的莫过于LAMP和LNMP,最近由于node.js的强势崛起,越来越多的后端开发也开始试水node.js了。我最近也因为各种原因,前前后后总够构建了好几台node.js + nginx + mongodb的Linux服务器。

+

首先关于Linux服务器,比起CentOS来说,我更加喜欢ubuntu一点。所以无论是阿里云还是一些海外的vps服务器上,我也倾向选用ubuntu服务器,本贴也是基于ubuntu服务器里说明的。

+

1.开始前的一些准备

首先还是需要刷新一下ubuntu的包索引并安装build-essential和libssl-dev这2个包以及curl这个工具。

+
1
2
3
sudo apt-get update
sudo apt-get install build-essential libssl-dev
sudo apt-get isntall curl
+ +

2.安装node.js

关于安装node.js这一点,我不是很推荐使用apt-get 来安装node.js的环境。主要是因为node.js和io.js合并以后,版本迭代速度相当频繁(主要还是因为更多ES6的特性得到了支持)。今后很有可能会有在一台服务器上使用不同版本的node.js的需求。

+

这里推荐一个管理不同版本node.js的工具:nvm,官网: https://github.com/creationix/nvm  。安装nvm,如果前面你安装了curl的话可以

+
1
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
+

如果没有按照curl的话,也可以使用wget来进行安装

+
1
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
+ +

然后nvm就会自动安装到home目录下面的.nvm目录里,并会在.bashrc里自动添加nvm的环境变量。为了让环境变量生效,最简单的方法就是通过ssh或是telnet重新连接你的服务器。

+

安装完nvm后,就可以通过nvm来安装指定版本的node.js了。

+
1
2
3
4
5
# 列出可以安装的node版本号
nvm ls-remote

# 安装指定版本的node (当前最新版本为v5.7.1, LTS版是v4.3.2)
nvm install v4.3.2
+ +

3.安装nginx

由于ubuntu源(尤其是阿里云的源)上的nginx经常不是最新的,如果需要安装最新版本nginx的时候需要手动添加nginx的源。

+
1
2
3
4
5
6
7
8
9
10
11
# 添加nginx的mainline仓库
cd /tmp/ && wget http://nginx.org/keys/nginx_signing.key
sudo apt-key add nginx_signing.key

# 编辑/etc/apt/sources.list.d/nginx.list 添加下面2行内容,井号不需要
# deb http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
# deb-src http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
sudo vi /etc/apt/sources.list.d/nginx.list

# 更新源,并安装nginx
sudo apt-get update && sudo apt-get install nginx
+ +

在编辑/etc/apt/sources.list.d/nginx.list的时候需要注意,“ubuntu代号”需要根据ubuntu服务器的版本不同手动调整的,比如14.04是trusty。通过下面的命令可以获取ubuntu的代号。

+
1
lsb_release -cs
+ +

4.安装mongodb

同样和nginx有同样的问题,要安装最新3.2版本的mongodb也需要手动添加ubuntu的源。

+
1
2
3
4
5
6
7
8
9
10
11
# 导入mongodb的public key
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927

# 生成mongodb的源list
echo "deb http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list

# 更新源
sudo apt-get update

# 安装最新版本的mongodb
sudo apt-get install -y mongodb-org
+ +

以上一台node.js + nginx + mongodb的ubuntu服务器就完成了。

+

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git "a/categories/\346\235\202\350\256\260/index.html" "b/categories/\346\235\202\350\256\260/index.html" new file mode 100644 index 0000000..9af6638 --- /dev/null +++ "b/categories/\346\235\202\350\256\260/index.html" @@ -0,0 +1,111 @@ + +分类: 杂记 - 西门闲话

2020年阅读总结

多灾多难的2020年已经没有几天了,整个2020年要说还真是像一记刹车,去年很多想法,计划都有因为疫情原因搁置了。平时的生活更是从宅家变成了重度宅家的状态,自然就多出了许多大段大段的连续时间,可以用来好好玩玩大作,看看小说。仔细盘点一下今年总共看了10本小说外加2本科普书籍( 2020年阅读书单
),算是自2018年以来读书最多的一个年份了。其中不乏拖了近3年的那本《西方文化中的数学》。接着就来絮叨絮叨今年看的这12本书吧。

+
+
+

《莱博维茨的赞歌》

+
+

第一次被安利《莱博维茨的赞歌》的机核的电台节目辐射-视觉、音乐以及文学, 当时听完节目就相当好奇,可以被一部载入史册的CRPG游戏誉为精神文本的小说到底是个什么样子的。但是当时新星版的译本几乎就是绝本了,某鱼的二手价也要已经飙到了3位数。直到今年年初在核市节目上听到了中信版译本的消息,才有机会买来拜读。

+

全书通过3个故事讲述了核战灭世后莱博维茨修道院从保卫文明,到重建,然后看着文明再度毁灭的故事。各种无不透露着辐射那股 War, War never changed的味道。

+
+

《占星术杀人事件》《屋顶上的小丑》《亿男》

+
+

这3本书都是2019年上海书展上买的。前两本都是岛田庄司的推理名篇,其中《占星术杀人事件》在本格派推理史上有着非凡的地位,各种被抄袭从未被超越过。《屋顶上的小丑》要是最吸引人的话就是多线叙事,每条线都掐在恰如其分的地方。《亿男》则是挂着悬疑羊头卖着人生哲理。

+
+

《神经漫游者》

+
+

威廉吉布森的蔓生三部曲之一,开创了赛博朋克的科幻流派,说实话这本书我看的很痛苦,很多地方实在读不懂,到现在我还是不明白那些无法读懂的地方到底是原著如此,还是翻译的不行,亦或者是Kindle版排版太烂了。

+
+

《神们自己》,《神的九十亿个名字》

+
+

阿瑟克拉克的中篇小说和短篇小说集,这两本书阅读体验奇佳,尤其是在读完《神经漫游者》后,虽然三本书被Amazon打在同一个包里来卖的,但是光从排版上就比《神经漫游者》高出不少,所以才会有我对后者排版问题的质疑。

+

《神们自己》三线叙事,讲述了2个平行宇宙中的2个物种在能源,种族存续,家庭伦理上的各种讨论和故事。 《神的九十亿个名字》作为短篇小说集收录了好几篇风格题材迥异的小说,花上十几分钟读上一个故事是相当惬意的。

+
+

《佐伊的战争》 《人类决裂》 《万物的终结》

+
+

约翰斯卡尔齐的《老人的战争》系列的后3部,前三部大概是3,4年前读的吧,在这个宇宙观里最吸引人的设定就绿皮的人类防卫军战士,以及跨种族间的政治斗争。系列的六本书相当于在这个大设定下讲的六个故事。每个故事虽说都是独立的故事,但是前后皆有关联,而且最重要的就是每本的长度都算不上很长,相当适合于一口气读完。

+
+

《西方文化中的数学》

+
+

这本书大概是从2017年开始读的,当时意气奋发觉得对于理科生出身的我,书中内容应该很好理解,没想到最后不光是文化还是数学读的都是一知半解。只能说自大了。

+
+

《一想到还有95%的问题留给人类,我就放心了》

+
+

大概是18年还是19年前后对量子物理和宇宙物理开始有了兴趣,虽然各中理论并不是很了解,但是仍然很好奇的买了这本书,全书插画相当有趣,同时使用浅显易懂的语言来解释各种复杂的物理现象,以及当今人类物理知识的边界。强烈推荐给想假装成学霸的朋友。

+
+
+

2021年读什么

+
+

具体读啥还没想好基本上还是以科幻和悬疑为主,但是肯定会读的应该有海明威的《丧钟为谁而鸣》和小林泰三的《醉步男》吧。

+

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git "a/categories/\350\256\276\350\256\241/index.html" "b/categories/\350\256\276\350\256\241/index.html" new file mode 100644 index 0000000..2a1100c --- /dev/null +++ "b/categories/\350\256\276\350\256\241/index.html" @@ -0,0 +1,193 @@ + +分类: 设计 - 西门闲话

H项目心路历程记(三)

现状

对于一个基于微服务的中台系统来说,需要以什么样的颗粒度来拆分系统。这一直是基于微服务架构的系统中绕不过去的问题。

+

已H项目来说,当初考量是这么划分微服务的,其中

+
    +
  • 基础服务: 作用主要是负责用户,角色权限,登陆鉴权,以及一些基础主档数据的日常维护功能
  • +
  • 商品服务: 物料,销售商品,款式,平台商品等和实际商品相关的主档数据
  • +
  • 库存服务:负责管理各个库存层级库存(总库存,冻结库存,在途库存等),以及展示各种出入库单据,并向其他微服务提供出入库服务接口。
  • +
  • 订单服务: 负责接收各个公域平台的ToC订单,售后单(含发货前退款,退货,换货单等),并根据配置策略进行单据路由(分仓,分物流,Hold单,加送赠品,通知仓库发货,通知平台发货等)
  • +
  • 接口服务: 主要承揽中台服务和第三方平台间的交互处理(主要是第三方平台发起的主调通知以及回调)
  • +
+

上述五个服务为核心服务,是保证整个中台系统基础业务功能运行的重要组成部分。除此之外还有,

+
    +
  • 采购服务:负责ToB业务线的处理
  • +
  • 财务服务:满足一些企业的财务对账以及开具电子发票的功能
  • +
  • 报表服务: 每天,每周定期从其他服务(主要是订单)
  • +
  • 会员服务:管理各类会员
  • +
+

等7,8个非核心服务,组成非核心业务。来构成一个完整的中台系统。

+

拆分策略以及缺点

当时主要考虑到整个中台系统是可以拆分出售,客户可以只选择购买自身需要的服务,而不需要的服务则无需购买服务,所以当初在拆分服务上在业务领域层面上需要更加低颗粒度;同时考虑到再业务高峰期(下午到晚上,就是各位疯狂刷淘宝,京东的时候)各个服务的负载是各不相同的,如果在高峰期对于特定服务需要进行精准扩容的话,拆分的颗粒度也需要更加细致,所以结果上导致整个系统满血运行起来要至少要十几个服务,外加一些没有云平台的中间件。整个Kubernetes环境中,至少要有十五六个Deployment,考虑到系统稳定性,一个Deployment也至少要有2到3个Pod,那整个系统至少就是50个Pod。

+

对于一个中大型的电商企业再服务器成本上可以负担这样一个能支持50个Pod的Kubernentes集群(考虑到需要一些中间件的云产品,当时估算最小的服务器成本都要到70K/年,一个中大型企业能面对双十一流量的服务器成本都要到300K/年以上)。所以整个系统对于中小型企业来说前期的投入负担相当大(尤其是在后疫情时代的2022年,2023年),以至于早起的好几个客户都是需要我们自己贴补服务器费用的。

+

复盘

现在重新复盘当初服务的拆分策略,个人觉得对于核心业务来说,相似领域的一些服务应该可以适当的聚合。比如上述的五大核心服务上中的商品服务,订单服务,库存服务三个核心服务应该可以合并在一起。首先这3个核心服务是H项目的最重要的竞争力,几乎不可能出现把这3个服务拆分出售的可能性。

+

而且在业务高峰期有订单服务有新订单处理时,势必会产生库存的变化,因此会调用到库存服务。三个服务之间的调用是相当频繁的,尤其是负责处理商品主档数据的商品服务,无论是通过HTTP或是其他方式的远程调用的话,都会有许多浪费的带宽。如果把三个服务整合,那么之间的调用基本上就是线程间的交互,无论是效率和可靠性上都会有更加显著的提升。

+

结尾

怎么拆分服务一直是个难题。虽然事后复盘的时候我的很多想法也不一定是正确,但是什么样的规模的客户需要使用什么样的拆分颗粒度,都是需要仔细考虑的。绝对不是一尘不变的。

+

H项目心路历程记 (二)

(书接上文)

+

前文已经吐槽了产品设计以及中间件上的相关问题。接下来讨论一下技术上的一些问题。

+

关于配置

对于一个Spring的应用来说,应用配置怎么存取其实并不能说是一个技术问题。更应该算是设计或是架构问题。哪些配置应该进入配置文件,通过微服务配置中心(例如Nacos, Apollo,Kubernetes ConfigMap等)来进行在线配置,哪些配置应该通过其他方式来配置,亦或者哪些配置项目看似是配置项其实本质上就是常量。实际上截止现在H项目的确在有些微服务上是完全没有厘清三者之间的区别,以及界限。以至于现在堆积在配置文件中的内容实在太多。多个运行环境需要统一维护的难度实在太大。

+

对于配置来说,理想的状态应该是下面这样的。

+
    +
  1. 通过微服务配置中心来影响Spring的配置文件来实现动态配置的方案,通常会伴随Spring容器的重启(有些情况下甚至需要整个Docker容器),所以存在这里的配置项应该是随着运行环境改变才会需要改动的配置。例如:

    +
      +
    • 外部系统的链接信息(比如MySQL, Redis的连接信息)
    • +
    • 外部系统的认证凭证(数据库用户密码,第三方系统的密钥等)
    • +
    • 因为运行环境不同导致的不同变量 (例如消息队列的Topic,Group需要根据运行环境使用不同的名称)
    • +
    +
  2. +
  3. 通过数据库或是Redis存储的配置信息,这类配置主要是应该以业务级别的配置,可以通过页面进行修改,瞬间或是几分钟内生效,且不会造成微服务重启(无论是Spring或是Docker容器的重启)。对于这类配置有几个问题还是需要注意的。

    +
      +
    • 配置是否需要集中管理,每个微服务,都有自己独特的业务配置,这些业务配置是否需要集中到一个微服务统一管理,还是分散到各自的微服务进行单独管理。
    • +
    • 全局性统一管理的业务配置对外保留接口应该利用缓存有效的减少数据库访问的频次。甚至可以只使用内存而不是Redis来缓存这部分内容。如果使用内存的话则需要考虑失效时效和重新装载的机制。
    • +
    +
  4. +
  5. 无效配置,实际上很多配置说是可以修改,其实自从开始使用的那天开始就没有修改过。比如用来做分布式锁的键值。这部分需要梳理出来直接以常量的形式做在代码里。

    +
  6. +
+

数据清理以及归档

对于电商行业的零售业务来说每天产生的单据比如订单少做200,300条,多的是上千条。加上明细数据。几个月以后这些数据就会给MySQL的库表增加检索压力。整个H项目在这块是相当薄弱的。只依靠云平台的能力,对一些不需要留档的数据进行清理。远远没有能定期归档。

+

对于数据清理这块大致可以分为这几类

+
    +
  • 定期可以无脑物理删除的数据,比如一些日志类数据。
  • +
  • 定期直接归档的数据,归档后即可清理的数据。
  • +
  • 业务必须处理完成后才能归档并清理的数据。例如订单数据等。
  • +
+

对于数据归档的时候,还需要考虑一些相关联单据数据一并归档。比如订单归档时,同时需要归档相关联的退换货单,发货单,以及操作日志等等。 归档格式基本上就是考虑单条数据以JSON格式存储,然后一个时间段的数据做成压缩包存储在OSS上,然后提供给客户下载。

+

而对于归档数据的检索功能,客户选择需要归档的数据,我们可以将归档的数据(每个压缩包内的所有数据)导入到一个主数据库以外的,临时文档数据库(例如MongoDB),然后针对这个临时的文档数据库进行检索。当然提供的检索项目也会远少于热数据的检索项目。这个临时文档数据库基本上保持一两天数据,定期就全部回收。

+

未完待续

+

H项目心路历程记 (一)

前言

2021年年中从前一家公司离职,经过朋友介绍到了现在公司参与了Halfling项目(以下简称H项目),并从零开始的设计开发到第一个客户交付的全部流程。

+

H项目是一个面向零售行业在电商时代的智能一体化中台产品。项目最早是从2021年中开始立项投入,期间经过数个版本迭代,以及2022年初的魔都封城等诸多困难。终于在2023年Q2季度完成了针对首个客户的上线。

+

作为从头开始全程参与项目的技术人员,经过2年多的产品研发期间有不少想吐槽和想说的。

+

业务设计

最初H项目是以打造一个面向零售行业业务中台为目的进行项目立项的,其中一个很重要的点就是希望除核心组件以外都可以独立售卖。所以在架构设计的使用当下最流行的微服务架构(基于Spring Boot + Kubernetes集群的解决方案), 再做业务拆分到服务的时候,进行了更细颗粒度的拆分。所以前前后后一共拆分了近20个微服务。

+

实际上到了2022年Q2前后,两个核心服务订单管理和库存服务实现以后发现,各个业务服务中间的耦合程度比想象中的还要精密,单个服务拆开单独售卖几乎不可能(应该说是完全不可能)。 然而由于服务拆分的过细,导致一个服务需要频繁访问其他服务获取大量数据。尤其类似订单关联数据比如商品的主档数据,会穿越好几个服务。而每个服务都会重复去获取主档数据,造成了大量的额外请求。 虽然最后在所有服务底部兜底使用同一个Redis作为缓存来减少额外的请求调用。

+

随着时间来到2023年产品组负责人离职后,整个H项目的方向有从行业的业务中台降级成中小企业的订单管理系统(Order Management System, OMS)。前期做的很多设计比如用户权限体系(当初需求是要精确到每个页面的每个按钮,如果下拉列表框有外调请求也需要做权限控制)等就显的很重,导致用户在使用以及配置都很不方便。而有些设计就显得稍显欠缺。 所以整个2022年Q4到2023年都是在对业务层上反复横跳,比如库存模型和商品模型的调整。这点是相当影响团队士气,同时也加剧了研发组和产品组之间的矛盾。

+

然后还有一个很重要的问题是多租户的态度上。早起项目是以服务中大体量客户为目的,所以设计之初并没有考虑多租户的问题。但实际项的进展过程中发现部分中大体量的客户也需要类似多租户的功能。这类客户需求与其说是需求多租户,不如说是希望有一个多租户级别的数据隔离,每个业务部门只能操作自己的业务部门的数据(数据包含商品,品牌,属性,平台店铺,订单,售后单据等),而总部依据可以查看一些汇总信息(例如销售对账单据等)。而随着2022年疫情状况严峻,很多中大客户对于内部平台升级和替换都变的更加谨慎和保守。所以当项目方向变成中小企业OMS时,多租户SaaS化的需求就更加迫切了。所以后期如何在原有系统上在扩展一套多租户SaaS化功能也是个课题。

+

中间件

H项目使用的中间件中比较特别的有分布式任务管理系统xxl-job,消息队列RocketMQ,这两个中间件都是第一次使用。关于这两个中间件也有不是可以谈谈的。

+

分布式任务管理系统 XXL-JOB

作为一个开源的分布式任务管理系统,xxl-job足够简单且相当容易上手使用,无论是基于Spring,还是原生Java做开发都是相当简单的。

+

而不足之处也很明显,最近1年版本迭代速度明显变慢,有些功能和BUG都有待处理的。比如想实动态创建定时任务这类功能就必须自己动手去对xxl-job-admin(调度中心)进行修改。

+

对于任务执行日志这块,现在xxl-job也是文件格式在执行器所在服务器上进行输出的,当执行器容器化或是运行在Kubernetes集群中的时候,当容器或是Pod重启后,就会丢失日志。这点在后期运维以及调试上还是有点麻烦的。

+

消息队列 RocketMQ

阿里出品的消息队列如果基于云产品来使用的话问题不大(阿里云卖的价钱也是有点黑的),但是在运维上还是有点问题的比如Topic,Group这些东西都只能人工去添加,阿里云并没有提供导入服务。全系统如果使用消息队列的场景多的话,维护这些Topic和Group还是很酸爽的。还有一点RocketMQ重复消息这一点是无法保证的。用阿里云的原话

+
+

绝大多数情况下,消息是不重复的。作为一款分布式消息中间件,在网络抖动、应用处理超时等异常情况下,无法保证消息不重复,但是能保证消息不丢失。

+
+

实际运用上,在大数据量并发的情况下,我们实际上是遇到了消息重复消费的问题。这点还是需要通过代码干预(例如增加一个唯一MsgId,对MsgId做分布式锁来解决)。

+

还有一点就是在旧版本SDK的默认配置下消费者的负载是有点小问题,总是有一个节点会比其他节点更容易消费到大量消息。据阿里云自己说明在新版本SDK的默认负载配置也已经修复这个问题了。

+

(未完待续)

+

Largrange项目架构与设计回顾 (二)

Largrange项目架构与设计回顾 (一) 里面我讲了一下项目开始架构和技术选型的一些内容。这一章来聊聊业务设计上的这点事。

+

总体来说项目设计的时候,我们对业务模块的划分和拆解总体上来说都是遵循着高内聚,低耦合的原则来进行划分的。大部分和业务相关的服务,都还是能很好的进行功能划分的。但是有一些和具体业务关联性并不是很高的服务在界定与实现时出现了一些问题。

+

任务调度服务

由于平台方面会有一些控制命令下发给Android设备,而且下发的命令并不一定都是实时的,大部分都是指定一个时间来下发,所以在设计的时候就考虑到需要一个任务调度服务(以下略称cron),来处理这些下发指令以及未来可能会有的定时批处理任务的业务需求。技术选型的时候考虑到整个服务是构建在Kubernetes上的一个分布式的微服务架构,所以就没有选择Spring Scheduler,而是使用了Quarter来支撑整个cron

+

从技术选型上来说cron没有什么问题,但是在设计如何使用cron上还是有点问题的, 我们先来看看已推送服务为例,整个平台中cron的处理流程是怎样的。

+
    +
  1. Platform 调用 cron的创建定时任务Job的接口(接口的参数为推送时使用的相关参数)
  2. +
  3. cron 创建Quartz的Job和Trigger,并将Job和Trigger的Name(Quartz中Job和Trigger的唯一标识符)返回个Platform
  4. +
  5. cron 在指定时间触发推送的Job,即调用Push服务的推送接口
  6. +
  7. Push的推送接口调用第三方服务商的推送服务,并将第三方推送服务的调用结果返回给cron
  8. +
  9. cron通过调用Platform预留的推送服务回调地址将第三方推送服务调用接口返还给Platform
  10. +
  11. Platform 将第三方推送服务的调用结果留档保存,并继续业务处理
  12. +
+

设计之初考虑到不想在cron中牵扯到具体的业务, 所以设计了一个Platform的回调接口来处理推送后的具体业务处理。虽然保证了cron尽量减少了和业务逻辑接触与数据库的访问。但是前前后后要访问集群内部的其他微服务2次,额外增加了网络开销,以及因为网络通信造成额外的通信失败风险。而且针对每种不同的定时任务,都需要额外开发一个QuartzJobBean,很难形成统一的QuartzJobBean来进行处理。今后如果还有相似项目还是需要重新考量一下如何设计一个更加完善的定时任务。

+

总结

暂时就想到了这些东西,今后想到啥还会继续在这里补存。

+

Largrange项目架构与设计回顾 (一)

+

项目背景

+
+

从去年年底开始一个老客户希望在他们的一个传统的机械设备(后面略称 E机关 )上外装一个Android设备。 Android设备和 E机关 之间通过串口或是RJ45接口进行数据交互,主要是Android设备获取 E机关 内部的数据,并不会通过接口来控制 E机关 。 Android设备则通过4G 来和平台交互,上报Android设备的状态数据,同时接受平台的控制。以此来实现让传统机械设备也能拥抱物联网的概念。

+

最后围绕着客户的需求分成了3个项目来并行推进

+
    +
  1. Android设备的硬件设备的设计、选材、样机制作与量产规划
  2. +
  3. 在Android设备上,进行与E机关以及平台进行交互的APP开发
  4. +
  5. 用于Android设备交互的平台的架构、设计与开发
  6. +
+

我们平台Team就负责 3.用于Android设备交互的平台 并命名Lagrange (拉格朗日) 。

+

设计&架构

整个平台这块不仅需要向Android设备的APP提供数据交互接口,还需要有一个供相关运营人员使用的前端Web应用,以及与云平台(主要是Aliyun) 交互的功能。所以考虑到多方面使用微服务的架构来实现整个平台端。
由于E机关的工作工况不能保证长期较的稳定的连接到4G网络,所以我们并不考虑使用Socket长连接的方式来做APP和平台之间的数据交互,要实现平台对Android设备进行反控的话,只能实现推送方式来把控制命令下发给Android设备,所以还需要有一个第三方推送服务商交互的服务。同时控制推送也有实时和非实时以及定期推送的需求,所以可能还需要一个任务调度的服务。

+

根据对上述业务进行梳理,我们将项目分成几个服务

+
    +
  • 提供Android设备的交互接口的 App Service
  • +
  • 提供运营人员使用前端Web应用 Platform Web Service
  • +
  • 前端Web应用使用到的一些接口 Platform Service
  • +
  • 负责第三方推送服务商交互的 Push Service
  • +
  • 提供云平台鉴权用的 Auth Service
  • +
  • 用来管理任务调度的 Cron Service
  • +
+

由于前一个项目实施的时候没有使用容器部署,每当访问量峰值的时候,我们这些码农兼运维就各种加班,所以这次项目决定直接将服务容器化,同时选择了Kubernetes来管理容器。由于客观原因线上的Kubernetes直接购买了Aliyun的托管版Kubernetes服务。 内部的开发测试环境则使用了 Rancher 2.0 来构建Kubernetes集群。

+

技术选型

a. 后端服务选型

由于不考虑长连接的原因,所以在后端服务在技术选型上基本就不考虑Netty了,直接上Spring大礼包。

+
    +
  • Spring Boot 开发接口
  • +
  • Spring Data 配合 JPA 来进行数据的持久化
  • +
  • Spring Cloud Kubenetes 来做数据的Config的autoreload
  • +
  • Quartz 负责处理任务调度
  • +
+

服务之间调用都使用HTTP服务, 服务发现也有Kubernetes的DNS机制支持。服务网格则选择了比较成熟的Istio,主要还是Aliyun的Kubernetes可以集成Istio,部署和使用都相当方便。

+

b. 前端Web应用选型

因为前端Web应用主要是给运营人员使用,所以我们考虑使用Single Page Application来做个前后端分离的Web应用。框架这块由于团队成员基本上没有什么前端开发经验,基本都是后台写Java的码农,所以框架选择有点随性,直接就点名了vue.js。前端控件库则用的是阿里系的Antd。

+

c. 数据持久层

主数据库选择了mysql,缓存用的redis。这些都是团队比较熟悉的。由于一些特殊的业务需求和使用场景,我们还加了一个mongodb来做为一个副数据库,主要存放一些特殊业务使用的数据。

+

d. DevOps

用了相当传统的GitLab CE 加上Jenkins的组合,实现前后端代码的自动编译,推送到私有的镜像仓库(使用Aliyun的镜像仓库服务)

+

最后

以上基本是项目最早做设计时候的各种考量。暂时先写这么多,过两天再回顾一下当初设计上有哪些觉得不足的地方。

+

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/content.json b/content.json new file mode 100644 index 0000000..9cba1c2 --- /dev/null +++ b/content.json @@ -0,0 +1 @@ +{"pages":[],"posts":[{"title":"2020年阅读总结","text":"多灾多难的2020年已经没有几天了,整个2020年要说还真是像一记刹车,去年很多想法,计划都有因为疫情原因搁置了。平时的生活更是从宅家变成了重度宅家的状态,自然就多出了许多大段大段的连续时间,可以用来好好玩玩大作,看看小说。仔细盘点一下今年总共看了10本小说外加2本科普书籍( 2020年阅读书单),算是自2018年以来读书最多的一个年份了。其中不乏拖了近3年的那本《西方文化中的数学》。接着就来絮叨絮叨今年看的这12本书吧。 《莱博维茨的赞歌》 第一次被安利《莱博维茨的赞歌》的机核的电台节目辐射-视觉、音乐以及文学, 当时听完节目就相当好奇,可以被一部载入史册的CRPG游戏誉为精神文本的小说到底是个什么样子的。但是当时新星版的译本几乎就是绝本了,某鱼的二手价也要已经飙到了3位数。直到今年年初在核市节目上听到了中信版译本的消息,才有机会买来拜读。 全书通过3个故事讲述了核战灭世后莱博维茨修道院从保卫文明,到重建,然后看着文明再度毁灭的故事。各种无不透露着辐射那股 War, War never changed的味道。 《占星术杀人事件》《屋顶上的小丑》《亿男》 这3本书都是2019年上海书展上买的。前两本都是岛田庄司的推理名篇,其中《占星术杀人事件》在本格派推理史上有着非凡的地位,各种被抄袭从未被超越过。《屋顶上的小丑》要是最吸引人的话就是多线叙事,每条线都掐在恰如其分的地方。《亿男》则是挂着悬疑羊头卖着人生哲理。 《神经漫游者》 威廉吉布森的蔓生三部曲之一,开创了赛博朋克的科幻流派,说实话这本书我看的很痛苦,很多地方实在读不懂,到现在我还是不明白那些无法读懂的地方到底是原著如此,还是翻译的不行,亦或者是Kindle版排版太烂了。 《神们自己》,《神的九十亿个名字》 阿瑟克拉克的中篇小说和短篇小说集,这两本书阅读体验奇佳,尤其是在读完《神经漫游者》后,虽然三本书被Amazon打在同一个包里来卖的,但是光从排版上就比《神经漫游者》高出不少,所以才会有我对后者排版问题的质疑。 《神们自己》三线叙事,讲述了2个平行宇宙中的2个物种在能源,种族存续,家庭伦理上的各种讨论和故事。 《神的九十亿个名字》作为短篇小说集收录了好几篇风格题材迥异的小说,花上十几分钟读上一个故事是相当惬意的。 《佐伊的战争》 《人类决裂》 《万物的终结》 约翰斯卡尔齐的《老人的战争》系列的后3部,前三部大概是3,4年前读的吧,在这个宇宙观里最吸引人的设定就绿皮的人类防卫军战士,以及跨种族间的政治斗争。系列的六本书相当于在这个大设定下讲的六个故事。每个故事虽说都是独立的故事,但是前后皆有关联,而且最重要的就是每本的长度都算不上很长,相当适合于一口气读完。 《西方文化中的数学》 这本书大概是从2017年开始读的,当时意气奋发觉得对于理科生出身的我,书中内容应该很好理解,没想到最后不光是文化还是数学读的都是一知半解。只能说自大了。 《一想到还有95%的问题留给人类,我就放心了》 大概是18年还是19年前后对量子物理和宇宙物理开始有了兴趣,虽然各中理论并不是很了解,但是仍然很好奇的买了这本书,全书插画相当有趣,同时使用浅显易懂的语言来解释各种复杂的物理现象,以及当今人类物理知识的边界。强烈推荐给想假装成学霸的朋友。 2021年读什么 具体读啥还没想好基本上还是以科幻和悬疑为主,但是肯定会读的应该有海明威的《丧钟为谁而鸣》和小林泰三的《醉步男》吧。","link":"/2020/12/29/2020%E5%B9%B4%E9%98%85%E8%AF%BB%E6%80%BB%E7%BB%93/"},{"title":"AKS中重写规则踩坑小记录","text":"前言最近在做标准产品在不同云平台中的部署验证,有幸体验了一下微软的Azure。负责采购的运维部门这次采用了Application Gateway来搭配AKS(Azure Kubernetes Service)对外暴露服务,正好借着这个机会来体验一下Application Gateway。 应用场景 域名api.demo.com指向Application Gateway的IP地址 在AKS内部2个Service, gateway-service和backend-service分别需要通过Application Gateway对外暴露。 /gateway/指向gateway-service, 然后/backend/指向backend-service。而且两个Service都没有context-path,所以需要做一个Rewrite重写URI到Service的根目录上。 定义重写集打开AKS对应的应用程序网关设置 > 重写。选择添加重写集。在1. 名称和关联这个Tab上只需要填写名称这项即可(名称后面在做ingress时需要使用), 关联的传递规则不需要选择。2. 重写规则配置里添加一个重写规则,然后填上重写规则的名称,并添加条件(默认新建重写规则时,只会生成操作,不会生成条件) 条件做如下设置 要检查的变量类型 : 服务器变量 服务器变量: request_uri 区分大小写: 否 运算符: 等号(=) 要匹配的模式: /(gateway|backend)/?(.*) 操作做如下设置 重写类型: URL 操作类型: 设置 组件: URL路径和URL查询字符串 URL路径值: /{var_request_uri_2} 重新计算路径映射: 不选中 URL查询字符串值: 留空不设值 特殊说明操作里的URL路径值不能使用正则表达式GROUP替换组,例如$1和$2之类的。Azure自己定义了一套对应的替换组命名规则。具体可以参考这个网页使用应用程序网关重写 HTTP 标头和 URL。 另外一个需要注意一点,如果在条件里选择了服务器变量的request_uri的时候,注意这个request_uri是完整的原始请求URI(携带了查询参数)。例如: 在请求http://api.demo.com/gateway/search?foo=bar&hello=world中,request_uri的值将为/gateway/search?foo=bar&hello=world。由于request_uri里包含了查询参数,所以在操作的组件中建议勾选URL路径和URL查询字符串。如果只选择URL路径的情况下可能出现无法预期的错误。以我们上述的配置来说明。 对象URL: http://api.demo.com/gateway/search?foo=bar&hello=world 组件 URL路径和URL查询字符串 URL路径 结果 /search?foo=bar&hello=world /search?foo=bar&hello=world?foo=bar&hello=world ACK的Ingress设置当选择了Application Gateway作为对外暴露Service的方式时,Kubernetes集群里(kube-system命名空间里)多一个Application Gateway Ingress Controller(Azure工单时通常会简称为agic)的Deployment,所以对外暴露服务时可以像传统nginx ingress controller一样添加一个Ingress对象即可(甚至配置也和ngic大致相同,只是多了2个annotations) 1234567891011121314151617181920212223242526272829apiVersion: networking.k8s.io/v1kind: Ingressmetadata: annotations: # 这里指定重写规则集(不是重写规则的名字) appgw.ingress.kubernetes.io/rewrite-rule-set: rule-backend # 指定说明你这里ingress的类型是agic kubernetes.io/ingress.class: azure/application-gateway name: backend-ingress namespace: defaultspec: rules: - host: api.demo.com http: paths: - backend: service: name: gateway-service port: number: 8080 path: /gateway/ pathType: Prefix - backend: service: name: backend-service port: number: 8080 path: /backend/ pathType: Prefix 总结由于微软云这块文档有部分缺失,导致在配置这块花了一点时间去排查,甚至开了工单。总结下来Ingress的配置主要是根据请求路径路由到对应的Service,重写规则集才是实际负责根据正则来进行匹配重写。","link":"/2022/09/23/AKS%E4%B8%AD%E9%87%8D%E5%86%99%E8%A7%84%E5%88%99%E8%B8%A9%E5%9D%91%E5%B0%8F%E8%AE%B0%E5%BD%95/"},{"title":"Largrange项目架构与设计回顾 (二)","text":"在Largrange项目架构与设计回顾 (一) 里面我讲了一下项目开始架构和技术选型的一些内容。这一章来聊聊业务设计上的这点事。 总体来说项目设计的时候,我们对业务模块的划分和拆解总体上来说都是遵循着高内聚,低耦合的原则来进行划分的。大部分和业务相关的服务,都还是能很好的进行功能划分的。但是有一些和具体业务关联性并不是很高的服务在界定与实现时出现了一些问题。 任务调度服务由于平台方面会有一些控制命令下发给Android设备,而且下发的命令并不一定都是实时的,大部分都是指定一个时间来下发,所以在设计的时候就考虑到需要一个任务调度服务(以下略称cron),来处理这些下发指令以及未来可能会有的定时批处理任务的业务需求。技术选型的时候考虑到整个服务是构建在Kubernetes上的一个分布式的微服务架构,所以就没有选择Spring Scheduler,而是使用了Quarter来支撑整个cron。 从技术选型上来说cron没有什么问题,但是在设计如何使用cron上还是有点问题的, 我们先来看看已推送服务为例,整个平台中cron的处理流程是怎样的。 Platform 调用 cron的创建定时任务Job的接口(接口的参数为推送时使用的相关参数) cron 创建Quartz的Job和Trigger,并将Job和Trigger的Name(Quartz中Job和Trigger的唯一标识符)返回个Platform cron 在指定时间触发推送的Job,即调用Push服务的推送接口 Push的推送接口调用第三方服务商的推送服务,并将第三方推送服务的调用结果返回给cron cron通过调用Platform预留的推送服务回调地址将第三方推送服务调用接口返还给Platform Platform 将第三方推送服务的调用结果留档保存,并继续业务处理 设计之初考虑到不想在cron中牵扯到具体的业务, 所以设计了一个Platform的回调接口来处理推送后的具体业务处理。虽然保证了cron尽量减少了和业务逻辑接触与数据库的访问。但是前前后后要访问集群内部的其他微服务2次,额外增加了网络开销,以及因为网络通信造成额外的通信失败风险。而且针对每种不同的定时任务,都需要额外开发一个QuartzJobBean,很难形成统一的QuartzJobBean来进行处理。今后如果还有相似项目还是需要重新考量一下如何设计一个更加完善的定时任务。 总结暂时就想到了这些东西,今后想到啥还会继续在这里补存。","link":"/2020/05/21/Lagrange%E9%A1%B9%E7%9B%AE%E5%9B%9E%E9%A1%BE2/"},{"title":"Spring Cloud Kubernetes环境下使用Jasypt","text":"前言最近半年着手开始做了基于微服务的中台项目,整个项目的技术栈采用的是Java + Spring Cloud + Kubernetes + Istio。 业务开放上还是相当顺利的。但是在安全审核上,运维组提出了一个简易。现在项目一些敏感配置,例如MySQL用户的密码,Redis的密码等现在都是明文保存在Kubernetes的ConfigMap中的(是的,我们并没有Nacos作为微服务的配置中心)。这样可能存在安全隐患。 首次尝试既然有问题,那就解决问题。要给配置文件中的属性项目加密很简单,稍微Google一下,就有现成的方案了。 现在比较常用的解决方案就是集成Jasypt,然后通过jasypt-spring-boot-starter来融合进Spring。 POM包加入jasypt-spring-boot-starter12345<dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>3.0.4</version></dependency> Dockerfile中增加java参数12...ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar app.jar --jasypt.encryptor.password=helloworld $PARAMS"] 在ConfigMap中添加加密属性1234567apiVersion: v1kind: ConfigMapmetadata: name: demodata: application.yaml: |- test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7) 利用actuator接口测试在management.endpoints.web.exposure.include属性中增加env,这样我们就可以通过调用/actuator/env来查看一下env接口返回的整个Spring 容器中所有的PropertySource。 1234567891011121314{ ... "propertySources": [ { "name": "bootstrapProperties-configmap.demo.default", "properties": { "test2": { "value": "Hello,world" } } } ... ]} OK, 这下配置项已经加密了。问题解决了。 但是… 新的问题自从项目集成了Jayspt以后,出现了一个奇怪的问题。每次项目试图通过修改ConfigMap的配置文件,然后试图通过spring-cloud-starter-kubernetes-fabric8-config来做自动Reload,都失败了。然而查阅应用日志,并没有出现任何异常。无奈只能打开spring-cloud和jasypt-spring-boot的DEBUG日志。 进过几天对日志和两边源代码的分析。终于找到了原因 原因在Spring Boot启动时jasypt-spring-boot会将下面6种配置(并不仅限与这6种配置文件) Classpath下的application.yaml Classpath下的bootstrap.yaml 集群里名称为${spring.cloud.kubernetes.config.name}的ConfigMap 集群里名称为${spring.cloud.kubernetes.config.name}-kubernetes的ConfigMap Java启动参数 环境变量 转换成jasypt-spring-boot自己的PropertySource实现类EncryptableMapPropertySourceWrapper。 但是如果使用Kubernetes的ConfigMap来作微服务配置中心的时候,Spring Cloud会在ConfigurationChangeDetector中查找配置类org.springframework.cloud.bootstrap.config.BootstrapPropertySource, 并依据BootstrapPropertySource的类型来判断容器内的配置与集群中ConfigMap里的配置是否有差异,来触发配置reload。 由于jasypt-spring-boot已经将所有的配置文件转型成了EncryptableMapPropertySourceWrapper, 所以ConfigurationChangeDetector无法找到BootstrapPropertySource所以会一直任务ConfigMap的里的配置没有变化,导致整个Reload失效(无论是使用polling还是event方式) 解决问题为了保证ConfigMap变化后自动Reload的功能,所以jasypt-spring-boot不能把BootstrapPropertySource转换成EncryptableMapPropertySourceWrapper 所以我们需要设置jasypt.encryptor.skip-property-sources配置项, Classpath中的application.yaml需要增加配置 123jasypt: encryptor: skip-property-sources: org.springframework.cloud.bootstrap.config.BootstrapPropertySource skip-property-sources配置项配置后,加密项目就不能配置在ConfigMap里了,毕竟已经被我们忽略了。那么我们只能另外找一个PropertySource来存放加密项目了。 Classpath中的两个Yaml由于编译时会被Maven打包进Jar文件,会牵涉多个CI/CD多个流程显然不合适,启动参数配置项的也要影响到Docker镜像制作这个流程。所以判断下来最适合的PropertySource就是环境变量了。 环境变量增加加密项在Kubernetes的部署Yaml中,添加加密数据项application.test.str 123456789101112131415161718192021222324apiVersion: apps/v1kind: Deploymentmetadata: labels: app: demo name: demospec: replicas: 1 selector: matchLabels: app: demo template: metadata: labels: app: demo spec: containers: - env: - name: TZ value: Asia/Shanghai - name: application.test.str value: >- ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7) .... 如果需要更加严密的加密方针的话,我们可以把环境变量的内容放进Kubernetes的Secrets中。 在ConfigMap中引用application.test.str12345678apiVersion: v1kind: ConfigMapmetadata: name: demodata: application.yaml: |- test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7) test3: ${application.test.str} 通过actuator接口来测试通过actuator\\env接口来测试一下 1234567891011121314151617{ ... "propertySources": [ { "name": "bootstrapProperties-configmap.demo.default", "properties": { "test2": { "value": "ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)" }, "test3": { "value": "Hello,world" } } } ... ]} 这样ConfigMap中的配置项test3就可以通过环境变量引用并使用加密配置项了。同时修改ConfigMap依然可以触发auto reload了。这下终于算是解决了。","link":"/2021/09/29/Spring-Cloud-Kubernetes%E7%8E%AF%E5%A2%83%E4%B8%8B%E4%BD%BF%E7%94%A8Jasypt/"},{"title":"Largrange项目架构与设计回顾 (一)","text":"项目背景 从去年年底开始一个老客户希望在他们的一个传统的机械设备(后面略称 E机关 )上外装一个Android设备。 Android设备和 E机关 之间通过串口或是RJ45接口进行数据交互,主要是Android设备获取 E机关 内部的数据,并不会通过接口来控制 E机关 。 Android设备则通过4G 来和平台交互,上报Android设备的状态数据,同时接受平台的控制。以此来实现让传统机械设备也能拥抱物联网的概念。 最后围绕着客户的需求分成了3个项目来并行推进 Android设备的硬件设备的设计、选材、样机制作与量产规划 在Android设备上,进行与E机关以及平台进行交互的APP开发 用于Android设备交互的平台的架构、设计与开发 我们平台Team就负责 3.用于Android设备交互的平台 并命名Lagrange (拉格朗日) 。 设计&架构整个平台这块不仅需要向Android设备的APP提供数据交互接口,还需要有一个供相关运营人员使用的前端Web应用,以及与云平台(主要是Aliyun) 交互的功能。所以考虑到多方面使用微服务的架构来实现整个平台端。由于E机关的工作工况不能保证长期较的稳定的连接到4G网络,所以我们并不考虑使用Socket长连接的方式来做APP和平台之间的数据交互,要实现平台对Android设备进行反控的话,只能实现推送方式来把控制命令下发给Android设备,所以还需要有一个第三方推送服务商交互的服务。同时控制推送也有实时和非实时以及定期推送的需求,所以可能还需要一个任务调度的服务。 根据对上述业务进行梳理,我们将项目分成几个服务 提供Android设备的交互接口的 App Service 提供运营人员使用前端Web应用 Platform Web Service 前端Web应用使用到的一些接口 Platform Service 负责第三方推送服务商交互的 Push Service 提供云平台鉴权用的 Auth Service 用来管理任务调度的 Cron Service 由于前一个项目实施的时候没有使用容器部署,每当访问量峰值的时候,我们这些码农兼运维就各种加班,所以这次项目决定直接将服务容器化,同时选择了Kubernetes来管理容器。由于客观原因线上的Kubernetes直接购买了Aliyun的托管版Kubernetes服务。 内部的开发测试环境则使用了 Rancher 2.0 来构建Kubernetes集群。 技术选型a. 后端服务选型由于不考虑长连接的原因,所以在后端服务在技术选型上基本就不考虑Netty了,直接上Spring大礼包。 Spring Boot 开发接口 Spring Data 配合 JPA 来进行数据的持久化 Spring Cloud Kubenetes 来做数据的Config的autoreload Quartz 负责处理任务调度 服务之间调用都使用HTTP服务, 服务发现也有Kubernetes的DNS机制支持。服务网格则选择了比较成熟的Istio,主要还是Aliyun的Kubernetes可以集成Istio,部署和使用都相当方便。 b. 前端Web应用选型因为前端Web应用主要是给运营人员使用,所以我们考虑使用Single Page Application来做个前后端分离的Web应用。框架这块由于团队成员基本上没有什么前端开发经验,基本都是后台写Java的码农,所以框架选择有点随性,直接就点名了vue.js。前端控件库则用的是阿里系的Antd。 c. 数据持久层主数据库选择了mysql,缓存用的redis。这些都是团队比较熟悉的。由于一些特殊的业务需求和使用场景,我们还加了一个mongodb来做为一个副数据库,主要存放一些特殊业务使用的数据。 d. DevOps用了相当传统的GitLab CE 加上Jenkins的组合,实现前后端代码的自动编译,推送到私有的镜像仓库(使用Aliyun的镜像仓库服务) 最后以上基本是项目最早做设计时候的各种考量。暂时先写这么多,过两天再回顾一下当初设计上有哪些觉得不足的地方。","link":"/2020/05/09/lagrange%E9%A1%B9%E7%9B%AE%E5%9B%9E%E9%A1%BE/"},{"title":"在ubuntu上从零搭建node.js + nginx + mongodb环境","text":"说到后端开发环境,最有名的莫过于LAMP和LNMP,最近由于node.js的强势崛起,越来越多的后端开发也开始试水node.js了。我最近也因为各种原因,前前后后总够构建了好几台node.js + nginx + mongodb的Linux服务器。 首先关于Linux服务器,比起CentOS来说,我更加喜欢ubuntu一点。所以无论是阿里云还是一些海外的vps服务器上,我也倾向选用ubuntu服务器,本贴也是基于ubuntu服务器里说明的。 1.开始前的一些准备首先还是需要刷新一下ubuntu的包索引并安装build-essential和libssl-dev这2个包以及curl这个工具。 123sudo apt-get updatesudo apt-get install build-essential libssl-devsudo apt-get isntall curl 2.安装node.js关于安装node.js这一点,我不是很推荐使用apt-get 来安装node.js的环境。主要是因为node.js和io.js合并以后,版本迭代速度相当频繁(主要还是因为更多ES6的特性得到了支持)。今后很有可能会有在一台服务器上使用不同版本的node.js的需求。 这里推荐一个管理不同版本node.js的工具:nvm,官网: https://github.com/creationix/nvm 。安装nvm,如果前面你安装了curl的话可以 1curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash 如果没有按照curl的话,也可以使用wget来进行安装 1wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash 然后nvm就会自动安装到home目录下面的.nvm目录里,并会在.bashrc里自动添加nvm的环境变量。为了让环境变量生效,最简单的方法就是通过ssh或是telnet重新连接你的服务器。 安装完nvm后,就可以通过nvm来安装指定版本的node.js了。 12345# 列出可以安装的node版本号nvm ls-remote# 安装指定版本的node (当前最新版本为v5.7.1, LTS版是v4.3.2)nvm install v4.3.2 3.安装nginx由于ubuntu源(尤其是阿里云的源)上的nginx经常不是最新的,如果需要安装最新版本nginx的时候需要手动添加nginx的源。 1234567891011# 添加nginx的mainline仓库cd /tmp/ && wget http://nginx.org/keys/nginx_signing.keysudo apt-key add nginx_signing.key# 编辑/etc/apt/sources.list.d/nginx.list 添加下面2行内容,井号不需要# deb http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx# deb-src http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginxsudo vi /etc/apt/sources.list.d/nginx.list# 更新源,并安装nginxsudo apt-get update && sudo apt-get install nginx 在编辑/etc/apt/sources.list.d/nginx.list的时候需要注意,“ubuntu代号”需要根据ubuntu服务器的版本不同手动调整的,比如14.04是trusty。通过下面的命令可以获取ubuntu的代号。 1lsb_release -cs 4.安装mongodb同样和nginx有同样的问题,要安装最新3.2版本的mongodb也需要手动添加ubuntu的源。 1234567891011# 导入mongodb的public keysudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927# 生成mongodb的源listecho "deb http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list# 更新源sudo apt-get update# 安装最新版本的mongodbsudo apt-get install -y mongodb-org 以上一台node.js + nginx + mongodb的ubuntu服务器就完成了。","link":"/2019/12/09/hot-to-install-nodejs-nginx-mongodb-on-ubuntu/"},{"title":"阿里云Kubernetes上线踩坑记","text":"12Update:2020-04-08 增加istio-ingressgateway高可用的设置 最近公司因为项目需要,在阿里云上部署了一个Kubernetes集群。虽然阿里云的文档说的还算细致,但是还是有些没有明确说明的细节。 1. 购买篇申请项目预算的时候,只考虑到Worker节点,1个SLB节点以及域名和证书的预算。但是实际购买的时候发现还有许多额外的开销。 1.1 SNAT这个和EIP一并购买,可以方便通过公网使用kubectl访问集群。关于SNAT网关至今不是很明白需要购买这个服务的意义何在,只是为了一个EIP来访问集群吗? 1.2 Ingress这个选上了后,阿里云会给你买个SLB而且还是带公网访问的,如果你后期考虑使用Istio的话,建议你集群创建后,直接停止这个SLB,以免产生额外的费用。 1.3 日志服务通过阿里云的日志服务来收集应用的的日志,挺好用的。但是另外收费,如果有能力的自建日志服务的可不购买。 2. Istio阿里云的Kubernetes集群完美集成了Istio,根据向导就能很简单的部署成功。 2.1 额外的SLBIstio的Gateway 需要绑定一个新的SLB,和Ingress的SLB不能是同一个,又是一笔额外的开销 2.2 集群外访问这个在阿里云的Istio FAQ中有提到,按照指导很容易解决 2.2 SLB的443监听为了方便443端口的证书绑定,我们直接删除了SLB上原有的443监听(TCP协议), 重新建了一个443监听(HTTPS协议),指向和80端口同样的虚拟服务器组。但是设置健康检查时一直出错,经过排查发现SLB健康检查发送的请求协议是HTTP 1.0的,Istio的envoy直接反悔了426(Upgrade Required)这个状态码,所以我们无奈只能把健康检查的检查返回状态改为http_4xx,这样就能通过SLB的健康检查了。 2.3 istio-ingressgateway的高可用istio-ingressgateway要达成高可用,只需要增加通过伸缩POD就可以实现,于istio-ingressgateway对应的SLB中的虚拟服务器组也会自动增加,完全不需要进行额外的手动设定。 由于istio-ingressgateway中挂载了HPAHorizontalPodAutoscaler(简称HPA),通常三节点的集群中最小POD数只有1台,在3节点的集群中,要实现高可用,需要手动修改HPA,增加最小POD数。 基本上现在遇到了这些坑,再有在总结吧。","link":"/2020/04/01/%E9%98%BF%E9%87%8C%E4%BA%91Kubernetes%E4%B8%8A%E7%BA%BF%E8%B8%A9%E5%9D%91%E8%AE%B0/"},{"title":"重学Java (一) 泛型","text":"1. 前言泛型编程自从 Java 5.0 中引入后已经超过15个年头了。对于现在的 Java 码农来说熟练使用泛型编程已经是家常便饭的事情了。所以本文就在不对泛型的基础使用在做说明了。 如果你还不会使用泛型的话,可以参考下面两个链接 Java 泛型详解 The Java™ Tutorials (Lesson: Generics) 这篇文章就简答聊一下,我实际在开发工作中很少用的到泛型方法这个知识点,以及在实际项目中有哪些东西会使用到泛型。 2. 泛型方法在阅读代码的时候我们经常会看到下面这样的方法 (这段代码摘自 java.util.AbstractCollection) 123456789101112131415161718192021222324252627 public <T> T[] toArray(T[] a) { // Estimate size of array; be prepared to see more or fewer elements int size = size(); T[] r = a.length >= size ? a : (T[])java.lang.reflect.Array .newInstance(a.getClass().getComponentType(), size); Iterator<E> it = iterator(); for (int i = 0; i < r.length; i++) { if (! it.hasNext()) { // fewer elements than expected if (a == r) { r[i] = null; // null-terminate } else if (a.length < i) { return Arrays.copyOf(r, i); } else { System.arraycopy(r, 0, a, 0, i); if (a.length > i) { a[i] = null; } } return a; } r[i] = (T)it.next(); } // more elements than expected return it.hasNext() ? finishToArray(r, it) : r;} 那么 pulic 关键字后面的那个 <T> 就是用来标记这个方法是一个泛型方法。 那什么是泛型方法呢。 官方的解释是这样的 1Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors. 通俗点来将就是将一个方法泛型化,让一个普通的类的某一个方法具有泛型功能。 如果在一个泛型类中增加一个泛型方法,那这个泛型方法就可以有一套独立于这个类的泛型类型。 通过一个简单的例子, 我们来看看 123456789101112131415161718192021/** * GenericClass 这个泛型类是一个简单的套皮的 HashMap */public class GenericClass<K, V> { private Map<K, V> map = new HashMap<>(); public V put(K key, V value) { return map.put(key, value); } public V get(K key) { return map.get(key); } // 泛型方法 genericMethod 可以接受一个全新的、作用域只限本函数的泛型类型T public <T> T genericMethod(T t) { return t; }} 实际使用起来 123456GenericClass<String, Integer> map = new GenericClass<>();// put 和 get 方法的参数必须使用定义时指定的 String 和 IntegerSystem.out.println(map.put("One", 1));System.out.println(map.get("One"));// 泛型方法 genericMethod 就可以接受一个 String 和 Integer 以外的类型System.out.println(map.genericMethod(new Double(1.0)).getClass()); 我们再来看看 JDK 中使用到泛型方法的例子。我们最常使用的泛型容器 ArrayList 中有个 toArray 方法。JDK 在它的实现中就提供了两个版本,其中一个就是泛型方法的版本 12345678910111213141516171819public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { // 这是一个普通版本,返回一个Object的数组 public Object[] toArray() { return Arrays.copyOf(elementData, size); } // 这是一个泛型方法的版本,将容器里存储的元素输出到 T[] 数组中。 其中 T 必须是 E 的父类,否则 System.arraycopy 会抛出 ArrayStoreException 异常 public <T> T[] toArray(T[] a) { if (a.length < size) // Make a new array of a's runtime type, but my contents: return (T[]) Arrays.copyOf(elementData, size, a.getClass()); System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; }} 泛型方法总体上来说就是可以给与现有的方法实现上,增加一个更加灵活的实现可能。 3. 实战应用在实际的项目中,对于泛型的使用,除了像倾倒垃圾一样往泛型容易里塞各种 java bean 和其他泛型对象。还能怎么使用泛型呢? 我们在实际的一些项目中,会对数据库中的一些表(多数时候是全部)先实现 CRUD (Create, Read, Update, Delete)的操作,并从这些操作中延伸出一些简单的 REST 风格的 WebAPI 接口,然后才会根据实际业务需要实现一些更复杂的业务接口。 大体上会是下面这个样子。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798// 这是一个简单的 Entity 对象// 通常现在的 Java 应用都会使用到 Lombok 和 Spring Boot@Data@AllArgsConstructor@NoArgsConstructor@ToString@Entity@Table(name = "user")public class User { @Id private Long id; private String username; private String password;}// 然后这个是 DAO 接口继承自 spring-data 的 JpaRepositorypublic interface UserDao extends JpaRepository<User, Long> {}// 在来是一个访问 User 资源的 Service 和他的实现public interface UserService { List<User> findAll(); Optional<User> findById(Long id); User save (User user) void deleteById(Long id);}@Servicepublic class UserSerivceImpl implements UserService { private UserDao userDao; public UserServiceImpl(UserDao userDao) { this.userDao = userDao; } @Override public List<User> findAll() { return this.dao.findAll(); } @Override public Optional<User> findById(Long id) { return this.dao.findById(id); } @Override public User save(User user) { return this.dao.save(user); } @Override public void deleteById(Long id) { this.dao.deleteById(id); }}// 最后就是 WebAPI 的接口了@RestController@RequestMapping("/user/")public class UserController{ private UserService userService; public UserController(userService userService) { this.userService = userService; } @GetMapping @ResponseBody public List<User> fetch() { return this.userService.findAll(); } @GetMapping("{id}") @ResponseBody public User get(@PathVariable("id") Long id) { // 由于是示例这里就不考虑没有数据的情况了 return this.userService.findById(id).get(); } @PostMapping @ResponseBody public User create(@RequestBody User user) { return this.userService.save(user); } @PutMapping("{id}") @ResponseBody public User update(@RequestBody User user) { return this.userService.save(user); } @DeleteMapping("{id}") @ResponseBody public User delete(@PathVariable("id") Long id) { User user = this.userService.findById(id); this.userService.deleteById(id); return user; }} 大致一个表的一套相关接口就是这个样子的。如果你的数据库中有大量表的话,而且每个表都需要提供 REST 风格的 WebAPI 接口的话,那么这将是一个相当枯燥的而又及其容易出错的工作。 为了不让这项枯燥而又容易犯错的工作占去我们宝贵的私人时间,我们可以通过泛型和继承的技巧来重构从 Service 层到 Controller 的这段代码(感谢 spring-data 提供了 JpaRepository, 让我们不至于从 DAO 层重构) 3.1 Service 层的重构首先是 Service 接口的重构,我们 Service 层接口就是定义了一组 CRUD 的操作,我们可以将这组 CRUD 操作抽象到一个父接口,然后所有 Service 层的接口都将继承自这个父接口。而接口中出现的 Entity 和主键的类型(上例中 User 的主键 id 的类型是 Long)就可以用泛型来展现。 1234567891011// 这里泛型表示 E 来指代 Entity, ID 用来指代 Entity 主键的类型public interface ICrudService<E, ID> { List<E> findAll(); Optional<E> findById(ID id); E save(E e); void deleteById(ID id);}// 然后 Service 层的接口,就可以简化成这样public interface UserService extends ICrudService<User, Long> {} 同样 Service 层的实现也可以使用相似的方法具体实现可以抽象到一个基类中。 1234567891011121314151617181920212223242526272829303132// 相比 ICrudService 这里有多了一个泛型 T 来代表 Entity 对应的 DAO, 我们的每一个 DAO 都继承自// spring-data 的 JpaRepository 所以,这里可以使用到泛型的边界public abstract class AbstractCrudService<T extends JpaRepository<E, ID>, E, ID> { private T dao; public AbstractCrudService(T dao) { this.dao = dao; } public List<E> findAll() { return this.dao.findAll(); } public Optional<E> findById(ID id) { return this.dao.findById(id); } public E save(E e) { return this.dao.save(e); } public void deleteById(ID id) { this.dao.deleteById(id); }}// 那 Service 的实现类可以简化成这样@Servicepublic class UserServiceImpl extends AbstractCrudService<UserDao, User, Long> implements UserService { public UserServiceImpl(UserDao dao) { supper(dao); }} 同样我们可以通过相同的方法来对 Controller 层进行重构 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849// Controller 层的基类public abstract class AbstractCrudController<T extends ICrudService<E, ID>, E, ID> { private T service; public AbstractCrudController(T service) { this.service = service; } @GetMapping @ResponseBody public List<E> fetch() { return this.service.findAll(); } @GetMapping("{id}") @ResponseBody public E get(@PathVariable("id") ID id) { // 由于是示例这里就不考虑没有数据的情况了 return this.service.findById(id).get(); } @PostMapping @ResponseBody public E create(@RequestBody E e) { return this.service.save(e); } @PutMapping("{id}") @ResponseBody public E update(@RequestBody E e) { return this.service.save(e); } @DeleteMapping("{id}") @ResponseBody public E delete(@PathVariable("id") ID id) { E e = this.service.findById(id).get(); this.service.deleteById(id); return e; }}// 具体的 WebAPI@RestController@RequestMapping("/user/")public class UserController extends AbstractCrudController<UserService, User, Long> { public UserController(UserService service) { super(service); }} 经过重构可以消减掉 Servcie 和 Controller 中的大量重复代码,使代码更容易维护了。 4. 结尾关于泛型就简单的说这些了,泛型作为 Java 日常开发中一个常用的知识点,其实还有很多知识点可以供我们挖掘,奈何本人才疏学浅,这么多年工作下来,只积累出来这么点内容。 文末放上示例代码的代码库: GitHub入口 gitee入口","link":"/2021/03/15/%E9%87%8D%E5%AD%A6Java-%E4%B8%80-%E6%B3%9B%E5%9E%8B/"},{"title":"H项目心路历程记 (一)","text":"前言2021年年中从前一家公司离职,经过朋友介绍到了现在公司参与了Halfling项目(以下简称H项目),并从零开始的设计开发到第一个客户交付的全部流程。 H项目是一个面向零售行业在电商时代的智能一体化中台产品。项目最早是从2021年中开始立项投入,期间经过数个版本迭代,以及2022年初的魔都封城等诸多困难。终于在2023年Q2季度完成了针对首个客户的上线。 作为从头开始全程参与项目的技术人员,经过2年多的产品研发期间有不少想吐槽和想说的。 业务设计最初H项目是以打造一个面向零售行业业务中台为目的进行项目立项的,其中一个很重要的点就是希望除核心组件以外都可以独立售卖。所以在架构设计的使用当下最流行的微服务架构(基于Spring Boot + Kubernetes集群的解决方案), 再做业务拆分到服务的时候,进行了更细颗粒度的拆分。所以前前后后一共拆分了近20个微服务。 实际上到了2022年Q2前后,两个核心服务订单管理和库存服务实现以后发现,各个业务服务中间的耦合程度比想象中的还要精密,单个服务拆开单独售卖几乎不可能(应该说是完全不可能)。 然而由于服务拆分的过细,导致一个服务需要频繁访问其他服务获取大量数据。尤其类似订单关联数据比如商品的主档数据,会穿越好几个服务。而每个服务都会重复去获取主档数据,造成了大量的额外请求。 虽然最后在所有服务底部兜底使用同一个Redis作为缓存来减少额外的请求调用。 随着时间来到2023年产品组负责人离职后,整个H项目的方向有从行业的业务中台降级成中小企业的订单管理系统(Order Management System, OMS)。前期做的很多设计比如用户权限体系(当初需求是要精确到每个页面的每个按钮,如果下拉列表框有外调请求也需要做权限控制)等就显的很重,导致用户在使用以及配置都很不方便。而有些设计就显得稍显欠缺。 所以整个2022年Q4到2023年都是在对业务层上反复横跳,比如库存模型和商品模型的调整。这点是相当影响团队士气,同时也加剧了研发组和产品组之间的矛盾。 然后还有一个很重要的问题是多租户的态度上。早起项目是以服务中大体量客户为目的,所以设计之初并没有考虑多租户的问题。但实际项的进展过程中发现部分中大体量的客户也需要类似多租户的功能。这类客户需求与其说是需求多租户,不如说是希望有一个多租户级别的数据隔离,每个业务部门只能操作自己的业务部门的数据(数据包含商品,品牌,属性,平台店铺,订单,售后单据等),而总部依据可以查看一些汇总信息(例如销售对账单据等)。而随着2022年疫情状况严峻,很多中大客户对于内部平台升级和替换都变的更加谨慎和保守。所以当项目方向变成中小企业OMS时,多租户SaaS化的需求就更加迫切了。所以后期如何在原有系统上在扩展一套多租户SaaS化功能也是个课题。 中间件H项目使用的中间件中比较特别的有分布式任务管理系统xxl-job,消息队列RocketMQ,这两个中间件都是第一次使用。关于这两个中间件也有不是可以谈谈的。 分布式任务管理系统 XXL-JOB作为一个开源的分布式任务管理系统,xxl-job足够简单且相当容易上手使用,无论是基于Spring,还是原生Java做开发都是相当简单的。 而不足之处也很明显,最近1年版本迭代速度明显变慢,有些功能和BUG都有待处理的。比如想实动态创建定时任务这类功能就必须自己动手去对xxl-job-admin(调度中心)进行修改。 对于任务执行日志这块,现在xxl-job也是文件格式在执行器所在服务器上进行输出的,当执行器容器化或是运行在Kubernetes集群中的时候,当容器或是Pod重启后,就会丢失日志。这点在后期运维以及调试上还是有点麻烦的。 消息队列 RocketMQ阿里出品的消息队列如果基于云产品来使用的话问题不大(阿里云卖的价钱也是有点黑的),但是在运维上还是有点问题的比如Topic,Group这些东西都只能人工去添加,阿里云并没有提供导入服务。全系统如果使用消息队列的场景多的话,维护这些Topic和Group还是很酸爽的。还有一点RocketMQ重复消息这一点是无法保证的。用阿里云的原话 绝大多数情况下,消息是不重复的。作为一款分布式消息中间件,在网络抖动、应用处理超时等异常情况下,无法保证消息不重复,但是能保证消息不丢失。 实际运用上,在大数据量并发的情况下,我们实际上是遇到了消息重复消费的问题。这点还是需要通过代码干预(例如增加一个唯一MsgId,对MsgId做分布式锁来解决)。 还有一点就是在旧版本SDK的默认配置下消费者的负载是有点小问题,总是有一个节点会比其他节点更容易消费到大量消息。据阿里云自己说明在新版本SDK的默认负载配置也已经修复这个问题了。 (未完待续)","link":"/2023/07/10/H%E9%A1%B9%E7%9B%AE%E5%BF%83%E8%B7%AF%E5%8E%86%E7%A8%8B%E8%AE%B0-1/"},{"title":"H项目心路历程记 (二)","text":"(书接上文) 前文已经吐槽了产品设计以及中间件上的相关问题。接下来讨论一下技术上的一些问题。 关于配置对于一个Spring的应用来说,应用配置怎么存取其实并不能说是一个技术问题。更应该算是设计或是架构问题。哪些配置应该进入配置文件,通过微服务配置中心(例如Nacos, Apollo,Kubernetes ConfigMap等)来进行在线配置,哪些配置应该通过其他方式来配置,亦或者哪些配置项目看似是配置项其实本质上就是常量。实际上截止现在H项目的确在有些微服务上是完全没有厘清三者之间的区别,以及界限。以至于现在堆积在配置文件中的内容实在太多。多个运行环境需要统一维护的难度实在太大。 对于配置来说,理想的状态应该是下面这样的。 通过微服务配置中心来影响Spring的配置文件来实现动态配置的方案,通常会伴随Spring容器的重启(有些情况下甚至需要整个Docker容器),所以存在这里的配置项应该是随着运行环境改变才会需要改动的配置。例如: 外部系统的链接信息(比如MySQL, Redis的连接信息) 外部系统的认证凭证(数据库用户密码,第三方系统的密钥等) 因为运行环境不同导致的不同变量 (例如消息队列的Topic,Group需要根据运行环境使用不同的名称) 通过数据库或是Redis存储的配置信息,这类配置主要是应该以业务级别的配置,可以通过页面进行修改,瞬间或是几分钟内生效,且不会造成微服务重启(无论是Spring或是Docker容器的重启)。对于这类配置有几个问题还是需要注意的。 配置是否需要集中管理,每个微服务,都有自己独特的业务配置,这些业务配置是否需要集中到一个微服务统一管理,还是分散到各自的微服务进行单独管理。 全局性统一管理的业务配置对外保留接口应该利用缓存有效的减少数据库访问的频次。甚至可以只使用内存而不是Redis来缓存这部分内容。如果使用内存的话则需要考虑失效时效和重新装载的机制。 无效配置,实际上很多配置说是可以修改,其实自从开始使用的那天开始就没有修改过。比如用来做分布式锁的键值。这部分需要梳理出来直接以常量的形式做在代码里。 数据清理以及归档对于电商行业的零售业务来说每天产生的单据比如订单少做200,300条,多的是上千条。加上明细数据。几个月以后这些数据就会给MySQL的库表增加检索压力。整个H项目在这块是相当薄弱的。只依靠云平台的能力,对一些不需要留档的数据进行清理。远远没有能定期归档。 对于数据清理这块大致可以分为这几类 定期可以无脑物理删除的数据,比如一些日志类数据。 定期直接归档的数据,归档后即可清理的数据。 业务必须处理完成后才能归档并清理的数据。例如订单数据等。 对于数据归档的时候,还需要考虑一些相关联单据数据一并归档。比如订单归档时,同时需要归档相关联的退换货单,发货单,以及操作日志等等。 归档格式基本上就是考虑单条数据以JSON格式存储,然后一个时间段的数据做成压缩包存储在OSS上,然后提供给客户下载。 而对于归档数据的检索功能,客户选择需要归档的数据,我们可以将归档的数据(每个压缩包内的所有数据)导入到一个主数据库以外的,临时文档数据库(例如MongoDB),然后针对这个临时的文档数据库进行检索。当然提供的检索项目也会远少于热数据的检索项目。这个临时文档数据库基本上保持一两天数据,定期就全部回收。 未完待续","link":"/2023/07/12/H%E9%A1%B9%E7%9B%AE%E5%BF%83%E8%B7%AF%E5%8E%86%E7%A8%8B%E8%AE%B0-2/"},{"title":"H项目心路历程记(三)","text":"现状对于一个基于微服务的中台系统来说,需要以什么样的颗粒度来拆分系统。这一直是基于微服务架构的系统中绕不过去的问题。 已H项目来说,当初考量是这么划分微服务的,其中 基础服务: 作用主要是负责用户,角色权限,登陆鉴权,以及一些基础主档数据的日常维护功能 商品服务: 物料,销售商品,款式,平台商品等和实际商品相关的主档数据 库存服务:负责管理各个库存层级库存(总库存,冻结库存,在途库存等),以及展示各种出入库单据,并向其他微服务提供出入库服务接口。 订单服务: 负责接收各个公域平台的ToC订单,售后单(含发货前退款,退货,换货单等),并根据配置策略进行单据路由(分仓,分物流,Hold单,加送赠品,通知仓库发货,通知平台发货等) 接口服务: 主要承揽中台服务和第三方平台间的交互处理(主要是第三方平台发起的主调通知以及回调) 上述五个服务为核心服务,是保证整个中台系统基础业务功能运行的重要组成部分。除此之外还有, 采购服务:负责ToB业务线的处理 财务服务:满足一些企业的财务对账以及开具电子发票的功能 报表服务: 每天,每周定期从其他服务(主要是订单) 会员服务:管理各类会员 等7,8个非核心服务,组成非核心业务。来构成一个完整的中台系统。 拆分策略以及缺点当时主要考虑到整个中台系统是可以拆分出售,客户可以只选择购买自身需要的服务,而不需要的服务则无需购买服务,所以当初在拆分服务上在业务领域层面上需要更加低颗粒度;同时考虑到再业务高峰期(下午到晚上,就是各位疯狂刷淘宝,京东的时候)各个服务的负载是各不相同的,如果在高峰期对于特定服务需要进行精准扩容的话,拆分的颗粒度也需要更加细致,所以结果上导致整个系统满血运行起来要至少要十几个服务,外加一些没有云平台的中间件。整个Kubernetes环境中,至少要有十五六个Deployment,考虑到系统稳定性,一个Deployment也至少要有2到3个Pod,那整个系统至少就是50个Pod。 对于一个中大型的电商企业再服务器成本上可以负担这样一个能支持50个Pod的Kubernentes集群(考虑到需要一些中间件的云产品,当时估算最小的服务器成本都要到70K/年,一个中大型企业能面对双十一流量的服务器成本都要到300K/年以上)。所以整个系统对于中小型企业来说前期的投入负担相当大(尤其是在后疫情时代的2022年,2023年),以至于早起的好几个客户都是需要我们自己贴补服务器费用的。 复盘现在重新复盘当初服务的拆分策略,个人觉得对于核心业务来说,相似领域的一些服务应该可以适当的聚合。比如上述的五大核心服务上中的商品服务,订单服务,库存服务三个核心服务应该可以合并在一起。首先这3个核心服务是H项目的最重要的竞争力,几乎不可能出现把这3个服务拆分出售的可能性。 而且在业务高峰期有订单服务有新订单处理时,势必会产生库存的变化,因此会调用到库存服务。三个服务之间的调用是相当频繁的,尤其是负责处理商品主档数据的商品服务,无论是通过HTTP或是其他方式的远程调用的话,都会有许多浪费的带宽。如果把三个服务整合,那么之间的调用基本上就是线程间的交互,无论是效率和可靠性上都会有更加显著的提升。 结尾怎么拆分服务一直是个难题。虽然事后复盘的时候我的很多想法也不一定是正确,但是什么样的规模的客户需要使用什么样的拆分颗粒度,都是需要仔细考虑的。绝对不是一尘不变的。","link":"/2023/09/21/H%E9%A1%B9%E7%9B%AE%E5%BF%83%E8%B7%AF%E5%8E%86%E7%A8%8B%E8%AE%B0-3/"}],"tags":[{"name":"阅读","slug":"阅读","link":"/tags/%E9%98%85%E8%AF%BB/"},{"name":"Aliyun","slug":"Aliyun","link":"/tags/Aliyun/"},{"name":"Kubernetes","slug":"Kubernetes","link":"/tags/Kubernetes/"},{"name":"架构","slug":"架构","link":"/tags/%E6%9E%B6%E6%9E%84/"},{"name":"Java","slug":"Java","link":"/tags/Java/"},{"name":"Spring","slug":"Spring","link":"/tags/Spring/"},{"name":"ubuntu","slug":"ubuntu","link":"/tags/ubuntu/"},{"name":"node.js","slug":"node-js","link":"/tags/node-js/"},{"name":"nginx","slug":"nginx","link":"/tags/nginx/"},{"name":"mongodb","slug":"mongodb","link":"/tags/mongodb/"},{"name":"java","slug":"java","link":"/tags/java/"},{"name":"generic","slug":"generic","link":"/tags/generic/"}],"categories":[{"name":"杂记","slug":"杂记","link":"/categories/%E6%9D%82%E8%AE%B0/"},{"name":"后端","slug":"后端","link":"/categories/%E5%90%8E%E7%AB%AF/"},{"name":"设计","slug":"设计","link":"/categories/%E8%AE%BE%E8%AE%A1/"},{"name":"Cloud","slug":"后端/Cloud","link":"/categories/%E5%90%8E%E7%AB%AF/Cloud/"}]} \ No newline at end of file diff --git a/css/cyberpunk.css b/css/cyberpunk.css new file mode 100644 index 0000000..502e34e --- /dev/null +++ b/css/cyberpunk.css @@ -0,0 +1,12301 @@ +@-moz-keyframes spinAround { + from { + transform: rotate(0deg); + } + to { + transform: rotate(359deg); + } +} +@-webkit-keyframes spinAround { + from { + transform: rotate(0deg); + } + to { + transform: rotate(359deg); + } +} +@-o-keyframes spinAround { + from { + transform: rotate(0deg); + } + to { + transform: rotate(359deg); + } +} +@keyframes spinAround { + from { + transform: rotate(0deg); + } + to { + transform: rotate(359deg); + } +} +.is-unselectable, +.breadcrumb, +.modal-close, +.pagination-previous, +.pagination-next, +.pagination-link, +.pagination-ellipsis, +.tabs, +.button, +.delete, +.file { + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.navbar-link:not(.is-arrowless)::after, +.select:not(.is-multiple):not(.is-loading)::after { + border: 3px solid transparent; + border-radius: 2px; + border-right: 0; + border-top: 0; + content: " "; + display: block; + height: 0.625em; + margin-top: -0.4375em; + pointer-events: none; + position: absolute; + top: 50%; + transform: rotate(-45deg); + transform-origin: center; + width: 0.625em; +} +.breadcrumb:not(:last-child), +.level:not(:last-child), +.list:not(:last-child), +.message:not(:last-child), +.pagination:not(:last-child), +.tabs:not(:last-child), +.box:not(:last-child), +.content:not(:last-child), +.notification:not(:last-child), +.progress:not(:last-child), +.table:not(:last-child), +.table-container:not(:last-child), +.title:not(:last-child), +.subtitle:not(:last-child), +.block:not(:last-child), +.highlight:not(:last-child) { + margin-bottom: 1.5rem; +} +.modal-close, +.delete { + -moz-appearance: none; + -webkit-appearance: none; + background-color: rgba(0,0,0,0.2); + border: none; + border-radius: 290486px; + cursor: pointer; + pointer-events: auto; + display: inline-block; + flex-grow: 0; + flex-shrink: 0; + font-size: 0; + height: 20px; + max-height: 20px; + max-width: 20px; + min-height: 20px; + min-width: 20px; + outline: none; + position: relative; + vertical-align: top; + width: 20px; +} +.modal-close::before, +.delete::before, +.modal-close::after, +.delete::after { + background-color: #000; + content: ""; + display: block; + left: 50%; + position: absolute; + top: 50%; + transform: translateX(-50%) translateY(-50%) rotate(45deg); + transform-origin: center center; +} +.modal-close::before, +.delete::before { + height: 2px; + width: 50%; +} +.modal-close::after, +.delete::after { + height: 50%; + width: 2px; +} +.modal-close:hover, +.delete:hover, +.modal-close:focus, +.delete:focus { + background-color: rgba(0,0,0,0.3); +} +.modal-close:active, +.delete:active { + background-color: rgba(0,0,0,0.4); +} +.modal-close.is-small, +.delete.is-small { + height: 16px; + max-height: 16px; + max-width: 16px; + min-height: 16px; + min-width: 16px; + width: 16px; +} +.modal-close.is-medium, +.delete.is-medium { + height: 24px; + max-height: 24px; + max-width: 24px; + min-height: 24px; + min-width: 24px; + width: 24px; +} +.modal-close.is-large, +.delete.is-large { + height: 32px; + max-height: 32px; + max-width: 32px; + min-height: 32px; + min-width: 32px; + width: 32px; +} +.button.is-loading::after, +.loader, +.select.is-loading::after, +.control.is-loading::after { + animation: spinAround 500ms infinite linear; + border: 2px solid #dbdbdb; + border-radius: 290486px; + border-right-color: transparent; + border-top-color: transparent; + content: ""; + display: block; + height: 1em; + position: relative; + width: 1em; +} +.is-overlay, +.modal, +.modal-background, +.image.is-square img, +.image.is-1by1 img, +.image.is-5by4 img, +.image.is-4by3 img, +.image.is-3by2 img, +.image.is-5by3 img, +.image.is-16by9 img, +.image.is-2by1 img, +.image.is-3by1 img, +.image.is-4by5 img, +.image.is-3by4 img, +.image.is-2by3 img, +.image.is-3by5 img, +.image.is-9by16 img, +.image.is-1by2 img, +.image.is-1by3 img, +.image.is-square .has-ratio, +.image.is-1by1 .has-ratio, +.image.is-5by4 .has-ratio, +.image.is-4by3 .has-ratio, +.image.is-3by2 .has-ratio, +.image.is-5by3 .has-ratio, +.image.is-16by9 .has-ratio, +.image.is-2by1 .has-ratio, +.image.is-3by1 .has-ratio, +.image.is-4by5 .has-ratio, +.image.is-3by4 .has-ratio, +.image.is-2by3 .has-ratio, +.image.is-3by5 .has-ratio, +.image.is-9by16 .has-ratio, +.image.is-1by2 .has-ratio, +.image.is-1by3 .has-ratio, +.hero-video { + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; +} +.pagination-previous, +.pagination-next, +.pagination-link, +.pagination-ellipsis, +.button, +.input, +.textarea, +.select select, +.file-cta, +.file-name { + -moz-appearance: none; + -webkit-appearance: none; + align-items: center; + border: 1px solid transparent; + border-radius: 0; + box-shadow: none; + display: inline-flex; + font-size: 1rem; + height: 2.25em; + justify-content: flex-start; + line-height: 1.5; + padding-bottom: calc(0.5em - 1px); + padding-left: calc(0.75em - 1px); + padding-right: calc(0.75em - 1px); + padding-top: calc(0.5em - 1px); + position: relative; + vertical-align: top; +} +.pagination-previous:focus, +.pagination-next:focus, +.pagination-link:focus, +.pagination-ellipsis:focus, +.button:focus, +.input:focus, +.textarea:focus, +.select select:focus, +.file-cta:focus, +.file-name:focus, +.pagination-previous.is-focused, +.pagination-next.is-focused, +.pagination-link.is-focused, +.pagination-ellipsis.is-focused, +.button.is-focused, +.input.is-focused, +.textarea.is-focused, +.select select.is-focused, +.file-cta.is-focused, +.file-name.is-focused, +.pagination-previous:active, +.pagination-next:active, +.pagination-link:active, +.pagination-ellipsis:active, +.button:active, +.input:active, +.textarea:active, +.select select:active, +.file-cta:active, +.file-name:active, +.pagination-previous.is-active, +.pagination-next.is-active, +.pagination-link.is-active, +.pagination-ellipsis.is-active, +.button.is-active, +.input.is-active, +.textarea.is-active, +.select select.is-active, +.file-cta.is-active, +.file-name.is-active { + outline: none; +} +.pagination-previous[disabled], +.pagination-next[disabled], +.pagination-link[disabled], +.pagination-ellipsis[disabled], +.button[disabled], +.input[disabled], +.textarea[disabled], +.select select[disabled], +.file-cta[disabled], +.file-name[disabled], +fieldset[disabled] .pagination-previous, +fieldset[disabled] .pagination-next, +fieldset[disabled] .pagination-link, +fieldset[disabled] .pagination-ellipsis, +fieldset[disabled] .button, +fieldset[disabled] .input, +fieldset[disabled] .textarea, +fieldset[disabled] .select select, +fieldset[disabled] .file-cta, +fieldset[disabled] .file-name { + cursor: not-allowed; +} +/* minireset.css v0.0.6 | MIT License | github.com/jgthms/minireset.css */ +html, +body, +p, +ol, +ul, +li, +dl, +dt, +dd, +blockquote, +figure, +fieldset, +legend, +textarea, +pre, +iframe, +hr, +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 0; + padding: 0; +} +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: 100%; + font-weight: normal; +} +ul { + list-style: none; +} +button, +input, +select, +textarea { + margin: 0; +} +html { + box-sizing: border-box; +} +*, +*::before, +*::after { + box-sizing: inherit; +} +img, +video { + height: auto; + max-width: 100%; +} +iframe { + border: 0; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +td, +th { + padding: 0; +} +td:not([align]), +th:not([align]) { + text-align: left; +} +html { + background-color: #000; + font-size: 14px; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + min-width: 300px; + overflow-x: hidden; + overflow-y: scroll; + text-rendering: optimizeLegibility; + text-size-adjust: 100%; +} +article, +aside, +figure, +footer, +header, +hgroup, +section { + display: block; +} +body, +button, +input, +select, +textarea { + font-family: 'Oxanium', Ubuntu, Roboto, 'Open Sans', 'Microsoft YaHei', sans-serif; +} +code, +pre { + -moz-osx-font-smoothing: auto; + -webkit-font-smoothing: auto; + font-family: 'Roboto Mono', monospace, 'Microsoft YaHei'; +} +body { + color: #cdcdcd; + font-size: 1em; + font-weight: 400; + line-height: 1.5; +} +a { + color: #02d7f2; + cursor: pointer; + text-decoration: none; +} +a strong { + color: currentColor; +} +a:hover { + color: #fcee09; +} +code { + background-color: #f5f5f5; + color: #ff003c; + font-size: 0.875em; + font-weight: normal; + padding: 0.25em 0.5em 0.25em; +} +hr { + background-color: #f5f5f5; + border: none; + display: block; + height: 2px; + margin: 1.5rem 0; +} +img { + height: auto; + max-width: 100%; +} +input[type="checkbox"], +input[type="radio"] { + vertical-align: baseline; +} +small { + font-size: 0.875em; +} +span { + font-style: inherit; + font-weight: inherit; +} +strong { + color: #fcee09; + font-weight: 700; +} +fieldset { + border: none; +} +pre { + -webkit-overflow-scrolling: touch; + background-color: #f5f5f5; + color: #cdcdcd; + font-size: 0.875em; + overflow-x: auto; + padding: 1.25rem 1.5rem; + white-space: pre; + word-wrap: normal; +} +pre code { + background-color: transparent; + color: currentColor; + font-size: 1em; + padding: 0; +} +table td, +table th { + vertical-align: top; +} +table td:not([align]), +table th:not([align]) { + text-align: left; +} +table th { + color: #fcee09; +} +.is-clearfix::after { + clear: both; + content: " "; + display: table; +} +.is-pulled-left { + float: left !important; +} +.is-pulled-right { + float: right !important; +} +.is-clipped { + overflow: hidden !important; +} +.is-size-1 { + font-size: 3rem !important; +} +.is-size-2 { + font-size: 2.5rem !important; +} +.is-size-3 { + font-size: 2rem !important; +} +.is-size-4 { + font-size: 1.5rem !important; +} +.is-size-5 { + font-size: 1.25rem !important; +} +.is-size-6, +article.media .title { + font-size: 1rem !important; +} +.is-size-7, +article.media .date, +article.media .categories, +.article-licensing .licensing-title a, +.article-licensing .licensing-meta h6 { + font-size: 0.85rem !important; +} +@media screen and (max-width: 768px) { + .is-size-1-mobile { + font-size: 3rem !important; + } + .is-size-2-mobile { + font-size: 2.5rem !important; + } + .is-size-3-mobile { + font-size: 2rem !important; + } + .is-size-4-mobile { + font-size: 1.5rem !important; + } + .is-size-5-mobile { + font-size: 1.25rem !important; + } + .is-size-6-mobile { + font-size: 1rem !important; + } + .is-size-7-mobile { + font-size: 0.85rem !important; + } +} +@media screen and (min-width: 769px), print { + .is-size-1-tablet { + font-size: 3rem !important; + } + .is-size-2-tablet { + font-size: 2.5rem !important; + } + .is-size-3-tablet { + font-size: 2rem !important; + } + .is-size-4-tablet { + font-size: 1.5rem !important; + } + .is-size-5-tablet { + font-size: 1.25rem !important; + } + .is-size-6-tablet { + font-size: 1rem !important; + } + .is-size-7-tablet { + font-size: 0.85rem !important; + } +} +@media screen and (max-width: 1087px) { + .is-size-1-touch { + font-size: 3rem !important; + } + .is-size-2-touch { + font-size: 2.5rem !important; + } + .is-size-3-touch { + font-size: 2rem !important; + } + .is-size-4-touch { + font-size: 1.5rem !important; + } + .is-size-5-touch { + font-size: 1.25rem !important; + } + .is-size-6-touch { + font-size: 1rem !important; + } + .is-size-7-touch { + font-size: 0.85rem !important; + } +} +@media screen and (min-width: 1088px) { + .is-size-1-desktop { + font-size: 3rem !important; + } + .is-size-2-desktop { + font-size: 2.5rem !important; + } + .is-size-3-desktop { + font-size: 2rem !important; + } + .is-size-4-desktop { + font-size: 1.5rem !important; + } + .is-size-5-desktop { + font-size: 1.25rem !important; + } + .is-size-6-desktop { + font-size: 1rem !important; + } + .is-size-7-desktop { + font-size: 0.85rem !important; + } +} +@media screen and (min-width: 1280px) { + .is-size-1-widescreen { + font-size: 3rem !important; + } + .is-size-2-widescreen { + font-size: 2.5rem !important; + } + .is-size-3-widescreen { + font-size: 2rem !important; + } + .is-size-4-widescreen { + font-size: 1.5rem !important; + } + .is-size-5-widescreen { + font-size: 1.25rem !important; + } + .is-size-6-widescreen { + font-size: 1rem !important; + } + .is-size-7-widescreen { + font-size: 0.85rem !important; + } +} +@media screen and (min-width: 1472px) { + .is-size-1-fullhd { + font-size: 3rem !important; + } + .is-size-2-fullhd { + font-size: 2.5rem !important; + } + .is-size-3-fullhd { + font-size: 2rem !important; + } + .is-size-4-fullhd { + font-size: 1.5rem !important; + } + .is-size-5-fullhd { + font-size: 1.25rem !important; + } + .is-size-6-fullhd { + font-size: 1rem !important; + } + .is-size-7-fullhd { + font-size: 0.85rem !important; + } +} +.has-text-centered { + text-align: center !important; +} +.has-text-justified { + text-align: justify !important; +} +.has-text-left { + text-align: left !important; +} +.has-text-right { + text-align: right !important; +} +@media screen and (max-width: 768px) { + .has-text-centered-mobile { + text-align: center !important; + } +} +@media screen and (min-width: 769px), print { + .has-text-centered-tablet { + text-align: center !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .has-text-centered-tablet-only { + text-align: center !important; + } +} +@media screen and (max-width: 1087px) { + .has-text-centered-touch { + text-align: center !important; + } +} +@media screen and (min-width: 1088px) { + .has-text-centered-desktop { + text-align: center !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .has-text-centered-desktop-only { + text-align: center !important; + } +} +@media screen and (min-width: 1280px) { + .has-text-centered-widescreen { + text-align: center !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .has-text-centered-widescreen-only { + text-align: center !important; + } +} +@media screen and (min-width: 1472px) { + .has-text-centered-fullhd { + text-align: center !important; + } +} +@media screen and (max-width: 768px) { + .has-text-justified-mobile { + text-align: justify !important; + } +} +@media screen and (min-width: 769px), print { + .has-text-justified-tablet { + text-align: justify !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .has-text-justified-tablet-only { + text-align: justify !important; + } +} +@media screen and (max-width: 1087px) { + .has-text-justified-touch { + text-align: justify !important; + } +} +@media screen and (min-width: 1088px) { + .has-text-justified-desktop { + text-align: justify !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .has-text-justified-desktop-only { + text-align: justify !important; + } +} +@media screen and (min-width: 1280px) { + .has-text-justified-widescreen { + text-align: justify !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .has-text-justified-widescreen-only { + text-align: justify !important; + } +} +@media screen and (min-width: 1472px) { + .has-text-justified-fullhd { + text-align: justify !important; + } +} +@media screen and (max-width: 768px) { + .has-text-left-mobile { + text-align: left !important; + } +} +@media screen and (min-width: 769px), print { + .has-text-left-tablet { + text-align: left !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .has-text-left-tablet-only { + text-align: left !important; + } +} +@media screen and (max-width: 1087px) { + .has-text-left-touch { + text-align: left !important; + } +} +@media screen and (min-width: 1088px) { + .has-text-left-desktop { + text-align: left !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .has-text-left-desktop-only { + text-align: left !important; + } +} +@media screen and (min-width: 1280px) { + .has-text-left-widescreen { + text-align: left !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .has-text-left-widescreen-only { + text-align: left !important; + } +} +@media screen and (min-width: 1472px) { + .has-text-left-fullhd { + text-align: left !important; + } +} +@media screen and (max-width: 768px) { + .has-text-right-mobile { + text-align: right !important; + } +} +@media screen and (min-width: 769px), print { + .has-text-right-tablet { + text-align: right !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .has-text-right-tablet-only { + text-align: right !important; + } +} +@media screen and (max-width: 1087px) { + .has-text-right-touch { + text-align: right !important; + } +} +@media screen and (min-width: 1088px) { + .has-text-right-desktop { + text-align: right !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .has-text-right-desktop-only { + text-align: right !important; + } +} +@media screen and (min-width: 1280px) { + .has-text-right-widescreen { + text-align: right !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .has-text-right-widescreen-only { + text-align: right !important; + } +} +@media screen and (min-width: 1472px) { + .has-text-right-fullhd { + text-align: right !important; + } +} +.is-capitalized { + text-transform: capitalize !important; +} +.is-lowercase { + text-transform: lowercase !important; +} +.is-uppercase, +article.media .categories { + text-transform: uppercase !important; +} +.is-italic { + font-style: italic !important; +} +.has-text-white { + color: #fff !important; +} +a.has-text-white:hover, +a.has-text-white:focus { + color: #e6e6e6 !important; +} +.has-background-white { + background-color: #fff !important; +} +.has-text-black { + color: #000 !important; +} +a.has-text-black:hover, +a.has-text-black:focus { + color: #000 !important; +} +.has-background-black { + background-color: #000 !important; +} +.has-text-light { + color: #f5f5f5 !important; +} +a.has-text-light:hover, +a.has-text-light:focus { + color: #dbdbdb !important; +} +.has-background-light { + background-color: #f5f5f5 !important; +} +.has-text-dark { + color: #363636 !important; +} +a.has-text-dark:hover, +a.has-text-dark:focus { + color: #1c1c1c !important; +} +.has-background-dark { + background-color: #363636 !important; +} +.has-text-primary { + color: #fcee09 !important; +} +a.has-text-primary:hover, +a.has-text-primary:focus { + color: #cfc403 !important; +} +.has-background-primary { + background-color: #fcee09 !important; +} +.has-text-link { + color: #02d7f2 !important; +} +a.has-text-link:hover, +a.has-text-link:focus { + color: #02aabf !important; +} +.has-background-link { + background-color: #02d7f2 !important; +} +.has-text-info { + color: #02d7f2 !important; +} +a.has-text-info:hover, +a.has-text-info:focus { + color: #02aabf !important; +} +.has-background-info { + background-color: #02d7f2 !important; +} +.has-text-success { + color: #00ff41 !important; +} +a.has-text-success:hover, +a.has-text-success:focus { + color: #00cc34 !important; +} +.has-background-success { + background-color: #00ff41 !important; +} +.has-text-warning { + color: #ff8e3c !important; +} +a.has-text-warning:hover, +a.has-text-warning:focus { + color: #ff7009 !important; +} +.has-background-warning { + background-color: #ff8e3c !important; +} +.has-text-danger { + color: #ff003c !important; +} +a.has-text-danger:hover, +a.has-text-danger:focus { + color: #cc0030 !important; +} +.has-background-danger { + background-color: #ff003c !important; +} +.has-text-grey-lightest { + color: #ededed !important; +} +a.has-text-grey-lightest:hover, +a.has-text-grey-lightest:focus { + color: #d4d4d4 !important; +} +.has-background-grey-lightest { + background-color: #ededed !important; +} +.has-text-black-bis { + color: #050a0e !important; +} +.has-background-black-bis { + background-color: #050a0e !important; +} +.has-text-black-ter { + color: #242424 !important; +} +.has-background-black-ter { + background-color: #242424 !important; +} +.has-text-grey-darker { + color: #363636 !important; +} +.has-background-grey-darker { + background-color: #363636 !important; +} +.has-text-grey-dark { + color: #4a4a4a !important; +} +.has-background-grey-dark { + background-color: #4a4a4a !important; +} +.has-text-grey, +.article-licensing .licensing-title a { + color: #848484 !important; +} +.has-background-grey { + background-color: #848484 !important; +} +.has-text-grey-light { + color: #b5b5b5 !important; +} +.has-background-grey-light { + background-color: #b5b5b5 !important; +} +.has-text-grey-lighter { + color: #dbdbdb !important; +} +.has-background-grey-lighter { + background-color: #dbdbdb !important; +} +.has-text-white-ter { + color: #f5f5f5 !important; +} +.has-background-white-ter { + background-color: #f5f5f5 !important; +} +.has-text-white-bis { + color: #cdcdcd !important; +} +.has-background-white-bis { + background-color: #cdcdcd !important; +} +.has-text-weight-light { + font-weight: 300 !important; +} +.has-text-weight-normal { + font-weight: 400 !important; +} +.has-text-weight-medium { + font-weight: 500 !important; +} +.has-text-weight-semibold { + font-weight: 600 !important; +} +.has-text-weight-bold { + font-weight: 700 !important; +} +.is-family-primary { + font-family: 'Oxanium', Ubuntu, Roboto, 'Open Sans', 'Microsoft YaHei', sans-serif !important; +} +.is-family-secondary { + font-family: 'Oxanium', Ubuntu, Roboto, 'Open Sans', 'Microsoft YaHei', sans-serif !important; +} +.is-family-sans-serif { + font-family: 'Oxanium', Ubuntu, Roboto, 'Open Sans', 'Microsoft YaHei', sans-serif !important; +} +.is-family-monospace { + font-family: monospace !important; +} +.is-family-code { + font-family: 'Roboto Mono', monospace, 'Microsoft YaHei' !important; +} +.is-block { + display: block !important; +} +@media screen and (max-width: 768px) { + .is-block-mobile { + display: block !important; + } +} +@media screen and (min-width: 769px), print { + .is-block-tablet { + display: block !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-block-tablet-only { + display: block !important; + } +} +@media screen and (max-width: 1087px) { + .is-block-touch { + display: block !important; + } +} +@media screen and (min-width: 1088px) { + .is-block-desktop { + display: block !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-block-desktop-only { + display: block !important; + } +} +@media screen and (min-width: 1280px) { + .is-block-widescreen { + display: block !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-block-widescreen-only { + display: block !important; + } +} +@media screen and (min-width: 1472px) { + .is-block-fullhd { + display: block !important; + } +} +.is-flex { + display: flex !important; +} +@media screen and (max-width: 768px) { + .is-flex-mobile { + display: flex !important; + } +} +@media screen and (min-width: 769px), print { + .is-flex-tablet { + display: flex !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-flex-tablet-only { + display: flex !important; + } +} +@media screen and (max-width: 1087px) { + .is-flex-touch { + display: flex !important; + } +} +@media screen and (min-width: 1088px) { + .is-flex-desktop { + display: flex !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-flex-desktop-only { + display: flex !important; + } +} +@media screen and (min-width: 1280px) { + .is-flex-widescreen { + display: flex !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-flex-widescreen-only { + display: flex !important; + } +} +@media screen and (min-width: 1472px) { + .is-flex-fullhd { + display: flex !important; + } +} +.is-inline { + display: inline !important; +} +@media screen and (max-width: 768px) { + .is-inline-mobile { + display: inline !important; + } +} +@media screen and (min-width: 769px), print { + .is-inline-tablet { + display: inline !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-inline-tablet-only { + display: inline !important; + } +} +@media screen and (max-width: 1087px) { + .is-inline-touch { + display: inline !important; + } +} +@media screen and (min-width: 1088px) { + .is-inline-desktop { + display: inline !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-inline-desktop-only { + display: inline !important; + } +} +@media screen and (min-width: 1280px) { + .is-inline-widescreen { + display: inline !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-inline-widescreen-only { + display: inline !important; + } +} +@media screen and (min-width: 1472px) { + .is-inline-fullhd { + display: inline !important; + } +} +.is-inline-block { + display: inline-block !important; +} +@media screen and (max-width: 768px) { + .is-inline-block-mobile { + display: inline-block !important; + } +} +@media screen and (min-width: 769px), print { + .is-inline-block-tablet { + display: inline-block !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-inline-block-tablet-only { + display: inline-block !important; + } +} +@media screen and (max-width: 1087px) { + .is-inline-block-touch { + display: inline-block !important; + } +} +@media screen and (min-width: 1088px) { + .is-inline-block-desktop { + display: inline-block !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-inline-block-desktop-only { + display: inline-block !important; + } +} +@media screen and (min-width: 1280px) { + .is-inline-block-widescreen { + display: inline-block !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-inline-block-widescreen-only { + display: inline-block !important; + } +} +@media screen and (min-width: 1472px) { + .is-inline-block-fullhd { + display: inline-block !important; + } +} +.is-inline-flex { + display: inline-flex !important; +} +@media screen and (max-width: 768px) { + .is-inline-flex-mobile { + display: inline-flex !important; + } +} +@media screen and (min-width: 769px), print { + .is-inline-flex-tablet { + display: inline-flex !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-inline-flex-tablet-only { + display: inline-flex !important; + } +} +@media screen and (max-width: 1087px) { + .is-inline-flex-touch { + display: inline-flex !important; + } +} +@media screen and (min-width: 1088px) { + .is-inline-flex-desktop { + display: inline-flex !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-inline-flex-desktop-only { + display: inline-flex !important; + } +} +@media screen and (min-width: 1280px) { + .is-inline-flex-widescreen { + display: inline-flex !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-inline-flex-widescreen-only { + display: inline-flex !important; + } +} +@media screen and (min-width: 1472px) { + .is-inline-flex-fullhd { + display: inline-flex !important; + } +} +.is-hidden { + display: none !important; +} +.is-sr-only { + border: none !important; + clip: rect(0, 0, 0, 0) !important; + height: 0.01em !important; + overflow: hidden !important; + padding: 0 !important; + position: absolute !important; + white-space: nowrap !important; + width: 0.01em !important; +} +@media screen and (max-width: 768px) { + .is-hidden-mobile { + display: none !important; + } +} +@media screen and (min-width: 769px), print { + .is-hidden-tablet { + display: none !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-hidden-tablet-only { + display: none !important; + } +} +@media screen and (max-width: 1087px) { + .is-hidden-touch { + display: none !important; + } +} +@media screen and (min-width: 1088px) { + .is-hidden-desktop { + display: none !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-hidden-desktop-only { + display: none !important; + } +} +@media screen and (min-width: 1280px) { + .is-hidden-widescreen { + display: none !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-hidden-widescreen-only { + display: none !important; + } +} +@media screen and (min-width: 1472px) { + .is-hidden-fullhd { + display: none !important; + } +} +.is-invisible { + visibility: hidden !important; +} +@media screen and (max-width: 768px) { + .is-invisible-mobile { + visibility: hidden !important; + } +} +@media screen and (min-width: 769px), print { + .is-invisible-tablet { + visibility: hidden !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-invisible-tablet-only { + visibility: hidden !important; + } +} +@media screen and (max-width: 1087px) { + .is-invisible-touch { + visibility: hidden !important; + } +} +@media screen and (min-width: 1088px) { + .is-invisible-desktop { + visibility: hidden !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-invisible-desktop-only { + visibility: hidden !important; + } +} +@media screen and (min-width: 1280px) { + .is-invisible-widescreen { + visibility: hidden !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-invisible-widescreen-only { + visibility: hidden !important; + } +} +@media screen and (min-width: 1472px) { + .is-invisible-fullhd { + visibility: hidden !important; + } +} +.is-marginless { + margin: 0 !important; +} +.is-paddingless { + padding: 0 !important; +} +.is-radiusless { + border-radius: 0 !important; +} +.is-shadowless { + box-shadow: none !important; +} +.is-relative { + position: relative !important; +} +.breadcrumb { + font-size: 1rem; + white-space: nowrap; +} +.breadcrumb a { + align-items: center; + color: #02d7f2; + display: flex; + justify-content: center; + padding: 0 0.75em; +} +.breadcrumb a:hover { + color: #fcee09; +} +.breadcrumb li { + align-items: center; + display: flex; +} +.breadcrumb li:first-child a { + padding-left: 0; +} +.breadcrumb li.is-active a { + color: #fcee09; + cursor: default; + pointer-events: none; +} +.breadcrumb li + li::before { + color: #b5b5b5; + content: "\0002f"; +} +.breadcrumb ul, +.breadcrumb ol { + align-items: flex-start; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; +} +.breadcrumb .icon:first-child { + margin-right: 0.5em; +} +.breadcrumb .icon:last-child { + margin-left: 0.5em; +} +.breadcrumb.is-centered ol, +.breadcrumb.is-centered ul { + justify-content: center; +} +.breadcrumb.is-right ol, +.breadcrumb.is-right ul { + justify-content: flex-end; +} +.breadcrumb.is-small { + font-size: 0.75rem; +} +.breadcrumb.is-medium { + font-size: 1.25rem; +} +.breadcrumb.is-large { + font-size: 1.5rem; +} +.breadcrumb.has-arrow-separator li + li::before { + content: "\02192"; +} +.breadcrumb.has-bullet-separator li + li::before { + content: "\02022"; +} +.breadcrumb.has-dot-separator li + li::before { + content: "\000b7"; +} +.breadcrumb.has-succeeds-separator li + li::before { + content: "\0227B"; +} +.card { + background-color: transparent; + box-shadow: none, 0 0 1px rgba(0,0,0,0.1); + color: #cdcdcd; + max-width: 100%; + position: relative; +} +.card-header { + background-color: transparent; + align-items: stretch; + box-shadow: 0 0.125em 0.25em rgba(0,0,0,0.1); + display: flex; +} +.card-header-title { + align-items: center; + color: #fcee09; + display: flex; + flex-grow: 1; + font-weight: 700; + padding: 0.75rem 1rem; +} +.card-header-title.is-centered { + justify-content: center; +} +.card-header-icon { + align-items: center; + cursor: pointer; + display: flex; + justify-content: center; + padding: 0.75rem 1rem; +} +.card-image { + display: block; + position: relative; +} +.card-content { + background-color: transparent; + padding: 1.5rem; +} +.card-footer { + background-color: transparent; + border-top: 1px solid #ededed; + align-items: stretch; + display: flex; +} +.card-footer-item { + align-items: center; + display: flex; + flex-basis: 0; + flex-grow: 1; + flex-shrink: 0; + justify-content: center; + padding: 0.75rem; +} +.card-footer-item:not(:last-child) { + border-right: 1px solid #ededed; +} +.card .media:not(:last-child) { + margin-bottom: 0.75rem; +} +.dropdown { + display: inline-flex; + position: relative; + vertical-align: top; +} +.dropdown.is-active .dropdown-menu, +.dropdown.is-hoverable:hover .dropdown-menu { + display: block; +} +.dropdown.is-right .dropdown-menu { + left: auto; + right: 0; +} +.dropdown.is-up .dropdown-menu { + bottom: 100%; + padding-bottom: 4px; + padding-top: initial; + top: auto; +} +.dropdown-menu { + display: none; + left: 0; + min-width: 12rem; + padding-top: 4px; + position: absolute; + top: 100%; + z-index: 20; +} +.dropdown-content { + background-color: #000; + border-radius: 0; + box-shadow: 0 0.5em 1em -0.125em rgba(0,0,0,0.1), 0 0px 0 1px rgba(0,0,0,0.02); + padding-bottom: 0.5rem; + padding-top: 0.5rem; +} +.dropdown-item { + color: #cdcdcd; + display: block; + font-size: 0.875rem; + line-height: 1.5; + padding: 0.375rem 1rem; + position: relative; +} +a.dropdown-item, +button.dropdown-item { + padding-right: 3rem; + text-align: left; + white-space: nowrap; + width: 100%; +} +a.dropdown-item:hover, +button.dropdown-item:hover { + background-color: #f5f5f5; + color: #000; +} +a.dropdown-item.is-active, +button.dropdown-item.is-active { + background-color: #02d7f2; + color: rgba(0,0,0,0.7); +} +.dropdown-divider { + background-color: #ededed; + border: none; + display: block; + height: 1px; + margin: 0.5rem 0; +} +.level { + align-items: center; + justify-content: space-between; +} +.level code { + border-radius: 0; +} +.level img { + display: inline-block; + vertical-align: top; +} +.level.is-mobile { + display: flex; +} +.level.is-mobile .level-left, +.level.is-mobile .level-right { + display: flex; +} +.level.is-mobile .level-left + .level-right { + margin-top: 0; +} +.level.is-mobile .level-item:not(:last-child) { + margin-bottom: 0; + margin-right: 0.75rem; +} +.level.is-mobile .level-item:not(.is-narrow) { + flex-grow: 1; +} +@media screen and (min-width: 769px), print { + .level { + display: flex; + } + .level > .level-item:not(.is-narrow) { + flex-grow: 1; + } +} +.level-item { + align-items: center; + display: flex; + flex-basis: auto; + flex-grow: 0; + flex-shrink: 0; + justify-content: center; +} +.level-item .title, +.level-item .subtitle { + margin-bottom: 0; +} +@media screen and (max-width: 768px) { + .level-item:not(:last-child) { + margin-bottom: 0.75rem; + } +} +.level-left, +.level-right { + flex-basis: auto; + flex-grow: 0; + flex-shrink: 0; +} +.level-left .level-item.is-flexible, +.level-right .level-item.is-flexible { + flex-grow: 1; +} +@media screen and (min-width: 769px), print { + .level-left .level-item:not(:last-child), + .level-right .level-item:not(:last-child) { + margin-right: 0.75rem; + } +} +.level-left { + align-items: center; + justify-content: flex-start; +} +@media screen and (max-width: 768px) { + .level-left + .level-right { + margin-top: 1.5rem; + } +} +@media screen and (min-width: 769px), print { + .level-left { + display: flex; + } +} +.level-right { + align-items: center; + justify-content: flex-end; +} +@media screen and (min-width: 769px), print { + .level-right { + display: flex; + } +} +.list { + background-color: #000; + border-radius: 0; + box-shadow: 0 2px 3px rgba(0,0,0,0.1), 0 0 0 1px rgba(0,0,0,0.1); +} +.list-item { + display: block; + padding: 0.5em 1em; +} +.list-item:not(a) { + color: #cdcdcd; +} +.list-item:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.list-item:last-child { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} +.list-item:not(:last-child) { + border-bottom: 1px solid #dbdbdb; +} +.list-item.is-active { + background-color: #02d7f2; + color: rgba(0,0,0,0.7); +} +a.list-item { + background-color: #f5f5f5; + cursor: pointer; +} +.media { + align-items: flex-start; + display: flex; + text-align: left; +} +.media .content:not(:last-child) { + margin-bottom: 0.75rem; +} +.media .media { + border-top: 1px solid rgba(219,219,219,0.5); + display: flex; + padding-top: 0.75rem; +} +.media .media .content:not(:last-child), +.media .media .control:not(:last-child) { + margin-bottom: 0.5rem; +} +.media .media .media { + padding-top: 0.5rem; +} +.media .media .media + .media { + margin-top: 0.5rem; +} +.media + .media { + border-top: 1px solid rgba(219,219,219,0.5); + margin-top: 1rem; + padding-top: 1rem; +} +.media.is-large + .media { + margin-top: 1.5rem; + padding-top: 1.5rem; +} +.media-left, +.media-right { + flex-basis: auto; + flex-grow: 0; + flex-shrink: 0; +} +.media-left { + margin-right: 1rem; +} +.media-right { + margin-left: 1rem; +} +.media-content { + flex-basis: auto; + flex-grow: 1; + flex-shrink: 1; + text-align: left; +} +@media screen and (max-width: 768px) { + .media-content { + overflow-x: auto; + } +} +.menu { + font-size: 1rem; +} +.menu.is-small { + font-size: 0.75rem; +} +.menu.is-medium { + font-size: 1.25rem; +} +.menu.is-large { + font-size: 1.5rem; +} +.menu-list { + line-height: 1.25; +} +.menu-list a { + border-radius: 0; + color: #cdcdcd; + display: block; + padding: 0.5em 0.75em; +} +.menu-list a:hover { + background-color: #fcee09; + color: #000; +} +.menu-list a.is-active { + background-color: #fcee09; + color: #000; +} +.menu-list li ul { + border-left: 1px solid #cdcdcd; + margin: 0.75em; + padding-left: 0.75em; +} +.menu-label { + color: #02d7f2; + font-size: 0.75em; + letter-spacing: 0.1em; + text-transform: uppercase; +} +.menu-label:not(:first-child) { + margin-top: 1em; +} +.menu-label:not(:last-child) { + margin-bottom: 1em; +} +.message { + background-color: #f5f5f5; + border-radius: 0; + font-size: 1rem; +} +.message strong { + color: currentColor; +} +.message a:not(.button):not(.tag):not(.dropdown-item) { + color: currentColor; + text-decoration: underline; +} +.message.is-small { + font-size: 0.75rem; +} +.message.is-medium { + font-size: 1.25rem; +} +.message.is-large { + font-size: 1.5rem; +} +.message.is-white { + background-color: #fff; +} +.message.is-white .message-header { + background-color: #fff; + color: #000; +} +.message.is-white .message-body { + border-color: #fff; +} +.message.is-black { + background-color: #fafafa; +} +.message.is-black .message-header { + background-color: #000; + color: #fff; +} +.message.is-black .message-body { + border-color: #000; +} +.message.is-light { + background-color: #fafafa; +} +.message.is-light .message-header { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.message.is-light .message-body { + border-color: #f5f5f5; +} +.message.is-dark { + background-color: #fafafa; +} +.message.is-dark .message-header { + background-color: #363636; + color: #fff; +} +.message.is-dark .message-body { + border-color: #363636; +} +.message.is-primary { + background-color: #fffeeb; +} +.message.is-primary .message-header { + background-color: #fcee09; + color: #121617; +} +.message.is-primary .message-body { + border-color: #fcee09; + color: #928a02; +} +.message.is-link { + background-color: #ebfdff; +} +.message.is-link .message-header { + background-color: #02d7f2; + color: rgba(0,0,0,0.7); +} +.message.is-link .message-body { + border-color: #02d7f2; + color: #018293; +} +.message.is-info { + background-color: #ebfdff; +} +.message.is-info .message-header { + background-color: #02d7f2; + color: #121617; +} +.message.is-info .message-body { + border-color: #02d7f2; + color: #018293; +} +.message.is-success { + background-color: #ebfff0; +} +.message.is-success .message-header { + background-color: #00ff41; + color: #121617; +} +.message.is-success .message-body { + border-color: #00ff41; + color: #009426; +} +.message.is-warning { + background-color: #fff3eb; +} +.message.is-warning .message-header { + background-color: #ff8e3c; + color: #121617; +} +.message.is-warning .message-body { + border-color: #ff8e3c; + color: #a84700; +} +.message.is-danger { + background-color: #ffebef; +} +.message.is-danger .message-header { + background-color: #ff003c; + color: #121617; +} +.message.is-danger .message-body { + border-color: #ff003c; + color: #eb0037; +} +.message.is-grey-lightest { + background-color: #fafafa; +} +.message.is-grey-lightest .message-header { + background-color: #ededed; + color: #363636; +} +.message.is-grey-lightest .message-body { + border-color: #ededed; +} +.message-header { + align-items: center; + background-color: #cdcdcd; + border-radius: 0 0 0 0; + color: rgba(0,0,0,0.7); + display: flex; + font-weight: 700; + justify-content: space-between; + line-height: 1.25; + padding: 0.75em 1em; + position: relative; +} +.message-header .delete { + flex-grow: 0; + flex-shrink: 0; + margin-left: 0.75em; +} +.message-header + .message-body { + border-width: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.message-body { + border-color: #dbdbdb; + border-radius: 0; + border-style: solid; + border-width: 0 0 0 4px; + color: #cdcdcd; + padding: 1.25em 1.5em; +} +.message-body code, +.message-body pre { + background-color: #000; +} +.message-body pre code { + background-color: transparent; +} +.modal { + align-items: center; + display: none; + flex-direction: column; + justify-content: center; + overflow: hidden; + position: fixed; + z-index: 40; +} +.modal.is-active { + display: flex; +} +.modal-background { + background-color: rgba(0,0,0,0.86); +} +.modal-content, +.modal-card { + margin: 0 20px; + max-height: calc(100vh - 160px); + overflow: auto; + position: relative; + width: 100%; +} +@media screen and (min-width: 769px), print { + .modal-content, + .modal-card { + margin: 0 auto; + max-height: calc(100vh - 40px); + width: 640px; + } +} +.modal-close { + background: none; + height: 40px; + position: fixed; + right: 20px; + top: 20px; + width: 40px; +} +.modal-card { + display: flex; + flex-direction: column; + max-height: calc(100vh - 40px); + overflow: hidden; + -ms-overflow-y: visible; +} +.modal-card-head, +.modal-card-foot { + align-items: center; + background-color: #f5f5f5; + display: flex; + flex-shrink: 0; + justify-content: flex-start; + padding: 20px; + position: relative; +} +.modal-card-head { + border-bottom: 1px solid #dbdbdb; + border-top-left-radius: 6px; + border-top-right-radius: 6px; +} +.modal-card-title { + color: #fcee09; + flex-grow: 1; + flex-shrink: 0; + font-size: 1.5rem; + line-height: 1; +} +.modal-card-foot { + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + border-top: 1px solid #dbdbdb; +} +.modal-card-foot .button:not(:last-child) { + margin-right: 0.5em; +} +.modal-card-body { + -webkit-overflow-scrolling: touch; + background-color: #000; + flex-grow: 1; + flex-shrink: 1; + overflow: auto; + padding: 20px; +} +.navbar { + background-color: #fcee09; + min-height: 3.25rem; + position: relative; + z-index: 30; +} +.navbar.is-white { + background-color: #fff; + color: #000; +} +.navbar.is-white .navbar-brand > .navbar-item, +.navbar.is-white .navbar-brand .navbar-link { + color: #000; +} +.navbar.is-white .navbar-brand > a.navbar-item:focus, +.navbar.is-white .navbar-brand .navbar-link:focus, +.navbar.is-white .navbar-brand > a.navbar-item:hover, +.navbar.is-white .navbar-brand .navbar-link:hover, +.navbar.is-white .navbar-brand > a.navbar-item.is-active, +.navbar.is-white .navbar-brand .navbar-link.is-active { + background-color: #f2f2f2; + color: #000; +} +.navbar.is-white .navbar-brand .navbar-link::after { + border-color: #000; +} +.navbar.is-white .navbar-burger { + color: #000; +} +@media screen and (min-width: 1088px) { + .navbar.is-white .navbar-start > .navbar-item, + .navbar.is-white .navbar-end > .navbar-item, + .navbar.is-white .navbar-start .navbar-link, + .navbar.is-white .navbar-end .navbar-link { + color: #000; + } + .navbar.is-white .navbar-start > a.navbar-item:focus, + .navbar.is-white .navbar-end > a.navbar-item:focus, + .navbar.is-white .navbar-start .navbar-link:focus, + .navbar.is-white .navbar-end .navbar-link:focus, + .navbar.is-white .navbar-start > a.navbar-item:hover, + .navbar.is-white .navbar-end > a.navbar-item:hover, + .navbar.is-white .navbar-start .navbar-link:hover, + .navbar.is-white .navbar-end .navbar-link:hover, + .navbar.is-white .navbar-start > a.navbar-item.is-active, + .navbar.is-white .navbar-end > a.navbar-item.is-active, + .navbar.is-white .navbar-start .navbar-link.is-active, + .navbar.is-white .navbar-end .navbar-link.is-active { + background-color: #f2f2f2; + color: #000; + } + .navbar.is-white .navbar-start .navbar-link::after, + .navbar.is-white .navbar-end .navbar-link::after { + border-color: #000; + } + .navbar.is-white .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-white .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-white .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #f2f2f2; + color: #000; + } + .navbar.is-white .navbar-dropdown a.navbar-item.is-active { + background-color: #fff; + color: #000; + } +} +.navbar.is-black { + background-color: #000; + color: #fff; +} +.navbar.is-black .navbar-brand > .navbar-item, +.navbar.is-black .navbar-brand .navbar-link { + color: #fff; +} +.navbar.is-black .navbar-brand > a.navbar-item:focus, +.navbar.is-black .navbar-brand .navbar-link:focus, +.navbar.is-black .navbar-brand > a.navbar-item:hover, +.navbar.is-black .navbar-brand .navbar-link:hover, +.navbar.is-black .navbar-brand > a.navbar-item.is-active, +.navbar.is-black .navbar-brand .navbar-link.is-active { + background-color: #000; + color: #fff; +} +.navbar.is-black .navbar-brand .navbar-link::after { + border-color: #fff; +} +.navbar.is-black .navbar-burger { + color: #fff; +} +@media screen and (min-width: 1088px) { + .navbar.is-black .navbar-start > .navbar-item, + .navbar.is-black .navbar-end > .navbar-item, + .navbar.is-black .navbar-start .navbar-link, + .navbar.is-black .navbar-end .navbar-link { + color: #fff; + } + .navbar.is-black .navbar-start > a.navbar-item:focus, + .navbar.is-black .navbar-end > a.navbar-item:focus, + .navbar.is-black .navbar-start .navbar-link:focus, + .navbar.is-black .navbar-end .navbar-link:focus, + .navbar.is-black .navbar-start > a.navbar-item:hover, + .navbar.is-black .navbar-end > a.navbar-item:hover, + .navbar.is-black .navbar-start .navbar-link:hover, + .navbar.is-black .navbar-end .navbar-link:hover, + .navbar.is-black .navbar-start > a.navbar-item.is-active, + .navbar.is-black .navbar-end > a.navbar-item.is-active, + .navbar.is-black .navbar-start .navbar-link.is-active, + .navbar.is-black .navbar-end .navbar-link.is-active { + background-color: #000; + color: #fff; + } + .navbar.is-black .navbar-start .navbar-link::after, + .navbar.is-black .navbar-end .navbar-link::after { + border-color: #fff; + } + .navbar.is-black .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-black .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-black .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #000; + color: #fff; + } + .navbar.is-black .navbar-dropdown a.navbar-item.is-active { + background-color: #000; + color: #fff; + } +} +.navbar.is-light { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.navbar.is-light .navbar-brand > .navbar-item, +.navbar.is-light .navbar-brand .navbar-link { + color: rgba(0,0,0,0.7); +} +.navbar.is-light .navbar-brand > a.navbar-item:focus, +.navbar.is-light .navbar-brand .navbar-link:focus, +.navbar.is-light .navbar-brand > a.navbar-item:hover, +.navbar.is-light .navbar-brand .navbar-link:hover, +.navbar.is-light .navbar-brand > a.navbar-item.is-active, +.navbar.is-light .navbar-brand .navbar-link.is-active { + background-color: #e8e8e8; + color: rgba(0,0,0,0.7); +} +.navbar.is-light .navbar-brand .navbar-link::after { + border-color: rgba(0,0,0,0.7); +} +.navbar.is-light .navbar-burger { + color: rgba(0,0,0,0.7); +} +@media screen and (min-width: 1088px) { + .navbar.is-light .navbar-start > .navbar-item, + .navbar.is-light .navbar-end > .navbar-item, + .navbar.is-light .navbar-start .navbar-link, + .navbar.is-light .navbar-end .navbar-link { + color: rgba(0,0,0,0.7); + } + .navbar.is-light .navbar-start > a.navbar-item:focus, + .navbar.is-light .navbar-end > a.navbar-item:focus, + .navbar.is-light .navbar-start .navbar-link:focus, + .navbar.is-light .navbar-end .navbar-link:focus, + .navbar.is-light .navbar-start > a.navbar-item:hover, + .navbar.is-light .navbar-end > a.navbar-item:hover, + .navbar.is-light .navbar-start .navbar-link:hover, + .navbar.is-light .navbar-end .navbar-link:hover, + .navbar.is-light .navbar-start > a.navbar-item.is-active, + .navbar.is-light .navbar-end > a.navbar-item.is-active, + .navbar.is-light .navbar-start .navbar-link.is-active, + .navbar.is-light .navbar-end .navbar-link.is-active { + background-color: #e8e8e8; + color: rgba(0,0,0,0.7); + } + .navbar.is-light .navbar-start .navbar-link::after, + .navbar.is-light .navbar-end .navbar-link::after { + border-color: rgba(0,0,0,0.7); + } + .navbar.is-light .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-light .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-light .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #e8e8e8; + color: rgba(0,0,0,0.7); + } + .navbar.is-light .navbar-dropdown a.navbar-item.is-active { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); + } +} +.navbar.is-dark { + background-color: #363636; + color: #fff; +} +.navbar.is-dark .navbar-brand > .navbar-item, +.navbar.is-dark .navbar-brand .navbar-link { + color: #fff; +} +.navbar.is-dark .navbar-brand > a.navbar-item:focus, +.navbar.is-dark .navbar-brand .navbar-link:focus, +.navbar.is-dark .navbar-brand > a.navbar-item:hover, +.navbar.is-dark .navbar-brand .navbar-link:hover, +.navbar.is-dark .navbar-brand > a.navbar-item.is-active, +.navbar.is-dark .navbar-brand .navbar-link.is-active { + background-color: #292929; + color: #fff; +} +.navbar.is-dark .navbar-brand .navbar-link::after { + border-color: #fff; +} +.navbar.is-dark .navbar-burger { + color: #fff; +} +@media screen and (min-width: 1088px) { + .navbar.is-dark .navbar-start > .navbar-item, + .navbar.is-dark .navbar-end > .navbar-item, + .navbar.is-dark .navbar-start .navbar-link, + .navbar.is-dark .navbar-end .navbar-link { + color: #fff; + } + .navbar.is-dark .navbar-start > a.navbar-item:focus, + .navbar.is-dark .navbar-end > a.navbar-item:focus, + .navbar.is-dark .navbar-start .navbar-link:focus, + .navbar.is-dark .navbar-end .navbar-link:focus, + .navbar.is-dark .navbar-start > a.navbar-item:hover, + .navbar.is-dark .navbar-end > a.navbar-item:hover, + .navbar.is-dark .navbar-start .navbar-link:hover, + .navbar.is-dark .navbar-end .navbar-link:hover, + .navbar.is-dark .navbar-start > a.navbar-item.is-active, + .navbar.is-dark .navbar-end > a.navbar-item.is-active, + .navbar.is-dark .navbar-start .navbar-link.is-active, + .navbar.is-dark .navbar-end .navbar-link.is-active { + background-color: #292929; + color: #fff; + } + .navbar.is-dark .navbar-start .navbar-link::after, + .navbar.is-dark .navbar-end .navbar-link::after { + border-color: #fff; + } + .navbar.is-dark .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-dark .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-dark .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #292929; + color: #fff; + } + .navbar.is-dark .navbar-dropdown a.navbar-item.is-active { + background-color: #363636; + color: #fff; + } +} +.navbar.is-primary { + background-color: #fcee09; + color: #121617; +} +.navbar.is-primary .navbar-brand > .navbar-item, +.navbar.is-primary .navbar-brand .navbar-link { + color: #121617; +} +.navbar.is-primary .navbar-brand > a.navbar-item:focus, +.navbar.is-primary .navbar-brand .navbar-link:focus, +.navbar.is-primary .navbar-brand > a.navbar-item:hover, +.navbar.is-primary .navbar-brand .navbar-link:hover, +.navbar.is-primary .navbar-brand > a.navbar-item.is-active, +.navbar.is-primary .navbar-brand .navbar-link.is-active { + background-color: #e9db03; + color: #121617; +} +.navbar.is-primary .navbar-brand .navbar-link::after { + border-color: #121617; +} +.navbar.is-primary .navbar-burger { + color: #121617; +} +@media screen and (min-width: 1088px) { + .navbar.is-primary .navbar-start > .navbar-item, + .navbar.is-primary .navbar-end > .navbar-item, + .navbar.is-primary .navbar-start .navbar-link, + .navbar.is-primary .navbar-end .navbar-link { + color: #121617; + } + .navbar.is-primary .navbar-start > a.navbar-item:focus, + .navbar.is-primary .navbar-end > a.navbar-item:focus, + .navbar.is-primary .navbar-start .navbar-link:focus, + .navbar.is-primary .navbar-end .navbar-link:focus, + .navbar.is-primary .navbar-start > a.navbar-item:hover, + .navbar.is-primary .navbar-end > a.navbar-item:hover, + .navbar.is-primary .navbar-start .navbar-link:hover, + .navbar.is-primary .navbar-end .navbar-link:hover, + .navbar.is-primary .navbar-start > a.navbar-item.is-active, + .navbar.is-primary .navbar-end > a.navbar-item.is-active, + .navbar.is-primary .navbar-start .navbar-link.is-active, + .navbar.is-primary .navbar-end .navbar-link.is-active { + background-color: #e9db03; + color: #121617; + } + .navbar.is-primary .navbar-start .navbar-link::after, + .navbar.is-primary .navbar-end .navbar-link::after { + border-color: #121617; + } + .navbar.is-primary .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-primary .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-primary .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #e9db03; + color: #121617; + } + .navbar.is-primary .navbar-dropdown a.navbar-item.is-active { + background-color: #fcee09; + color: #121617; + } +} +.navbar.is-link { + background-color: #02d7f2; + color: rgba(0,0,0,0.7); +} +.navbar.is-link .navbar-brand > .navbar-item, +.navbar.is-link .navbar-brand .navbar-link { + color: rgba(0,0,0,0.7); +} +.navbar.is-link .navbar-brand > a.navbar-item:focus, +.navbar.is-link .navbar-brand .navbar-link:focus, +.navbar.is-link .navbar-brand > a.navbar-item:hover, +.navbar.is-link .navbar-brand .navbar-link:hover, +.navbar.is-link .navbar-brand > a.navbar-item.is-active, +.navbar.is-link .navbar-brand .navbar-link.is-active { + background-color: #02c1d9; + color: rgba(0,0,0,0.7); +} +.navbar.is-link .navbar-brand .navbar-link::after { + border-color: rgba(0,0,0,0.7); +} +.navbar.is-link .navbar-burger { + color: rgba(0,0,0,0.7); +} +@media screen and (min-width: 1088px) { + .navbar.is-link .navbar-start > .navbar-item, + .navbar.is-link .navbar-end > .navbar-item, + .navbar.is-link .navbar-start .navbar-link, + .navbar.is-link .navbar-end .navbar-link { + color: rgba(0,0,0,0.7); + } + .navbar.is-link .navbar-start > a.navbar-item:focus, + .navbar.is-link .navbar-end > a.navbar-item:focus, + .navbar.is-link .navbar-start .navbar-link:focus, + .navbar.is-link .navbar-end .navbar-link:focus, + .navbar.is-link .navbar-start > a.navbar-item:hover, + .navbar.is-link .navbar-end > a.navbar-item:hover, + .navbar.is-link .navbar-start .navbar-link:hover, + .navbar.is-link .navbar-end .navbar-link:hover, + .navbar.is-link .navbar-start > a.navbar-item.is-active, + .navbar.is-link .navbar-end > a.navbar-item.is-active, + .navbar.is-link .navbar-start .navbar-link.is-active, + .navbar.is-link .navbar-end .navbar-link.is-active { + background-color: #02c1d9; + color: rgba(0,0,0,0.7); + } + .navbar.is-link .navbar-start .navbar-link::after, + .navbar.is-link .navbar-end .navbar-link::after { + border-color: rgba(0,0,0,0.7); + } + .navbar.is-link .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-link .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-link .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #02c1d9; + color: rgba(0,0,0,0.7); + } + .navbar.is-link .navbar-dropdown a.navbar-item.is-active { + background-color: #02d7f2; + color: rgba(0,0,0,0.7); + } +} +.navbar.is-info { + background-color: #02d7f2; + color: #121617; +} +.navbar.is-info .navbar-brand > .navbar-item, +.navbar.is-info .navbar-brand .navbar-link { + color: #121617; +} +.navbar.is-info .navbar-brand > a.navbar-item:focus, +.navbar.is-info .navbar-brand .navbar-link:focus, +.navbar.is-info .navbar-brand > a.navbar-item:hover, +.navbar.is-info .navbar-brand .navbar-link:hover, +.navbar.is-info .navbar-brand > a.navbar-item.is-active, +.navbar.is-info .navbar-brand .navbar-link.is-active { + background-color: #02c1d9; + color: #121617; +} +.navbar.is-info .navbar-brand .navbar-link::after { + border-color: #121617; +} +.navbar.is-info .navbar-burger { + color: #121617; +} +@media screen and (min-width: 1088px) { + .navbar.is-info .navbar-start > .navbar-item, + .navbar.is-info .navbar-end > .navbar-item, + .navbar.is-info .navbar-start .navbar-link, + .navbar.is-info .navbar-end .navbar-link { + color: #121617; + } + .navbar.is-info .navbar-start > a.navbar-item:focus, + .navbar.is-info .navbar-end > a.navbar-item:focus, + .navbar.is-info .navbar-start .navbar-link:focus, + .navbar.is-info .navbar-end .navbar-link:focus, + .navbar.is-info .navbar-start > a.navbar-item:hover, + .navbar.is-info .navbar-end > a.navbar-item:hover, + .navbar.is-info .navbar-start .navbar-link:hover, + .navbar.is-info .navbar-end .navbar-link:hover, + .navbar.is-info .navbar-start > a.navbar-item.is-active, + .navbar.is-info .navbar-end > a.navbar-item.is-active, + .navbar.is-info .navbar-start .navbar-link.is-active, + .navbar.is-info .navbar-end .navbar-link.is-active { + background-color: #02c1d9; + color: #121617; + } + .navbar.is-info .navbar-start .navbar-link::after, + .navbar.is-info .navbar-end .navbar-link::after { + border-color: #121617; + } + .navbar.is-info .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-info .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-info .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #02c1d9; + color: #121617; + } + .navbar.is-info .navbar-dropdown a.navbar-item.is-active { + background-color: #02d7f2; + color: #121617; + } +} +.navbar.is-success { + background-color: #00ff41; + color: #121617; +} +.navbar.is-success .navbar-brand > .navbar-item, +.navbar.is-success .navbar-brand .navbar-link { + color: #121617; +} +.navbar.is-success .navbar-brand > a.navbar-item:focus, +.navbar.is-success .navbar-brand .navbar-link:focus, +.navbar.is-success .navbar-brand > a.navbar-item:hover, +.navbar.is-success .navbar-brand .navbar-link:hover, +.navbar.is-success .navbar-brand > a.navbar-item.is-active, +.navbar.is-success .navbar-brand .navbar-link.is-active { + background-color: #00e63a; + color: #121617; +} +.navbar.is-success .navbar-brand .navbar-link::after { + border-color: #121617; +} +.navbar.is-success .navbar-burger { + color: #121617; +} +@media screen and (min-width: 1088px) { + .navbar.is-success .navbar-start > .navbar-item, + .navbar.is-success .navbar-end > .navbar-item, + .navbar.is-success .navbar-start .navbar-link, + .navbar.is-success .navbar-end .navbar-link { + color: #121617; + } + .navbar.is-success .navbar-start > a.navbar-item:focus, + .navbar.is-success .navbar-end > a.navbar-item:focus, + .navbar.is-success .navbar-start .navbar-link:focus, + .navbar.is-success .navbar-end .navbar-link:focus, + .navbar.is-success .navbar-start > a.navbar-item:hover, + .navbar.is-success .navbar-end > a.navbar-item:hover, + .navbar.is-success .navbar-start .navbar-link:hover, + .navbar.is-success .navbar-end .navbar-link:hover, + .navbar.is-success .navbar-start > a.navbar-item.is-active, + .navbar.is-success .navbar-end > a.navbar-item.is-active, + .navbar.is-success .navbar-start .navbar-link.is-active, + .navbar.is-success .navbar-end .navbar-link.is-active { + background-color: #00e63a; + color: #121617; + } + .navbar.is-success .navbar-start .navbar-link::after, + .navbar.is-success .navbar-end .navbar-link::after { + border-color: #121617; + } + .navbar.is-success .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-success .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-success .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #00e63a; + color: #121617; + } + .navbar.is-success .navbar-dropdown a.navbar-item.is-active { + background-color: #00ff41; + color: #121617; + } +} +.navbar.is-warning { + background-color: #ff8e3c; + color: #121617; +} +.navbar.is-warning .navbar-brand > .navbar-item, +.navbar.is-warning .navbar-brand .navbar-link { + color: #121617; +} +.navbar.is-warning .navbar-brand > a.navbar-item:focus, +.navbar.is-warning .navbar-brand .navbar-link:focus, +.navbar.is-warning .navbar-brand > a.navbar-item:hover, +.navbar.is-warning .navbar-brand .navbar-link:hover, +.navbar.is-warning .navbar-brand > a.navbar-item.is-active, +.navbar.is-warning .navbar-brand .navbar-link.is-active { + background-color: #ff7f22; + color: #121617; +} +.navbar.is-warning .navbar-brand .navbar-link::after { + border-color: #121617; +} +.navbar.is-warning .navbar-burger { + color: #121617; +} +@media screen and (min-width: 1088px) { + .navbar.is-warning .navbar-start > .navbar-item, + .navbar.is-warning .navbar-end > .navbar-item, + .navbar.is-warning .navbar-start .navbar-link, + .navbar.is-warning .navbar-end .navbar-link { + color: #121617; + } + .navbar.is-warning .navbar-start > a.navbar-item:focus, + .navbar.is-warning .navbar-end > a.navbar-item:focus, + .navbar.is-warning .navbar-start .navbar-link:focus, + .navbar.is-warning .navbar-end .navbar-link:focus, + .navbar.is-warning .navbar-start > a.navbar-item:hover, + .navbar.is-warning .navbar-end > a.navbar-item:hover, + .navbar.is-warning .navbar-start .navbar-link:hover, + .navbar.is-warning .navbar-end .navbar-link:hover, + .navbar.is-warning .navbar-start > a.navbar-item.is-active, + .navbar.is-warning .navbar-end > a.navbar-item.is-active, + .navbar.is-warning .navbar-start .navbar-link.is-active, + .navbar.is-warning .navbar-end .navbar-link.is-active { + background-color: #ff7f22; + color: #121617; + } + .navbar.is-warning .navbar-start .navbar-link::after, + .navbar.is-warning .navbar-end .navbar-link::after { + border-color: #121617; + } + .navbar.is-warning .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-warning .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-warning .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #ff7f22; + color: #121617; + } + .navbar.is-warning .navbar-dropdown a.navbar-item.is-active { + background-color: #ff8e3c; + color: #121617; + } +} +.navbar.is-danger { + background-color: #ff003c; + color: #121617; +} +.navbar.is-danger .navbar-brand > .navbar-item, +.navbar.is-danger .navbar-brand .navbar-link { + color: #121617; +} +.navbar.is-danger .navbar-brand > a.navbar-item:focus, +.navbar.is-danger .navbar-brand .navbar-link:focus, +.navbar.is-danger .navbar-brand > a.navbar-item:hover, +.navbar.is-danger .navbar-brand .navbar-link:hover, +.navbar.is-danger .navbar-brand > a.navbar-item.is-active, +.navbar.is-danger .navbar-brand .navbar-link.is-active { + background-color: #e60036; + color: #121617; +} +.navbar.is-danger .navbar-brand .navbar-link::after { + border-color: #121617; +} +.navbar.is-danger .navbar-burger { + color: #121617; +} +@media screen and (min-width: 1088px) { + .navbar.is-danger .navbar-start > .navbar-item, + .navbar.is-danger .navbar-end > .navbar-item, + .navbar.is-danger .navbar-start .navbar-link, + .navbar.is-danger .navbar-end .navbar-link { + color: #121617; + } + .navbar.is-danger .navbar-start > a.navbar-item:focus, + .navbar.is-danger .navbar-end > a.navbar-item:focus, + .navbar.is-danger .navbar-start .navbar-link:focus, + .navbar.is-danger .navbar-end .navbar-link:focus, + .navbar.is-danger .navbar-start > a.navbar-item:hover, + .navbar.is-danger .navbar-end > a.navbar-item:hover, + .navbar.is-danger .navbar-start .navbar-link:hover, + .navbar.is-danger .navbar-end .navbar-link:hover, + .navbar.is-danger .navbar-start > a.navbar-item.is-active, + .navbar.is-danger .navbar-end > a.navbar-item.is-active, + .navbar.is-danger .navbar-start .navbar-link.is-active, + .navbar.is-danger .navbar-end .navbar-link.is-active { + background-color: #e60036; + color: #121617; + } + .navbar.is-danger .navbar-start .navbar-link::after, + .navbar.is-danger .navbar-end .navbar-link::after { + border-color: #121617; + } + .navbar.is-danger .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-danger .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-danger .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #e60036; + color: #121617; + } + .navbar.is-danger .navbar-dropdown a.navbar-item.is-active { + background-color: #ff003c; + color: #121617; + } +} +.navbar.is-grey-lightest { + background-color: #ededed; + color: #363636; +} +.navbar.is-grey-lightest .navbar-brand > .navbar-item, +.navbar.is-grey-lightest .navbar-brand .navbar-link { + color: #363636; +} +.navbar.is-grey-lightest .navbar-brand > a.navbar-item:focus, +.navbar.is-grey-lightest .navbar-brand .navbar-link:focus, +.navbar.is-grey-lightest .navbar-brand > a.navbar-item:hover, +.navbar.is-grey-lightest .navbar-brand .navbar-link:hover, +.navbar.is-grey-lightest .navbar-brand > a.navbar-item.is-active, +.navbar.is-grey-lightest .navbar-brand .navbar-link.is-active { + background-color: #e0e0e0; + color: #363636; +} +.navbar.is-grey-lightest .navbar-brand .navbar-link::after { + border-color: #363636; +} +.navbar.is-grey-lightest .navbar-burger { + color: #363636; +} +@media screen and (min-width: 1088px) { + .navbar.is-grey-lightest .navbar-start > .navbar-item, + .navbar.is-grey-lightest .navbar-end > .navbar-item, + .navbar.is-grey-lightest .navbar-start .navbar-link, + .navbar.is-grey-lightest .navbar-end .navbar-link { + color: #363636; + } + .navbar.is-grey-lightest .navbar-start > a.navbar-item:focus, + .navbar.is-grey-lightest .navbar-end > a.navbar-item:focus, + .navbar.is-grey-lightest .navbar-start .navbar-link:focus, + .navbar.is-grey-lightest .navbar-end .navbar-link:focus, + .navbar.is-grey-lightest .navbar-start > a.navbar-item:hover, + .navbar.is-grey-lightest .navbar-end > a.navbar-item:hover, + .navbar.is-grey-lightest .navbar-start .navbar-link:hover, + .navbar.is-grey-lightest .navbar-end .navbar-link:hover, + .navbar.is-grey-lightest .navbar-start > a.navbar-item.is-active, + .navbar.is-grey-lightest .navbar-end > a.navbar-item.is-active, + .navbar.is-grey-lightest .navbar-start .navbar-link.is-active, + .navbar.is-grey-lightest .navbar-end .navbar-link.is-active { + background-color: #e0e0e0; + color: #363636; + } + .navbar.is-grey-lightest .navbar-start .navbar-link::after, + .navbar.is-grey-lightest .navbar-end .navbar-link::after { + border-color: #363636; + } + .navbar.is-grey-lightest .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-grey-lightest .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-grey-lightest .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #e0e0e0; + color: #363636; + } + .navbar.is-grey-lightest .navbar-dropdown a.navbar-item.is-active { + background-color: #ededed; + color: #363636; + } +} +.navbar > .container { + align-items: stretch; + display: flex; + min-height: 3.25rem; + width: 100%; +} +.navbar.has-shadow { + box-shadow: 0 2px 0 0 #f5f5f5; +} +.navbar.is-fixed-bottom, +.navbar.is-fixed-top { + left: 0; + position: fixed; + right: 0; + z-index: 30; +} +.navbar.is-fixed-bottom { + bottom: 0; +} +.navbar.is-fixed-bottom.has-shadow { + box-shadow: 0 -2px 0 0 #f5f5f5; +} +.navbar.is-fixed-top { + top: 0; +} +html.has-navbar-fixed-top, +body.has-navbar-fixed-top { + padding-top: 3.25rem; +} +html.has-navbar-fixed-bottom, +body.has-navbar-fixed-bottom { + padding-bottom: 3.25rem; +} +.navbar-brand, +.navbar-tabs { + align-items: stretch; + display: flex; + flex-shrink: 0; + min-height: 3.25rem; +} +.navbar-brand a.navbar-item:focus, +.navbar-brand a.navbar-item:hover { + background-color: transparent; +} +.navbar-tabs { + -webkit-overflow-scrolling: touch; + max-width: 100vw; + overflow-x: auto; + overflow-y: hidden; +} +.navbar-burger { + color: #000; + cursor: pointer; + display: block; + height: 3.25rem; + position: relative; + width: 3.25rem; + margin-left: auto; +} +.navbar-burger span { + background-color: currentColor; + display: block; + height: 1px; + left: calc(50% - 8px); + position: absolute; + transform-origin: center; + transition-duration: 86ms; + transition-property: background-color, opacity, transform; + transition-timing-function: ease-out; + width: 16px; +} +.navbar-burger span:nth-child(1) { + top: calc(50% - 6px); +} +.navbar-burger span:nth-child(2) { + top: calc(50% - 1px); +} +.navbar-burger span:nth-child(3) { + top: calc(50% + 4px); +} +.navbar-burger:hover { + background-color: rgba(0,0,0,0.05); +} +.navbar-burger.is-active span:nth-child(1) { + transform: translateY(5px) rotate(45deg); +} +.navbar-burger.is-active span:nth-child(2) { + opacity: 0; +} +.navbar-burger.is-active span:nth-child(3) { + transform: translateY(-5px) rotate(-45deg); +} +.navbar-menu { + display: none; +} +.navbar-item, +.navbar-link { + color: #000; + display: block; + line-height: 1.5; + padding: 0.5rem 0.75rem; + position: relative; +} +.navbar-item .icon:only-child, +.navbar-link .icon:only-child { + margin-left: -0.25rem; + margin-right: -0.25rem; +} +a.navbar-item, +.navbar-link { + cursor: pointer; +} +a.navbar-item:focus, +.navbar-link:focus, +a.navbar-item:focus-within, +.navbar-link:focus-within, +a.navbar-item:hover, +.navbar-link:hover, +a.navbar-item.is-active, +.navbar-link.is-active { + background-color: transparent; + color: #000; +} +.navbar-item { + display: block; + flex-grow: 0; + flex-shrink: 0; +} +.navbar-item img { + max-height: 1.75rem; +} +.navbar-item.has-dropdown { + padding: 0; +} +.navbar-item.is-expanded { + flex-grow: 1; + flex-shrink: 1; +} +.navbar-item.is-tab { + border-bottom: 1px solid transparent; + min-height: 3.25rem; + padding-bottom: calc(0.5rem - 1px); +} +.navbar-item.is-tab:focus, +.navbar-item.is-tab:hover { + background-color: transparent; + border-bottom-color: #02d7f2; +} +.navbar-item.is-tab.is-active { + background-color: transparent; + border-bottom-color: #02d7f2; + border-bottom-style: solid; + border-bottom-width: 3px; + color: #02d7f2; + padding-bottom: calc(0.5rem - 3px); +} +.navbar-content { + flex-grow: 1; + flex-shrink: 1; +} +.navbar-link:not(.is-arrowless) { + padding-right: 2.5em; +} +.navbar-link:not(.is-arrowless)::after { + border-color: #02d7f2; + margin-top: -0.375em; + right: 1.125em; +} +.navbar-dropdown { + font-size: 0.875rem; + padding-bottom: 0.5rem; + padding-top: 0.5rem; +} +.navbar-dropdown .navbar-item { + padding-left: 1.5rem; + padding-right: 1.5rem; +} +.navbar-divider { + background-color: #f5f5f5; + border: none; + display: none; + height: 2px; + margin: 0.5rem 0; +} +@media screen and (max-width: 1087px) { + .navbar > .container { + display: block; + } + .navbar-brand .navbar-item, + .navbar-tabs .navbar-item { + align-items: center; + display: flex; + } + .navbar-link::after { + display: none; + } + .navbar-menu { + background-color: #fcee09; + box-shadow: 0 8px 16px rgba(0,0,0,0.1); + padding: 0.5rem 0; + } + .navbar-menu.is-active { + display: block; + } + .navbar.is-fixed-bottom-touch, + .navbar.is-fixed-top-touch { + left: 0; + position: fixed; + right: 0; + z-index: 30; + } + .navbar.is-fixed-bottom-touch { + bottom: 0; + } + .navbar.is-fixed-bottom-touch.has-shadow { + box-shadow: 0 -2px 3px rgba(0,0,0,0.1); + } + .navbar.is-fixed-top-touch { + top: 0; + } + .navbar.is-fixed-top .navbar-menu, + .navbar.is-fixed-top-touch .navbar-menu { + -webkit-overflow-scrolling: touch; + max-height: calc(100vh - 3.25rem); + overflow: auto; + } + html.has-navbar-fixed-top-touch, + body.has-navbar-fixed-top-touch { + padding-top: 3.25rem; + } + html.has-navbar-fixed-bottom-touch, + body.has-navbar-fixed-bottom-touch { + padding-bottom: 3.25rem; + } +} +@media screen and (min-width: 1088px) { + .navbar, + .navbar-menu, + .navbar-start, + .navbar-end { + align-items: stretch; + display: flex; + } + .navbar { + min-height: 3.25rem; + } + .navbar.is-spaced { + padding: 1rem 2rem; + } + .navbar.is-spaced .navbar-start, + .navbar.is-spaced .navbar-end { + align-items: center; + } + .navbar.is-spaced a.navbar-item, + .navbar.is-spaced .navbar-link { + border-radius: 0; + } + .navbar.is-transparent a.navbar-item:focus, + .navbar.is-transparent .navbar-link:focus, + .navbar.is-transparent a.navbar-item:hover, + .navbar.is-transparent .navbar-link:hover, + .navbar.is-transparent a.navbar-item.is-active, + .navbar.is-transparent .navbar-link.is-active { + background-color: transparent !important; + } + .navbar.is-transparent .navbar-item.has-dropdown.is-active .navbar-link, + .navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:focus .navbar-link, + .navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:focus-within .navbar-link, + .navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:hover .navbar-link { + background-color: transparent !important; + } + .navbar.is-transparent .navbar-dropdown a.navbar-item:focus, + .navbar.is-transparent .navbar-dropdown a.navbar-item:hover { + background-color: #f5f5f5; + color: #000; + } + .navbar.is-transparent .navbar-dropdown a.navbar-item.is-active { + background-color: #f5f5f5; + color: #02d7f2; + } + .navbar-burger { + display: none; + } + .navbar-item, + .navbar-link { + align-items: center; + display: flex; + } + .navbar-item { + display: flex; + } + .navbar-item.has-dropdown { + align-items: stretch; + } + .navbar-item.has-dropdown-up .navbar-link::after { + transform: rotate(135deg) translate(0.25em, -0.25em); + } + .navbar-item.has-dropdown-up .navbar-dropdown { + border-bottom: 2px solid #dbdbdb; + border-radius: 6px 6px 0 0; + border-top: none; + bottom: 100%; + box-shadow: 0 -8px 8px rgba(0,0,0,0.1); + top: auto; + } + .navbar-item.is-active .navbar-dropdown, + .navbar-item.is-hoverable:focus .navbar-dropdown, + .navbar-item.is-hoverable:focus-within .navbar-dropdown, + .navbar-item.is-hoverable:hover .navbar-dropdown { + display: block; + } + .navbar.is-spaced .navbar-item.is-active .navbar-dropdown, + .navbar.is-spaced .navbar-item.is-hoverable:focus .navbar-dropdown, + .navbar.is-spaced .navbar-item.is-hoverable:focus-within .navbar-dropdown, + .navbar.is-spaced .navbar-item.is-hoverable:hover .navbar-dropdown, + .navbar-item.is-active .navbar-dropdown.is-boxed, + .navbar-item.is-hoverable:focus .navbar-dropdown.is-boxed, + .navbar-item.is-hoverable:focus-within .navbar-dropdown.is-boxed, + .navbar-item.is-hoverable:hover .navbar-dropdown.is-boxed { + opacity: 1; + pointer-events: auto; + transform: translateY(0); + } + .navbar-menu { + flex-grow: 1; + flex-shrink: 0; + } + .navbar-start { + justify-content: flex-start; + margin-right: auto; + } + .navbar-end { + justify-content: flex-end; + margin-left: auto; + } + .navbar-dropdown { + background-color: #000; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + border-top: 2px solid #dbdbdb; + box-shadow: 0 8px 8px rgba(0,0,0,0.1); + display: none; + font-size: 0.875rem; + left: 0; + min-width: 100%; + position: absolute; + top: 100%; + z-index: 20; + } + .navbar-dropdown .navbar-item { + padding: 0.375rem 1rem; + white-space: nowrap; + } + .navbar-dropdown a.navbar-item { + padding-right: 3rem; + } + .navbar-dropdown a.navbar-item:focus, + .navbar-dropdown a.navbar-item:hover { + background-color: #f5f5f5; + color: #000; + } + .navbar-dropdown a.navbar-item.is-active { + background-color: #f5f5f5; + color: #02d7f2; + } + .navbar.is-spaced .navbar-dropdown, + .navbar-dropdown.is-boxed { + border-radius: 6px; + border-top: none; + box-shadow: 0 8px 8px rgba(0,0,0,0.1), 0 0 0 1px rgba(0,0,0,0.1); + display: block; + opacity: 0; + pointer-events: none; + top: calc(100% + -4px); + transform: translateY(-5px); + transition-duration: 86ms; + transition-property: opacity, transform; + } + .navbar-dropdown.is-right { + left: auto; + right: 0; + } + .navbar-divider { + display: block; + } + .navbar > .container .navbar-brand, + .container > .navbar .navbar-brand { + margin-left: -0.75rem; + } + .navbar > .container .navbar-menu, + .container > .navbar .navbar-menu { + margin-right: -0.75rem; + } + .navbar.is-fixed-bottom-desktop, + .navbar.is-fixed-top-desktop { + left: 0; + position: fixed; + right: 0; + z-index: 30; + } + .navbar.is-fixed-bottom-desktop { + bottom: 0; + } + .navbar.is-fixed-bottom-desktop.has-shadow { + box-shadow: 0 -2px 3px rgba(0,0,0,0.1); + } + .navbar.is-fixed-top-desktop { + top: 0; + } + html.has-navbar-fixed-top-desktop, + body.has-navbar-fixed-top-desktop { + padding-top: 3.25rem; + } + html.has-navbar-fixed-bottom-desktop, + body.has-navbar-fixed-bottom-desktop { + padding-bottom: 3.25rem; + } + html.has-spaced-navbar-fixed-top, + body.has-spaced-navbar-fixed-top { + padding-top: 5.25rem; + } + html.has-spaced-navbar-fixed-bottom, + body.has-spaced-navbar-fixed-bottom { + padding-bottom: 5.25rem; + } + a.navbar-item.is-active, + .navbar-link.is-active { + color: #000; + } + a.navbar-item.is-active:not(:focus):not(:hover), + .navbar-link.is-active:not(:focus):not(:hover) { + background-color: transparent; + } + .navbar-item.has-dropdown:focus .navbar-link, + .navbar-item.has-dropdown:hover .navbar-link, + .navbar-item.has-dropdown.is-active .navbar-link { + background-color: transparent; + } +} +.hero.is-fullheight-with-navbar { + min-height: calc(100vh - 3.25rem); +} +.pagination { + font-size: 1rem; + margin: -0.25rem; +} +.pagination.is-small { + font-size: 0.75rem; +} +.pagination.is-medium { + font-size: 1.25rem; +} +.pagination.is-large { + font-size: 1.5rem; +} +.pagination.is-rounded .pagination-previous, +.pagination.is-rounded .pagination-next { + padding-left: 1em; + padding-right: 1em; + border-radius: 290486px; +} +.pagination.is-rounded .pagination-link { + border-radius: 290486px; +} +.pagination, +.pagination-list { + align-items: center; + display: flex; + justify-content: center; + text-align: center; +} +.pagination-previous, +.pagination-next, +.pagination-link, +.pagination-ellipsis { + font-size: 1em; + justify-content: center; + margin: 0.25rem; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: center; +} +.pagination-previous, +.pagination-next, +.pagination-link { + border-color: #dbdbdb; + color: #fcee09; + min-width: 2.25em; +} +.pagination-previous:hover, +.pagination-next:hover, +.pagination-link:hover { + border-color: #b5b5b5; + color: #fcee09; +} +.pagination-previous:focus, +.pagination-next:focus, +.pagination-link:focus { + border-color: #02d7f2; +} +.pagination-previous:active, +.pagination-next:active, +.pagination-link:active { + box-shadow: inset 0 1px 2px rgba(0,0,0,0.2); +} +.pagination-previous[disabled], +.pagination-next[disabled], +.pagination-link[disabled] { + background-color: #dbdbdb; + border-color: #dbdbdb; + box-shadow: none; + color: #848484; + opacity: 0.5; +} +.pagination-previous, +.pagination-next { + padding-left: 0.75em; + padding-right: 0.75em; + white-space: nowrap; +} +.pagination-link.is-current { + background-color: #02d7f2; + border-color: #02d7f2; + color: rgba(0,0,0,0.7); +} +.pagination-ellipsis { + color: #b5b5b5; + pointer-events: none; +} +.pagination-list { + flex-wrap: wrap; +} +@media screen and (max-width: 768px) { + .pagination { + flex-wrap: wrap; + } + .pagination-previous, + .pagination-next { + flex-grow: 1; + flex-shrink: 1; + } + .pagination-list li { + flex-grow: 1; + flex-shrink: 1; + } +} +@media screen and (min-width: 769px), print { + .pagination-list { + flex-grow: 1; + flex-shrink: 1; + justify-content: flex-start; + order: 1; + } + .pagination-previous { + order: 2; + } + .pagination-next { + order: 3; + } + .pagination { + justify-content: space-between; + } + .pagination.is-centered .pagination-previous, + .pagination .pagination-previous { + order: 1; + } + .pagination.is-centered .pagination-list, + .pagination .pagination-list { + justify-content: center; + order: 2; + } + .pagination.is-centered .pagination-next, + .pagination .pagination-next { + order: 3; + } + .pagination.is-right .pagination-previous { + order: 1; + } + .pagination.is-right .pagination-next { + order: 2; + } + .pagination.is-right .pagination-list { + justify-content: flex-end; + order: 3; + } +} +.panel { + border-radius: 6px; + box-shadow: 0 0.5em 1em -0.125em rgba(0,0,0,0.1), 0 0px 0 1px rgba(0,0,0,0.02); + font-size: 1rem; +} +.panel:not(:last-child) { + margin-bottom: 1.5rem; +} +.panel.is-white .panel-heading { + background-color: #fff; + color: #000; +} +.panel.is-white .panel-tabs a.is-active { + border-bottom-color: #fff; +} +.panel.is-white .panel-block.is-active .panel-icon { + color: #fff; +} +.panel.is-black .panel-heading { + background-color: #000; + color: #fff; +} +.panel.is-black .panel-tabs a.is-active { + border-bottom-color: #000; +} +.panel.is-black .panel-block.is-active .panel-icon { + color: #000; +} +.panel.is-light .panel-heading { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.panel.is-light .panel-tabs a.is-active { + border-bottom-color: #f5f5f5; +} +.panel.is-light .panel-block.is-active .panel-icon { + color: #f5f5f5; +} +.panel.is-dark .panel-heading { + background-color: #363636; + color: #fff; +} +.panel.is-dark .panel-tabs a.is-active { + border-bottom-color: #363636; +} +.panel.is-dark .panel-block.is-active .panel-icon { + color: #363636; +} +.panel.is-primary .panel-heading { + background-color: #fcee09; + color: #121617; +} +.panel.is-primary .panel-tabs a.is-active { + border-bottom-color: #fcee09; +} +.panel.is-primary .panel-block.is-active .panel-icon { + color: #fcee09; +} +.panel.is-link .panel-heading { + background-color: #02d7f2; + color: rgba(0,0,0,0.7); +} +.panel.is-link .panel-tabs a.is-active { + border-bottom-color: #02d7f2; +} +.panel.is-link .panel-block.is-active .panel-icon { + color: #02d7f2; +} +.panel.is-info .panel-heading { + background-color: #02d7f2; + color: #121617; +} +.panel.is-info .panel-tabs a.is-active { + border-bottom-color: #02d7f2; +} +.panel.is-info .panel-block.is-active .panel-icon { + color: #02d7f2; +} +.panel.is-success .panel-heading { + background-color: #00ff41; + color: #121617; +} +.panel.is-success .panel-tabs a.is-active { + border-bottom-color: #00ff41; +} +.panel.is-success .panel-block.is-active .panel-icon { + color: #00ff41; +} +.panel.is-warning .panel-heading { + background-color: #ff8e3c; + color: #121617; +} +.panel.is-warning .panel-tabs a.is-active { + border-bottom-color: #ff8e3c; +} +.panel.is-warning .panel-block.is-active .panel-icon { + color: #ff8e3c; +} +.panel.is-danger .panel-heading { + background-color: #ff003c; + color: #121617; +} +.panel.is-danger .panel-tabs a.is-active { + border-bottom-color: #ff003c; +} +.panel.is-danger .panel-block.is-active .panel-icon { + color: #ff003c; +} +.panel.is-grey-lightest .panel-heading { + background-color: #ededed; + color: #363636; +} +.panel.is-grey-lightest .panel-tabs a.is-active { + border-bottom-color: #ededed; +} +.panel.is-grey-lightest .panel-block.is-active .panel-icon { + color: #ededed; +} +.panel-tabs:not(:last-child), +.panel-block:not(:last-child) { + border-bottom: 1px solid #ededed; +} +.panel-heading { + background-color: #ededed; + border-radius: 6px 6px 0 0; + color: #fcee09; + font-size: 1.25em; + font-weight: 700; + line-height: 1.25; + padding: 0.75em 1em; +} +.panel-tabs { + align-items: flex-end; + display: flex; + font-size: 0.875em; + justify-content: center; +} +.panel-tabs a { + border-bottom: 1px solid #dbdbdb; + margin-bottom: -1px; + padding: 0.5em; +} +.panel-tabs a.is-active { + border-bottom-color: #4a4a4a; + color: #363636; +} +.panel-list a { + color: #cdcdcd; +} +.panel-list a:hover { + color: #02d7f2; +} +.panel-block { + align-items: center; + color: #fcee09; + display: flex; + justify-content: flex-start; + padding: 0.5em 0.75em; +} +.panel-block input[type="checkbox"] { + margin-right: 0.75em; +} +.panel-block > .control { + flex-grow: 1; + flex-shrink: 1; + width: 100%; +} +.panel-block.is-wrapped { + flex-wrap: wrap; +} +.panel-block.is-active { + border-left-color: #02d7f2; + color: #363636; +} +.panel-block.is-active .panel-icon { + color: #02d7f2; +} +.panel-block:last-child { + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; +} +a.panel-block, +label.panel-block { + cursor: pointer; +} +a.panel-block:hover, +label.panel-block:hover { + background-color: #f5f5f5; +} +.panel-icon { + display: inline-block; + font-size: 14px; + height: 1em; + line-height: 1em; + text-align: center; + vertical-align: top; + width: 1em; + color: #848484; + margin-right: 0.75em; +} +.panel-icon .fa { + font-size: inherit; + line-height: inherit; +} +.tabs { + -webkit-overflow-scrolling: touch; + align-items: stretch; + display: flex; + font-size: 1rem; + justify-content: space-between; + overflow: hidden; + overflow-x: auto; + white-space: nowrap; +} +.tabs a { + align-items: center; + border-bottom-color: #dbdbdb; + border-bottom-style: solid; + border-bottom-width: 1px; + color: #cdcdcd; + display: flex; + justify-content: center; + margin-bottom: -1px; + padding: 0.5em 1em; + vertical-align: top; +} +.tabs a:hover { + border-bottom-color: #fcee09; + color: #fcee09; +} +.tabs li { + display: block; +} +.tabs li.is-active a { + border-bottom-color: #02d7f2; + color: #02d7f2; +} +.tabs ul { + align-items: center; + border-bottom-color: #dbdbdb; + border-bottom-style: solid; + border-bottom-width: 1px; + display: flex; + flex-grow: 1; + flex-shrink: 0; + justify-content: flex-start; +} +.tabs ul.is-left { + padding-right: 0.75em; +} +.tabs ul.is-center { + flex: none; + justify-content: center; + padding-left: 0.75em; + padding-right: 0.75em; +} +.tabs ul.is-right { + justify-content: flex-end; + padding-left: 0.75em; +} +.tabs .icon:first-child { + margin-right: 0.5em; +} +.tabs .icon:last-child { + margin-left: 0.5em; +} +.tabs.is-centered ul { + justify-content: center; +} +.tabs.is-right ul { + justify-content: flex-end; +} +.tabs.is-boxed a { + border: 1px solid transparent; + border-radius: 0 0 0 0; +} +.tabs.is-boxed a:hover { + background-color: #f5f5f5; + border-bottom-color: #dbdbdb; +} +.tabs.is-boxed li.is-active a { + background-color: #000; + border-color: #dbdbdb; + border-bottom-color: transparent !important; +} +.tabs.is-fullwidth li { + flex-grow: 1; + flex-shrink: 0; +} +.tabs.is-toggle a { + border-color: #dbdbdb; + border-style: solid; + border-width: 1px; + margin-bottom: 0; + position: relative; +} +.tabs.is-toggle a:hover { + background-color: #f5f5f5; + border-color: #b5b5b5; + z-index: 2; +} +.tabs.is-toggle li + li { + margin-left: -1px; +} +.tabs.is-toggle li:first-child a { + border-radius: 0 0 0 0; +} +.tabs.is-toggle li:last-child a { + border-radius: 0 0 0 0; +} +.tabs.is-toggle li.is-active a { + background-color: #02d7f2; + border-color: #02d7f2; + color: rgba(0,0,0,0.7); + z-index: 1; +} +.tabs.is-toggle ul { + border-bottom: none; +} +.tabs.is-toggle.is-toggle-rounded li:first-child a { + border-bottom-left-radius: 290486px; + border-top-left-radius: 290486px; + padding-left: 1.25em; +} +.tabs.is-toggle.is-toggle-rounded li:last-child a { + border-bottom-right-radius: 290486px; + border-top-right-radius: 290486px; + padding-right: 1.25em; +} +.tabs.is-small { + font-size: 0.75rem; +} +.tabs.is-medium { + font-size: 1.25rem; +} +.tabs.is-large { + font-size: 1.5rem; +} +.box { + background-color: #000; + border-radius: 6px; + box-shadow: 0 0.5em 1em -0.125em rgba(0,0,0,0.1), 0 0px 0 1px rgba(0,0,0,0.02); + color: #cdcdcd; + display: block; + padding: 1.25rem; +} +a.box:hover, +a.box:focus { + box-shadow: 0 0.5em 1em -0.125em rgba(0,0,0,0.1), 0 0 0 1px #02d7f2; +} +a.box:active { + box-shadow: inset 0 1px 2px rgba(0,0,0,0.2), 0 0 0 1px #02d7f2; +} +.button { + background-color: #000; + border-color: #dbdbdb; + border-width: 1px; + color: #fcee09; + cursor: pointer; + justify-content: center; + padding-bottom: calc(0.375em - 1px); + padding-left: 1em; + padding-right: 1em; + padding-top: calc(0.375em - 1px); + text-align: center; + white-space: nowrap; +} +.button strong { + color: inherit; +} +.button .icon, +.button .icon.is-small, +.button .icon.is-medium, +.button .icon.is-large { + height: 1.5em; + width: 1.5em; +} +.button .icon:first-child:not(:last-child) { + margin-left: calc(-0.5em - 1px); + margin-right: 0.25em; +} +.button .icon:last-child:not(:first-child) { + margin-left: 0.25em; + margin-right: calc(-0.5em - 1px); +} +.button .icon:first-child:last-child { + margin-left: calc(-0.5em - 1px); + margin-right: calc(-0.5em - 1px); +} +.button:hover, +.button.is-hovered { + border-color: #b5b5b5; + color: #fcee09; +} +.button:focus, +.button.is-focused { + border-color: #02d7f2; + color: #363636; +} +.button:focus:not(:active), +.button.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(2,215,242,0.25); +} +.button:active, +.button.is-active { + border-color: #4a4a4a; + color: #363636; +} +.button.is-text { + background-color: transparent; + border-color: transparent; + color: #cdcdcd; + text-decoration: underline; +} +.button.is-text:hover, +.button.is-text.is-hovered, +.button.is-text:focus, +.button.is-text.is-focused { + background-color: #f5f5f5; + color: #fcee09; +} +.button.is-text:active, +.button.is-text.is-active { + background-color: #e8e8e8; + color: #fcee09; +} +.button.is-text[disabled], +fieldset[disabled] .button.is-text { + background-color: transparent; + border-color: transparent; + box-shadow: none; +} +.button.is-white { + background-color: #fff; + border-color: transparent; + color: #000; +} +.button.is-white:hover, +.button.is-white.is-hovered { + background-color: #f9f9f9; + border-color: transparent; + color: #000; +} +.button.is-white:focus, +.button.is-white.is-focused { + border-color: transparent; + color: #000; +} +.button.is-white:focus:not(:active), +.button.is-white.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(255,255,255,0.25); +} +.button.is-white:active, +.button.is-white.is-active { + background-color: #f2f2f2; + border-color: transparent; + color: #000; +} +.button.is-white[disabled], +fieldset[disabled] .button.is-white { + background-color: #fff; + border-color: transparent; + box-shadow: none; +} +.button.is-white.is-inverted { + background-color: #000; + color: #fff; +} +.button.is-white.is-inverted:hover, +.button.is-white.is-inverted.is-hovered { + background-color: #000; +} +.button.is-white.is-inverted[disabled], +fieldset[disabled] .button.is-white.is-inverted { + background-color: #000; + border-color: transparent; + box-shadow: none; + color: #fff; +} +.button.is-white.is-loading::after { + border-color: transparent transparent #000 #000 !important; +} +.button.is-white.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; +} +.button.is-white.is-outlined:hover, +.button.is-white.is-outlined.is-hovered, +.button.is-white.is-outlined:focus, +.button.is-white.is-outlined.is-focused { + background-color: #fff; + border-color: #fff; + color: #000; +} +.button.is-white.is-outlined.is-loading::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-white.is-outlined.is-loading:hover::after, +.button.is-white.is-outlined.is-loading.is-hovered::after, +.button.is-white.is-outlined.is-loading:focus::after, +.button.is-white.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #000 #000 !important; +} +.button.is-white.is-outlined[disabled], +fieldset[disabled] .button.is-white.is-outlined { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; +} +.button.is-white.is-inverted.is-outlined { + background-color: transparent; + border-color: #000; + color: #000; +} +.button.is-white.is-inverted.is-outlined:hover, +.button.is-white.is-inverted.is-outlined.is-hovered, +.button.is-white.is-inverted.is-outlined:focus, +.button.is-white.is-inverted.is-outlined.is-focused { + background-color: #000; + color: #fff; +} +.button.is-white.is-inverted.is-outlined.is-loading:hover::after, +.button.is-white.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-white.is-inverted.is-outlined.is-loading:focus::after, +.button.is-white.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-white.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-white.is-inverted.is-outlined { + background-color: transparent; + border-color: #000; + box-shadow: none; + color: #000; +} +.button.is-black { + background-color: #000; + border-color: transparent; + color: #fff; +} +.button.is-black:hover, +.button.is-black.is-hovered { + background-color: #000; + border-color: transparent; + color: #fff; +} +.button.is-black:focus, +.button.is-black.is-focused { + border-color: transparent; + color: #fff; +} +.button.is-black:focus:not(:active), +.button.is-black.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(0,0,0,0.25); +} +.button.is-black:active, +.button.is-black.is-active { + background-color: #000; + border-color: transparent; + color: #fff; +} +.button.is-black[disabled], +fieldset[disabled] .button.is-black { + background-color: #000; + border-color: transparent; + box-shadow: none; +} +.button.is-black.is-inverted { + background-color: #fff; + color: #000; +} +.button.is-black.is-inverted:hover, +.button.is-black.is-inverted.is-hovered { + background-color: #f2f2f2; +} +.button.is-black.is-inverted[disabled], +fieldset[disabled] .button.is-black.is-inverted { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #000; +} +.button.is-black.is-loading::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-black.is-outlined { + background-color: transparent; + border-color: #000; + color: #000; +} +.button.is-black.is-outlined:hover, +.button.is-black.is-outlined.is-hovered, +.button.is-black.is-outlined:focus, +.button.is-black.is-outlined.is-focused { + background-color: #000; + border-color: #000; + color: #fff; +} +.button.is-black.is-outlined.is-loading::after { + border-color: transparent transparent #000 #000 !important; +} +.button.is-black.is-outlined.is-loading:hover::after, +.button.is-black.is-outlined.is-loading.is-hovered::after, +.button.is-black.is-outlined.is-loading:focus::after, +.button.is-black.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-black.is-outlined[disabled], +fieldset[disabled] .button.is-black.is-outlined { + background-color: transparent; + border-color: #000; + box-shadow: none; + color: #000; +} +.button.is-black.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; +} +.button.is-black.is-inverted.is-outlined:hover, +.button.is-black.is-inverted.is-outlined.is-hovered, +.button.is-black.is-inverted.is-outlined:focus, +.button.is-black.is-inverted.is-outlined.is-focused { + background-color: #fff; + color: #000; +} +.button.is-black.is-inverted.is-outlined.is-loading:hover::after, +.button.is-black.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-black.is-inverted.is-outlined.is-loading:focus::after, +.button.is-black.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #000 #000 !important; +} +.button.is-black.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-black.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; +} +.button.is-light, +article.article .article-more { + background-color: #f5f5f5; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-light:hover, +article.article .article-more:hover, +.button.is-light.is-hovered, +article.article .article-more.is-hovered { + background-color: #eee; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-light:focus, +article.article .article-more:focus, +.button.is-light.is-focused, +article.article .article-more.is-focused { + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-light:focus:not(:active), +article.article .article-more:focus:not(:active), +.button.is-light.is-focused:not(:active), +article.article .article-more.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(245,245,245,0.25); +} +.button.is-light:active, +article.article .article-more:active, +.button.is-light.is-active, +article.article .article-more.is-active { + background-color: #e8e8e8; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-light[disabled], +article.article .article-more[disabled], +fieldset[disabled] .button.is-light, +fieldset[disabled] article.article .article-more { + background-color: #f5f5f5; + border-color: transparent; + box-shadow: none; +} +.button.is-light.is-inverted, +article.article .article-more.is-inverted { + background-color: rgba(0,0,0,0.7); + color: #f5f5f5; +} +.button.is-light.is-inverted:hover, +article.article .article-more.is-inverted:hover, +.button.is-light.is-inverted.is-hovered, +article.article .article-more.is-inverted.is-hovered { + background-color: rgba(0,0,0,0.7); +} +.button.is-light.is-inverted[disabled], +article.article .article-more.is-inverted[disabled], +fieldset[disabled] .button.is-light.is-inverted, +fieldset[disabled] article.article .article-more.is-inverted { + background-color: rgba(0,0,0,0.7); + border-color: transparent; + box-shadow: none; + color: #f5f5f5; +} +.button.is-light.is-loading::after, +article.article .article-more.is-loading::after { + border-color: transparent transparent rgba(0,0,0,0.7) rgba(0,0,0,0.7) !important; +} +.button.is-light.is-outlined, +article.article .article-more.is-outlined { + background-color: transparent; + border-color: #f5f5f5; + color: #f5f5f5; +} +.button.is-light.is-outlined:hover, +article.article .article-more.is-outlined:hover, +.button.is-light.is-outlined.is-hovered, +article.article .article-more.is-outlined.is-hovered, +.button.is-light.is-outlined:focus, +article.article .article-more.is-outlined:focus, +.button.is-light.is-outlined.is-focused, +article.article .article-more.is-outlined.is-focused { + background-color: #f5f5f5; + border-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.button.is-light.is-outlined.is-loading::after, +article.article .article-more.is-outlined.is-loading::after { + border-color: transparent transparent #f5f5f5 #f5f5f5 !important; +} +.button.is-light.is-outlined.is-loading:hover::after, +article.article .article-more.is-outlined.is-loading:hover::after, +.button.is-light.is-outlined.is-loading.is-hovered::after, +article.article .article-more.is-outlined.is-loading.is-hovered::after, +.button.is-light.is-outlined.is-loading:focus::after, +article.article .article-more.is-outlined.is-loading:focus::after, +.button.is-light.is-outlined.is-loading.is-focused::after, +article.article .article-more.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent rgba(0,0,0,0.7) rgba(0,0,0,0.7) !important; +} +.button.is-light.is-outlined[disabled], +article.article .article-more.is-outlined[disabled], +fieldset[disabled] .button.is-light.is-outlined, +fieldset[disabled] article.article .article-more.is-outlined { + background-color: transparent; + border-color: #f5f5f5; + box-shadow: none; + color: #f5f5f5; +} +.button.is-light.is-inverted.is-outlined, +article.article .article-more.is-inverted.is-outlined { + background-color: transparent; + border-color: rgba(0,0,0,0.7); + color: rgba(0,0,0,0.7); +} +.button.is-light.is-inverted.is-outlined:hover, +article.article .article-more.is-inverted.is-outlined:hover, +.button.is-light.is-inverted.is-outlined.is-hovered, +article.article .article-more.is-inverted.is-outlined.is-hovered, +.button.is-light.is-inverted.is-outlined:focus, +article.article .article-more.is-inverted.is-outlined:focus, +.button.is-light.is-inverted.is-outlined.is-focused, +article.article .article-more.is-inverted.is-outlined.is-focused { + background-color: rgba(0,0,0,0.7); + color: #f5f5f5; +} +.button.is-light.is-inverted.is-outlined.is-loading:hover::after, +article.article .article-more.is-inverted.is-outlined.is-loading:hover::after, +.button.is-light.is-inverted.is-outlined.is-loading.is-hovered::after, +article.article .article-more.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-light.is-inverted.is-outlined.is-loading:focus::after, +article.article .article-more.is-inverted.is-outlined.is-loading:focus::after, +.button.is-light.is-inverted.is-outlined.is-loading.is-focused::after, +article.article .article-more.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #f5f5f5 #f5f5f5 !important; +} +.button.is-light.is-inverted.is-outlined[disabled], +article.article .article-more.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-light.is-inverted.is-outlined, +fieldset[disabled] article.article .article-more.is-inverted.is-outlined { + background-color: transparent; + border-color: rgba(0,0,0,0.7); + box-shadow: none; + color: rgba(0,0,0,0.7); +} +.button.is-dark { + background-color: #363636; + border-color: transparent; + color: #fff; +} +.button.is-dark:hover, +.button.is-dark.is-hovered { + background-color: #2f2f2f; + border-color: transparent; + color: #fff; +} +.button.is-dark:focus, +.button.is-dark.is-focused { + border-color: transparent; + color: #fff; +} +.button.is-dark:focus:not(:active), +.button.is-dark.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(54,54,54,0.25); +} +.button.is-dark:active, +.button.is-dark.is-active { + background-color: #292929; + border-color: transparent; + color: #fff; +} +.button.is-dark[disabled], +fieldset[disabled] .button.is-dark { + background-color: #363636; + border-color: transparent; + box-shadow: none; +} +.button.is-dark.is-inverted { + background-color: #fff; + color: #363636; +} +.button.is-dark.is-inverted:hover, +.button.is-dark.is-inverted.is-hovered { + background-color: #f2f2f2; +} +.button.is-dark.is-inverted[disabled], +fieldset[disabled] .button.is-dark.is-inverted { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #363636; +} +.button.is-dark.is-loading::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-dark.is-outlined { + background-color: transparent; + border-color: #363636; + color: #363636; +} +.button.is-dark.is-outlined:hover, +.button.is-dark.is-outlined.is-hovered, +.button.is-dark.is-outlined:focus, +.button.is-dark.is-outlined.is-focused { + background-color: #363636; + border-color: #363636; + color: #fff; +} +.button.is-dark.is-outlined.is-loading::after { + border-color: transparent transparent #363636 #363636 !important; +} +.button.is-dark.is-outlined.is-loading:hover::after, +.button.is-dark.is-outlined.is-loading.is-hovered::after, +.button.is-dark.is-outlined.is-loading:focus::after, +.button.is-dark.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-dark.is-outlined[disabled], +fieldset[disabled] .button.is-dark.is-outlined { + background-color: transparent; + border-color: #363636; + box-shadow: none; + color: #363636; +} +.button.is-dark.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; +} +.button.is-dark.is-inverted.is-outlined:hover, +.button.is-dark.is-inverted.is-outlined.is-hovered, +.button.is-dark.is-inverted.is-outlined:focus, +.button.is-dark.is-inverted.is-outlined.is-focused { + background-color: #fff; + color: #363636; +} +.button.is-dark.is-inverted.is-outlined.is-loading:hover::after, +.button.is-dark.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-dark.is-inverted.is-outlined.is-loading:focus::after, +.button.is-dark.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #363636 #363636 !important; +} +.button.is-dark.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-dark.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; +} +.button.is-primary { + background-color: #fcee09; + border-color: transparent; + color: #121617; +} +.button.is-primary:hover, +.button.is-primary.is-hovered { + background-color: #f5e703; + border-color: transparent; + color: #121617; +} +.button.is-primary:focus, +.button.is-primary.is-focused { + border-color: transparent; + color: #121617; +} +.button.is-primary:focus:not(:active), +.button.is-primary.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(252,238,9,0.25); +} +.button.is-primary:active, +.button.is-primary.is-active { + background-color: #e9db03; + border-color: transparent; + color: #121617; +} +.button.is-primary[disabled], +fieldset[disabled] .button.is-primary { + background-color: #fcee09; + border-color: transparent; + box-shadow: none; +} +.button.is-primary.is-inverted { + background-color: #121617; + color: #fcee09; +} +.button.is-primary.is-inverted:hover, +.button.is-primary.is-inverted.is-hovered { + background-color: #070809; +} +.button.is-primary.is-inverted[disabled], +fieldset[disabled] .button.is-primary.is-inverted { + background-color: #121617; + border-color: transparent; + box-shadow: none; + color: #fcee09; +} +.button.is-primary.is-loading::after { + border-color: transparent transparent #121617 #121617 !important; +} +.button.is-primary.is-outlined { + background-color: transparent; + border-color: #fcee09; + color: #fcee09; +} +.button.is-primary.is-outlined:hover, +.button.is-primary.is-outlined.is-hovered, +.button.is-primary.is-outlined:focus, +.button.is-primary.is-outlined.is-focused { + background-color: #fcee09; + border-color: #fcee09; + color: #121617; +} +.button.is-primary.is-outlined.is-loading::after { + border-color: transparent transparent #fcee09 #fcee09 !important; +} +.button.is-primary.is-outlined.is-loading:hover::after, +.button.is-primary.is-outlined.is-loading.is-hovered::after, +.button.is-primary.is-outlined.is-loading:focus::after, +.button.is-primary.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #121617 #121617 !important; +} +.button.is-primary.is-outlined[disabled], +fieldset[disabled] .button.is-primary.is-outlined { + background-color: transparent; + border-color: #fcee09; + box-shadow: none; + color: #fcee09; +} +.button.is-primary.is-inverted.is-outlined { + background-color: transparent; + border-color: #121617; + color: #121617; +} +.button.is-primary.is-inverted.is-outlined:hover, +.button.is-primary.is-inverted.is-outlined.is-hovered, +.button.is-primary.is-inverted.is-outlined:focus, +.button.is-primary.is-inverted.is-outlined.is-focused { + background-color: #121617; + color: #fcee09; +} +.button.is-primary.is-inverted.is-outlined.is-loading:hover::after, +.button.is-primary.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-primary.is-inverted.is-outlined.is-loading:focus::after, +.button.is-primary.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #fcee09 #fcee09 !important; +} +.button.is-primary.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-primary.is-inverted.is-outlined { + background-color: transparent; + border-color: #121617; + box-shadow: none; + color: #121617; +} +.button.is-primary.is-light { + background-color: #fffeeb; + color: #928a02; +} +.button.is-primary.is-light:hover, +.button.is-primary.is-light.is-hovered { + background-color: #fffdde; + border-color: transparent; + color: #928a02; +} +.button.is-primary.is-light:active, +.button.is-primary.is-light.is-active { + background-color: #fefcd2; + border-color: transparent; + color: #928a02; +} +.button.is-link { + background-color: #02d7f2; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-link:hover, +.button.is-link.is-hovered { + background-color: #02cce5; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-link:focus, +.button.is-link.is-focused { + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-link:focus:not(:active), +.button.is-link.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(2,215,242,0.25); +} +.button.is-link:active, +.button.is-link.is-active { + background-color: #02c1d9; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-link[disabled], +fieldset[disabled] .button.is-link { + background-color: #02d7f2; + border-color: transparent; + box-shadow: none; +} +.button.is-link.is-inverted { + background-color: rgba(0,0,0,0.7); + color: #02d7f2; +} +.button.is-link.is-inverted:hover, +.button.is-link.is-inverted.is-hovered { + background-color: rgba(0,0,0,0.7); +} +.button.is-link.is-inverted[disabled], +fieldset[disabled] .button.is-link.is-inverted { + background-color: rgba(0,0,0,0.7); + border-color: transparent; + box-shadow: none; + color: #02d7f2; +} +.button.is-link.is-loading::after { + border-color: transparent transparent rgba(0,0,0,0.7) rgba(0,0,0,0.7) !important; +} +.button.is-link.is-outlined { + background-color: transparent; + border-color: #02d7f2; + color: #02d7f2; +} +.button.is-link.is-outlined:hover, +.button.is-link.is-outlined.is-hovered, +.button.is-link.is-outlined:focus, +.button.is-link.is-outlined.is-focused { + background-color: #02d7f2; + border-color: #02d7f2; + color: rgba(0,0,0,0.7); +} +.button.is-link.is-outlined.is-loading::after { + border-color: transparent transparent #02d7f2 #02d7f2 !important; +} +.button.is-link.is-outlined.is-loading:hover::after, +.button.is-link.is-outlined.is-loading.is-hovered::after, +.button.is-link.is-outlined.is-loading:focus::after, +.button.is-link.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent rgba(0,0,0,0.7) rgba(0,0,0,0.7) !important; +} +.button.is-link.is-outlined[disabled], +fieldset[disabled] .button.is-link.is-outlined { + background-color: transparent; + border-color: #02d7f2; + box-shadow: none; + color: #02d7f2; +} +.button.is-link.is-inverted.is-outlined { + background-color: transparent; + border-color: rgba(0,0,0,0.7); + color: rgba(0,0,0,0.7); +} +.button.is-link.is-inverted.is-outlined:hover, +.button.is-link.is-inverted.is-outlined.is-hovered, +.button.is-link.is-inverted.is-outlined:focus, +.button.is-link.is-inverted.is-outlined.is-focused { + background-color: rgba(0,0,0,0.7); + color: #02d7f2; +} +.button.is-link.is-inverted.is-outlined.is-loading:hover::after, +.button.is-link.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-link.is-inverted.is-outlined.is-loading:focus::after, +.button.is-link.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #02d7f2 #02d7f2 !important; +} +.button.is-link.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-link.is-inverted.is-outlined { + background-color: transparent; + border-color: rgba(0,0,0,0.7); + box-shadow: none; + color: rgba(0,0,0,0.7); +} +.button.is-link.is-light { + background-color: #ebfdff; + color: #018293; +} +.button.is-link.is-light:hover, +.button.is-link.is-light.is-hovered { + background-color: #defbff; + border-color: transparent; + color: #018293; +} +.button.is-link.is-light:active, +.button.is-link.is-light.is-active { + background-color: #d1faff; + border-color: transparent; + color: #018293; +} +.button.is-info { + background-color: #02d7f2; + border-color: transparent; + color: #121617; +} +.button.is-info:hover, +.button.is-info.is-hovered { + background-color: #02cce5; + border-color: transparent; + color: #121617; +} +.button.is-info:focus, +.button.is-info.is-focused { + border-color: transparent; + color: #121617; +} +.button.is-info:focus:not(:active), +.button.is-info.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(2,215,242,0.25); +} +.button.is-info:active, +.button.is-info.is-active { + background-color: #02c1d9; + border-color: transparent; + color: #121617; +} +.button.is-info[disabled], +fieldset[disabled] .button.is-info { + background-color: #02d7f2; + border-color: transparent; + box-shadow: none; +} +.button.is-info.is-inverted { + background-color: #121617; + color: #02d7f2; +} +.button.is-info.is-inverted:hover, +.button.is-info.is-inverted.is-hovered { + background-color: #070809; +} +.button.is-info.is-inverted[disabled], +fieldset[disabled] .button.is-info.is-inverted { + background-color: #121617; + border-color: transparent; + box-shadow: none; + color: #02d7f2; +} +.button.is-info.is-loading::after { + border-color: transparent transparent #121617 #121617 !important; +} +.button.is-info.is-outlined { + background-color: transparent; + border-color: #02d7f2; + color: #02d7f2; +} +.button.is-info.is-outlined:hover, +.button.is-info.is-outlined.is-hovered, +.button.is-info.is-outlined:focus, +.button.is-info.is-outlined.is-focused { + background-color: #02d7f2; + border-color: #02d7f2; + color: #121617; +} +.button.is-info.is-outlined.is-loading::after { + border-color: transparent transparent #02d7f2 #02d7f2 !important; +} +.button.is-info.is-outlined.is-loading:hover::after, +.button.is-info.is-outlined.is-loading.is-hovered::after, +.button.is-info.is-outlined.is-loading:focus::after, +.button.is-info.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #121617 #121617 !important; +} +.button.is-info.is-outlined[disabled], +fieldset[disabled] .button.is-info.is-outlined { + background-color: transparent; + border-color: #02d7f2; + box-shadow: none; + color: #02d7f2; +} +.button.is-info.is-inverted.is-outlined { + background-color: transparent; + border-color: #121617; + color: #121617; +} +.button.is-info.is-inverted.is-outlined:hover, +.button.is-info.is-inverted.is-outlined.is-hovered, +.button.is-info.is-inverted.is-outlined:focus, +.button.is-info.is-inverted.is-outlined.is-focused { + background-color: #121617; + color: #02d7f2; +} +.button.is-info.is-inverted.is-outlined.is-loading:hover::after, +.button.is-info.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-info.is-inverted.is-outlined.is-loading:focus::after, +.button.is-info.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #02d7f2 #02d7f2 !important; +} +.button.is-info.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-info.is-inverted.is-outlined { + background-color: transparent; + border-color: #121617; + box-shadow: none; + color: #121617; +} +.button.is-info.is-light { + background-color: #ebfdff; + color: #018293; +} +.button.is-info.is-light:hover, +.button.is-info.is-light.is-hovered { + background-color: #defbff; + border-color: transparent; + color: #018293; +} +.button.is-info.is-light:active, +.button.is-info.is-light.is-active { + background-color: #d1faff; + border-color: transparent; + color: #018293; +} +.button.is-success { + background-color: #00ff41; + border-color: transparent; + color: #121617; +} +.button.is-success:hover, +.button.is-success.is-hovered { + background-color: #00f23e; + border-color: transparent; + color: #121617; +} +.button.is-success:focus, +.button.is-success.is-focused { + border-color: transparent; + color: #121617; +} +.button.is-success:focus:not(:active), +.button.is-success.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(0,255,65,0.25); +} +.button.is-success:active, +.button.is-success.is-active { + background-color: #00e63a; + border-color: transparent; + color: #121617; +} +.button.is-success[disabled], +fieldset[disabled] .button.is-success { + background-color: #00ff41; + border-color: transparent; + box-shadow: none; +} +.button.is-success.is-inverted { + background-color: #121617; + color: #00ff41; +} +.button.is-success.is-inverted:hover, +.button.is-success.is-inverted.is-hovered { + background-color: #070809; +} +.button.is-success.is-inverted[disabled], +fieldset[disabled] .button.is-success.is-inverted { + background-color: #121617; + border-color: transparent; + box-shadow: none; + color: #00ff41; +} +.button.is-success.is-loading::after { + border-color: transparent transparent #121617 #121617 !important; +} +.button.is-success.is-outlined { + background-color: transparent; + border-color: #00ff41; + color: #00ff41; +} +.button.is-success.is-outlined:hover, +.button.is-success.is-outlined.is-hovered, +.button.is-success.is-outlined:focus, +.button.is-success.is-outlined.is-focused { + background-color: #00ff41; + border-color: #00ff41; + color: #121617; +} +.button.is-success.is-outlined.is-loading::after { + border-color: transparent transparent #00ff41 #00ff41 !important; +} +.button.is-success.is-outlined.is-loading:hover::after, +.button.is-success.is-outlined.is-loading.is-hovered::after, +.button.is-success.is-outlined.is-loading:focus::after, +.button.is-success.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #121617 #121617 !important; +} +.button.is-success.is-outlined[disabled], +fieldset[disabled] .button.is-success.is-outlined { + background-color: transparent; + border-color: #00ff41; + box-shadow: none; + color: #00ff41; +} +.button.is-success.is-inverted.is-outlined { + background-color: transparent; + border-color: #121617; + color: #121617; +} +.button.is-success.is-inverted.is-outlined:hover, +.button.is-success.is-inverted.is-outlined.is-hovered, +.button.is-success.is-inverted.is-outlined:focus, +.button.is-success.is-inverted.is-outlined.is-focused { + background-color: #121617; + color: #00ff41; +} +.button.is-success.is-inverted.is-outlined.is-loading:hover::after, +.button.is-success.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-success.is-inverted.is-outlined.is-loading:focus::after, +.button.is-success.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #00ff41 #00ff41 !important; +} +.button.is-success.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-success.is-inverted.is-outlined { + background-color: transparent; + border-color: #121617; + box-shadow: none; + color: #121617; +} +.button.is-success.is-light { + background-color: #ebfff0; + color: #009426; +} +.button.is-success.is-light:hover, +.button.is-success.is-light.is-hovered { + background-color: #deffe6; + border-color: transparent; + color: #009426; +} +.button.is-success.is-light:active, +.button.is-success.is-light.is-active { + background-color: #d1ffdd; + border-color: transparent; + color: #009426; +} +.button.is-warning { + background-color: #ff8e3c; + border-color: transparent; + color: #121617; +} +.button.is-warning:hover, +.button.is-warning.is-hovered { + background-color: #ff872f; + border-color: transparent; + color: #121617; +} +.button.is-warning:focus, +.button.is-warning.is-focused { + border-color: transparent; + color: #121617; +} +.button.is-warning:focus:not(:active), +.button.is-warning.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(255,142,60,0.25); +} +.button.is-warning:active, +.button.is-warning.is-active { + background-color: #ff7f22; + border-color: transparent; + color: #121617; +} +.button.is-warning[disabled], +fieldset[disabled] .button.is-warning { + background-color: #ff8e3c; + border-color: transparent; + box-shadow: none; +} +.button.is-warning.is-inverted { + background-color: #121617; + color: #ff8e3c; +} +.button.is-warning.is-inverted:hover, +.button.is-warning.is-inverted.is-hovered { + background-color: #070809; +} +.button.is-warning.is-inverted[disabled], +fieldset[disabled] .button.is-warning.is-inverted { + background-color: #121617; + border-color: transparent; + box-shadow: none; + color: #ff8e3c; +} +.button.is-warning.is-loading::after { + border-color: transparent transparent #121617 #121617 !important; +} +.button.is-warning.is-outlined { + background-color: transparent; + border-color: #ff8e3c; + color: #ff8e3c; +} +.button.is-warning.is-outlined:hover, +.button.is-warning.is-outlined.is-hovered, +.button.is-warning.is-outlined:focus, +.button.is-warning.is-outlined.is-focused { + background-color: #ff8e3c; + border-color: #ff8e3c; + color: #121617; +} +.button.is-warning.is-outlined.is-loading::after { + border-color: transparent transparent #ff8e3c #ff8e3c !important; +} +.button.is-warning.is-outlined.is-loading:hover::after, +.button.is-warning.is-outlined.is-loading.is-hovered::after, +.button.is-warning.is-outlined.is-loading:focus::after, +.button.is-warning.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #121617 #121617 !important; +} +.button.is-warning.is-outlined[disabled], +fieldset[disabled] .button.is-warning.is-outlined { + background-color: transparent; + border-color: #ff8e3c; + box-shadow: none; + color: #ff8e3c; +} +.button.is-warning.is-inverted.is-outlined { + background-color: transparent; + border-color: #121617; + color: #121617; +} +.button.is-warning.is-inverted.is-outlined:hover, +.button.is-warning.is-inverted.is-outlined.is-hovered, +.button.is-warning.is-inverted.is-outlined:focus, +.button.is-warning.is-inverted.is-outlined.is-focused { + background-color: #121617; + color: #ff8e3c; +} +.button.is-warning.is-inverted.is-outlined.is-loading:hover::after, +.button.is-warning.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-warning.is-inverted.is-outlined.is-loading:focus::after, +.button.is-warning.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #ff8e3c #ff8e3c !important; +} +.button.is-warning.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-warning.is-inverted.is-outlined { + background-color: transparent; + border-color: #121617; + box-shadow: none; + color: #121617; +} +.button.is-warning.is-light { + background-color: #fff3eb; + color: #a84700; +} +.button.is-warning.is-light:hover, +.button.is-warning.is-light.is-hovered { + background-color: #ffecde; + border-color: transparent; + color: #a84700; +} +.button.is-warning.is-light:active, +.button.is-warning.is-light.is-active { + background-color: #ffe4d1; + border-color: transparent; + color: #a84700; +} +.button.is-danger { + background-color: #ff003c; + border-color: transparent; + color: #121617; +} +.button.is-danger:hover, +.button.is-danger.is-hovered { + background-color: #f20039; + border-color: transparent; + color: #121617; +} +.button.is-danger:focus, +.button.is-danger.is-focused { + border-color: transparent; + color: #121617; +} +.button.is-danger:focus:not(:active), +.button.is-danger.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(255,0,60,0.25); +} +.button.is-danger:active, +.button.is-danger.is-active { + background-color: #e60036; + border-color: transparent; + color: #121617; +} +.button.is-danger[disabled], +fieldset[disabled] .button.is-danger { + background-color: #ff003c; + border-color: transparent; + box-shadow: none; +} +.button.is-danger.is-inverted { + background-color: #121617; + color: #ff003c; +} +.button.is-danger.is-inverted:hover, +.button.is-danger.is-inverted.is-hovered { + background-color: #070809; +} +.button.is-danger.is-inverted[disabled], +fieldset[disabled] .button.is-danger.is-inverted { + background-color: #121617; + border-color: transparent; + box-shadow: none; + color: #ff003c; +} +.button.is-danger.is-loading::after { + border-color: transparent transparent #121617 #121617 !important; +} +.button.is-danger.is-outlined { + background-color: transparent; + border-color: #ff003c; + color: #ff003c; +} +.button.is-danger.is-outlined:hover, +.button.is-danger.is-outlined.is-hovered, +.button.is-danger.is-outlined:focus, +.button.is-danger.is-outlined.is-focused { + background-color: #ff003c; + border-color: #ff003c; + color: #121617; +} +.button.is-danger.is-outlined.is-loading::after { + border-color: transparent transparent #ff003c #ff003c !important; +} +.button.is-danger.is-outlined.is-loading:hover::after, +.button.is-danger.is-outlined.is-loading.is-hovered::after, +.button.is-danger.is-outlined.is-loading:focus::after, +.button.is-danger.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #121617 #121617 !important; +} +.button.is-danger.is-outlined[disabled], +fieldset[disabled] .button.is-danger.is-outlined { + background-color: transparent; + border-color: #ff003c; + box-shadow: none; + color: #ff003c; +} +.button.is-danger.is-inverted.is-outlined { + background-color: transparent; + border-color: #121617; + color: #121617; +} +.button.is-danger.is-inverted.is-outlined:hover, +.button.is-danger.is-inverted.is-outlined.is-hovered, +.button.is-danger.is-inverted.is-outlined:focus, +.button.is-danger.is-inverted.is-outlined.is-focused { + background-color: #121617; + color: #ff003c; +} +.button.is-danger.is-inverted.is-outlined.is-loading:hover::after, +.button.is-danger.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-danger.is-inverted.is-outlined.is-loading:focus::after, +.button.is-danger.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #ff003c #ff003c !important; +} +.button.is-danger.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-danger.is-inverted.is-outlined { + background-color: transparent; + border-color: #121617; + box-shadow: none; + color: #121617; +} +.button.is-danger.is-light { + background-color: #ffebef; + color: #eb0037; +} +.button.is-danger.is-light:hover, +.button.is-danger.is-light.is-hovered { + background-color: #ffdee6; + border-color: transparent; + color: #eb0037; +} +.button.is-danger.is-light:active, +.button.is-danger.is-light.is-active { + background-color: #ffd1dc; + border-color: transparent; + color: #eb0037; +} +.button.is-grey-lightest { + background-color: #ededed; + border-color: transparent; + color: #363636; +} +.button.is-grey-lightest:hover, +.button.is-grey-lightest.is-hovered { + background-color: #e7e7e7; + border-color: transparent; + color: #363636; +} +.button.is-grey-lightest:focus, +.button.is-grey-lightest.is-focused { + border-color: transparent; + color: #363636; +} +.button.is-grey-lightest:focus:not(:active), +.button.is-grey-lightest.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(237,237,237,0.25); +} +.button.is-grey-lightest:active, +.button.is-grey-lightest.is-active { + background-color: #e0e0e0; + border-color: transparent; + color: #363636; +} +.button.is-grey-lightest[disabled], +fieldset[disabled] .button.is-grey-lightest { + background-color: #ededed; + border-color: transparent; + box-shadow: none; +} +.button.is-grey-lightest.is-inverted { + background-color: #363636; + color: #ededed; +} +.button.is-grey-lightest.is-inverted:hover, +.button.is-grey-lightest.is-inverted.is-hovered { + background-color: #292929; +} +.button.is-grey-lightest.is-inverted[disabled], +fieldset[disabled] .button.is-grey-lightest.is-inverted { + background-color: #363636; + border-color: transparent; + box-shadow: none; + color: #ededed; +} +.button.is-grey-lightest.is-loading::after { + border-color: transparent transparent #363636 #363636 !important; +} +.button.is-grey-lightest.is-outlined { + background-color: transparent; + border-color: #ededed; + color: #ededed; +} +.button.is-grey-lightest.is-outlined:hover, +.button.is-grey-lightest.is-outlined.is-hovered, +.button.is-grey-lightest.is-outlined:focus, +.button.is-grey-lightest.is-outlined.is-focused { + background-color: #ededed; + border-color: #ededed; + color: #363636; +} +.button.is-grey-lightest.is-outlined.is-loading::after { + border-color: transparent transparent #ededed #ededed !important; +} +.button.is-grey-lightest.is-outlined.is-loading:hover::after, +.button.is-grey-lightest.is-outlined.is-loading.is-hovered::after, +.button.is-grey-lightest.is-outlined.is-loading:focus::after, +.button.is-grey-lightest.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #363636 #363636 !important; +} +.button.is-grey-lightest.is-outlined[disabled], +fieldset[disabled] .button.is-grey-lightest.is-outlined { + background-color: transparent; + border-color: #ededed; + box-shadow: none; + color: #ededed; +} +.button.is-grey-lightest.is-inverted.is-outlined { + background-color: transparent; + border-color: #363636; + color: #363636; +} +.button.is-grey-lightest.is-inverted.is-outlined:hover, +.button.is-grey-lightest.is-inverted.is-outlined.is-hovered, +.button.is-grey-lightest.is-inverted.is-outlined:focus, +.button.is-grey-lightest.is-inverted.is-outlined.is-focused { + background-color: #363636; + color: #ededed; +} +.button.is-grey-lightest.is-inverted.is-outlined.is-loading:hover::after, +.button.is-grey-lightest.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-grey-lightest.is-inverted.is-outlined.is-loading:focus::after, +.button.is-grey-lightest.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #ededed #ededed !important; +} +.button.is-grey-lightest.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-grey-lightest.is-inverted.is-outlined { + background-color: transparent; + border-color: #363636; + box-shadow: none; + color: #363636; +} +.button.is-small { + border-radius: 0; + font-size: 0.75rem; +} +.button.is-normal { + font-size: 1rem; +} +.button.is-medium { + font-size: 1.25rem; +} +.button.is-large { + font-size: 1.5rem; +} +.button[disabled], +fieldset[disabled] .button { + background-color: #000; + border-color: #dbdbdb; + box-shadow: none; + opacity: 0.5; +} +.button.is-fullwidth { + display: flex; + width: 100%; +} +.button.is-loading { + color: transparent !important; + pointer-events: none; +} +.button.is-loading::after { + position: absolute; + left: calc(50% - (1em / 2)); + top: calc(50% - (1em / 2)); + position: absolute !important; +} +.button.is-static { + background-color: #f5f5f5; + border-color: #dbdbdb; + color: #848484; + box-shadow: none; + pointer-events: none; +} +.button.is-rounded { + border-radius: 290486px; + padding-left: calc(1em + 0.25em); + padding-right: calc(1em + 0.25em); +} +.buttons { + align-items: center; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; +} +.buttons .button { + margin-bottom: 0.5rem; +} +.buttons .button:not(:last-child):not(.is-fullwidth) { + margin-right: 0.5rem; +} +.buttons:last-child { + margin-bottom: -0.5rem; +} +.buttons:not(:last-child) { + margin-bottom: 1rem; +} +.buttons.are-small .button:not(.is-normal):not(.is-medium):not(.is-large) { + border-radius: 0; + font-size: 0.75rem; +} +.buttons.are-medium .button:not(.is-small):not(.is-normal):not(.is-large) { + font-size: 1.25rem; +} +.buttons.are-large .button:not(.is-small):not(.is-normal):not(.is-medium) { + font-size: 1.5rem; +} +.buttons.has-addons .button:not(:first-child) { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.buttons.has-addons .button:not(:last-child) { + border-bottom-right-radius: 0; + border-top-right-radius: 0; + margin-right: -1px; +} +.buttons.has-addons .button:last-child { + margin-right: 0; +} +.buttons.has-addons .button:hover, +.buttons.has-addons .button.is-hovered { + z-index: 2; +} +.buttons.has-addons .button:focus, +.buttons.has-addons .button.is-focused, +.buttons.has-addons .button:active, +.buttons.has-addons .button.is-active, +.buttons.has-addons .button.is-selected { + z-index: 3; +} +.buttons.has-addons .button:focus:hover, +.buttons.has-addons .button.is-focused:hover, +.buttons.has-addons .button:active:hover, +.buttons.has-addons .button.is-active:hover, +.buttons.has-addons .button.is-selected:hover { + z-index: 4; +} +.buttons.has-addons .button.is-expanded { + flex-grow: 1; + flex-shrink: 1; +} +.buttons.is-centered { + justify-content: center; +} +.buttons.is-centered:not(.has-addons) .button:not(.is-fullwidth) { + margin-left: 0.25rem; + margin-right: 0.25rem; +} +.buttons.is-right { + justify-content: flex-end; +} +.buttons.is-right:not(.has-addons) .button:not(.is-fullwidth) { + margin-left: 0.25rem; + margin-right: 0.25rem; +} +.container { + flex-grow: 1; + margin: 0 auto; + position: relative; + width: auto; +} +.container.is-fluid { + max-width: none; + padding-left: 64px; + padding-right: 64px; + width: 100%; +} +@media screen and (min-width: 1088px) { + .container { + max-width: 960px; + } +} +@media screen and (max-width: 1279px) { + .container.is-widescreen { + max-width: 1152px; + } +} +@media screen and (max-width: 1471px) { + .container.is-fullhd { + max-width: 1344px; + } +} +@media screen and (min-width: 1280px) { + .container { + max-width: 1152px; + } +} +@media screen and (min-width: 1472px) { + .container { + max-width: 1344px; + } +} +.content li + li { + margin-top: 0.25em; +} +.content p:not(:last-child), +.content dl:not(:last-child), +.content ol:not(:last-child), +.content ul:not(:last-child), +.content blockquote:not(:last-child), +.content pre:not(:last-child), +.content table:not(:last-child) { + margin-bottom: 1em; +} +.content h1, +.content h2, +.content h3, +.content h4, +.content h5, +.content h6 { + color: #fcee09; + font-weight: 400; + line-height: 1.125; +} +.content h1 { + font-size: 2em; + margin-bottom: 0.5em; +} +.content h1:not(:first-child) { + margin-top: 1em; +} +.content h2 { + font-size: 1.75em; + margin-bottom: 0.5714em; +} +.content h2:not(:first-child) { + margin-top: 1.1428em; +} +.content h3 { + font-size: 1.5em; + margin-bottom: 0.6666em; +} +.content h3:not(:first-child) { + margin-top: 1.3333em; +} +.content h4 { + font-size: 1.25em; + margin-bottom: 0.8em; +} +.content h5 { + font-size: 1.125em; + margin-bottom: 0.8888em; +} +.content h6 { + font-size: 1em; + margin-bottom: 1em; +} +.content blockquote { + background-color: #f5f5f5; + border-left: 5px solid #dbdbdb; + padding: 1.25em 1.5em; +} +.content ol { + list-style-position: outside; + margin-left: 2em; + margin-top: 1em; +} +.content ol:not([type]) { + list-style-type: decimal; +} +.content ol:not([type]).is-lower-alpha { + list-style-type: lower-alpha; +} +.content ol:not([type]).is-lower-roman { + list-style-type: lower-roman; +} +.content ol:not([type]).is-upper-alpha { + list-style-type: upper-alpha; +} +.content ol:not([type]).is-upper-roman { + list-style-type: upper-roman; +} +.content ul { + list-style: disc outside; + margin-left: 2em; + margin-top: 1em; +} +.content ul ul { + list-style-type: circle; + margin-top: 0.5em; +} +.content ul ul ul { + list-style-type: square; +} +.content dd { + margin-left: 2em; +} +.content figure { + margin-left: 2em; + margin-right: 2em; + text-align: center; +} +.content figure:not(:first-child) { + margin-top: 2em; +} +.content figure:not(:last-child) { + margin-bottom: 2em; +} +.content figure img { + display: inline-block; +} +.content figure figcaption { + font-style: italic; +} +.content pre { + -webkit-overflow-scrolling: touch; + overflow-x: auto; + padding: 1.25em 1.5em; + white-space: pre; + word-wrap: normal; +} +.content sup, +.content sub { + font-size: 75%; +} +.content table { + width: 100%; +} +.content table td, +.content table th { + border: 1px solid #dbdbdb; + border-width: 0 0 1px; + padding: 0.5em 0.75em; + vertical-align: top; +} +.content table th { + color: #fcee09; +} +.content table th:not([align]) { + text-align: left; +} +.content table thead td, +.content table thead th { + border-width: 0 0 2px; + color: #fcee09; +} +.content table tfoot td, +.content table tfoot th { + border-width: 2px 0 0; + color: #fcee09; +} +.content table tbody tr:last-child td, +.content table tbody tr:last-child th { + border-bottom-width: 0; +} +.content .tabs li + li { + margin-top: 0; +} +.content.is-small { + font-size: 0.75rem; +} +.content.is-medium { + font-size: 1.25rem; +} +.content.is-large { + font-size: 1.5rem; +} +.icon { + align-items: center; + display: inline-flex; + justify-content: center; + height: 1.5rem; + width: 1.5rem; +} +.icon.is-small { + height: 1rem; + width: 1rem; +} +.icon.is-medium { + height: 2rem; + width: 2rem; +} +.icon.is-large { + height: 3rem; + width: 3rem; +} +.image { + display: block; + position: relative; +} +.image img { + display: block; + height: auto; + width: 100%; +} +.image img.is-rounded { + border-radius: 290486px; +} +.image.is-fullwidth { + width: 100%; +} +.image.is-square img, +.image.is-1by1 img, +.image.is-5by4 img, +.image.is-4by3 img, +.image.is-3by2 img, +.image.is-5by3 img, +.image.is-16by9 img, +.image.is-2by1 img, +.image.is-3by1 img, +.image.is-4by5 img, +.image.is-3by4 img, +.image.is-2by3 img, +.image.is-3by5 img, +.image.is-9by16 img, +.image.is-1by2 img, +.image.is-1by3 img, +.image.is-square .has-ratio, +.image.is-1by1 .has-ratio, +.image.is-5by4 .has-ratio, +.image.is-4by3 .has-ratio, +.image.is-3by2 .has-ratio, +.image.is-5by3 .has-ratio, +.image.is-16by9 .has-ratio, +.image.is-2by1 .has-ratio, +.image.is-3by1 .has-ratio, +.image.is-4by5 .has-ratio, +.image.is-3by4 .has-ratio, +.image.is-2by3 .has-ratio, +.image.is-3by5 .has-ratio, +.image.is-9by16 .has-ratio, +.image.is-1by2 .has-ratio, +.image.is-1by3 .has-ratio { + height: 100%; + width: 100%; +} +.image.is-square, +.image.is-1by1 { + padding-top: 100%; +} +.image.is-5by4 { + padding-top: 80%; +} +.image.is-4by3 { + padding-top: 75%; +} +.image.is-3by2 { + padding-top: 66.6666%; +} +.image.is-5by3 { + padding-top: 60%; +} +.image.is-16by9 { + padding-top: 56.25%; +} +.image.is-2by1 { + padding-top: 50%; +} +.image.is-3by1 { + padding-top: 33.3333%; +} +.image.is-4by5 { + padding-top: 125%; +} +.image.is-3by4 { + padding-top: 133.3333%; +} +.image.is-2by3 { + padding-top: 150%; +} +.image.is-3by5 { + padding-top: 166.6666%; +} +.image.is-9by16 { + padding-top: 177.7777%; +} +.image.is-1by2 { + padding-top: 200%; +} +.image.is-1by3 { + padding-top: 300%; +} +.image.is-16x16 { + height: 16px; + width: 16px; +} +.image.is-24x24 { + height: 24px; + width: 24px; +} +.image.is-32x32 { + height: 32px; + width: 32px; +} +.image.is-48x48 { + height: 48px; + width: 48px; +} +.image.is-64x64 { + height: 64px; + width: 64px; +} +.image.is-96x96 { + height: 96px; + width: 96px; +} +.image.is-128x128 { + height: 128px; + width: 128px; +} +.notification { + background-color: #f5f5f5; + border-radius: 0; + padding: 1.25rem 2.5rem 1.25rem 1.5rem; + position: relative; +} +.notification a:not(.button):not(.dropdown-item) { + color: currentColor; + text-decoration: underline; +} +.notification strong { + color: currentColor; +} +.notification code, +.notification pre { + background: #000; +} +.notification pre code { + background: transparent; +} +.notification > .delete { + position: absolute; + right: 0.5rem; + top: 0.5rem; +} +.notification .title, +.notification .subtitle, +.notification .content { + color: currentColor; +} +.notification.is-white { + background-color: #fff; + color: #000; +} +.notification.is-black { + background-color: #000; + color: #fff; +} +.notification.is-light { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.notification.is-dark { + background-color: #363636; + color: #fff; +} +.notification.is-primary { + background-color: #fcee09; + color: #121617; +} +.notification.is-link { + background-color: #02d7f2; + color: rgba(0,0,0,0.7); +} +.notification.is-info { + background-color: #02d7f2; + color: #121617; +} +.notification.is-success { + background-color: #00ff41; + color: #121617; +} +.notification.is-warning { + background-color: #ff8e3c; + color: #121617; +} +.notification.is-danger { + background-color: #ff003c; + color: #121617; +} +.notification.is-grey-lightest { + background-color: #ededed; + color: #363636; +} +.progress { + -moz-appearance: none; + -webkit-appearance: none; + border: none; + border-radius: 290486px; + display: block; + height: 1rem; + overflow: hidden; + padding: 0; + width: 100%; +} +.progress::-webkit-progress-bar { + background-color: #ededed; +} +.progress::-webkit-progress-value { + background-color: #cdcdcd; +} +.progress::-moz-progress-bar { + background-color: #cdcdcd; +} +.progress::-ms-fill { + background-color: #cdcdcd; + border: none; +} +.progress.is-white::-webkit-progress-value { + background-color: #fff; +} +.progress.is-white::-moz-progress-bar { + background-color: #fff; +} +.progress.is-white::-ms-fill { + background-color: #fff; +} +.progress.is-white:indeterminate { + background-image: linear-gradient(to right, #fff 30%, #ededed 30%); +} +.progress.is-black::-webkit-progress-value { + background-color: #000; +} +.progress.is-black::-moz-progress-bar { + background-color: #000; +} +.progress.is-black::-ms-fill { + background-color: #000; +} +.progress.is-black:indeterminate { + background-image: linear-gradient(to right, #000 30%, #ededed 30%); +} +.progress.is-light::-webkit-progress-value { + background-color: #f5f5f5; +} +.progress.is-light::-moz-progress-bar { + background-color: #f5f5f5; +} +.progress.is-light::-ms-fill { + background-color: #f5f5f5; +} +.progress.is-light:indeterminate { + background-image: linear-gradient(to right, #f5f5f5 30%, #ededed 30%); +} +.progress.is-dark::-webkit-progress-value { + background-color: #363636; +} +.progress.is-dark::-moz-progress-bar { + background-color: #363636; +} +.progress.is-dark::-ms-fill { + background-color: #363636; +} +.progress.is-dark:indeterminate { + background-image: linear-gradient(to right, #363636 30%, #ededed 30%); +} +.progress.is-primary::-webkit-progress-value { + background-color: #fcee09; +} +.progress.is-primary::-moz-progress-bar { + background-color: #fcee09; +} +.progress.is-primary::-ms-fill { + background-color: #fcee09; +} +.progress.is-primary:indeterminate { + background-image: linear-gradient(to right, #fcee09 30%, #ededed 30%); +} +.progress.is-link::-webkit-progress-value { + background-color: #02d7f2; +} +.progress.is-link::-moz-progress-bar { + background-color: #02d7f2; +} +.progress.is-link::-ms-fill { + background-color: #02d7f2; +} +.progress.is-link:indeterminate { + background-image: linear-gradient(to right, #02d7f2 30%, #ededed 30%); +} +.progress.is-info::-webkit-progress-value { + background-color: #02d7f2; +} +.progress.is-info::-moz-progress-bar { + background-color: #02d7f2; +} +.progress.is-info::-ms-fill { + background-color: #02d7f2; +} +.progress.is-info:indeterminate { + background-image: linear-gradient(to right, #02d7f2 30%, #ededed 30%); +} +.progress.is-success::-webkit-progress-value { + background-color: #00ff41; +} +.progress.is-success::-moz-progress-bar { + background-color: #00ff41; +} +.progress.is-success::-ms-fill { + background-color: #00ff41; +} +.progress.is-success:indeterminate { + background-image: linear-gradient(to right, #00ff41 30%, #ededed 30%); +} +.progress.is-warning::-webkit-progress-value { + background-color: #ff8e3c; +} +.progress.is-warning::-moz-progress-bar { + background-color: #ff8e3c; +} +.progress.is-warning::-ms-fill { + background-color: #ff8e3c; +} +.progress.is-warning:indeterminate { + background-image: linear-gradient(to right, #ff8e3c 30%, #ededed 30%); +} +.progress.is-danger::-webkit-progress-value { + background-color: #ff003c; +} +.progress.is-danger::-moz-progress-bar { + background-color: #ff003c; +} +.progress.is-danger::-ms-fill { + background-color: #ff003c; +} +.progress.is-danger:indeterminate { + background-image: linear-gradient(to right, #ff003c 30%, #ededed 30%); +} +.progress.is-grey-lightest::-webkit-progress-value { + background-color: #ededed; +} +.progress.is-grey-lightest::-moz-progress-bar { + background-color: #ededed; +} +.progress.is-grey-lightest::-ms-fill { + background-color: #ededed; +} +.progress.is-grey-lightest:indeterminate { + background-image: linear-gradient(to right, #ededed 30%, #ededed 30%); +} +.progress:indeterminate { + animation-duration: 1.5s; + animation-iteration-count: infinite; + animation-name: moveIndeterminate; + animation-timing-function: linear; + background-color: #ededed; + background-image: linear-gradient(to right, #cdcdcd 30%, #ededed 30%); + background-position: top left; + background-repeat: no-repeat; + background-size: 150% 150%; +} +.progress:indeterminate::-webkit-progress-bar { + background-color: transparent; +} +.progress:indeterminate::-moz-progress-bar { + background-color: transparent; +} +.progress.is-small { + height: 0.75rem; +} +.progress.is-medium { + height: 1.25rem; +} +.progress.is-large { + height: 1.5rem; +} +@-moz-keyframes moveIndeterminate { + from { + background-position: 200% 0; + } + to { + background-position: -200% 0; + } +} +@-webkit-keyframes moveIndeterminate { + from { + background-position: 200% 0; + } + to { + background-position: -200% 0; + } +} +@-o-keyframes moveIndeterminate { + from { + background-position: 200% 0; + } + to { + background-position: -200% 0; + } +} +@keyframes moveIndeterminate { + from { + background-position: 200% 0; + } + to { + background-position: -200% 0; + } +} +.table { + background-color: #000; + color: #fcee09; +} +.table td, +.table th { + border: 1px solid #dbdbdb; + border-width: 0 0 1px; + padding: 0.5em 0.75em; + vertical-align: top; +} +.table td.is-white, +.table th.is-white { + background-color: #fff; + border-color: #fff; + color: #000; +} +.table td.is-black, +.table th.is-black { + background-color: #000; + border-color: #000; + color: #fff; +} +.table td.is-light, +.table th.is-light { + background-color: #f5f5f5; + border-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.table td.is-dark, +.table th.is-dark { + background-color: #363636; + border-color: #363636; + color: #fff; +} +.table td.is-primary, +.table th.is-primary { + background-color: #fcee09; + border-color: #fcee09; + color: #121617; +} +.table td.is-link, +.table th.is-link { + background-color: #02d7f2; + border-color: #02d7f2; + color: rgba(0,0,0,0.7); +} +.table td.is-info, +.table th.is-info { + background-color: #02d7f2; + border-color: #02d7f2; + color: #121617; +} +.table td.is-success, +.table th.is-success { + background-color: #00ff41; + border-color: #00ff41; + color: #121617; +} +.table td.is-warning, +.table th.is-warning { + background-color: #ff8e3c; + border-color: #ff8e3c; + color: #121617; +} +.table td.is-danger, +.table th.is-danger { + background-color: #ff003c; + border-color: #ff003c; + color: #121617; +} +.table td.is-grey-lightest, +.table th.is-grey-lightest { + background-color: #ededed; + border-color: #ededed; + color: #363636; +} +.table td.is-narrow, +.table th.is-narrow { + white-space: nowrap; + width: 1%; +} +.table td.is-selected, +.table th.is-selected { + background-color: #fcee09; + color: #121617; +} +.table td.is-selected a, +.table th.is-selected a, +.table td.is-selected strong, +.table th.is-selected strong { + color: currentColor; +} +.table th { + color: #fcee09; +} +.table th:not([align]) { + text-align: left; +} +.table tr.is-selected { + background-color: #fcee09; + color: #121617; +} +.table tr.is-selected a, +.table tr.is-selected strong { + color: currentColor; +} +.table tr.is-selected td, +.table tr.is-selected th { + border-color: #121617; + color: currentColor; +} +.table thead { + background-color: transparent; +} +.table thead td, +.table thead th { + border-width: 0 0 2px; + color: #fcee09; +} +.table tfoot { + background-color: transparent; +} +.table tfoot td, +.table tfoot th { + border-width: 2px 0 0; + color: #fcee09; +} +.table tbody { + background-color: transparent; +} +.table tbody tr:last-child td, +.table tbody tr:last-child th { + border-bottom-width: 0; +} +.table.is-bordered td, +.table.is-bordered th { + border-width: 1px; +} +.table.is-bordered tr:last-child td, +.table.is-bordered tr:last-child th { + border-bottom-width: 1px; +} +.table.is-fullwidth { + width: 100%; +} +.table.is-hoverable tbody tr:not(.is-selected):hover { + background-color: #cdcdcd; +} +.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover { + background-color: #cdcdcd; +} +.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover:nth-child(even) { + background-color: #f5f5f5; +} +.table.is-narrow td, +.table.is-narrow th { + padding: 0.25em 0.5em; +} +.table.is-striped tbody tr:not(.is-selected):nth-child(even) { + background-color: #cdcdcd; +} +.table-container { + -webkit-overflow-scrolling: touch; + overflow: auto; + overflow-y: hidden; + max-width: 100%; +} +.tags { + align-items: center; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; +} +.tags .tag { + margin-bottom: 0.5rem; +} +.tags .tag:not(:last-child) { + margin-right: 0.5rem; +} +.tags:last-child { + margin-bottom: -0.5rem; +} +.tags:not(:last-child) { + margin-bottom: 1rem; +} +.tags.are-medium .tag:not(.is-normal):not(.is-large) { + font-size: 1rem; +} +.tags.are-large .tag:not(.is-normal):not(.is-medium) { + font-size: 1.25rem; +} +.tags.is-centered { + justify-content: center; +} +.tags.is-centered .tag { + margin-right: 0.25rem; + margin-left: 0.25rem; +} +.tags.is-right { + justify-content: flex-end; +} +.tags.is-right .tag:not(:first-child) { + margin-left: 0.5rem; +} +.tags.is-right .tag:not(:last-child) { + margin-right: 0; +} +.tags.has-addons .tag { + margin-right: 0; +} +.tags.has-addons .tag:not(:first-child) { + margin-left: 0; + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.tags.has-addons .tag:not(:last-child) { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.tag:not(body) { + align-items: center; + background-color: #02d7f2; + border-radius: 0; + color: #000; + display: inline-flex; + font-size: 0.75rem; + height: 2em; + justify-content: center; + line-height: 1.5; + padding-left: 0.75em; + padding-right: 0.75em; + white-space: nowrap; +} +.tag:not(body) .delete { + margin-left: 0.25rem; + margin-right: -0.375rem; +} +.tag:not(body).is-white { + background-color: #fff; + color: #000; +} +.tag:not(body).is-black { + background-color: #000; + color: #fff; +} +.tag:not(body).is-light { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.tag:not(body).is-dark { + background-color: #363636; + color: #fff; +} +.tag:not(body).is-primary { + background-color: #fcee09; + color: #121617; +} +.tag:not(body).is-primary.is-light { + background-color: #fffeeb; + color: #928a02; +} +.tag:not(body).is-link { + background-color: #02d7f2; + color: rgba(0,0,0,0.7); +} +.tag:not(body).is-link.is-light { + background-color: #ebfdff; + color: #018293; +} +.tag:not(body).is-info { + background-color: #02d7f2; + color: #121617; +} +.tag:not(body).is-info.is-light { + background-color: #ebfdff; + color: #018293; +} +.tag:not(body).is-success { + background-color: #00ff41; + color: #121617; +} +.tag:not(body).is-success.is-light { + background-color: #ebfff0; + color: #009426; +} +.tag:not(body).is-warning { + background-color: #ff8e3c; + color: #121617; +} +.tag:not(body).is-warning.is-light { + background-color: #fff3eb; + color: #a84700; +} +.tag:not(body).is-danger { + background-color: #ff003c; + color: #121617; +} +.tag:not(body).is-danger.is-light { + background-color: #ffebef; + color: #eb0037; +} +.tag:not(body).is-grey-lightest { + background-color: #ededed; + color: #363636; +} +.tag:not(body).is-normal { + font-size: 0.75rem; +} +.tag:not(body).is-medium { + font-size: 1rem; +} +.tag:not(body).is-large { + font-size: 1.25rem; +} +.tag:not(body) .icon:first-child:not(:last-child) { + margin-left: -0.375em; + margin-right: 0.1875em; +} +.tag:not(body) .icon:last-child:not(:first-child) { + margin-left: 0.1875em; + margin-right: -0.375em; +} +.tag:not(body) .icon:first-child:last-child { + margin-left: -0.375em; + margin-right: -0.375em; +} +.tag:not(body).is-delete { + margin-left: 1px; + padding: 0; + position: relative; + width: 2em; +} +.tag:not(body).is-delete::before, +.tag:not(body).is-delete::after { + background-color: currentColor; + content: ""; + display: block; + left: 50%; + position: absolute; + top: 50%; + transform: translateX(-50%) translateY(-50%) rotate(45deg); + transform-origin: center center; +} +.tag:not(body).is-delete::before { + height: 1px; + width: 50%; +} +.tag:not(body).is-delete::after { + height: 50%; + width: 1px; +} +.tag:not(body).is-delete:hover, +.tag:not(body).is-delete:focus { + background-color: #02c1d9; +} +.tag:not(body).is-delete:active { + background-color: #02aabf; +} +.tag:not(body).is-rounded { + border-radius: 290486px; +} +a.tag:hover { + text-decoration: underline; +} +.title, +.subtitle { + word-break: break-word; +} +.title em, +.subtitle em, +.title span, +.subtitle span { + font-weight: inherit; +} +.title sub, +.subtitle sub { + font-size: 0.75em; +} +.title sup, +.subtitle sup { + font-size: 0.75em; +} +.title .tag, +.subtitle .tag { + vertical-align: middle; +} +.title { + color: #fcee09; + font-size: 2rem; + font-weight: 400; + line-height: 1.125; +} +.title strong { + color: inherit; + font-weight: inherit; +} +.title + .highlight { + margin-top: -0.75rem; +} +.title:not(.is-spaced) + .subtitle { + margin-top: -1.25rem; +} +.title.is-1 { + font-size: 3rem; +} +.title.is-2 { + font-size: 2.5rem; +} +.title.is-3 { + font-size: 2rem; +} +.title.is-4 { + font-size: 1.5rem; +} +.title.is-5 { + font-size: 1.25rem; +} +.title.is-6 { + font-size: 1rem; +} +.title.is-7 { + font-size: 0.85rem; +} +.subtitle { + color: #cdcdcd; + font-size: 1.25rem; + font-weight: 400; + line-height: 1.25; +} +.subtitle strong { + color: #fcee09; + font-weight: 600; +} +.subtitle:not(.is-spaced) + .title { + margin-top: -1.25rem; +} +.subtitle.is-1 { + font-size: 3rem; +} +.subtitle.is-2 { + font-size: 2.5rem; +} +.subtitle.is-3 { + font-size: 2rem; +} +.subtitle.is-4 { + font-size: 1.5rem; +} +.subtitle.is-5 { + font-size: 1.25rem; +} +.subtitle.is-6 { + font-size: 1rem; +} +.subtitle.is-7 { + font-size: 0.85rem; +} +.heading { + display: block; + font-size: 11px; + letter-spacing: 1px; + margin-bottom: 5px; + text-transform: uppercase; +} +.highlight { + font-weight: 400; + max-width: 100%; + overflow: hidden; + padding: 0; +} +.highlight pre { + overflow: auto; + max-width: 100%; +} +.number { + align-items: center; + background-color: #f5f5f5; + border-radius: 290486px; + display: inline-flex; + font-size: 1.25rem; + height: 2em; + justify-content: center; + margin-right: 1.5rem; + min-width: 2.5em; + padding: 0.25rem 0.5rem; + text-align: center; + vertical-align: top; +} +.input, +.textarea, +.select select { + background-color: #000; + border-color: #dbdbdb; + border-radius: 0; + color: #cdcdcd; +} +.input::-moz-placeholder, +.textarea::-moz-placeholder, +.select select::-moz-placeholder { + color: rgba(205,205,205,0.8); +} +.input::-webkit-input-placeholder, +.textarea::-webkit-input-placeholder, +.select select::-webkit-input-placeholder { + color: rgba(205,205,205,0.8); +} +.input:-moz-placeholder, +.textarea:-moz-placeholder, +.select select:-moz-placeholder { + color: rgba(205,205,205,0.8); +} +.input:-ms-input-placeholder, +.textarea:-ms-input-placeholder, +.select select:-ms-input-placeholder { + color: rgba(205,205,205,0.8); +} +.input:hover, +.textarea:hover, +.select select:hover, +.input.is-hovered, +.textarea.is-hovered, +.select select.is-hovered { + border-color: #b5b5b5; +} +.input:focus, +.textarea:focus, +.select select:focus, +.input.is-focused, +.textarea.is-focused, +.select select.is-focused, +.input:active, +.textarea:active, +.select select:active, +.input.is-active, +.textarea.is-active, +.select select.is-active { + border-color: #02d7f2; + box-shadow: 0 0 0 0.125em rgba(2,215,242,0.25); +} +.input[disabled], +.textarea[disabled], +.select select[disabled], +fieldset[disabled] .input, +fieldset[disabled] .textarea, +fieldset[disabled] .select select { + background-color: #f5f5f5; + border-color: #f5f5f5; + box-shadow: none; + color: #848484; +} +.input[disabled]::-moz-placeholder, +.textarea[disabled]::-moz-placeholder, +.select select[disabled]::-moz-placeholder, +fieldset[disabled] .input::-moz-placeholder, +fieldset[disabled] .textarea::-moz-placeholder, +fieldset[disabled] .select select::-moz-placeholder { + color: rgba(132,132,132,0.3); +} +.input[disabled]::-webkit-input-placeholder, +.textarea[disabled]::-webkit-input-placeholder, +.select select[disabled]::-webkit-input-placeholder, +fieldset[disabled] .input::-webkit-input-placeholder, +fieldset[disabled] .textarea::-webkit-input-placeholder, +fieldset[disabled] .select select::-webkit-input-placeholder { + color: rgba(132,132,132,0.3); +} +.input[disabled]:-moz-placeholder, +.textarea[disabled]:-moz-placeholder, +.select select[disabled]:-moz-placeholder, +fieldset[disabled] .input:-moz-placeholder, +fieldset[disabled] .textarea:-moz-placeholder, +fieldset[disabled] .select select:-moz-placeholder { + color: rgba(132,132,132,0.3); +} +.input[disabled]:-ms-input-placeholder, +.textarea[disabled]:-ms-input-placeholder, +.select select[disabled]:-ms-input-placeholder, +fieldset[disabled] .input:-ms-input-placeholder, +fieldset[disabled] .textarea:-ms-input-placeholder, +fieldset[disabled] .select select:-ms-input-placeholder { + color: rgba(132,132,132,0.3); +} +.input, +.textarea { + box-shadow: inset 0 0.0625em 0.125em rgba(0,0,0,0.05); + max-width: 100%; + width: 100%; +} +.input[readonly], +.textarea[readonly] { + box-shadow: none; +} +.input.is-white, +.textarea.is-white { + border-color: #fff; +} +.input.is-white:focus, +.textarea.is-white:focus, +.input.is-white.is-focused, +.textarea.is-white.is-focused, +.input.is-white:active, +.textarea.is-white:active, +.input.is-white.is-active, +.textarea.is-white.is-active { + box-shadow: 0 0 0 0.125em rgba(255,255,255,0.25); +} +.input.is-black, +.textarea.is-black { + border-color: #000; +} +.input.is-black:focus, +.textarea.is-black:focus, +.input.is-black.is-focused, +.textarea.is-black.is-focused, +.input.is-black:active, +.textarea.is-black:active, +.input.is-black.is-active, +.textarea.is-black.is-active { + box-shadow: 0 0 0 0.125em rgba(0,0,0,0.25); +} +.input.is-light, +.textarea.is-light { + border-color: #f5f5f5; +} +.input.is-light:focus, +.textarea.is-light:focus, +.input.is-light.is-focused, +.textarea.is-light.is-focused, +.input.is-light:active, +.textarea.is-light:active, +.input.is-light.is-active, +.textarea.is-light.is-active { + box-shadow: 0 0 0 0.125em rgba(245,245,245,0.25); +} +.input.is-dark, +.textarea.is-dark { + border-color: #363636; +} +.input.is-dark:focus, +.textarea.is-dark:focus, +.input.is-dark.is-focused, +.textarea.is-dark.is-focused, +.input.is-dark:active, +.textarea.is-dark:active, +.input.is-dark.is-active, +.textarea.is-dark.is-active { + box-shadow: 0 0 0 0.125em rgba(54,54,54,0.25); +} +.input.is-primary, +.textarea.is-primary { + border-color: #fcee09; +} +.input.is-primary:focus, +.textarea.is-primary:focus, +.input.is-primary.is-focused, +.textarea.is-primary.is-focused, +.input.is-primary:active, +.textarea.is-primary:active, +.input.is-primary.is-active, +.textarea.is-primary.is-active { + box-shadow: 0 0 0 0.125em rgba(252,238,9,0.25); +} +.input.is-link, +.textarea.is-link { + border-color: #02d7f2; +} +.input.is-link:focus, +.textarea.is-link:focus, +.input.is-link.is-focused, +.textarea.is-link.is-focused, +.input.is-link:active, +.textarea.is-link:active, +.input.is-link.is-active, +.textarea.is-link.is-active { + box-shadow: 0 0 0 0.125em rgba(2,215,242,0.25); +} +.input.is-info, +.textarea.is-info { + border-color: #02d7f2; +} +.input.is-info:focus, +.textarea.is-info:focus, +.input.is-info.is-focused, +.textarea.is-info.is-focused, +.input.is-info:active, +.textarea.is-info:active, +.input.is-info.is-active, +.textarea.is-info.is-active { + box-shadow: 0 0 0 0.125em rgba(2,215,242,0.25); +} +.input.is-success, +.textarea.is-success { + border-color: #00ff41; +} +.input.is-success:focus, +.textarea.is-success:focus, +.input.is-success.is-focused, +.textarea.is-success.is-focused, +.input.is-success:active, +.textarea.is-success:active, +.input.is-success.is-active, +.textarea.is-success.is-active { + box-shadow: 0 0 0 0.125em rgba(0,255,65,0.25); +} +.input.is-warning, +.textarea.is-warning { + border-color: #ff8e3c; +} +.input.is-warning:focus, +.textarea.is-warning:focus, +.input.is-warning.is-focused, +.textarea.is-warning.is-focused, +.input.is-warning:active, +.textarea.is-warning:active, +.input.is-warning.is-active, +.textarea.is-warning.is-active { + box-shadow: 0 0 0 0.125em rgba(255,142,60,0.25); +} +.input.is-danger, +.textarea.is-danger { + border-color: #ff003c; +} +.input.is-danger:focus, +.textarea.is-danger:focus, +.input.is-danger.is-focused, +.textarea.is-danger.is-focused, +.input.is-danger:active, +.textarea.is-danger:active, +.input.is-danger.is-active, +.textarea.is-danger.is-active { + box-shadow: 0 0 0 0.125em rgba(255,0,60,0.25); +} +.input.is-grey-lightest, +.textarea.is-grey-lightest { + border-color: #ededed; +} +.input.is-grey-lightest:focus, +.textarea.is-grey-lightest:focus, +.input.is-grey-lightest.is-focused, +.textarea.is-grey-lightest.is-focused, +.input.is-grey-lightest:active, +.textarea.is-grey-lightest:active, +.input.is-grey-lightest.is-active, +.textarea.is-grey-lightest.is-active { + box-shadow: 0 0 0 0.125em rgba(237,237,237,0.25); +} +.input.is-small, +.textarea.is-small { + border-radius: 0; + font-size: 0.75rem; +} +.input.is-medium, +.textarea.is-medium { + font-size: 1.25rem; +} +.input.is-large, +.textarea.is-large { + font-size: 1.5rem; +} +.input.is-fullwidth, +.textarea.is-fullwidth { + display: block; + width: 100%; +} +.input.is-inline, +.textarea.is-inline { + display: inline; + width: auto; +} +.input.is-rounded { + border-radius: 290486px; + padding-left: calc(calc(0.75em - 1px) + 0.375em); + padding-right: calc(calc(0.75em - 1px) + 0.375em); +} +.input.is-static { + background-color: transparent; + border-color: transparent; + box-shadow: none; + padding-left: 0; + padding-right: 0; +} +.textarea { + display: block; + max-width: 100%; + min-width: 100%; + padding: calc(0.75em - 1px); + resize: vertical; +} +.textarea:not([rows]) { + max-height: 40em; + min-height: 8em; +} +.textarea[rows] { + height: initial; +} +.textarea.has-fixed-size { + resize: none; +} +.checkbox, +.radio { + cursor: pointer; + display: inline-block; + line-height: 1.25; + position: relative; +} +.checkbox input, +.radio input { + cursor: pointer; +} +.checkbox:hover, +.radio:hover { + color: #fcee09; +} +.checkbox[disabled], +.radio[disabled], +fieldset[disabled] .checkbox, +fieldset[disabled] .radio { + color: #848484; + cursor: not-allowed; +} +.radio + .radio { + margin-left: 0.5em; +} +.select { + display: inline-block; + max-width: 100%; + position: relative; + vertical-align: top; +} +.select:not(.is-multiple) { + height: 2.25em; +} +.select:not(.is-multiple):not(.is-loading)::after { + border-color: #02d7f2; + right: 1.125em; + z-index: 4; +} +.select.is-rounded select { + border-radius: 290486px; + padding-left: 1em; +} +.select select { + cursor: pointer; + display: block; + font-size: 1em; + max-width: 100%; + outline: none; +} +.select select::-ms-expand { + display: none; +} +.select select[disabled]:hover, +fieldset[disabled] .select select:hover { + border-color: #f5f5f5; +} +.select select:not([multiple]) { + padding-right: 2.5em; +} +.select select[multiple] { + height: auto; + padding: 0; +} +.select select[multiple] option { + padding: 0.5em 1em; +} +.select:not(.is-multiple):not(.is-loading):hover::after { + border-color: #fcee09; +} +.select.is-white:not(:hover)::after { + border-color: #fff; +} +.select.is-white select { + border-color: #fff; +} +.select.is-white select:hover, +.select.is-white select.is-hovered { + border-color: #f2f2f2; +} +.select.is-white select:focus, +.select.is-white select.is-focused, +.select.is-white select:active, +.select.is-white select.is-active { + box-shadow: 0 0 0 0.125em rgba(255,255,255,0.25); +} +.select.is-black:not(:hover)::after { + border-color: #000; +} +.select.is-black select { + border-color: #000; +} +.select.is-black select:hover, +.select.is-black select.is-hovered { + border-color: #000; +} +.select.is-black select:focus, +.select.is-black select.is-focused, +.select.is-black select:active, +.select.is-black select.is-active { + box-shadow: 0 0 0 0.125em rgba(0,0,0,0.25); +} +.select.is-light:not(:hover)::after { + border-color: #f5f5f5; +} +.select.is-light select { + border-color: #f5f5f5; +} +.select.is-light select:hover, +.select.is-light select.is-hovered { + border-color: #e8e8e8; +} +.select.is-light select:focus, +.select.is-light select.is-focused, +.select.is-light select:active, +.select.is-light select.is-active { + box-shadow: 0 0 0 0.125em rgba(245,245,245,0.25); +} +.select.is-dark:not(:hover)::after { + border-color: #363636; +} +.select.is-dark select { + border-color: #363636; +} +.select.is-dark select:hover, +.select.is-dark select.is-hovered { + border-color: #292929; +} +.select.is-dark select:focus, +.select.is-dark select.is-focused, +.select.is-dark select:active, +.select.is-dark select.is-active { + box-shadow: 0 0 0 0.125em rgba(54,54,54,0.25); +} +.select.is-primary:not(:hover)::after { + border-color: #fcee09; +} +.select.is-primary select { + border-color: #fcee09; +} +.select.is-primary select:hover, +.select.is-primary select.is-hovered { + border-color: #e9db03; +} +.select.is-primary select:focus, +.select.is-primary select.is-focused, +.select.is-primary select:active, +.select.is-primary select.is-active { + box-shadow: 0 0 0 0.125em rgba(252,238,9,0.25); +} +.select.is-link:not(:hover)::after { + border-color: #02d7f2; +} +.select.is-link select { + border-color: #02d7f2; +} +.select.is-link select:hover, +.select.is-link select.is-hovered { + border-color: #02c1d9; +} +.select.is-link select:focus, +.select.is-link select.is-focused, +.select.is-link select:active, +.select.is-link select.is-active { + box-shadow: 0 0 0 0.125em rgba(2,215,242,0.25); +} +.select.is-info:not(:hover)::after { + border-color: #02d7f2; +} +.select.is-info select { + border-color: #02d7f2; +} +.select.is-info select:hover, +.select.is-info select.is-hovered { + border-color: #02c1d9; +} +.select.is-info select:focus, +.select.is-info select.is-focused, +.select.is-info select:active, +.select.is-info select.is-active { + box-shadow: 0 0 0 0.125em rgba(2,215,242,0.25); +} +.select.is-success:not(:hover)::after { + border-color: #00ff41; +} +.select.is-success select { + border-color: #00ff41; +} +.select.is-success select:hover, +.select.is-success select.is-hovered { + border-color: #00e63a; +} +.select.is-success select:focus, +.select.is-success select.is-focused, +.select.is-success select:active, +.select.is-success select.is-active { + box-shadow: 0 0 0 0.125em rgba(0,255,65,0.25); +} +.select.is-warning:not(:hover)::after { + border-color: #ff8e3c; +} +.select.is-warning select { + border-color: #ff8e3c; +} +.select.is-warning select:hover, +.select.is-warning select.is-hovered { + border-color: #ff7f22; +} +.select.is-warning select:focus, +.select.is-warning select.is-focused, +.select.is-warning select:active, +.select.is-warning select.is-active { + box-shadow: 0 0 0 0.125em rgba(255,142,60,0.25); +} +.select.is-danger:not(:hover)::after { + border-color: #ff003c; +} +.select.is-danger select { + border-color: #ff003c; +} +.select.is-danger select:hover, +.select.is-danger select.is-hovered { + border-color: #e60036; +} +.select.is-danger select:focus, +.select.is-danger select.is-focused, +.select.is-danger select:active, +.select.is-danger select.is-active { + box-shadow: 0 0 0 0.125em rgba(255,0,60,0.25); +} +.select.is-grey-lightest:not(:hover)::after { + border-color: #ededed; +} +.select.is-grey-lightest select { + border-color: #ededed; +} +.select.is-grey-lightest select:hover, +.select.is-grey-lightest select.is-hovered { + border-color: #e0e0e0; +} +.select.is-grey-lightest select:focus, +.select.is-grey-lightest select.is-focused, +.select.is-grey-lightest select:active, +.select.is-grey-lightest select.is-active { + box-shadow: 0 0 0 0.125em rgba(237,237,237,0.25); +} +.select.is-small { + border-radius: 0; + font-size: 0.75rem; +} +.select.is-medium { + font-size: 1.25rem; +} +.select.is-large { + font-size: 1.5rem; +} +.select.is-disabled::after { + border-color: #848484; +} +.select.is-fullwidth { + width: 100%; +} +.select.is-fullwidth select { + width: 100%; +} +.select.is-loading::after { + margin-top: 0; + position: absolute; + right: 0.625em; + top: 0.625em; + transform: none; +} +.select.is-loading.is-small:after { + font-size: 0.75rem; +} +.select.is-loading.is-medium:after { + font-size: 1.25rem; +} +.select.is-loading.is-large:after { + font-size: 1.5rem; +} +.file { + align-items: stretch; + display: flex; + justify-content: flex-start; + position: relative; +} +.file.is-white .file-cta { + background-color: #fff; + border-color: transparent; + color: #000; +} +.file.is-white:hover .file-cta, +.file.is-white.is-hovered .file-cta { + background-color: #f9f9f9; + border-color: transparent; + color: #000; +} +.file.is-white:focus .file-cta, +.file.is-white.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(255,255,255,0.25); + color: #000; +} +.file.is-white:active .file-cta, +.file.is-white.is-active .file-cta { + background-color: #f2f2f2; + border-color: transparent; + color: #000; +} +.file.is-black .file-cta { + background-color: #000; + border-color: transparent; + color: #fff; +} +.file.is-black:hover .file-cta, +.file.is-black.is-hovered .file-cta { + background-color: #000; + border-color: transparent; + color: #fff; +} +.file.is-black:focus .file-cta, +.file.is-black.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(0,0,0,0.25); + color: #fff; +} +.file.is-black:active .file-cta, +.file.is-black.is-active .file-cta { + background-color: #000; + border-color: transparent; + color: #fff; +} +.file.is-light .file-cta { + background-color: #f5f5f5; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.file.is-light:hover .file-cta, +.file.is-light.is-hovered .file-cta { + background-color: #eee; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.file.is-light:focus .file-cta, +.file.is-light.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(245,245,245,0.25); + color: rgba(0,0,0,0.7); +} +.file.is-light:active .file-cta, +.file.is-light.is-active .file-cta { + background-color: #e8e8e8; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.file.is-dark .file-cta { + background-color: #363636; + border-color: transparent; + color: #fff; +} +.file.is-dark:hover .file-cta, +.file.is-dark.is-hovered .file-cta { + background-color: #2f2f2f; + border-color: transparent; + color: #fff; +} +.file.is-dark:focus .file-cta, +.file.is-dark.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(54,54,54,0.25); + color: #fff; +} +.file.is-dark:active .file-cta, +.file.is-dark.is-active .file-cta { + background-color: #292929; + border-color: transparent; + color: #fff; +} +.file.is-primary .file-cta { + background-color: #fcee09; + border-color: transparent; + color: #121617; +} +.file.is-primary:hover .file-cta, +.file.is-primary.is-hovered .file-cta { + background-color: #f5e703; + border-color: transparent; + color: #121617; +} +.file.is-primary:focus .file-cta, +.file.is-primary.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(252,238,9,0.25); + color: #121617; +} +.file.is-primary:active .file-cta, +.file.is-primary.is-active .file-cta { + background-color: #e9db03; + border-color: transparent; + color: #121617; +} +.file.is-link .file-cta { + background-color: #02d7f2; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.file.is-link:hover .file-cta, +.file.is-link.is-hovered .file-cta { + background-color: #02cce5; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.file.is-link:focus .file-cta, +.file.is-link.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(2,215,242,0.25); + color: rgba(0,0,0,0.7); +} +.file.is-link:active .file-cta, +.file.is-link.is-active .file-cta { + background-color: #02c1d9; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.file.is-info .file-cta { + background-color: #02d7f2; + border-color: transparent; + color: #121617; +} +.file.is-info:hover .file-cta, +.file.is-info.is-hovered .file-cta { + background-color: #02cce5; + border-color: transparent; + color: #121617; +} +.file.is-info:focus .file-cta, +.file.is-info.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(2,215,242,0.25); + color: #121617; +} +.file.is-info:active .file-cta, +.file.is-info.is-active .file-cta { + background-color: #02c1d9; + border-color: transparent; + color: #121617; +} +.file.is-success .file-cta { + background-color: #00ff41; + border-color: transparent; + color: #121617; +} +.file.is-success:hover .file-cta, +.file.is-success.is-hovered .file-cta { + background-color: #00f23e; + border-color: transparent; + color: #121617; +} +.file.is-success:focus .file-cta, +.file.is-success.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(0,255,65,0.25); + color: #121617; +} +.file.is-success:active .file-cta, +.file.is-success.is-active .file-cta { + background-color: #00e63a; + border-color: transparent; + color: #121617; +} +.file.is-warning .file-cta { + background-color: #ff8e3c; + border-color: transparent; + color: #121617; +} +.file.is-warning:hover .file-cta, +.file.is-warning.is-hovered .file-cta { + background-color: #ff872f; + border-color: transparent; + color: #121617; +} +.file.is-warning:focus .file-cta, +.file.is-warning.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(255,142,60,0.25); + color: #121617; +} +.file.is-warning:active .file-cta, +.file.is-warning.is-active .file-cta { + background-color: #ff7f22; + border-color: transparent; + color: #121617; +} +.file.is-danger .file-cta { + background-color: #ff003c; + border-color: transparent; + color: #121617; +} +.file.is-danger:hover .file-cta, +.file.is-danger.is-hovered .file-cta { + background-color: #f20039; + border-color: transparent; + color: #121617; +} +.file.is-danger:focus .file-cta, +.file.is-danger.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(255,0,60,0.25); + color: #121617; +} +.file.is-danger:active .file-cta, +.file.is-danger.is-active .file-cta { + background-color: #e60036; + border-color: transparent; + color: #121617; +} +.file.is-grey-lightest .file-cta { + background-color: #ededed; + border-color: transparent; + color: #363636; +} +.file.is-grey-lightest:hover .file-cta, +.file.is-grey-lightest.is-hovered .file-cta { + background-color: #e7e7e7; + border-color: transparent; + color: #363636; +} +.file.is-grey-lightest:focus .file-cta, +.file.is-grey-lightest.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(237,237,237,0.25); + color: #363636; +} +.file.is-grey-lightest:active .file-cta, +.file.is-grey-lightest.is-active .file-cta { + background-color: #e0e0e0; + border-color: transparent; + color: #363636; +} +.file.is-small { + font-size: 0.75rem; +} +.file.is-medium { + font-size: 1.25rem; +} +.file.is-medium .file-icon .fa { + font-size: 21px; +} +.file.is-large { + font-size: 1.5rem; +} +.file.is-large .file-icon .fa { + font-size: 28px; +} +.file.has-name .file-cta { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.file.has-name .file-name { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.file.has-name.is-empty .file-cta { + border-radius: 0; +} +.file.has-name.is-empty .file-name { + display: none; +} +.file.is-boxed .file-label { + flex-direction: column; +} +.file.is-boxed .file-cta { + flex-direction: column; + height: auto; + padding: 1em 3em; +} +.file.is-boxed .file-name { + border-width: 0 1px 1px; +} +.file.is-boxed .file-icon { + height: 1.5em; + width: 1.5em; +} +.file.is-boxed .file-icon .fa { + font-size: 21px; +} +.file.is-boxed.is-small .file-icon .fa { + font-size: 14px; +} +.file.is-boxed.is-medium .file-icon .fa { + font-size: 28px; +} +.file.is-boxed.is-large .file-icon .fa { + font-size: 35px; +} +.file.is-boxed.has-name .file-cta { + border-radius: 0 0 0 0; +} +.file.is-boxed.has-name .file-name { + border-radius: 0 0 0 0; + border-width: 0 1px 1px; +} +.file.is-centered { + justify-content: center; +} +.file.is-fullwidth .file-label { + width: 100%; +} +.file.is-fullwidth .file-name { + flex-grow: 1; + max-width: none; +} +.file.is-right { + justify-content: flex-end; +} +.file.is-right .file-cta { + border-radius: 0 0 0 0; +} +.file.is-right .file-name { + border-radius: 0 0 0 0; + border-width: 1px 0 1px 1px; + order: -1; +} +.file-label { + align-items: stretch; + display: flex; + cursor: pointer; + justify-content: flex-start; + overflow: hidden; + position: relative; +} +.file-label:hover .file-cta { + background-color: #eee; + color: #fcee09; +} +.file-label:hover .file-name { + border-color: #d5d5d5; +} +.file-label:active .file-cta { + background-color: #e8e8e8; + color: #fcee09; +} +.file-label:active .file-name { + border-color: #cfcfcf; +} +.file-input { + height: 100%; + left: 0; + opacity: 0; + outline: none; + position: absolute; + top: 0; + width: 100%; +} +.file-cta, +.file-name { + border-color: #dbdbdb; + border-radius: 0; + font-size: 1em; + padding-left: 1em; + padding-right: 1em; + white-space: nowrap; +} +.file-cta { + background-color: #f5f5f5; + color: #cdcdcd; +} +.file-name { + border-color: #dbdbdb; + border-style: solid; + border-width: 1px 1px 1px 0; + display: block; + max-width: 16em; + overflow: hidden; + text-align: left; + text-overflow: ellipsis; +} +.file-icon { + align-items: center; + display: flex; + height: 1em; + justify-content: center; + margin-right: 0.5em; + width: 1em; +} +.file-icon .fa { + font-size: 14px; +} +.label { + color: #fcee09; + display: block; + font-size: 1rem; + font-weight: 700; +} +.label:not(:last-child) { + margin-bottom: 0.5em; +} +.label.is-small { + font-size: 0.75rem; +} +.label.is-medium { + font-size: 1.25rem; +} +.label.is-large { + font-size: 1.5rem; +} +.help { + display: block; + font-size: 0.75rem; + margin-top: 0.25rem; +} +.help.is-white { + color: #fff; +} +.help.is-black { + color: #000; +} +.help.is-light { + color: #f5f5f5; +} +.help.is-dark { + color: #363636; +} +.help.is-primary { + color: #fcee09; +} +.help.is-link { + color: #02d7f2; +} +.help.is-info { + color: #02d7f2; +} +.help.is-success { + color: #00ff41; +} +.help.is-warning { + color: #ff8e3c; +} +.help.is-danger { + color: #ff003c; +} +.help.is-grey-lightest { + color: #ededed; +} +.field:not(:last-child) { + margin-bottom: 0.75rem; +} +.field.has-addons { + display: flex; + justify-content: flex-start; +} +.field.has-addons .control:not(:last-child) { + margin-right: -1px; +} +.field.has-addons .control:not(:first-child):not(:last-child) .button, +.field.has-addons .control:not(:first-child):not(:last-child) .input, +.field.has-addons .control:not(:first-child):not(:last-child) .select select { + border-radius: 0; +} +.field.has-addons .control:first-child:not(:only-child) .button, +.field.has-addons .control:first-child:not(:only-child) .input, +.field.has-addons .control:first-child:not(:only-child) .select select { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.field.has-addons .control:last-child:not(:only-child) .button, +.field.has-addons .control:last-child:not(:only-child) .input, +.field.has-addons .control:last-child:not(:only-child) .select select { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.field.has-addons .control .button:not([disabled]):hover, +.field.has-addons .control .input:not([disabled]):hover, +.field.has-addons .control .select select:not([disabled]):hover, +.field.has-addons .control .button:not([disabled]).is-hovered, +.field.has-addons .control .input:not([disabled]).is-hovered, +.field.has-addons .control .select select:not([disabled]).is-hovered { + z-index: 2; +} +.field.has-addons .control .button:not([disabled]):focus, +.field.has-addons .control .input:not([disabled]):focus, +.field.has-addons .control .select select:not([disabled]):focus, +.field.has-addons .control .button:not([disabled]).is-focused, +.field.has-addons .control .input:not([disabled]).is-focused, +.field.has-addons .control .select select:not([disabled]).is-focused, +.field.has-addons .control .button:not([disabled]):active, +.field.has-addons .control .input:not([disabled]):active, +.field.has-addons .control .select select:not([disabled]):active, +.field.has-addons .control .button:not([disabled]).is-active, +.field.has-addons .control .input:not([disabled]).is-active, +.field.has-addons .control .select select:not([disabled]).is-active { + z-index: 3; +} +.field.has-addons .control .button:not([disabled]):focus:hover, +.field.has-addons .control .input:not([disabled]):focus:hover, +.field.has-addons .control .select select:not([disabled]):focus:hover, +.field.has-addons .control .button:not([disabled]).is-focused:hover, +.field.has-addons .control .input:not([disabled]).is-focused:hover, +.field.has-addons .control .select select:not([disabled]).is-focused:hover, +.field.has-addons .control .button:not([disabled]):active:hover, +.field.has-addons .control .input:not([disabled]):active:hover, +.field.has-addons .control .select select:not([disabled]):active:hover, +.field.has-addons .control .button:not([disabled]).is-active:hover, +.field.has-addons .control .input:not([disabled]).is-active:hover, +.field.has-addons .control .select select:not([disabled]).is-active:hover { + z-index: 4; +} +.field.has-addons .control.is-expanded { + flex-grow: 1; + flex-shrink: 1; +} +.field.has-addons.has-addons-centered { + justify-content: center; +} +.field.has-addons.has-addons-right { + justify-content: flex-end; +} +.field.has-addons.has-addons-fullwidth .control { + flex-grow: 1; + flex-shrink: 0; +} +.field.is-grouped { + display: flex; + justify-content: flex-start; +} +.field.is-grouped > .control { + flex-shrink: 0; +} +.field.is-grouped > .control:not(:last-child) { + margin-bottom: 0; + margin-right: 0.75rem; +} +.field.is-grouped > .control.is-expanded { + flex-grow: 1; + flex-shrink: 1; +} +.field.is-grouped.is-grouped-centered { + justify-content: center; +} +.field.is-grouped.is-grouped-right { + justify-content: flex-end; +} +.field.is-grouped.is-grouped-multiline { + flex-wrap: wrap; +} +.field.is-grouped.is-grouped-multiline > .control:last-child, +.field.is-grouped.is-grouped-multiline > .control:not(:last-child) { + margin-bottom: 0.75rem; +} +.field.is-grouped.is-grouped-multiline:last-child { + margin-bottom: -0.75rem; +} +.field.is-grouped.is-grouped-multiline:not(:last-child) { + margin-bottom: 0; +} +@media screen and (min-width: 769px), print { + .field.is-horizontal { + display: flex; + } +} +.field-label .label { + font-size: inherit; +} +@media screen and (max-width: 768px) { + .field-label { + margin-bottom: 0.5rem; + } +} +@media screen and (min-width: 769px), print { + .field-label { + flex-basis: 0; + flex-grow: 1; + flex-shrink: 0; + margin-right: 1.5rem; + text-align: right; + } + .field-label.is-small { + font-size: 0.75rem; + padding-top: 0.375em; + } + .field-label.is-normal { + padding-top: 0.375em; + } + .field-label.is-medium { + font-size: 1.25rem; + padding-top: 0.375em; + } + .field-label.is-large { + font-size: 1.5rem; + padding-top: 0.375em; + } +} +.field-body .field .field { + margin-bottom: 0; +} +@media screen and (min-width: 769px), print { + .field-body { + display: flex; + flex-basis: 0; + flex-grow: 5; + flex-shrink: 1; + } + .field-body .field { + margin-bottom: 0; + } + .field-body > .field { + flex-shrink: 1; + } + .field-body > .field:not(.is-narrow) { + flex-grow: 1; + } + .field-body > .field:not(:last-child) { + margin-right: 0.75rem; + } +} +.control { + box-sizing: border-box; + clear: both; + font-size: 1rem; + position: relative; + text-align: left; +} +.control.has-icons-left .input:focus ~ .icon, +.control.has-icons-right .input:focus ~ .icon, +.control.has-icons-left .select:focus ~ .icon, +.control.has-icons-right .select:focus ~ .icon { + color: #cdcdcd; +} +.control.has-icons-left .input.is-small ~ .icon, +.control.has-icons-right .input.is-small ~ .icon, +.control.has-icons-left .select.is-small ~ .icon, +.control.has-icons-right .select.is-small ~ .icon { + font-size: 0.75rem; +} +.control.has-icons-left .input.is-medium ~ .icon, +.control.has-icons-right .input.is-medium ~ .icon, +.control.has-icons-left .select.is-medium ~ .icon, +.control.has-icons-right .select.is-medium ~ .icon { + font-size: 1.25rem; +} +.control.has-icons-left .input.is-large ~ .icon, +.control.has-icons-right .input.is-large ~ .icon, +.control.has-icons-left .select.is-large ~ .icon, +.control.has-icons-right .select.is-large ~ .icon { + font-size: 1.5rem; +} +.control.has-icons-left .icon, +.control.has-icons-right .icon { + color: #dbdbdb; + height: 2.25em; + pointer-events: none; + position: absolute; + top: 0; + width: 2.25em; + z-index: 4; +} +.control.has-icons-left .input, +.control.has-icons-left .select select { + padding-left: 2.25em; +} +.control.has-icons-left .icon.is-left { + left: 0; +} +.control.has-icons-right .input, +.control.has-icons-right .select select { + padding-right: 2.25em; +} +.control.has-icons-right .icon.is-right { + right: 0; +} +.control.is-loading::after { + position: absolute !important; + right: 0.625em; + top: 0.625em; + z-index: 4; +} +.control.is-loading.is-small:after { + font-size: 0.75rem; +} +.control.is-loading.is-medium:after { + font-size: 1.25rem; +} +.control.is-loading.is-large:after { + font-size: 1.5rem; +} +.column { + display: block; + flex-basis: 0; + flex-grow: 1; + flex-shrink: 1; + padding: 0.75rem; +} +.columns.is-mobile > .column.is-narrow { + flex: none; +} +.columns.is-mobile > .column.is-full { + flex: none; + width: 100%; +} +.columns.is-mobile > .column.is-three-quarters { + flex: none; + width: 75%; +} +.columns.is-mobile > .column.is-two-thirds { + flex: none; + width: 66.6666%; +} +.columns.is-mobile > .column.is-half { + flex: none; + width: 50%; +} +.columns.is-mobile > .column.is-one-third { + flex: none; + width: 33.3333%; +} +.columns.is-mobile > .column.is-one-quarter { + flex: none; + width: 25%; +} +.columns.is-mobile > .column.is-one-fifth { + flex: none; + width: 20%; +} +.columns.is-mobile > .column.is-two-fifths { + flex: none; + width: 40%; +} +.columns.is-mobile > .column.is-three-fifths { + flex: none; + width: 60%; +} +.columns.is-mobile > .column.is-four-fifths { + flex: none; + width: 80%; +} +.columns.is-mobile > .column.is-offset-three-quarters { + margin-left: 75%; +} +.columns.is-mobile > .column.is-offset-two-thirds { + margin-left: 66.6666%; +} +.columns.is-mobile > .column.is-offset-half { + margin-left: 50%; +} +.columns.is-mobile > .column.is-offset-one-third { + margin-left: 33.3333%; +} +.columns.is-mobile > .column.is-offset-one-quarter { + margin-left: 25%; +} +.columns.is-mobile > .column.is-offset-one-fifth { + margin-left: 20%; +} +.columns.is-mobile > .column.is-offset-two-fifths { + margin-left: 40%; +} +.columns.is-mobile > .column.is-offset-three-fifths { + margin-left: 60%; +} +.columns.is-mobile > .column.is-offset-four-fifths { + margin-left: 80%; +} +.columns.is-mobile > .column.is-0 { + flex: none; + width: 0%; +} +.columns.is-mobile > .column.is-offset-0 { + margin-left: 0%; +} +.columns.is-mobile > .column.is-1 { + flex: none; + width: 8.33333%; +} +.columns.is-mobile > .column.is-offset-1 { + margin-left: 8.33333%; +} +.columns.is-mobile > .column.is-2 { + flex: none; + width: 16.66667%; +} +.columns.is-mobile > .column.is-offset-2 { + margin-left: 16.66667%; +} +.columns.is-mobile > .column.is-3 { + flex: none; + width: 25%; +} +.columns.is-mobile > .column.is-offset-3 { + margin-left: 25%; +} +.columns.is-mobile > .column.is-4 { + flex: none; + width: 33.33333%; +} +.columns.is-mobile > .column.is-offset-4 { + margin-left: 33.33333%; +} +.columns.is-mobile > .column.is-5 { + flex: none; + width: 41.66667%; +} +.columns.is-mobile > .column.is-offset-5 { + margin-left: 41.66667%; +} +.columns.is-mobile > .column.is-6 { + flex: none; + width: 50%; +} +.columns.is-mobile > .column.is-offset-6 { + margin-left: 50%; +} +.columns.is-mobile > .column.is-7 { + flex: none; + width: 58.33333%; +} +.columns.is-mobile > .column.is-offset-7 { + margin-left: 58.33333%; +} +.columns.is-mobile > .column.is-8 { + flex: none; + width: 66.66667%; +} +.columns.is-mobile > .column.is-offset-8 { + margin-left: 66.66667%; +} +.columns.is-mobile > .column.is-9 { + flex: none; + width: 75%; +} +.columns.is-mobile > .column.is-offset-9 { + margin-left: 75%; +} +.columns.is-mobile > .column.is-10 { + flex: none; + width: 83.33333%; +} +.columns.is-mobile > .column.is-offset-10 { + margin-left: 83.33333%; +} +.columns.is-mobile > .column.is-11 { + flex: none; + width: 91.66667%; +} +.columns.is-mobile > .column.is-offset-11 { + margin-left: 91.66667%; +} +.columns.is-mobile > .column.is-12 { + flex: none; + width: 100%; +} +.columns.is-mobile > .column.is-offset-12 { + margin-left: 100%; +} +@media screen and (max-width: 768px) { + .column.is-narrow-mobile { + flex: none; + } + .column.is-full-mobile { + flex: none; + width: 100%; + } + .column.is-three-quarters-mobile { + flex: none; + width: 75%; + } + .column.is-two-thirds-mobile { + flex: none; + width: 66.6666%; + } + .column.is-half-mobile { + flex: none; + width: 50%; + } + .column.is-one-third-mobile { + flex: none; + width: 33.3333%; + } + .column.is-one-quarter-mobile { + flex: none; + width: 25%; + } + .column.is-one-fifth-mobile { + flex: none; + width: 20%; + } + .column.is-two-fifths-mobile { + flex: none; + width: 40%; + } + .column.is-three-fifths-mobile { + flex: none; + width: 60%; + } + .column.is-four-fifths-mobile { + flex: none; + width: 80%; + } + .column.is-offset-three-quarters-mobile { + margin-left: 75%; + } + .column.is-offset-two-thirds-mobile { + margin-left: 66.6666%; + } + .column.is-offset-half-mobile { + margin-left: 50%; + } + .column.is-offset-one-third-mobile { + margin-left: 33.3333%; + } + .column.is-offset-one-quarter-mobile { + margin-left: 25%; + } + .column.is-offset-one-fifth-mobile { + margin-left: 20%; + } + .column.is-offset-two-fifths-mobile { + margin-left: 40%; + } + .column.is-offset-three-fifths-mobile { + margin-left: 60%; + } + .column.is-offset-four-fifths-mobile { + margin-left: 80%; + } + .column.is-0-mobile { + flex: none; + width: 0%; + } + .column.is-offset-0-mobile { + margin-left: 0%; + } + .column.is-1-mobile { + flex: none; + width: 8.33333%; + } + .column.is-offset-1-mobile { + margin-left: 8.33333%; + } + .column.is-2-mobile { + flex: none; + width: 16.66667%; + } + .column.is-offset-2-mobile { + margin-left: 16.66667%; + } + .column.is-3-mobile { + flex: none; + width: 25%; + } + .column.is-offset-3-mobile { + margin-left: 25%; + } + .column.is-4-mobile { + flex: none; + width: 33.33333%; + } + .column.is-offset-4-mobile { + margin-left: 33.33333%; + } + .column.is-5-mobile { + flex: none; + width: 41.66667%; + } + .column.is-offset-5-mobile { + margin-left: 41.66667%; + } + .column.is-6-mobile { + flex: none; + width: 50%; + } + .column.is-offset-6-mobile { + margin-left: 50%; + } + .column.is-7-mobile { + flex: none; + width: 58.33333%; + } + .column.is-offset-7-mobile { + margin-left: 58.33333%; + } + .column.is-8-mobile { + flex: none; + width: 66.66667%; + } + .column.is-offset-8-mobile { + margin-left: 66.66667%; + } + .column.is-9-mobile { + flex: none; + width: 75%; + } + .column.is-offset-9-mobile { + margin-left: 75%; + } + .column.is-10-mobile { + flex: none; + width: 83.33333%; + } + .column.is-offset-10-mobile { + margin-left: 83.33333%; + } + .column.is-11-mobile { + flex: none; + width: 91.66667%; + } + .column.is-offset-11-mobile { + margin-left: 91.66667%; + } + .column.is-12-mobile { + flex: none; + width: 100%; + } + .column.is-offset-12-mobile { + margin-left: 100%; + } +} +@media screen and (min-width: 769px), print { + .column.is-narrow, + .column.is-narrow-tablet { + flex: none; + } + .column.is-full, + .column.is-full-tablet { + flex: none; + width: 100%; + } + .column.is-three-quarters, + .column.is-three-quarters-tablet { + flex: none; + width: 75%; + } + .column.is-two-thirds, + .column.is-two-thirds-tablet { + flex: none; + width: 66.6666%; + } + .column.is-half, + .column.is-half-tablet { + flex: none; + width: 50%; + } + .column.is-one-third, + .column.is-one-third-tablet { + flex: none; + width: 33.3333%; + } + .column.is-one-quarter, + .column.is-one-quarter-tablet { + flex: none; + width: 25%; + } + .column.is-one-fifth, + .column.is-one-fifth-tablet { + flex: none; + width: 20%; + } + .column.is-two-fifths, + .column.is-two-fifths-tablet { + flex: none; + width: 40%; + } + .column.is-three-fifths, + .column.is-three-fifths-tablet { + flex: none; + width: 60%; + } + .column.is-four-fifths, + .column.is-four-fifths-tablet { + flex: none; + width: 80%; + } + .column.is-offset-three-quarters, + .column.is-offset-three-quarters-tablet { + margin-left: 75%; + } + .column.is-offset-two-thirds, + .column.is-offset-two-thirds-tablet { + margin-left: 66.6666%; + } + .column.is-offset-half, + .column.is-offset-half-tablet { + margin-left: 50%; + } + .column.is-offset-one-third, + .column.is-offset-one-third-tablet { + margin-left: 33.3333%; + } + .column.is-offset-one-quarter, + .column.is-offset-one-quarter-tablet { + margin-left: 25%; + } + .column.is-offset-one-fifth, + .column.is-offset-one-fifth-tablet { + margin-left: 20%; + } + .column.is-offset-two-fifths, + .column.is-offset-two-fifths-tablet { + margin-left: 40%; + } + .column.is-offset-three-fifths, + .column.is-offset-three-fifths-tablet { + margin-left: 60%; + } + .column.is-offset-four-fifths, + .column.is-offset-four-fifths-tablet { + margin-left: 80%; + } + .column.is-0, + .column.is-0-tablet { + flex: none; + width: 0%; + } + .column.is-offset-0, + .column.is-offset-0-tablet { + margin-left: 0%; + } + .column.is-1, + .column.is-1-tablet { + flex: none; + width: 8.33333%; + } + .column.is-offset-1, + .column.is-offset-1-tablet { + margin-left: 8.33333%; + } + .column.is-2, + .column.is-2-tablet { + flex: none; + width: 16.66667%; + } + .column.is-offset-2, + .column.is-offset-2-tablet { + margin-left: 16.66667%; + } + .column.is-3, + .column.is-3-tablet { + flex: none; + width: 25%; + } + .column.is-offset-3, + .column.is-offset-3-tablet { + margin-left: 25%; + } + .column.is-4, + .column.is-4-tablet { + flex: none; + width: 33.33333%; + } + .column.is-offset-4, + .column.is-offset-4-tablet { + margin-left: 33.33333%; + } + .column.is-5, + .column.is-5-tablet { + flex: none; + width: 41.66667%; + } + .column.is-offset-5, + .column.is-offset-5-tablet { + margin-left: 41.66667%; + } + .column.is-6, + .column.is-6-tablet { + flex: none; + width: 50%; + } + .column.is-offset-6, + .column.is-offset-6-tablet { + margin-left: 50%; + } + .column.is-7, + .column.is-7-tablet { + flex: none; + width: 58.33333%; + } + .column.is-offset-7, + .column.is-offset-7-tablet { + margin-left: 58.33333%; + } + .column.is-8, + .column.is-8-tablet { + flex: none; + width: 66.66667%; + } + .column.is-offset-8, + .column.is-offset-8-tablet { + margin-left: 66.66667%; + } + .column.is-9, + .column.is-9-tablet { + flex: none; + width: 75%; + } + .column.is-offset-9, + .column.is-offset-9-tablet { + margin-left: 75%; + } + .column.is-10, + .column.is-10-tablet { + flex: none; + width: 83.33333%; + } + .column.is-offset-10, + .column.is-offset-10-tablet { + margin-left: 83.33333%; + } + .column.is-11, + .column.is-11-tablet { + flex: none; + width: 91.66667%; + } + .column.is-offset-11, + .column.is-offset-11-tablet { + margin-left: 91.66667%; + } + .column.is-12, + .column.is-12-tablet { + flex: none; + width: 100%; + } + .column.is-offset-12, + .column.is-offset-12-tablet { + margin-left: 100%; + } +} +@media screen and (max-width: 1087px) { + .column.is-narrow-touch { + flex: none; + } + .column.is-full-touch { + flex: none; + width: 100%; + } + .column.is-three-quarters-touch { + flex: none; + width: 75%; + } + .column.is-two-thirds-touch { + flex: none; + width: 66.6666%; + } + .column.is-half-touch { + flex: none; + width: 50%; + } + .column.is-one-third-touch { + flex: none; + width: 33.3333%; + } + .column.is-one-quarter-touch { + flex: none; + width: 25%; + } + .column.is-one-fifth-touch { + flex: none; + width: 20%; + } + .column.is-two-fifths-touch { + flex: none; + width: 40%; + } + .column.is-three-fifths-touch { + flex: none; + width: 60%; + } + .column.is-four-fifths-touch { + flex: none; + width: 80%; + } + .column.is-offset-three-quarters-touch { + margin-left: 75%; + } + .column.is-offset-two-thirds-touch { + margin-left: 66.6666%; + } + .column.is-offset-half-touch { + margin-left: 50%; + } + .column.is-offset-one-third-touch { + margin-left: 33.3333%; + } + .column.is-offset-one-quarter-touch { + margin-left: 25%; + } + .column.is-offset-one-fifth-touch { + margin-left: 20%; + } + .column.is-offset-two-fifths-touch { + margin-left: 40%; + } + .column.is-offset-three-fifths-touch { + margin-left: 60%; + } + .column.is-offset-four-fifths-touch { + margin-left: 80%; + } + .column.is-0-touch { + flex: none; + width: 0%; + } + .column.is-offset-0-touch { + margin-left: 0%; + } + .column.is-1-touch { + flex: none; + width: 8.33333%; + } + .column.is-offset-1-touch { + margin-left: 8.33333%; + } + .column.is-2-touch { + flex: none; + width: 16.66667%; + } + .column.is-offset-2-touch { + margin-left: 16.66667%; + } + .column.is-3-touch { + flex: none; + width: 25%; + } + .column.is-offset-3-touch { + margin-left: 25%; + } + .column.is-4-touch { + flex: none; + width: 33.33333%; + } + .column.is-offset-4-touch { + margin-left: 33.33333%; + } + .column.is-5-touch { + flex: none; + width: 41.66667%; + } + .column.is-offset-5-touch { + margin-left: 41.66667%; + } + .column.is-6-touch { + flex: none; + width: 50%; + } + .column.is-offset-6-touch { + margin-left: 50%; + } + .column.is-7-touch { + flex: none; + width: 58.33333%; + } + .column.is-offset-7-touch { + margin-left: 58.33333%; + } + .column.is-8-touch { + flex: none; + width: 66.66667%; + } + .column.is-offset-8-touch { + margin-left: 66.66667%; + } + .column.is-9-touch { + flex: none; + width: 75%; + } + .column.is-offset-9-touch { + margin-left: 75%; + } + .column.is-10-touch { + flex: none; + width: 83.33333%; + } + .column.is-offset-10-touch { + margin-left: 83.33333%; + } + .column.is-11-touch { + flex: none; + width: 91.66667%; + } + .column.is-offset-11-touch { + margin-left: 91.66667%; + } + .column.is-12-touch { + flex: none; + width: 100%; + } + .column.is-offset-12-touch { + margin-left: 100%; + } +} +@media screen and (min-width: 1088px) { + .column.is-narrow-desktop { + flex: none; + } + .column.is-full-desktop { + flex: none; + width: 100%; + } + .column.is-three-quarters-desktop { + flex: none; + width: 75%; + } + .column.is-two-thirds-desktop { + flex: none; + width: 66.6666%; + } + .column.is-half-desktop { + flex: none; + width: 50%; + } + .column.is-one-third-desktop { + flex: none; + width: 33.3333%; + } + .column.is-one-quarter-desktop { + flex: none; + width: 25%; + } + .column.is-one-fifth-desktop { + flex: none; + width: 20%; + } + .column.is-two-fifths-desktop { + flex: none; + width: 40%; + } + .column.is-three-fifths-desktop { + flex: none; + width: 60%; + } + .column.is-four-fifths-desktop { + flex: none; + width: 80%; + } + .column.is-offset-three-quarters-desktop { + margin-left: 75%; + } + .column.is-offset-two-thirds-desktop { + margin-left: 66.6666%; + } + .column.is-offset-half-desktop { + margin-left: 50%; + } + .column.is-offset-one-third-desktop { + margin-left: 33.3333%; + } + .column.is-offset-one-quarter-desktop { + margin-left: 25%; + } + .column.is-offset-one-fifth-desktop { + margin-left: 20%; + } + .column.is-offset-two-fifths-desktop { + margin-left: 40%; + } + .column.is-offset-three-fifths-desktop { + margin-left: 60%; + } + .column.is-offset-four-fifths-desktop { + margin-left: 80%; + } + .column.is-0-desktop { + flex: none; + width: 0%; + } + .column.is-offset-0-desktop { + margin-left: 0%; + } + .column.is-1-desktop { + flex: none; + width: 8.33333%; + } + .column.is-offset-1-desktop { + margin-left: 8.33333%; + } + .column.is-2-desktop { + flex: none; + width: 16.66667%; + } + .column.is-offset-2-desktop { + margin-left: 16.66667%; + } + .column.is-3-desktop { + flex: none; + width: 25%; + } + .column.is-offset-3-desktop { + margin-left: 25%; + } + .column.is-4-desktop { + flex: none; + width: 33.33333%; + } + .column.is-offset-4-desktop { + margin-left: 33.33333%; + } + .column.is-5-desktop { + flex: none; + width: 41.66667%; + } + .column.is-offset-5-desktop { + margin-left: 41.66667%; + } + .column.is-6-desktop { + flex: none; + width: 50%; + } + .column.is-offset-6-desktop { + margin-left: 50%; + } + .column.is-7-desktop { + flex: none; + width: 58.33333%; + } + .column.is-offset-7-desktop { + margin-left: 58.33333%; + } + .column.is-8-desktop { + flex: none; + width: 66.66667%; + } + .column.is-offset-8-desktop { + margin-left: 66.66667%; + } + .column.is-9-desktop { + flex: none; + width: 75%; + } + .column.is-offset-9-desktop { + margin-left: 75%; + } + .column.is-10-desktop { + flex: none; + width: 83.33333%; + } + .column.is-offset-10-desktop { + margin-left: 83.33333%; + } + .column.is-11-desktop { + flex: none; + width: 91.66667%; + } + .column.is-offset-11-desktop { + margin-left: 91.66667%; + } + .column.is-12-desktop { + flex: none; + width: 100%; + } + .column.is-offset-12-desktop { + margin-left: 100%; + } +} +@media screen and (min-width: 1280px) { + .column.is-narrow-widescreen { + flex: none; + } + .column.is-full-widescreen { + flex: none; + width: 100%; + } + .column.is-three-quarters-widescreen { + flex: none; + width: 75%; + } + .column.is-two-thirds-widescreen { + flex: none; + width: 66.6666%; + } + .column.is-half-widescreen { + flex: none; + width: 50%; + } + .column.is-one-third-widescreen { + flex: none; + width: 33.3333%; + } + .column.is-one-quarter-widescreen { + flex: none; + width: 25%; + } + .column.is-one-fifth-widescreen { + flex: none; + width: 20%; + } + .column.is-two-fifths-widescreen { + flex: none; + width: 40%; + } + .column.is-three-fifths-widescreen { + flex: none; + width: 60%; + } + .column.is-four-fifths-widescreen { + flex: none; + width: 80%; + } + .column.is-offset-three-quarters-widescreen { + margin-left: 75%; + } + .column.is-offset-two-thirds-widescreen { + margin-left: 66.6666%; + } + .column.is-offset-half-widescreen { + margin-left: 50%; + } + .column.is-offset-one-third-widescreen { + margin-left: 33.3333%; + } + .column.is-offset-one-quarter-widescreen { + margin-left: 25%; + } + .column.is-offset-one-fifth-widescreen { + margin-left: 20%; + } + .column.is-offset-two-fifths-widescreen { + margin-left: 40%; + } + .column.is-offset-three-fifths-widescreen { + margin-left: 60%; + } + .column.is-offset-four-fifths-widescreen { + margin-left: 80%; + } + .column.is-0-widescreen { + flex: none; + width: 0%; + } + .column.is-offset-0-widescreen { + margin-left: 0%; + } + .column.is-1-widescreen { + flex: none; + width: 8.33333%; + } + .column.is-offset-1-widescreen { + margin-left: 8.33333%; + } + .column.is-2-widescreen { + flex: none; + width: 16.66667%; + } + .column.is-offset-2-widescreen { + margin-left: 16.66667%; + } + .column.is-3-widescreen { + flex: none; + width: 25%; + } + .column.is-offset-3-widescreen { + margin-left: 25%; + } + .column.is-4-widescreen { + flex: none; + width: 33.33333%; + } + .column.is-offset-4-widescreen { + margin-left: 33.33333%; + } + .column.is-5-widescreen { + flex: none; + width: 41.66667%; + } + .column.is-offset-5-widescreen { + margin-left: 41.66667%; + } + .column.is-6-widescreen { + flex: none; + width: 50%; + } + .column.is-offset-6-widescreen { + margin-left: 50%; + } + .column.is-7-widescreen { + flex: none; + width: 58.33333%; + } + .column.is-offset-7-widescreen { + margin-left: 58.33333%; + } + .column.is-8-widescreen { + flex: none; + width: 66.66667%; + } + .column.is-offset-8-widescreen { + margin-left: 66.66667%; + } + .column.is-9-widescreen { + flex: none; + width: 75%; + } + .column.is-offset-9-widescreen { + margin-left: 75%; + } + .column.is-10-widescreen { + flex: none; + width: 83.33333%; + } + .column.is-offset-10-widescreen { + margin-left: 83.33333%; + } + .column.is-11-widescreen { + flex: none; + width: 91.66667%; + } + .column.is-offset-11-widescreen { + margin-left: 91.66667%; + } + .column.is-12-widescreen { + flex: none; + width: 100%; + } + .column.is-offset-12-widescreen { + margin-left: 100%; + } +} +@media screen and (min-width: 1472px) { + .column.is-narrow-fullhd { + flex: none; + } + .column.is-full-fullhd { + flex: none; + width: 100%; + } + .column.is-three-quarters-fullhd { + flex: none; + width: 75%; + } + .column.is-two-thirds-fullhd { + flex: none; + width: 66.6666%; + } + .column.is-half-fullhd { + flex: none; + width: 50%; + } + .column.is-one-third-fullhd { + flex: none; + width: 33.3333%; + } + .column.is-one-quarter-fullhd { + flex: none; + width: 25%; + } + .column.is-one-fifth-fullhd { + flex: none; + width: 20%; + } + .column.is-two-fifths-fullhd { + flex: none; + width: 40%; + } + .column.is-three-fifths-fullhd { + flex: none; + width: 60%; + } + .column.is-four-fifths-fullhd { + flex: none; + width: 80%; + } + .column.is-offset-three-quarters-fullhd { + margin-left: 75%; + } + .column.is-offset-two-thirds-fullhd { + margin-left: 66.6666%; + } + .column.is-offset-half-fullhd { + margin-left: 50%; + } + .column.is-offset-one-third-fullhd { + margin-left: 33.3333%; + } + .column.is-offset-one-quarter-fullhd { + margin-left: 25%; + } + .column.is-offset-one-fifth-fullhd { + margin-left: 20%; + } + .column.is-offset-two-fifths-fullhd { + margin-left: 40%; + } + .column.is-offset-three-fifths-fullhd { + margin-left: 60%; + } + .column.is-offset-four-fifths-fullhd { + margin-left: 80%; + } + .column.is-0-fullhd { + flex: none; + width: 0%; + } + .column.is-offset-0-fullhd { + margin-left: 0%; + } + .column.is-1-fullhd { + flex: none; + width: 8.33333%; + } + .column.is-offset-1-fullhd { + margin-left: 8.33333%; + } + .column.is-2-fullhd { + flex: none; + width: 16.66667%; + } + .column.is-offset-2-fullhd { + margin-left: 16.66667%; + } + .column.is-3-fullhd { + flex: none; + width: 25%; + } + .column.is-offset-3-fullhd { + margin-left: 25%; + } + .column.is-4-fullhd { + flex: none; + width: 33.33333%; + } + .column.is-offset-4-fullhd { + margin-left: 33.33333%; + } + .column.is-5-fullhd { + flex: none; + width: 41.66667%; + } + .column.is-offset-5-fullhd { + margin-left: 41.66667%; + } + .column.is-6-fullhd { + flex: none; + width: 50%; + } + .column.is-offset-6-fullhd { + margin-left: 50%; + } + .column.is-7-fullhd { + flex: none; + width: 58.33333%; + } + .column.is-offset-7-fullhd { + margin-left: 58.33333%; + } + .column.is-8-fullhd { + flex: none; + width: 66.66667%; + } + .column.is-offset-8-fullhd { + margin-left: 66.66667%; + } + .column.is-9-fullhd { + flex: none; + width: 75%; + } + .column.is-offset-9-fullhd { + margin-left: 75%; + } + .column.is-10-fullhd { + flex: none; + width: 83.33333%; + } + .column.is-offset-10-fullhd { + margin-left: 83.33333%; + } + .column.is-11-fullhd { + flex: none; + width: 91.66667%; + } + .column.is-offset-11-fullhd { + margin-left: 91.66667%; + } + .column.is-12-fullhd { + flex: none; + width: 100%; + } + .column.is-offset-12-fullhd { + margin-left: 100%; + } +} +.columns { + margin-left: -0.75rem; + margin-right: -0.75rem; + margin-top: -0.75rem; +} +.columns:last-child { + margin-bottom: -0.75rem; +} +.columns:not(:last-child) { + margin-bottom: calc(1.5rem - 0.75rem); +} +.columns.is-centered { + justify-content: center; +} +.columns.is-gapless { + margin-left: 0; + margin-right: 0; + margin-top: 0; +} +.columns.is-gapless > .column { + margin: 0; + padding: 0 !important; +} +.columns.is-gapless:not(:last-child) { + margin-bottom: 1.5rem; +} +.columns.is-gapless:last-child { + margin-bottom: 0; +} +.columns.is-mobile { + display: flex; +} +.columns.is-multiline { + flex-wrap: wrap; +} +.columns.is-vcentered { + align-items: center; +} +@media screen and (min-width: 769px), print { + .columns:not(.is-desktop) { + display: flex; + } +} +@media screen and (min-width: 1088px) { + .columns.is-desktop { + display: flex; + } +} +.columns.is-variable { + --columnGap: 0.75rem; + margin-left: calc(-1 * var(--columnGap)); + margin-right: calc(-1 * var(--columnGap)); +} +.columns.is-variable .column { + padding-left: var(--columnGap); + padding-right: var(--columnGap); +} +.columns.is-variable.is-0 { + --columnGap: 0rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-0-mobile { + --columnGap: 0rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-0-tablet { + --columnGap: 0rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-0-tablet-only { + --columnGap: 0rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-0-touch { + --columnGap: 0rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-0-desktop { + --columnGap: 0rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-0-desktop-only { + --columnGap: 0rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-0-widescreen { + --columnGap: 0rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-0-widescreen-only { + --columnGap: 0rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-0-fullhd { + --columnGap: 0rem; + } +} +.columns.is-variable.is-1 { + --columnGap: 0.25rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-1-mobile { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-1-tablet { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-1-tablet-only { + --columnGap: 0.25rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-1-touch { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-1-desktop { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-1-desktop-only { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-1-widescreen { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-1-widescreen-only { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-1-fullhd { + --columnGap: 0.25rem; + } +} +.columns.is-variable.is-2 { + --columnGap: 0.5rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-2-mobile { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-2-tablet { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-2-tablet-only { + --columnGap: 0.5rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-2-touch { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-2-desktop { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-2-desktop-only { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-2-widescreen { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-2-widescreen-only { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-2-fullhd { + --columnGap: 0.5rem; + } +} +.columns.is-variable.is-3 { + --columnGap: 0.75rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-3-mobile { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-3-tablet { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-3-tablet-only { + --columnGap: 0.75rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-3-touch { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-3-desktop { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-3-desktop-only { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-3-widescreen { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-3-widescreen-only { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-3-fullhd { + --columnGap: 0.75rem; + } +} +.columns.is-variable.is-4 { + --columnGap: 1rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-4-mobile { + --columnGap: 1rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-4-tablet { + --columnGap: 1rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-4-tablet-only { + --columnGap: 1rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-4-touch { + --columnGap: 1rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-4-desktop { + --columnGap: 1rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-4-desktop-only { + --columnGap: 1rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-4-widescreen { + --columnGap: 1rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-4-widescreen-only { + --columnGap: 1rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-4-fullhd { + --columnGap: 1rem; + } +} +.columns.is-variable.is-5 { + --columnGap: 1.25rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-5-mobile { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-5-tablet { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-5-tablet-only { + --columnGap: 1.25rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-5-touch { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-5-desktop { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-5-desktop-only { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-5-widescreen { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-5-widescreen-only { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-5-fullhd { + --columnGap: 1.25rem; + } +} +.columns.is-variable.is-6 { + --columnGap: 1.5rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-6-mobile { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-6-tablet { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-6-tablet-only { + --columnGap: 1.5rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-6-touch { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-6-desktop { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-6-desktop-only { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-6-widescreen { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-6-widescreen-only { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-6-fullhd { + --columnGap: 1.5rem; + } +} +.columns.is-variable.is-7 { + --columnGap: 1.75rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-7-mobile { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-7-tablet { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-7-tablet-only { + --columnGap: 1.75rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-7-touch { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-7-desktop { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-7-desktop-only { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-7-widescreen { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-7-widescreen-only { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-7-fullhd { + --columnGap: 1.75rem; + } +} +.columns.is-variable.is-8 { + --columnGap: 2rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-8-mobile { + --columnGap: 2rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-8-tablet { + --columnGap: 2rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-8-tablet-only { + --columnGap: 2rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-8-touch { + --columnGap: 2rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-8-desktop { + --columnGap: 2rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-8-desktop-only { + --columnGap: 2rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-8-widescreen { + --columnGap: 2rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-8-widescreen-only { + --columnGap: 2rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-8-fullhd { + --columnGap: 2rem; + } +} +.tile { + align-items: stretch; + display: block; + flex-basis: 0; + flex-grow: 1; + flex-shrink: 1; + min-height: min-content; +} +.tile.is-ancestor { + margin-left: -0.75rem; + margin-right: -0.75rem; + margin-top: -0.75rem; +} +.tile.is-ancestor:last-child { + margin-bottom: -0.75rem; +} +.tile.is-ancestor:not(:last-child) { + margin-bottom: 0.75rem; +} +.tile.is-child { + margin: 0 !important; +} +.tile.is-parent { + padding: 0.75rem; +} +.tile.is-vertical { + flex-direction: column; +} +.tile.is-vertical > .tile.is-child:not(:last-child) { + margin-bottom: 1.5rem !important; +} +@media screen and (min-width: 769px), print { + .tile:not(.is-child) { + display: flex; + } + .tile.is-1 { + flex: none; + width: 8.33333%; + } + .tile.is-2 { + flex: none; + width: 16.66667%; + } + .tile.is-3 { + flex: none; + width: 25%; + } + .tile.is-4 { + flex: none; + width: 33.33333%; + } + .tile.is-5 { + flex: none; + width: 41.66667%; + } + .tile.is-6 { + flex: none; + width: 50%; + } + .tile.is-7 { + flex: none; + width: 58.33333%; + } + .tile.is-8 { + flex: none; + width: 66.66667%; + } + .tile.is-9 { + flex: none; + width: 75%; + } + .tile.is-10 { + flex: none; + width: 83.33333%; + } + .tile.is-11 { + flex: none; + width: 91.66667%; + } + .tile.is-12 { + flex: none; + width: 100%; + } +} +.hero { + align-items: stretch; + display: flex; + flex-direction: column; + justify-content: space-between; +} +.hero .navbar { + background: none; +} +.hero .tabs ul { + border-bottom: none; +} +.hero.is-white { + background-color: #fff; + color: #000; +} +.hero.is-white a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-white strong { + color: inherit; +} +.hero.is-white .title { + color: #000; +} +.hero.is-white .subtitle { + color: rgba(0,0,0,0.9); +} +.hero.is-white .subtitle a:not(.button), +.hero.is-white .subtitle strong { + color: #000; +} +@media screen and (max-width: 1087px) { + .hero.is-white .navbar-menu { + background-color: #fff; + } +} +.hero.is-white .navbar-item, +.hero.is-white .navbar-link { + color: rgba(0,0,0,0.7); +} +.hero.is-white a.navbar-item:hover, +.hero.is-white .navbar-link:hover, +.hero.is-white a.navbar-item.is-active, +.hero.is-white .navbar-link.is-active { + background-color: #f2f2f2; + color: #000; +} +.hero.is-white .tabs a { + color: #000; + opacity: 0.9; +} +.hero.is-white .tabs a:hover { + opacity: 1; +} +.hero.is-white .tabs li.is-active a { + opacity: 1; +} +.hero.is-white .tabs.is-boxed a, +.hero.is-white .tabs.is-toggle a { + color: #000; +} +.hero.is-white .tabs.is-boxed a:hover, +.hero.is-white .tabs.is-toggle a:hover { + background-color: rgba(0,0,0,0.1); +} +.hero.is-white .tabs.is-boxed li.is-active a, +.hero.is-white .tabs.is-toggle li.is-active a, +.hero.is-white .tabs.is-boxed li.is-active a:hover, +.hero.is-white .tabs.is-toggle li.is-active a:hover { + background-color: #000; + border-color: #000; + color: #fff; +} +.hero.is-white.is-bold { + background-image: linear-gradient(141deg, #e8e3e3 0%, #fff 71%, #fff 100%); +} +@media screen and (max-width: 768px) { + .hero.is-white.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #e8e3e3 0%, #fff 71%, #fff 100%); + } +} +.hero.is-black { + background-color: #000; + color: #fff; +} +.hero.is-black a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-black strong { + color: inherit; +} +.hero.is-black .title { + color: #fff; +} +.hero.is-black .subtitle { + color: rgba(255,255,255,0.9); +} +.hero.is-black .subtitle a:not(.button), +.hero.is-black .subtitle strong { + color: #fff; +} +@media screen and (max-width: 1087px) { + .hero.is-black .navbar-menu { + background-color: #000; + } +} +.hero.is-black .navbar-item, +.hero.is-black .navbar-link { + color: rgba(255,255,255,0.7); +} +.hero.is-black a.navbar-item:hover, +.hero.is-black .navbar-link:hover, +.hero.is-black a.navbar-item.is-active, +.hero.is-black .navbar-link.is-active { + background-color: #000; + color: #fff; +} +.hero.is-black .tabs a { + color: #fff; + opacity: 0.9; +} +.hero.is-black .tabs a:hover { + opacity: 1; +} +.hero.is-black .tabs li.is-active a { + opacity: 1; +} +.hero.is-black .tabs.is-boxed a, +.hero.is-black .tabs.is-toggle a { + color: #fff; +} +.hero.is-black .tabs.is-boxed a:hover, +.hero.is-black .tabs.is-toggle a:hover { + background-color: rgba(0,0,0,0.1); +} +.hero.is-black .tabs.is-boxed li.is-active a, +.hero.is-black .tabs.is-toggle li.is-active a, +.hero.is-black .tabs.is-boxed li.is-active a:hover, +.hero.is-black .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #000; +} +.hero.is-black.is-bold { + background-image: linear-gradient(141deg, #000 0%, #000 71%, #0d0c0c 100%); +} +@media screen and (max-width: 768px) { + .hero.is-black.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #000 0%, #000 71%, #0d0c0c 100%); + } +} +.hero.is-light { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.hero.is-light a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-light strong { + color: inherit; +} +.hero.is-light .title { + color: rgba(0,0,0,0.7); +} +.hero.is-light .subtitle { + color: rgba(0,0,0,0.9); +} +.hero.is-light .subtitle a:not(.button), +.hero.is-light .subtitle strong { + color: rgba(0,0,0,0.7); +} +@media screen and (max-width: 1087px) { + .hero.is-light .navbar-menu { + background-color: #f5f5f5; + } +} +.hero.is-light .navbar-item, +.hero.is-light .navbar-link { + color: rgba(0,0,0,0.7); +} +.hero.is-light a.navbar-item:hover, +.hero.is-light .navbar-link:hover, +.hero.is-light a.navbar-item.is-active, +.hero.is-light .navbar-link.is-active { + background-color: #e8e8e8; + color: rgba(0,0,0,0.7); +} +.hero.is-light .tabs a { + color: rgba(0,0,0,0.7); + opacity: 0.9; +} +.hero.is-light .tabs a:hover { + opacity: 1; +} +.hero.is-light .tabs li.is-active a { + opacity: 1; +} +.hero.is-light .tabs.is-boxed a, +.hero.is-light .tabs.is-toggle a { + color: rgba(0,0,0,0.7); +} +.hero.is-light .tabs.is-boxed a:hover, +.hero.is-light .tabs.is-toggle a:hover { + background-color: rgba(0,0,0,0.1); +} +.hero.is-light .tabs.is-boxed li.is-active a, +.hero.is-light .tabs.is-toggle li.is-active a, +.hero.is-light .tabs.is-boxed li.is-active a:hover, +.hero.is-light .tabs.is-toggle li.is-active a:hover { + background-color: rgba(0,0,0,0.7); + border-color: rgba(0,0,0,0.7); + color: #f5f5f5; +} +.hero.is-light.is-bold { + background-image: linear-gradient(141deg, #dfd8d8 0%, #f5f5f5 71%, #fff 100%); +} +@media screen and (max-width: 768px) { + .hero.is-light.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #dfd8d8 0%, #f5f5f5 71%, #fff 100%); + } +} +.hero.is-dark { + background-color: #363636; + color: #fff; +} +.hero.is-dark a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-dark strong { + color: inherit; +} +.hero.is-dark .title { + color: #fff; +} +.hero.is-dark .subtitle { + color: rgba(255,255,255,0.9); +} +.hero.is-dark .subtitle a:not(.button), +.hero.is-dark .subtitle strong { + color: #fff; +} +@media screen and (max-width: 1087px) { + .hero.is-dark .navbar-menu { + background-color: #363636; + } +} +.hero.is-dark .navbar-item, +.hero.is-dark .navbar-link { + color: rgba(255,255,255,0.7); +} +.hero.is-dark a.navbar-item:hover, +.hero.is-dark .navbar-link:hover, +.hero.is-dark a.navbar-item.is-active, +.hero.is-dark .navbar-link.is-active { + background-color: #292929; + color: #fff; +} +.hero.is-dark .tabs a { + color: #fff; + opacity: 0.9; +} +.hero.is-dark .tabs a:hover { + opacity: 1; +} +.hero.is-dark .tabs li.is-active a { + opacity: 1; +} +.hero.is-dark .tabs.is-boxed a, +.hero.is-dark .tabs.is-toggle a { + color: #fff; +} +.hero.is-dark .tabs.is-boxed a:hover, +.hero.is-dark .tabs.is-toggle a:hover { + background-color: rgba(0,0,0,0.1); +} +.hero.is-dark .tabs.is-boxed li.is-active a, +.hero.is-dark .tabs.is-toggle li.is-active a, +.hero.is-dark .tabs.is-boxed li.is-active a:hover, +.hero.is-dark .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #363636; +} +.hero.is-dark.is-bold { + background-image: linear-gradient(141deg, #1f1a1a 0%, #363636 71%, #463f3f 100%); +} +@media screen and (max-width: 768px) { + .hero.is-dark.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #1f1a1a 0%, #363636 71%, #463f3f 100%); + } +} +.hero.is-primary { + background-color: #fcee09; + color: #121617; +} +.hero.is-primary a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-primary strong { + color: inherit; +} +.hero.is-primary .title { + color: #121617; +} +.hero.is-primary .subtitle { + color: rgba(18,22,23,0.9); +} +.hero.is-primary .subtitle a:not(.button), +.hero.is-primary .subtitle strong { + color: #121617; +} +@media screen and (max-width: 1087px) { + .hero.is-primary .navbar-menu { + background-color: #fcee09; + } +} +.hero.is-primary .navbar-item, +.hero.is-primary .navbar-link { + color: rgba(18,22,23,0.7); +} +.hero.is-primary a.navbar-item:hover, +.hero.is-primary .navbar-link:hover, +.hero.is-primary a.navbar-item.is-active, +.hero.is-primary .navbar-link.is-active { + background-color: #e9db03; + color: #121617; +} +.hero.is-primary .tabs a { + color: #121617; + opacity: 0.9; +} +.hero.is-primary .tabs a:hover { + opacity: 1; +} +.hero.is-primary .tabs li.is-active a { + opacity: 1; +} +.hero.is-primary .tabs.is-boxed a, +.hero.is-primary .tabs.is-toggle a { + color: #121617; +} +.hero.is-primary .tabs.is-boxed a:hover, +.hero.is-primary .tabs.is-toggle a:hover { + background-color: rgba(0,0,0,0.1); +} +.hero.is-primary .tabs.is-boxed li.is-active a, +.hero.is-primary .tabs.is-toggle li.is-active a, +.hero.is-primary .tabs.is-boxed li.is-active a:hover, +.hero.is-primary .tabs.is-toggle li.is-active a:hover { + background-color: #121617; + border-color: #121617; + color: #fcee09; +} +.hero.is-primary.is-bold { + background-image: linear-gradient(141deg, #d2a300 0%, #fcee09 71%, #e7ff20 100%); +} +@media screen and (max-width: 768px) { + .hero.is-primary.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #d2a300 0%, #fcee09 71%, #e7ff20 100%); + } +} +.hero.is-link { + background-color: #02d7f2; + color: rgba(0,0,0,0.7); +} +.hero.is-link a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-link strong { + color: inherit; +} +.hero.is-link .title { + color: rgba(0,0,0,0.7); +} +.hero.is-link .subtitle { + color: rgba(0,0,0,0.9); +} +.hero.is-link .subtitle a:not(.button), +.hero.is-link .subtitle strong { + color: rgba(0,0,0,0.7); +} +@media screen and (max-width: 1087px) { + .hero.is-link .navbar-menu { + background-color: #02d7f2; + } +} +.hero.is-link .navbar-item, +.hero.is-link .navbar-link { + color: rgba(0,0,0,0.7); +} +.hero.is-link a.navbar-item:hover, +.hero.is-link .navbar-link:hover, +.hero.is-link a.navbar-item.is-active, +.hero.is-link .navbar-link.is-active { + background-color: #02c1d9; + color: rgba(0,0,0,0.7); +} +.hero.is-link .tabs a { + color: rgba(0,0,0,0.7); + opacity: 0.9; +} +.hero.is-link .tabs a:hover { + opacity: 1; +} +.hero.is-link .tabs li.is-active a { + opacity: 1; +} +.hero.is-link .tabs.is-boxed a, +.hero.is-link .tabs.is-toggle a { + color: rgba(0,0,0,0.7); +} +.hero.is-link .tabs.is-boxed a:hover, +.hero.is-link .tabs.is-toggle a:hover { + background-color: rgba(0,0,0,0.1); +} +.hero.is-link .tabs.is-boxed li.is-active a, +.hero.is-link .tabs.is-toggle li.is-active a, +.hero.is-link .tabs.is-boxed li.is-active a:hover, +.hero.is-link .tabs.is-toggle li.is-active a:hover { + background-color: rgba(0,0,0,0.7); + border-color: rgba(0,0,0,0.7); + color: #02d7f2; +} +.hero.is-link.is-bold { + background-image: linear-gradient(141deg, #00c1b7 0%, #02d7f2 71%, #0ebcff 100%); +} +@media screen and (max-width: 768px) { + .hero.is-link.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #00c1b7 0%, #02d7f2 71%, #0ebcff 100%); + } +} +.hero.is-info { + background-color: #02d7f2; + color: #121617; +} +.hero.is-info a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-info strong { + color: inherit; +} +.hero.is-info .title { + color: #121617; +} +.hero.is-info .subtitle { + color: rgba(18,22,23,0.9); +} +.hero.is-info .subtitle a:not(.button), +.hero.is-info .subtitle strong { + color: #121617; +} +@media screen and (max-width: 1087px) { + .hero.is-info .navbar-menu { + background-color: #02d7f2; + } +} +.hero.is-info .navbar-item, +.hero.is-info .navbar-link { + color: rgba(18,22,23,0.7); +} +.hero.is-info a.navbar-item:hover, +.hero.is-info .navbar-link:hover, +.hero.is-info a.navbar-item.is-active, +.hero.is-info .navbar-link.is-active { + background-color: #02c1d9; + color: #121617; +} +.hero.is-info .tabs a { + color: #121617; + opacity: 0.9; +} +.hero.is-info .tabs a:hover { + opacity: 1; +} +.hero.is-info .tabs li.is-active a { + opacity: 1; +} +.hero.is-info .tabs.is-boxed a, +.hero.is-info .tabs.is-toggle a { + color: #121617; +} +.hero.is-info .tabs.is-boxed a:hover, +.hero.is-info .tabs.is-toggle a:hover { + background-color: rgba(0,0,0,0.1); +} +.hero.is-info .tabs.is-boxed li.is-active a, +.hero.is-info .tabs.is-toggle li.is-active a, +.hero.is-info .tabs.is-boxed li.is-active a:hover, +.hero.is-info .tabs.is-toggle li.is-active a:hover { + background-color: #121617; + border-color: #121617; + color: #02d7f2; +} +.hero.is-info.is-bold { + background-image: linear-gradient(141deg, #00c1b7 0%, #02d7f2 71%, #0ebcff 100%); +} +@media screen and (max-width: 768px) { + .hero.is-info.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #00c1b7 0%, #02d7f2 71%, #0ebcff 100%); + } +} +.hero.is-success { + background-color: #00ff41; + color: #121617; +} +.hero.is-success a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-success strong { + color: inherit; +} +.hero.is-success .title { + color: #121617; +} +.hero.is-success .subtitle { + color: rgba(18,22,23,0.9); +} +.hero.is-success .subtitle a:not(.button), +.hero.is-success .subtitle strong { + color: #121617; +} +@media screen and (max-width: 1087px) { + .hero.is-success .navbar-menu { + background-color: #00ff41; + } +} +.hero.is-success .navbar-item, +.hero.is-success .navbar-link { + color: rgba(18,22,23,0.7); +} +.hero.is-success a.navbar-item:hover, +.hero.is-success .navbar-link:hover, +.hero.is-success a.navbar-item.is-active, +.hero.is-success .navbar-link.is-active { + background-color: #00e63a; + color: #121617; +} +.hero.is-success .tabs a { + color: #121617; + opacity: 0.9; +} +.hero.is-success .tabs a:hover { + opacity: 1; +} +.hero.is-success .tabs li.is-active a { + opacity: 1; +} +.hero.is-success .tabs.is-boxed a, +.hero.is-success .tabs.is-toggle a { + color: #121617; +} +.hero.is-success .tabs.is-boxed a:hover, +.hero.is-success .tabs.is-toggle a:hover { + background-color: rgba(0,0,0,0.1); +} +.hero.is-success .tabs.is-boxed li.is-active a, +.hero.is-success .tabs.is-toggle li.is-active a, +.hero.is-success .tabs.is-boxed li.is-active a:hover, +.hero.is-success .tabs.is-toggle li.is-active a:hover { + background-color: #121617; + border-color: #121617; + color: #00ff41; +} +.hero.is-success.is-bold { + background-image: linear-gradient(141deg, #00cc12 0%, #00ff41 71%, #1aff7a 100%); +} +@media screen and (max-width: 768px) { + .hero.is-success.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #00cc12 0%, #00ff41 71%, #1aff7a 100%); + } +} +.hero.is-warning { + background-color: #ff8e3c; + color: #121617; +} +.hero.is-warning a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-warning strong { + color: inherit; +} +.hero.is-warning .title { + color: #121617; +} +.hero.is-warning .subtitle { + color: rgba(18,22,23,0.9); +} +.hero.is-warning .subtitle a:not(.button), +.hero.is-warning .subtitle strong { + color: #121617; +} +@media screen and (max-width: 1087px) { + .hero.is-warning .navbar-menu { + background-color: #ff8e3c; + } +} +.hero.is-warning .navbar-item, +.hero.is-warning .navbar-link { + color: rgba(18,22,23,0.7); +} +.hero.is-warning a.navbar-item:hover, +.hero.is-warning .navbar-link:hover, +.hero.is-warning a.navbar-item.is-active, +.hero.is-warning .navbar-link.is-active { + background-color: #ff7f22; + color: #121617; +} +.hero.is-warning .tabs a { + color: #121617; + opacity: 0.9; +} +.hero.is-warning .tabs a:hover { + opacity: 1; +} +.hero.is-warning .tabs li.is-active a { + opacity: 1; +} +.hero.is-warning .tabs.is-boxed a, +.hero.is-warning .tabs.is-toggle a { + color: #121617; +} +.hero.is-warning .tabs.is-boxed a:hover, +.hero.is-warning .tabs.is-toggle a:hover { + background-color: rgba(0,0,0,0.1); +} +.hero.is-warning .tabs.is-boxed li.is-active a, +.hero.is-warning .tabs.is-toggle li.is-active a, +.hero.is-warning .tabs.is-boxed li.is-active a:hover, +.hero.is-warning .tabs.is-toggle li.is-active a:hover { + background-color: #121617; + border-color: #121617; + color: #ff8e3c; +} +.hero.is-warning.is-bold { + background-image: linear-gradient(141deg, #ff4809 0%, #ff8e3c 71%, #ffb956 100%); +} +@media screen and (max-width: 768px) { + .hero.is-warning.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #ff4809 0%, #ff8e3c 71%, #ffb956 100%); + } +} +.hero.is-danger { + background-color: #ff003c; + color: #121617; +} +.hero.is-danger a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-danger strong { + color: inherit; +} +.hero.is-danger .title { + color: #121617; +} +.hero.is-danger .subtitle { + color: rgba(18,22,23,0.9); +} +.hero.is-danger .subtitle a:not(.button), +.hero.is-danger .subtitle strong { + color: #121617; +} +@media screen and (max-width: 1087px) { + .hero.is-danger .navbar-menu { + background-color: #ff003c; + } +} +.hero.is-danger .navbar-item, +.hero.is-danger .navbar-link { + color: rgba(18,22,23,0.7); +} +.hero.is-danger a.navbar-item:hover, +.hero.is-danger .navbar-link:hover, +.hero.is-danger a.navbar-item.is-active, +.hero.is-danger .navbar-link.is-active { + background-color: #e60036; + color: #121617; +} +.hero.is-danger .tabs a { + color: #121617; + opacity: 0.9; +} +.hero.is-danger .tabs a:hover { + opacity: 1; +} +.hero.is-danger .tabs li.is-active a { + opacity: 1; +} +.hero.is-danger .tabs.is-boxed a, +.hero.is-danger .tabs.is-toggle a { + color: #121617; +} +.hero.is-danger .tabs.is-boxed a:hover, +.hero.is-danger .tabs.is-toggle a:hover { + background-color: rgba(0,0,0,0.1); +} +.hero.is-danger .tabs.is-boxed li.is-active a, +.hero.is-danger .tabs.is-toggle li.is-active a, +.hero.is-danger .tabs.is-boxed li.is-active a:hover, +.hero.is-danger .tabs.is-toggle li.is-active a:hover { + background-color: #121617; + border-color: #121617; + color: #ff003c; +} +.hero.is-danger.is-bold { + background-image: linear-gradient(141deg, #cc0052 0%, #ff003c 71%, #ff1a2a 100%); +} +@media screen and (max-width: 768px) { + .hero.is-danger.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #cc0052 0%, #ff003c 71%, #ff1a2a 100%); + } +} +.hero.is-grey-lightest { + background-color: #ededed; + color: #363636; +} +.hero.is-grey-lightest a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-grey-lightest strong { + color: inherit; +} +.hero.is-grey-lightest .title { + color: #363636; +} +.hero.is-grey-lightest .subtitle { + color: rgba(54,54,54,0.9); +} +.hero.is-grey-lightest .subtitle a:not(.button), +.hero.is-grey-lightest .subtitle strong { + color: #363636; +} +@media screen and (max-width: 1087px) { + .hero.is-grey-lightest .navbar-menu { + background-color: #ededed; + } +} +.hero.is-grey-lightest .navbar-item, +.hero.is-grey-lightest .navbar-link { + color: rgba(54,54,54,0.7); +} +.hero.is-grey-lightest a.navbar-item:hover, +.hero.is-grey-lightest .navbar-link:hover, +.hero.is-grey-lightest a.navbar-item.is-active, +.hero.is-grey-lightest .navbar-link.is-active { + background-color: #e0e0e0; + color: #363636; +} +.hero.is-grey-lightest .tabs a { + color: #363636; + opacity: 0.9; +} +.hero.is-grey-lightest .tabs a:hover { + opacity: 1; +} +.hero.is-grey-lightest .tabs li.is-active a { + opacity: 1; +} +.hero.is-grey-lightest .tabs.is-boxed a, +.hero.is-grey-lightest .tabs.is-toggle a { + color: #363636; +} +.hero.is-grey-lightest .tabs.is-boxed a:hover, +.hero.is-grey-lightest .tabs.is-toggle a:hover { + background-color: rgba(0,0,0,0.1); +} +.hero.is-grey-lightest .tabs.is-boxed li.is-active a, +.hero.is-grey-lightest .tabs.is-toggle li.is-active a, +.hero.is-grey-lightest .tabs.is-boxed li.is-active a:hover, +.hero.is-grey-lightest .tabs.is-toggle li.is-active a:hover { + background-color: #363636; + border-color: #363636; + color: #ededed; +} +.hero.is-grey-lightest.is-bold { + background-image: linear-gradient(141deg, #d8cfcf 0%, #ededed 71%, #faf9f9 100%); +} +@media screen and (max-width: 768px) { + .hero.is-grey-lightest.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #d8cfcf 0%, #ededed 71%, #faf9f9 100%); + } +} +.hero.is-small .hero-body { + padding-bottom: 1.5rem; + padding-top: 1.5rem; +} +@media screen and (min-width: 769px), print { + .hero.is-medium .hero-body { + padding-bottom: 9rem; + padding-top: 9rem; + } +} +@media screen and (min-width: 769px), print { + .hero.is-large .hero-body { + padding-bottom: 18rem; + padding-top: 18rem; + } +} +.hero.is-halfheight .hero-body, +.hero.is-fullheight .hero-body, +.hero.is-fullheight-with-navbar .hero-body { + align-items: center; + display: flex; +} +.hero.is-halfheight .hero-body > .container, +.hero.is-fullheight .hero-body > .container, +.hero.is-fullheight-with-navbar .hero-body > .container { + flex-grow: 1; + flex-shrink: 1; +} +.hero.is-halfheight { + min-height: 50vh; +} +.hero.is-fullheight { + min-height: 100vh; +} +.hero-video { + overflow: hidden; +} +.hero-video video { + left: 50%; + min-height: 100%; + min-width: 100%; + position: absolute; + top: 50%; + transform: translate3d(-50%, -50%, 0); +} +.hero-video.is-transparent { + opacity: 0.3; +} +@media screen and (max-width: 768px) { + .hero-video { + display: none; + } +} +.hero-buttons { + margin-top: 1.5rem; +} +@media screen and (max-width: 768px) { + .hero-buttons .button { + display: flex; + } + .hero-buttons .button:not(:last-child) { + margin-bottom: 0.75rem; + } +} +@media screen and (min-width: 769px), print { + .hero-buttons { + display: flex; + justify-content: center; + } + .hero-buttons .button:not(:last-child) { + margin-right: 1.5rem; + } +} +.hero-head, +.hero-foot { + flex-grow: 0; + flex-shrink: 0; +} +.hero-body { + flex-grow: 1; + flex-shrink: 0; + padding: 3rem 1.5rem; +} +.section { + padding: 3rem 1.5rem; +} +@media screen and (min-width: 1088px) { + .section.is-medium { + padding: 9rem 1.5rem; + } + .section.is-large { + padding: 18rem 1.5rem; + } +} +.footer { + background-color: #fcee09; + padding: 3rem 1.5rem 6rem; + color: #000; +} +html { + height: 100%; + -webkit-text-size-adjust: 100%; + -moz-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + text-size-adjust: 100%; +} +body { + min-height: 100%; + display: flex; + flex-direction: column; +} +body > .section { + flex-grow: 1; +} +@media screen and (min-width: 1088px) { + ::-webkit-scrollbar { + width: 8px; + height: 8px; + } + ::-webkit-scrollbar-track { + border-radius: 3px; + background: rgba(0,0,0,0.06); + box-shadow: inset 0 0 5px rgba(0,0,0,0.1); + } + ::-webkit-scrollbar-thumb { + border-radius: 3px; + background: rgba(0,0,0,0.12); + box-shadow: inset 0 0 10px rgba(0,0,0,0.2); + } + ::-webkit-scrollbar-thumb:hover { + background: rgba(0,0,0,0.24); + } +} +.ml-0, +.mx-0 { + margin-left: 0 !important; +} +.mr-0, +.mx-0 { + margin-right: 0 !important; +} +.ml-n0, +.mx-n0 { + margin-left: 0 !important; +} +.mr-n0, +.mx-n0 { + margin-right: 0 !important; +} +.mt-0, +.my-0 { + margin-top: 0 !important; +} +.mb-0, +.my-0 { + margin-bottom: 0 !important; +} +.mt-n0, +.my-n0 { + margin-top: 0 !important; +} +.mb-n0, +.my-n0 { + margin-bottom: 0 !important; +} +.pl-0, +.px-0 { + padding-left: 0 !important; +} +.pr-0, +.px-0 { + padding-right: 0 !important; +} +.pl-n0, +.px-n0 { + padding-left: 0 !important; +} +.pr-n0, +.px-n0 { + padding-right: 0 !important; +} +.pt-0, +.py-0 { + padding-top: 0 !important; +} +.pb-0, +.py-0 { + padding-bottom: 0 !important; +} +.pt-n0, +.py-n0 { + padding-top: 0 !important; +} +.pb-n0, +.py-n0 { + padding-bottom: 0 !important; +} +.ml-1, +.mx-1 { + margin-left: 0.25rem !important; +} +.mr-1, +.mx-1, +.article-licensing .licensing-meta .icon { + margin-right: 0.25rem !important; +} +.ml-n1, +.mx-n1 { + margin-left: -0.25rem !important; +} +.mr-n1, +.mx-n1 { + margin-right: -0.25rem !important; +} +.mt-1, +.my-1 { + margin-top: 0.25rem !important; +} +.mb-1, +.my-1, +.article-licensing .licensing-title p:not(:last-child) { + margin-bottom: 0.25rem !important; +} +.mt-n1, +.my-n1 { + margin-top: -0.25rem !important; +} +.mb-n1, +.my-n1 { + margin-bottom: -0.25rem !important; +} +.pl-1, +.px-1 { + padding-left: 0.25rem !important; +} +.pr-1, +.px-1 { + padding-right: 0.25rem !important; +} +.pl-n1, +.px-n1 { + padding-left: -0.25rem !important; +} +.pr-n1, +.px-n1 { + padding-right: -0.25rem !important; +} +.pt-1, +.py-1 { + padding-top: 0.25rem !important; +} +.pb-1, +.py-1 { + padding-bottom: 0.25rem !important; +} +.pt-n1, +.py-n1 { + padding-top: -0.25rem !important; +} +.pb-n1, +.py-n1 { + padding-bottom: -0.25rem !important; +} +.ml-2, +.mx-2 { + margin-left: 0.5rem !important; +} +.mr-2, +.mx-2 { + margin-right: 0.5rem !important; +} +.ml-n2, +.mx-n2 { + margin-left: -0.5rem !important; +} +.mr-n2, +.mx-n2 { + margin-right: -0.5rem !important; +} +.mt-2, +.my-2 { + margin-top: 0.5rem !important; +} +.mb-2, +.my-2 { + margin-bottom: 0.5rem !important; +} +.mt-n2, +.my-n2 { + margin-top: -0.5rem !important; +} +.mb-n2, +.my-n2 { + margin-bottom: -0.5rem !important; +} +.pl-2, +.px-2 { + padding-left: 0.5rem !important; +} +.pr-2, +.px-2 { + padding-right: 0.5rem !important; +} +.pl-n2, +.px-n2 { + padding-left: -0.5rem !important; +} +.pr-n2, +.px-n2 { + padding-right: -0.5rem !important; +} +.pt-2, +.py-2 { + padding-top: 0.5rem !important; +} +.pb-2, +.py-2 { + padding-bottom: 0.5rem !important; +} +.pt-n2, +.py-n2 { + padding-top: -0.5rem !important; +} +.pb-n2, +.py-n2 { + padding-bottom: -0.5rem !important; +} +.ml-3, +.mx-3 { + margin-left: 1rem !important; +} +.mr-3, +.mx-3 { + margin-right: 1rem !important; +} +.ml-n3, +.mx-n3 { + margin-left: -1rem !important; +} +.mr-n3, +.mx-n3 { + margin-right: -1rem !important; +} +.mt-3, +.my-3 { + margin-top: 1rem !important; +} +.mb-3, +.my-3, +.article-licensing .licensing-title { + margin-bottom: 1rem !important; +} +.mt-n3, +.my-n3 { + margin-top: -1rem !important; +} +.mb-n3, +.my-n3 { + margin-bottom: -1rem !important; +} +.pl-3, +.px-3 { + padding-left: 1rem !important; +} +.pr-3, +.px-3 { + padding-right: 1rem !important; +} +.pl-n3, +.px-n3 { + padding-left: -1rem !important; +} +.pr-n3, +.px-n3 { + padding-right: -1rem !important; +} +.pt-3, +.py-3 { + padding-top: 1rem !important; +} +.pb-3, +.py-3 { + padding-bottom: 1rem !important; +} +.pt-n3, +.py-n3 { + padding-top: -1rem !important; +} +.pb-n3, +.py-n3 { + padding-bottom: -1rem !important; +} +.ml-4, +.mx-4 { + margin-left: 1.5rem !important; +} +.mr-4, +.mx-4, +.article-licensing .licensing-meta .level-item { + margin-right: 1.5rem !important; +} +.ml-n4, +.mx-n4 { + margin-left: -1.5rem !important; +} +.mr-n4, +.mx-n4 { + margin-right: -1.5rem !important; +} +.mt-4, +.my-4 { + margin-top: 1.5rem !important; +} +.mb-4, +.my-4 { + margin-bottom: 1.5rem !important; +} +.mt-n4, +.my-n4 { + margin-top: -1.5rem !important; +} +.mb-n4, +.my-n4 { + margin-bottom: -1.5rem !important; +} +.pl-4, +.px-4 { + padding-left: 1.5rem !important; +} +.pr-4, +.px-4 { + padding-right: 1.5rem !important; +} +.pl-n4, +.px-n4 { + padding-left: -1.5rem !important; +} +.pr-n4, +.px-n4 { + padding-right: -1.5rem !important; +} +.pt-4, +.py-4 { + padding-top: 1.5rem !important; +} +.pb-4, +.py-4 { + padding-bottom: 1.5rem !important; +} +.pt-n4, +.py-n4 { + padding-top: -1.5rem !important; +} +.pb-n4, +.py-n4 { + padding-bottom: -1.5rem !important; +} +.ml-5, +.mx-5 { + margin-left: 3rem !important; +} +.mr-5, +.mx-5 { + margin-right: 3rem !important; +} +.ml-n5, +.mx-n5 { + margin-left: -3rem !important; +} +.mr-n5, +.mx-n5 { + margin-right: -3rem !important; +} +.mt-5, +.my-5 { + margin-top: 3rem !important; +} +.mb-5, +.my-5 { + margin-bottom: 3rem !important; +} +.mt-n5, +.my-n5 { + margin-top: -3rem !important; +} +.mb-n5, +.my-n5 { + margin-bottom: -3rem !important; +} +.pl-5, +.px-5 { + padding-left: 3rem !important; +} +.pr-5, +.px-5 { + padding-right: 3rem !important; +} +.pl-n5, +.px-n5 { + padding-left: -3rem !important; +} +.pr-n5, +.px-n5 { + padding-right: -3rem !important; +} +.pt-5, +.py-5 { + padding-top: 3rem !important; +} +.pb-5, +.py-5 { + padding-bottom: 3rem !important; +} +.pt-n5, +.py-n5 { + padding-top: -3rem !important; +} +.pb-n5, +.py-n5 { + padding-bottom: -3rem !important; +} +.ml-auto, +.mx-auto { + margin-left: auto !important; +} +.mr-auto, +.mx-auto { + margin-right: auto !important; +} +.mt-auto, +.my-auto { + margin-top: auto !important; +} +.mb-auto, +.my-auto { + margin-bottom: auto !important; +} +.pl-auto, +.px-auto { + margin-left: auto !important; +} +.pr-auto, +.px-auto { + margin-right: auto !important; +} +.pt-auto, +.py-auto { + margin-top: auto !important; +} +.pb-auto, +.py-auto { + margin-bottom: auto !important; +} +.order-0 { + order: 0 !important; +} +.order-1 { + order: 1 !important; +} +.order-2 { + order: 2 !important; +} +.order-3 { + order: 3 !important; +} +.order-4 { + order: 4 !important; +} +.order-5 { + order: 5 !important; +} +.justify-content-start { + justify-content: start !important; +} +.justify-content-center { + justify-content: center !important; +} +.flex-shrink-1 { + flex-shrink: 1 !important; +} +.link-muted { + color: inherit; +} +.link-muted:hover { + color: #fcee09 !important; +} +.image.is-7by3 { + padding-top: 42.8%; +} +.image.is-7by3 img { + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; +} +.image .avatar { + height: 100%; + object-fit: cover; +} +.image .fill { + object-fit: cover; + width: 100% !important; + height: 100% !important; +} +.button.is-transparent { + color: inherit; + background: transparent; + border-color: transparent; +} +.card { + overflow: visible; + border-radius: 0; +} +.card + .card, +.card + .column-right-shadow { + margin-top: 1.5rem; +} +.card .card-image { + overflow: hidden; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.card .media + .media { + border: none; + margin-top: 0; +} +article.media { + color: #848484; +} +article.media a { + color: inherit; +} +article.media a:hover { + color: #fcee09; +} +article.media .image { + width: 64px; + height: 64px; +} +article.media .image img { + object-fit: cover; + width: 100%; + height: 100%; +} +article.media .title { + margin-bottom: 0.25em; +} +article.media .media-content { + color: #848484; +} +article.media .media-content .title { + margin: 0; + line-height: inherit; +} +article.article .article-meta, +article.article .article-tags { + color: #848484; +} +article.article .article-meta { + overflow-x: auto; + margin-bottom: 0.5rem; +} +article.article .content { + word-wrap: break-word; + font-size: 1.1rem; +} +article.article .content h1 { + font-size: 1.75em; +} +article.article .content h2 { + font-size: 1.5em; +} +article.article .content h3 { + font-size: 1.25em; +} +article.article .content h4 { + font-size: 1.125em; +} +article.article .content h5 { + font-size: 1em; +} +article.article .content pre { + font-size: 0.85em; +} +article.article .content code { + padding: 0; + background: transparent; + overflow-wrap: break-word; +} +article.article .content blockquote.pullquote { + float: right; + max-width: 50%; + font-size: 1.15rem; + position: relative; +} +article.article .content blockquote footer strong + cite { + margin-left: 0.5em; +} +article.article .content .message.message-immersive { + border-radius: 0; + margin: 0 -1.5rem 1.5rem -1.5rem; +} +article.article .content .message.message-immersive .message-body { + border: none; +} +.rtl { + direction: rtl; +} +.rtl .level .level-item:not(:last-child), +.rtl .level.is-mobile .level-item:not(:last-child) { + margin-left: 0.75rem; + margin-right: 0; +} +.table-overflow { + overflow-x: auto; +} +.table-overflow table { + width: auto !important; +} +.table-overflow table th { + word-break: keep-all; +} +.video-container { + position: relative; + padding-bottom: 56.25%; + padding-top: 25px; + height: 0; +} +.video-container iframe { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.article-licensing { + position: relative; + z-index: 1; + box-shadow: none; + background: #f5f5f5; + border-radius: 0; + overflow: hidden; +} +.article-licensing:after { + position: absolute; + z-index: -1; + right: -50px; + top: -87.87px; + content: '\f25e'; + font-size: 200px; + font-family: 'Font Awesome 5 Brands'; + opacity: 0.1; +} +.article-licensing .level-left { + flex-wrap: wrap; + max-width: 100%; +} +.article-licensing .licensing-title { + line-height: 1.2; +} +.article-licensing .licensing-meta .icon { + width: 1.2em; + height: 1.2em; + font-size: 1.2em; + vertical-align: bottom; +} +.article-licensing .licensing-meta a { + color: inherit; +} +a.article-nav-prev span { + text-align: left; + flex-shrink: 1; + word-wrap: break-word; + white-space: normal; +} +a.article-nav-next span { + text-align: right; + flex-shrink: 1; + word-wrap: break-word; + white-space: normal; +} +.navbar-main { + box-shadow: none; +} +.navbar-main .navbar-menu, +.navbar-main .navbar-start, +.navbar-main .navbar-end { + align-items: stretch; + display: flex; + padding: 0; + flex-shrink: 0; +} +.navbar-main .navbar-menu { + flex-grow: 1; + flex-shrink: 0; + overflow-x: auto; +} +.navbar-main .navbar-start { + justify-content: flex-start; + margin-right: auto; +} +.navbar-main .navbar-end { + justify-content: flex-end; + margin-left: auto; +} +.navbar-main .navbar-item { + display: flex; + align-items: center; + padding: 0 0.5rem; + margin: 1.25rem 0.25rem; +} +.navbar-main .navbar-item.is-active { + background-color: transparent; +} +@media screen and (max-width: 1087px) { + .navbar-main .navbar-menu { + justify-content: center; + box-shadow: none; + } + .navbar-main .navbar-start { + margin-right: 0; + } + .navbar-main .navbar-end { + margin-left: 0; + } +} +.navbar-logo img { + max-height: 1.75rem; +} +@media screen and (max-width: 768px) { + footer.footer .level-start { + text-align: center; + } +} +footer.footer .level-end .field { + flex-wrap: wrap; + align-items: center; +} +@media screen and (max-width: 768px) { + footer.footer .level-end .field { + justify-content: center; + margin-top: 1rem; + } +} +.footer-logo img { + max-height: 1.75rem; +} +.pagination { + margin-top: 1.5rem; +} +.pagination .pagination-link a, +.pagination .pagination-ellipsis a, +.pagination .pagination-previous a, +.pagination .pagination-next a { + color: #fcee09; +} +.pagination .pagination-link, +.pagination .pagination-previous, +.pagination .pagination-next { + border: none; + background: #000; + box-shadow: none, 0 0 1px rgba(0,0,0,0.1); +} +.pagination .pagination-link.is-current { + background: #02d7f2; +} +.post-navigation { + color: #cdcdcd; + flex-wrap: wrap; + justify-content: space-around; +} +.post-navigation .level-item { + margin-bottom: 0; +} +.timeline { + margin-left: 1rem; + padding: 1rem 0 0 1.5rem; + border-left: 1px solid #02d7f2; +} +.timeline .media { + position: relative; +} +.timeline .media:before, +.timeline .media:last-child:after { + content: ''; + display: block; + position: absolute; + left: calc(-0.375rem - 1.5rem - 0.25px); +} +.timeline .media:before { + width: 0.75rem; + height: 0.75rem; + top: calc(1rem + 1.5 * 0.85rem / 2 - 0.75rem / 2); + background: #02d7f2; + border-radius: 50%; +} +.timeline .media:first-child:before { + top: calc(1.5 * 0.85rem / 2 - 0.75rem / 2); +} +.timeline .media:last-child:after { + width: 0.75rem; + top: calc(1rem + 1.5 * 0.85rem / 2 + 0.75rem / 2); + bottom: 0; + background: #000; +} +.timeline .media:first-child:last-child:after { + top: calc(1.5 * 0.85rem / 2 + 0.75rem / 2); +} +.searchbox { + display: none; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 100; + font-size: 1rem; + line-height: 0; + background: rgba(0,0,0,0.86); +} +.searchbox.show { + display: flex; +} +.searchbox a, +.searchbox a:hover { + color: inherit; + text-decoration: none; +} +.searchbox input { + font-size: 1rem; + border: none; + outline: none; + box-shadow: none; + border-radius: 0; +} +.searchbox, +.searchbox .searchbox-container { + position: fixed; + align-items: center; + flex-direction: column; + line-height: 1.25em; +} +.searchbox .searchbox-container { + z-index: 101; + display: flex; + overflow: hidden; + box-shadow: none, 0 0 1px rgba(0,0,0,0.1); + border-radius: 0; + background-color: #050a0e; + width: 540px; + top: 100px; + bottom: 100px; +} +.searchbox .searchbox-header, +.searchbox .searchbox-body, +.searchbox .searchbox-footer { + width: 100%; +} +.searchbox .searchbox-header { + display: flex; + flex-direction: row; + line-height: 1.5em; + font-weight: normal; + background-color: #050a0e; + min-height: 3rem; +} +.searchbox .searchbox-input-container { + display: flex; + flex-grow: 1; +} +.searchbox .searchbox-input { + flex-grow: 1; + color: inherit; + box-sizing: border-box; + padding: 0.75em 0 0.75em 1.25em; + background: #050a0e; +} +.searchbox .searchbox-close { + display: inline-block; + font-size: 1.5em; + padding: 0.5em 0.75em; + cursor: pointer; +} +.searchbox .searchbox-close:hover { + background: #000; +} +.searchbox .searchbox-close:active { + background: #000; +} +.searchbox .searchbox-body { + flex-grow: 1; + overflow-y: auto; + border-top: 1px solid #02d7f2; +} +.searchbox .searchbox-result-section header, +.searchbox .searchbox-result-item { + padding: 0.75em 1em; +} +.searchbox .searchbox-result-section { + border-bottom: 1px solid #02d7f2; +} +.searchbox .searchbox-result-section header { + color: #b5b5b5; +} +.searchbox .searchbox-result-item { + display: flex; + flex-direction: row; +} +.searchbox .searchbox-result-item:not(.disabled):not(.active):not(:active):hover { + background-color: #000; +} +.searchbox .searchbox-result-item:active, +.searchbox .searchbox-result-item.active { + color: rgba(0,0,0,0.7); + background-color: #fcee09; +} +.searchbox .searchbox-result-item em { + font-style: normal; + background: #fcee09; +} +.searchbox .searchbox-result-icon { + margin-right: 1em; +} +.searchbox .searchbox-result-content { + overflow: hidden; +} +.searchbox .searchbox-result-title, +.searchbox .searchbox-result-preview { + display: block; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.searchbox .searchbox-result-title-secondary { + color: #b5b5b5; +} +.searchbox .searchbox-result-preview { + margin-top: 0.25em; +} +.searchbox .searchbox-result-item:not(:active):not(.active) .searchbox-result-preview { + color: #b5b5b5; +} +.searchbox .searchbox-footer { + padding: 0.5em 1em; +} +.searchbox .searchbox-pagination { + margin: 0; + padding: 0; + list-style: none; + text-align: center; +} +.searchbox .searchbox-pagination .searchbox-pagination-item { + margin: 0 0.25rem; +} +.searchbox .searchbox-pagination .searchbox-pagination-item, +.searchbox .searchbox-pagination .searchbox-pagination-link { + display: inline-block; +} +.searchbox .searchbox-pagination .searchbox-pagination-link { + overflow: hidden; + padding: 0.5em 0.8em; + box-shadow: none, 0 0 1px rgba(0,0,0,0.1); + border-radius: 0; + background-color: #050a0e; +} +.searchbox .searchbox-pagination .searchbox-pagination-item.active .searchbox-pagination-link { + color: rgba(0,0,0,0.7); + background-color: #fcee09; +} +.searchbox .searchbox-pagination .searchbox-pagination-item.disabled .searchbox-pagination-link { + cursor: not-allowed; + background-color: #050a0e; +} +.searchbox .searchbox-pagination .searchbox-pagination-item:not(.active):not(.disabled) .searchbox-pagination-link:hover { + background-color: #050a0e; +} +@media screen and (max-width: 559px), screen and (max-height: 479px) { + .searchbox .searchbox-container { + top: 0; + left: 0; + width: 100%; + height: 100%; + border-radius: 0; + } +} +figure.highlight { + padding: 0; + width: 100%; + position: relative; + margin: 1em 0 1em !important; + border-radius: 0; +} +figure.highlight.folded .highlight-body { + height: 0; +} +figure.highlight .copy { + opacity: 0.7; +} +figure.highlight pre, +figure.highlight table tr:hover { + color: inherit; + background: transparent; +} +figure.highlight table { + width: auto; +} +figure.highlight table tr td { + border: none; +} +figure.highlight table tr:not(:first-child) td { + padding-top: 0; +} +figure.highlight table tr:not(:last-child) td { + padding-bottom: 0; +} +figure.highlight pre { + padding: 0; + overflow: visible; +} +figure.highlight pre .line, +figure.highlight pre code .hljs { + line-height: 1.5rem; +} +figure.highlight figcaption, +figure.highlight .gutter { + background: rgba(200,200,200,0.15); +} +figure.highlight figcaption { + margin: 0 !important; + padding: 0.3em 0em 0.3em 0.75em; + font-style: normal; + font-size: 0.8em; +} +figure.highlight figcaption * { + color: inherit; +} +figure.highlight figcaption span { + font-weight: 500; + font-family: 'Roboto Mono', monospace, 'Microsoft YaHei'; +} +figure.highlight figcaption .level-left *:not(:last-child) { + margin-right: 0.5em; +} +figure.highlight figcaption .level-right *:not(:first-child) { + margin-left: 0.5em; +} +figure.highlight figcaption .fold { + cursor: pointer; +} +figure.highlight figcaption.level { + overflow: auto; +} +figure.highlight figcaption.level .level-right a { + padding: 0em 0.75em; +} +figure.highlight .highlight-body { + overflow: auto; +} +figure.highlight .gutter { + text-align: right; +} +figure.highlight .tag, +figure.highlight .title, +figure.highlight .number, +figure.highlight .section { + display: inherit; + font: inherit; + margin: inherit; + padding: inherit; + background: inherit; + height: inherit; + text-align: inherit; + vertical-align: inherit; + min-width: inherit; + border-radius: inherit; +} +.gist table tr:hover { + background: transparent; +} +.gist table td { + border: none; +} +.gist .file { + all: initial; +} +.widget .menu-list li ul { + margin-right: 0; +} +.widget .menu-list .level { + margin-bottom: 0; +} +.widget .menu-list .level .level-left, +.widget .menu-list .level .level-right, +.widget .menu-list .level .level-item { + flex-shrink: 1; +} +.widget .menu-list .level .level-left, +.widget .menu-list .level .level-right { + align-items: flex-start; +} +.widget .menu-list .tag { + background: $light-grey; + color: $white-invert; +} +.widget .tags .tag:first-child { + background: #fcee09; + color: #121617; +} +.widget .tags .tag:last-child { + background: $light-grey; + color: $white-invert; +} +.level.is-multiline { + flex-wrap: wrap; +} +.donate { + position: relative; +} +.donate .qrcode { + display: none; + position: absolute; + z-index: 99; + bottom: 2.5em; + line-height: 0; + overflow: hidden; + box-shadow: none, 0 0 1px rgba(0,0,0,0.1); + border-radius: 0; +} +.donate .qrcode img { + max-width: 280px; +} +.donate:hover .qrcode { + display: block; +} +.donate:first-child:not(:last-child) .qrcode { + left: -0.75rem; +} +.donate:last-child:not(:first-child) .qrcode { + right: -0.75rem; +} +.donate[data-type="afdian"] { + color: #fff; + background-color: #885fd9; + border-color: transparent; +} +.donate[data-type="afdian"]:active { + background-color: #794ad4; +} +.donate[data-type="afdian"]:hover { + background-color: #8055d7; +} +.donate[data-type="afdian"]:focus:not(:active) { + box-shadow: 0 0 0 0.125em rgba(136,95,217,0.25); +} +.donate[data-type="alipay"] { + color: #fff; + background-color: #00a0e8; + border-color: transparent; +} +.donate[data-type="alipay"]:active { + background-color: #008ecf; +} +.donate[data-type="alipay"]:hover { + background-color: #0097db; +} +.donate[data-type="alipay"]:focus:not(:active) { + box-shadow: 0 0 0 0.125em rgba(0,160,232,0.25); +} +.donate[data-type="buymeacoffee"] { + color: rgba(0,0,0,0.7); + background-color: #fd0; + border-color: transparent; +} +.donate[data-type="buymeacoffee"]:active { + background-color: #e6c700; +} +.donate[data-type="buymeacoffee"]:hover { + background-color: #f2d200; +} +.donate[data-type="buymeacoffee"]:focus:not(:active) { + box-shadow: 0 0 0 0.125em rgba(255,221,0,0.25); +} +.donate[data-type="paypal"] { + color: rgba(0,0,0,0.7); + background-color: #feb700; + border-color: transparent; +} +.donate[data-type="paypal"]:active { + background-color: #e5a500; +} +.donate[data-type="paypal"]:hover { + background-color: #f1ae00; +} +.donate[data-type="paypal"]:focus:not(:active) { + box-shadow: 0 0 0 0.125em rgba(254,183,0,0.25); +} +.donate[data-type="patreon"] { + color: #fff; + background-color: #ff424d; + border-color: transparent; +} +.donate[data-type="patreon"]:active { + background-color: #ff2835; +} +.donate[data-type="patreon"]:hover { + background-color: #ff3541; +} +.donate[data-type="patreon"]:focus:not(:active) { + box-shadow: 0 0 0 0.125em rgba(255,66,77,0.25); +} +.donate[data-type="wechat"] { + color: #fff; + background-color: #1aad19; + border-color: transparent; +} +.donate[data-type="wechat"]:active { + background-color: #179716; +} +.donate[data-type="wechat"]:hover { + background-color: #18a217; +} +.donate[data-type="wechat"]:focus:not(:active) { + box-shadow: 0 0 0 0.125em rgba(26,173,25,0.25); +} +#back-to-top { + position: fixed; + opacity: 0; + outline: none; + padding: 8px 0; + line-height: 24px; + border-radius: 0; + transform: translateY(120px); + transition: 0.4s ease opacity, 0.4s ease width, 0.4s ease transform, 0.4s ease border-radius; +} +#back-to-top.is-rounded { + border-radius: 50%; +} +#back-to-top.fade-in { + opacity: 1; +} +#back-to-top.rise-up { + transform: translateY(0); +} +.gallery-item .caption { + color: #848484; +} +@media screen and (max-width: 768px) { + #toc { + display: none; + position: fixed; + margin: 1rem; + left: 0; + right: 0; + bottom: 0; + z-index: 100; + max-height: calc(100vh - 2rem); + overflow-y: auto; + } + #toc-mask { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 99; + background: rgba(0,0,0,0.7); + } + #toc.is-active, + #toc-mask.is-active { + display: block; + } +} +.pace { + user-select: none; + pointer-events: none; +} +.pace .pace-progress { + top: 0; + right: 100%; + width: 100%; + height: 2px; + z-index: 2000; + position: fixed; + background: #fcee09; +} +.pace-inactive { + display: none; +} +.fa, +.fab, +.fal, +.far, +.fas { + line-height: inherit; +} +.MathJax, +.katex-display { + overflow-x: auto; + overflow-y: hidden; +} +.katex { + white-space: nowrap; +} +.katex-display { + margin-top: -1em !important; +} +.katex-html { + padding-top: 1em; +} +.katex-html .tag { + align-items: unset; + background-color: unset; + border-radius: unset; + color: unset; + display: unset; + font-size: unset; + height: unset; + justify-content: unset; + line-height: unset; + padding-left: unset; + padding-right: unset; + white-space: unset; +} +.cc-window, +.cc-revoke { + font-size: 1.1rem !important; + font-family: 'Oxanium', Ubuntu, Roboto, 'Open Sans', 'Microsoft YaHei', sans-serif !important; +} +.cc-window { + color: #cdcdcd !important; + background-color: #000 !important; +} +.cc-window.cc-floating { + border-radius: 0; + box-shadow: none, 0 0 1px rgba(0,0,0,0.1); +} +.cc-window.cc-banner { + background-color: #000 !important; +} +.cc-window.cc-theme-block .cc-compliance > .cc-btn, +.cc-window.cc-theme-classic .cc-compliance > .cc-btn { + border-radius: 290486px; +} +.cc-window .cc-compliance > .cc-btn { + font-weight: 400; + border: none; + color: #121617; + background-color: #fcee09; +} +.cc-window .cc-compliance > .cc-btn:hover, +.cc-window .cc-compliance > .cc-btn:focus { + background-color: #f5e703; +} +.cc-window .cc-compliance > .cc-btn.cc-deny:hover { + color: #fcee09; + text-decoration: none; +} +.cc-revoke { + padding: 0.5rem 1rem !important; + color: #121617 !important; + background-color: #fcee09 !important; +} +.cc-revoke:hover { + text-decoration: none !important; + background-color: #f5e703; +} +@media screen and (min-width: 1280px) { + .is-1-column .container, + .is-2-column .container { + max-width: 960px; + width: 960px; + } +} +@media screen and (min-width: 1472px) { + .is-2-column .container { + max-width: 1152px; + width: 1152px; + } + .is-1-column .container { + max-width: 960px; + width: 960px; + } +} +@media screen and (min-width: 769px), print { + .is-sticky { + position: -webkit-sticky; + position: sticky; + top: 1.5rem; + z-index: 99; + } + .column-main.is-sticky, + .column-left.is-sticky, + .column-right.is-sticky, + .column-right-shadow.is-sticky { + top: 0.75rem; + align-self: flex-start; + } +} +@media screen and (max-width: 768px) { + .section { + padding: 1.5rem 1rem; + } +} +body { + counter-reset: card; +} +::selection { + color: #000; + background: #02d7f2; +} +.card:not(#back-to-top) { + position: relative; + counter-increment: card; +} +.card:not(#back-to-top), +.card:not(#back-to-top) .card-content { + position: relative; +} +.card:not(#back-to-top):before, +.card:not(#back-to-top) .card-content:before { + content: ''; + position: absolute; + z-index: -1; + top: 0; + left: 0; + right: 0; + bottom: 0; +} +.card:not(#back-to-top):before { + top: -1.2px; + left: -1.2px; + right: -1.2px; + bottom: -1.2px; + background-color: #02d7f2; + clip-path: polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 16px 100%, 0 calc(100% - 16px), 0 0); + -webkit-clip-path: polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 16px 100%, 0 calc(100% - 16px), 0 0); +} +.card:not(#back-to-top):after { + content: 'R' counter(card); + position: absolute; + color: #02d7f2; + right: 2rem; + bottom: -0.6em; + font-size: 0.75rem; + padding: 0 0.25em; + background: #000; +} +.card:not(#back-to-top) .card-image { + clip-path: polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 0 100%); + -webkit-clip-path: polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 0 100%); +} +.card:not(#back-to-top) .card-content:before { + background-color: #000; + clip-path: polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 16px 100%, 0 calc(100% - 16px), 0 0); + -webkit-clip-path: polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 16px 100%, 0 calc(100% - 16px), 0 0); +} +.card:not(#back-to-top) .card-image + .card-content:before { + clip-path: polygon(0 0, 100% 0, 100% 100%, 16px 100%, 0 calc(100% - 16px)); + -webkit-clip-path: polygon(0 0, 100% 0, 100% 100%, 16px 100%, 0 calc(100% - 16px)); +} +.button:not(input) { + border: none; + outline: none; + background: transparent !important; + position: relative; +} +.button:not(input):before { + content: ''; + position: absolute; + z-index: -1; + top: 0; + left: 0; + right: 0; + bottom: 0; +} +.button:not(input):before { + clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px); + -webkit-clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px); +} +.button:not(input).is-white:before { + background-color: #fff; + color: #000; +} +.button:not(input).is-white:hover:before, +.button:not(input).is-white.is-hovered:before { + background-color: #f9f9f9; + color: #000; +} +.button:not(input).is-white:focus:before, +.button:not(input).is-white.is-focused:before { + color: #000; +} +.button:not(input).is-white:active:before, +.button:not(input).is-white.is-active:before { + background-color: #f2f2f2; + color: #000; +} +.button:not(input).is-white[disabled]:before, +fieldset[disabled] .button:not(input).is-white:before { + background-color: #fff; +} +.button:not(input).is-white.is-inverted:before { + background-color: #000; + color: #fff; +} +.button:not(input).is-white.is-inverted:before:hover:before, +.button:not(input).is-white.is-inverted:before.is-hovered:before { + background-color: #000; +} +.button:not(input).is-white.is-inverted:before[disabled]:before, +fieldset[disabled] .button:not(input).is-white.is-inverted:before:before { + background-color: #000; + border-color: transparent; + box-shadow: none; + color: #fff; +} +.button:not(input).is-black:before { + background-color: #000; + color: #fff; +} +.button:not(input).is-black:hover:before, +.button:not(input).is-black.is-hovered:before { + background-color: #000; + color: #fff; +} +.button:not(input).is-black:focus:before, +.button:not(input).is-black.is-focused:before { + color: #fff; +} +.button:not(input).is-black:active:before, +.button:not(input).is-black.is-active:before { + background-color: #000; + color: #fff; +} +.button:not(input).is-black[disabled]:before, +fieldset[disabled] .button:not(input).is-black:before { + background-color: #000; +} +.button:not(input).is-black.is-inverted:before { + background-color: #fff; + color: #000; +} +.button:not(input).is-black.is-inverted:before:hover:before, +.button:not(input).is-black.is-inverted:before.is-hovered:before { + background-color: #f2f2f2; +} +.button:not(input).is-black.is-inverted:before[disabled]:before, +fieldset[disabled] .button:not(input).is-black.is-inverted:before:before { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #000; +} +.button:not(input).is-light:before { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.button:not(input).is-light:hover:before, +.button:not(input).is-light.is-hovered:before { + background-color: #eee; + color: rgba(0,0,0,0.7); +} +.button:not(input).is-light:focus:before, +.button:not(input).is-light.is-focused:before { + color: rgba(0,0,0,0.7); +} +.button:not(input).is-light:active:before, +.button:not(input).is-light.is-active:before { + background-color: #e8e8e8; + color: rgba(0,0,0,0.7); +} +.button:not(input).is-light[disabled]:before, +fieldset[disabled] .button:not(input).is-light:before { + background-color: #f5f5f5; +} +.button:not(input).is-light.is-inverted:before { + background-color: rgba(0,0,0,0.7); + color: #f5f5f5; +} +.button:not(input).is-light.is-inverted:before:hover:before, +.button:not(input).is-light.is-inverted:before.is-hovered:before { + background-color: rgba(0,0,0,0.7); +} +.button:not(input).is-light.is-inverted:before[disabled]:before, +fieldset[disabled] .button:not(input).is-light.is-inverted:before:before { + background-color: rgba(0,0,0,0.7); + border-color: transparent; + box-shadow: none; + color: #f5f5f5; +} +.button:not(input).is-dark:before { + background-color: #363636; + color: #fff; +} +.button:not(input).is-dark:hover:before, +.button:not(input).is-dark.is-hovered:before { + background-color: #2f2f2f; + color: #fff; +} +.button:not(input).is-dark:focus:before, +.button:not(input).is-dark.is-focused:before { + color: #fff; +} +.button:not(input).is-dark:active:before, +.button:not(input).is-dark.is-active:before { + background-color: #292929; + color: #fff; +} +.button:not(input).is-dark[disabled]:before, +fieldset[disabled] .button:not(input).is-dark:before { + background-color: #363636; +} +.button:not(input).is-dark.is-inverted:before { + background-color: #fff; + color: #363636; +} +.button:not(input).is-dark.is-inverted:before:hover:before, +.button:not(input).is-dark.is-inverted:before.is-hovered:before { + background-color: #f2f2f2; +} +.button:not(input).is-dark.is-inverted:before[disabled]:before, +fieldset[disabled] .button:not(input).is-dark.is-inverted:before:before { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #363636; +} +.button:not(input).is-primary:before { + background-color: #fcee09; + color: #121617; +} +.button:not(input).is-primary:hover:before, +.button:not(input).is-primary.is-hovered:before { + background-color: #f5e703; + color: #121617; +} +.button:not(input).is-primary:focus:before, +.button:not(input).is-primary.is-focused:before { + color: #121617; +} +.button:not(input).is-primary:active:before, +.button:not(input).is-primary.is-active:before { + background-color: #e9db03; + color: #121617; +} +.button:not(input).is-primary[disabled]:before, +fieldset[disabled] .button:not(input).is-primary:before { + background-color: #fcee09; +} +.button:not(input).is-primary.is-inverted:before { + background-color: #121617; + color: #fcee09; +} +.button:not(input).is-primary.is-inverted:before:hover:before, +.button:not(input).is-primary.is-inverted:before.is-hovered:before { + background-color: #070809; +} +.button:not(input).is-primary.is-inverted:before[disabled]:before, +fieldset[disabled] .button:not(input).is-primary.is-inverted:before:before { + background-color: #121617; + border-color: transparent; + box-shadow: none; + color: #fcee09; +} +.button:not(input).is-link:before { + background-color: #02d7f2; + color: rgba(0,0,0,0.7); +} +.button:not(input).is-link:hover:before, +.button:not(input).is-link.is-hovered:before { + background-color: #02cce5; + color: rgba(0,0,0,0.7); +} +.button:not(input).is-link:focus:before, +.button:not(input).is-link.is-focused:before { + color: rgba(0,0,0,0.7); +} +.button:not(input).is-link:active:before, +.button:not(input).is-link.is-active:before { + background-color: #02c1d9; + color: rgba(0,0,0,0.7); +} +.button:not(input).is-link[disabled]:before, +fieldset[disabled] .button:not(input).is-link:before { + background-color: #02d7f2; +} +.button:not(input).is-link.is-inverted:before { + background-color: rgba(0,0,0,0.7); + color: #02d7f2; +} +.button:not(input).is-link.is-inverted:before:hover:before, +.button:not(input).is-link.is-inverted:before.is-hovered:before { + background-color: rgba(0,0,0,0.7); +} +.button:not(input).is-link.is-inverted:before[disabled]:before, +fieldset[disabled] .button:not(input).is-link.is-inverted:before:before { + background-color: rgba(0,0,0,0.7); + border-color: transparent; + box-shadow: none; + color: #02d7f2; +} +.button:not(input).is-info:before { + background-color: #02d7f2; + color: #121617; +} +.button:not(input).is-info:hover:before, +.button:not(input).is-info.is-hovered:before { + background-color: #02cce5; + color: #121617; +} +.button:not(input).is-info:focus:before, +.button:not(input).is-info.is-focused:before { + color: #121617; +} +.button:not(input).is-info:active:before, +.button:not(input).is-info.is-active:before { + background-color: #02c1d9; + color: #121617; +} +.button:not(input).is-info[disabled]:before, +fieldset[disabled] .button:not(input).is-info:before { + background-color: #02d7f2; +} +.button:not(input).is-info.is-inverted:before { + background-color: #121617; + color: #02d7f2; +} +.button:not(input).is-info.is-inverted:before:hover:before, +.button:not(input).is-info.is-inverted:before.is-hovered:before { + background-color: #070809; +} +.button:not(input).is-info.is-inverted:before[disabled]:before, +fieldset[disabled] .button:not(input).is-info.is-inverted:before:before { + background-color: #121617; + border-color: transparent; + box-shadow: none; + color: #02d7f2; +} +.button:not(input).is-success:before { + background-color: #00ff41; + color: #121617; +} +.button:not(input).is-success:hover:before, +.button:not(input).is-success.is-hovered:before { + background-color: #00f23e; + color: #121617; +} +.button:not(input).is-success:focus:before, +.button:not(input).is-success.is-focused:before { + color: #121617; +} +.button:not(input).is-success:active:before, +.button:not(input).is-success.is-active:before { + background-color: #00e63a; + color: #121617; +} +.button:not(input).is-success[disabled]:before, +fieldset[disabled] .button:not(input).is-success:before { + background-color: #00ff41; +} +.button:not(input).is-success.is-inverted:before { + background-color: #121617; + color: #00ff41; +} +.button:not(input).is-success.is-inverted:before:hover:before, +.button:not(input).is-success.is-inverted:before.is-hovered:before { + background-color: #070809; +} +.button:not(input).is-success.is-inverted:before[disabled]:before, +fieldset[disabled] .button:not(input).is-success.is-inverted:before:before { + background-color: #121617; + border-color: transparent; + box-shadow: none; + color: #00ff41; +} +.button:not(input).is-warning:before { + background-color: #ff8e3c; + color: #121617; +} +.button:not(input).is-warning:hover:before, +.button:not(input).is-warning.is-hovered:before { + background-color: #ff872f; + color: #121617; +} +.button:not(input).is-warning:focus:before, +.button:not(input).is-warning.is-focused:before { + color: #121617; +} +.button:not(input).is-warning:active:before, +.button:not(input).is-warning.is-active:before { + background-color: #ff7f22; + color: #121617; +} +.button:not(input).is-warning[disabled]:before, +fieldset[disabled] .button:not(input).is-warning:before { + background-color: #ff8e3c; +} +.button:not(input).is-warning.is-inverted:before { + background-color: #121617; + color: #ff8e3c; +} +.button:not(input).is-warning.is-inverted:before:hover:before, +.button:not(input).is-warning.is-inverted:before.is-hovered:before { + background-color: #070809; +} +.button:not(input).is-warning.is-inverted:before[disabled]:before, +fieldset[disabled] .button:not(input).is-warning.is-inverted:before:before { + background-color: #121617; + border-color: transparent; + box-shadow: none; + color: #ff8e3c; +} +.button:not(input).is-danger:before { + background-color: #ff003c; + color: #121617; +} +.button:not(input).is-danger:hover:before, +.button:not(input).is-danger.is-hovered:before { + background-color: #f20039; + color: #121617; +} +.button:not(input).is-danger:focus:before, +.button:not(input).is-danger.is-focused:before { + color: #121617; +} +.button:not(input).is-danger:active:before, +.button:not(input).is-danger.is-active:before { + background-color: #e60036; + color: #121617; +} +.button:not(input).is-danger[disabled]:before, +fieldset[disabled] .button:not(input).is-danger:before { + background-color: #ff003c; +} +.button:not(input).is-danger.is-inverted:before { + background-color: #121617; + color: #ff003c; +} +.button:not(input).is-danger.is-inverted:before:hover:before, +.button:not(input).is-danger.is-inverted:before.is-hovered:before { + background-color: #070809; +} +.button:not(input).is-danger.is-inverted:before[disabled]:before, +fieldset[disabled] .button:not(input).is-danger.is-inverted:before:before { + background-color: #121617; + border-color: transparent; + box-shadow: none; + color: #ff003c; +} +.button:not(input).is-grey-lightest:before { + background-color: #ededed; + color: #363636; +} +.button:not(input).is-grey-lightest:hover:before, +.button:not(input).is-grey-lightest.is-hovered:before { + background-color: #e7e7e7; + color: #363636; +} +.button:not(input).is-grey-lightest:focus:before, +.button:not(input).is-grey-lightest.is-focused:before { + color: #363636; +} +.button:not(input).is-grey-lightest:active:before, +.button:not(input).is-grey-lightest.is-active:before { + background-color: #e0e0e0; + color: #363636; +} +.button:not(input).is-grey-lightest[disabled]:before, +fieldset[disabled] .button:not(input).is-grey-lightest:before { + background-color: #ededed; +} +.button:not(input).is-grey-lightest.is-inverted:before { + background-color: #363636; + color: #ededed; +} +.button:not(input).is-grey-lightest.is-inverted:before:hover:before, +.button:not(input).is-grey-lightest.is-inverted:before.is-hovered:before { + background-color: #292929; +} +.button:not(input).is-grey-lightest.is-inverted:before[disabled]:before, +fieldset[disabled] .button:not(input).is-grey-lightest.is-inverted:before:before { + background-color: #363636; + border-color: transparent; + box-shadow: none; + color: #ededed; +} +.field.has-addons .control:not(:first-child) .button { + clip-path: polygon(0 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%); + -webkit-clip-path: polygon(0 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%); +} +.menu-list a { + clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px); + -webkit-clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px); +} +.tags.has-addons .tag:first-child { + background: #fcee09 !important; +} +.tags.has-addons .tag:last-child { + background: #02d7f2 !important; +} +.pagination-previous, +.pagination-next, +.pagination-link { + clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px); + -webkit-clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px); +} +.pagination-previous:hover, +.pagination-next:hover, +.pagination-link:hover { + background-color: #02d7f2; +} +.pagination-previous:hover, +.pagination-next:hover, +.pagination-link:hover, +.pagination-previous:hover a, +.pagination-next:hover a, +.pagination-link:hover a { + color: #000; +} +.navbar-main { + padding-top: 10px; + padding-bottom: 30px; +} +.navbar-main:after { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: -2px; + background: url("../img/razor-top-black.svg") repeat-x top; + height: 40px; +} +.navbar-main .navbar-menu .navbar-item:hover, +.navbar-main .navbar-menu .navbar-item.is-active { + color: #fcee09; + background-color: #000 !important; +} +article.article .title a, +article.media .title a { + background-image: linear-gradient(transparent calc(100% - 2px), #fcee09 2px); + background-repeat: no-repeat; + background-size: 0 100%; + transition: background-size 0.25s ease-in-out; +} +article.article .title:hover a, +article.media .title:hover a { + background-size: 100% 100%; +} +article.article .article-more:before { + background-color: #02d7f2; + color: #121617; +} +article.article .article-more:hover:before, +article.article .article-more.is-hovered:before { + background-color: #02cce5; + color: #121617; +} +article.article .article-more:focus:before, +article.article .article-more.is-focused:before { + color: #121617; +} +article.article .article-more:active:before, +article.article .article-more.is-active:before { + background-color: #02c1d9; + color: #121617; +} +article.article .article-more[disabled]:before, +fieldset[disabled] article.article .article-more:before { + background-color: #02d7f2; +} +article.article .article-more.is-inverted:before { + background-color: #121617; + color: #02d7f2; +} +article.article .article-more.is-inverted:before:hover:before, +article.article .article-more.is-inverted:before.is-hovered:before { + background-color: #070809; +} +article.article .article-more.is-inverted:before[disabled]:before, +fieldset[disabled] article.article .article-more.is-inverted:before:before { + background-color: #121617; + border-color: transparent; + box-shadow: none; + color: #02d7f2; +} +.article-licensing { + background: #242424; +} +.content blockquote { + background: transparent; + border: 0.5px solid #02d7f2; + border-left: 5px solid #02d7f2; +} +.footer { + position: relative; +} +.footer:before { + content: ''; + position: absolute; + left: 0; + right: 0; + top: -1px; + height: 39px; + background: url("../img/razor-bottom-black.svg") repeat-x top; +} +.footer > .container { + padding-top: 40px; +} +.timeline .media:before { + clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%); + -webkit-clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%); +} +.searchbox .searchbox-container { + border: 1px solid #02d7f2; +} +.searchbox .searchbox-container .searchbox-body { + border-bottom: 1px solid #02d7f2; +} +.searchbox .searchbox-container .searchbox-body li:last-child .searchbox-result-section { + border-bottom: none; +} +.searchbox .searchbox-container .searchbox-result-item em { + color: #000; +} +#back-to-top { + color: #000; + background: #02d7f2; + margin-top: 45px; + clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px); + -webkit-clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px); +} +.cc-window, +.cc-revoke { + border-radius: 0 !important; +} +.cc-window:not(.cc-banner) { + border: 1px solid #02d7f2; +} +.cc-window.cc-theme-classic .cc-compliance > .cc-btn, +.cc-window.cc-theme-block .cc-compliance > .cc-btn { + border-radius: 0; +} +.cc-window.cc-banner .cc-compliance > .cc-btn { + background-color: #02d7f2; +} +.cc-window.cc-banner .cc-compliance > .cc-btn:hover, +.cc-window.cc-banner .cc-compliance > .cc-btn:focus { + background-color: #02cce5; +} diff --git a/css/default.css b/css/default.css new file mode 100644 index 0000000..245d44a --- /dev/null +++ b/css/default.css @@ -0,0 +1,11654 @@ +@-moz-keyframes spinAround { + from { + transform: rotate(0deg); + } + to { + transform: rotate(359deg); + } +} +@-webkit-keyframes spinAround { + from { + transform: rotate(0deg); + } + to { + transform: rotate(359deg); + } +} +@-o-keyframes spinAround { + from { + transform: rotate(0deg); + } + to { + transform: rotate(359deg); + } +} +@keyframes spinAround { + from { + transform: rotate(0deg); + } + to { + transform: rotate(359deg); + } +} +.is-unselectable, +.breadcrumb, +.modal-close, +.pagination-previous, +.pagination-next, +.pagination-link, +.pagination-ellipsis, +.tabs, +.button, +.delete, +.file { + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.navbar-link:not(.is-arrowless)::after, +.select:not(.is-multiple):not(.is-loading)::after { + border: 3px solid transparent; + border-radius: 2px; + border-right: 0; + border-top: 0; + content: " "; + display: block; + height: 0.625em; + margin-top: -0.4375em; + pointer-events: none; + position: absolute; + top: 50%; + transform: rotate(-45deg); + transform-origin: center; + width: 0.625em; +} +.breadcrumb:not(:last-child), +.level:not(:last-child), +.list:not(:last-child), +.message:not(:last-child), +.pagination:not(:last-child), +.tabs:not(:last-child), +.box:not(:last-child), +.content:not(:last-child), +.notification:not(:last-child), +.progress:not(:last-child), +.table:not(:last-child), +.table-container:not(:last-child), +.title:not(:last-child), +.subtitle:not(:last-child), +.block:not(:last-child), +.highlight:not(:last-child) { + margin-bottom: 1.5rem; +} +.modal-close, +.delete { + -moz-appearance: none; + -webkit-appearance: none; + background-color: rgba(10,10,10,0.2); + border: none; + border-radius: 290486px; + cursor: pointer; + pointer-events: auto; + display: inline-block; + flex-grow: 0; + flex-shrink: 0; + font-size: 0; + height: 20px; + max-height: 20px; + max-width: 20px; + min-height: 20px; + min-width: 20px; + outline: none; + position: relative; + vertical-align: top; + width: 20px; +} +.modal-close::before, +.delete::before, +.modal-close::after, +.delete::after { + background-color: #fff; + content: ""; + display: block; + left: 50%; + position: absolute; + top: 50%; + transform: translateX(-50%) translateY(-50%) rotate(45deg); + transform-origin: center center; +} +.modal-close::before, +.delete::before { + height: 2px; + width: 50%; +} +.modal-close::after, +.delete::after { + height: 50%; + width: 2px; +} +.modal-close:hover, +.delete:hover, +.modal-close:focus, +.delete:focus { + background-color: rgba(10,10,10,0.3); +} +.modal-close:active, +.delete:active { + background-color: rgba(10,10,10,0.4); +} +.modal-close.is-small, +.delete.is-small { + height: 16px; + max-height: 16px; + max-width: 16px; + min-height: 16px; + min-width: 16px; + width: 16px; +} +.modal-close.is-medium, +.delete.is-medium { + height: 24px; + max-height: 24px; + max-width: 24px; + min-height: 24px; + min-width: 24px; + width: 24px; +} +.modal-close.is-large, +.delete.is-large { + height: 32px; + max-height: 32px; + max-width: 32px; + min-height: 32px; + min-width: 32px; + width: 32px; +} +.button.is-loading::after, +.loader, +.select.is-loading::after, +.control.is-loading::after { + animation: spinAround 500ms infinite linear; + border: 2px solid #dbdbdb; + border-radius: 290486px; + border-right-color: transparent; + border-top-color: transparent; + content: ""; + display: block; + height: 1em; + position: relative; + width: 1em; +} +.is-overlay, +.modal, +.modal-background, +.image.is-square img, +.image.is-1by1 img, +.image.is-5by4 img, +.image.is-4by3 img, +.image.is-3by2 img, +.image.is-5by3 img, +.image.is-16by9 img, +.image.is-2by1 img, +.image.is-3by1 img, +.image.is-4by5 img, +.image.is-3by4 img, +.image.is-2by3 img, +.image.is-3by5 img, +.image.is-9by16 img, +.image.is-1by2 img, +.image.is-1by3 img, +.image.is-square .has-ratio, +.image.is-1by1 .has-ratio, +.image.is-5by4 .has-ratio, +.image.is-4by3 .has-ratio, +.image.is-3by2 .has-ratio, +.image.is-5by3 .has-ratio, +.image.is-16by9 .has-ratio, +.image.is-2by1 .has-ratio, +.image.is-3by1 .has-ratio, +.image.is-4by5 .has-ratio, +.image.is-3by4 .has-ratio, +.image.is-2by3 .has-ratio, +.image.is-3by5 .has-ratio, +.image.is-9by16 .has-ratio, +.image.is-1by2 .has-ratio, +.image.is-1by3 .has-ratio, +.hero-video { + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; +} +.pagination-previous, +.pagination-next, +.pagination-link, +.pagination-ellipsis, +.button, +.input, +.textarea, +.select select, +.file-cta, +.file-name { + -moz-appearance: none; + -webkit-appearance: none; + align-items: center; + border: 1px solid transparent; + border-radius: 4px; + box-shadow: none; + display: inline-flex; + font-size: 1rem; + height: 2.25em; + justify-content: flex-start; + line-height: 1.5; + padding-bottom: calc(0.5em - 1px); + padding-left: calc(0.75em - 1px); + padding-right: calc(0.75em - 1px); + padding-top: calc(0.5em - 1px); + position: relative; + vertical-align: top; +} +.pagination-previous:focus, +.pagination-next:focus, +.pagination-link:focus, +.pagination-ellipsis:focus, +.button:focus, +.input:focus, +.textarea:focus, +.select select:focus, +.file-cta:focus, +.file-name:focus, +.pagination-previous.is-focused, +.pagination-next.is-focused, +.pagination-link.is-focused, +.pagination-ellipsis.is-focused, +.button.is-focused, +.input.is-focused, +.textarea.is-focused, +.select select.is-focused, +.file-cta.is-focused, +.file-name.is-focused, +.pagination-previous:active, +.pagination-next:active, +.pagination-link:active, +.pagination-ellipsis:active, +.button:active, +.input:active, +.textarea:active, +.select select:active, +.file-cta:active, +.file-name:active, +.pagination-previous.is-active, +.pagination-next.is-active, +.pagination-link.is-active, +.pagination-ellipsis.is-active, +.button.is-active, +.input.is-active, +.textarea.is-active, +.select select.is-active, +.file-cta.is-active, +.file-name.is-active { + outline: none; +} +.pagination-previous[disabled], +.pagination-next[disabled], +.pagination-link[disabled], +.pagination-ellipsis[disabled], +.button[disabled], +.input[disabled], +.textarea[disabled], +.select select[disabled], +.file-cta[disabled], +.file-name[disabled], +fieldset[disabled] .pagination-previous, +fieldset[disabled] .pagination-next, +fieldset[disabled] .pagination-link, +fieldset[disabled] .pagination-ellipsis, +fieldset[disabled] .button, +fieldset[disabled] .input, +fieldset[disabled] .textarea, +fieldset[disabled] .select select, +fieldset[disabled] .file-cta, +fieldset[disabled] .file-name { + cursor: not-allowed; +} +/* minireset.css v0.0.6 | MIT License | github.com/jgthms/minireset.css */ +html, +body, +p, +ol, +ul, +li, +dl, +dt, +dd, +blockquote, +figure, +fieldset, +legend, +textarea, +pre, +iframe, +hr, +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 0; + padding: 0; +} +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: 100%; + font-weight: normal; +} +ul { + list-style: none; +} +button, +input, +select, +textarea { + margin: 0; +} +html { + box-sizing: border-box; +} +*, +*::before, +*::after { + box-sizing: inherit; +} +img, +video { + height: auto; + max-width: 100%; +} +iframe { + border: 0; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +td, +th { + padding: 0; +} +td:not([align]), +th:not([align]) { + text-align: left; +} +html { + background-color: #f7f7f7; + font-size: 14px; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + min-width: 300px; + overflow-x: hidden; + overflow-y: scroll; + text-rendering: optimizeLegibility; + text-size-adjust: 100%; +} +article, +aside, +figure, +footer, +header, +hgroup, +section { + display: block; +} +body, +button, +input, +select, +textarea { + font-family: Ubuntu, Roboto, 'Open Sans', 'Microsoft YaHei', sans-serif; +} +code, +pre { + -moz-osx-font-smoothing: auto; + -webkit-font-smoothing: auto; + font-family: 'Source Code Pro', monospace, 'Microsoft YaHei'; +} +body { + color: #4a4a4a; + font-size: 1em; + font-weight: 400; + line-height: 1.5; +} +a { + color: #3273dc; + cursor: pointer; + text-decoration: none; +} +a strong { + color: currentColor; +} +a:hover { + color: #363636; +} +code { + background-color: #f5f5f5; + color: #f14668; + font-size: 0.875em; + font-weight: normal; + padding: 0.25em 0.5em 0.25em; +} +hr { + background-color: #f5f5f5; + border: none; + display: block; + height: 2px; + margin: 1.5rem 0; +} +img { + height: auto; + max-width: 100%; +} +input[type="checkbox"], +input[type="radio"] { + vertical-align: baseline; +} +small { + font-size: 0.875em; +} +span { + font-style: inherit; + font-weight: inherit; +} +strong { + color: #363636; + font-weight: 700; +} +fieldset { + border: none; +} +pre { + -webkit-overflow-scrolling: touch; + background-color: #f5f5f5; + color: #4a4a4a; + font-size: 0.875em; + overflow-x: auto; + padding: 1.25rem 1.5rem; + white-space: pre; + word-wrap: normal; +} +pre code { + background-color: transparent; + color: currentColor; + font-size: 1em; + padding: 0; +} +table td, +table th { + vertical-align: top; +} +table td:not([align]), +table th:not([align]) { + text-align: left; +} +table th { + color: #363636; +} +.is-clearfix::after { + clear: both; + content: " "; + display: table; +} +.is-pulled-left { + float: left !important; +} +.is-pulled-right { + float: right !important; +} +.is-clipped { + overflow: hidden !important; +} +.is-size-1 { + font-size: 3rem !important; +} +.is-size-2 { + font-size: 2.5rem !important; +} +.is-size-3 { + font-size: 2rem !important; +} +.is-size-4 { + font-size: 1.5rem !important; +} +.is-size-5 { + font-size: 1.25rem !important; +} +.is-size-6, +article.media .title { + font-size: 1rem !important; +} +.is-size-7, +article.media .date, +article.media .categories, +.article-licensing .licensing-title a, +.article-licensing .licensing-meta h6 { + font-size: 0.85rem !important; +} +@media screen and (max-width: 768px) { + .is-size-1-mobile { + font-size: 3rem !important; + } + .is-size-2-mobile { + font-size: 2.5rem !important; + } + .is-size-3-mobile { + font-size: 2rem !important; + } + .is-size-4-mobile { + font-size: 1.5rem !important; + } + .is-size-5-mobile { + font-size: 1.25rem !important; + } + .is-size-6-mobile { + font-size: 1rem !important; + } + .is-size-7-mobile { + font-size: 0.85rem !important; + } +} +@media screen and (min-width: 769px), print { + .is-size-1-tablet { + font-size: 3rem !important; + } + .is-size-2-tablet { + font-size: 2.5rem !important; + } + .is-size-3-tablet { + font-size: 2rem !important; + } + .is-size-4-tablet { + font-size: 1.5rem !important; + } + .is-size-5-tablet { + font-size: 1.25rem !important; + } + .is-size-6-tablet { + font-size: 1rem !important; + } + .is-size-7-tablet { + font-size: 0.85rem !important; + } +} +@media screen and (max-width: 1087px) { + .is-size-1-touch { + font-size: 3rem !important; + } + .is-size-2-touch { + font-size: 2.5rem !important; + } + .is-size-3-touch { + font-size: 2rem !important; + } + .is-size-4-touch { + font-size: 1.5rem !important; + } + .is-size-5-touch { + font-size: 1.25rem !important; + } + .is-size-6-touch { + font-size: 1rem !important; + } + .is-size-7-touch { + font-size: 0.85rem !important; + } +} +@media screen and (min-width: 1088px) { + .is-size-1-desktop { + font-size: 3rem !important; + } + .is-size-2-desktop { + font-size: 2.5rem !important; + } + .is-size-3-desktop { + font-size: 2rem !important; + } + .is-size-4-desktop { + font-size: 1.5rem !important; + } + .is-size-5-desktop { + font-size: 1.25rem !important; + } + .is-size-6-desktop { + font-size: 1rem !important; + } + .is-size-7-desktop { + font-size: 0.85rem !important; + } +} +@media screen and (min-width: 1280px) { + .is-size-1-widescreen { + font-size: 3rem !important; + } + .is-size-2-widescreen { + font-size: 2.5rem !important; + } + .is-size-3-widescreen { + font-size: 2rem !important; + } + .is-size-4-widescreen { + font-size: 1.5rem !important; + } + .is-size-5-widescreen { + font-size: 1.25rem !important; + } + .is-size-6-widescreen { + font-size: 1rem !important; + } + .is-size-7-widescreen { + font-size: 0.85rem !important; + } +} +@media screen and (min-width: 1472px) { + .is-size-1-fullhd { + font-size: 3rem !important; + } + .is-size-2-fullhd { + font-size: 2.5rem !important; + } + .is-size-3-fullhd { + font-size: 2rem !important; + } + .is-size-4-fullhd { + font-size: 1.5rem !important; + } + .is-size-5-fullhd { + font-size: 1.25rem !important; + } + .is-size-6-fullhd { + font-size: 1rem !important; + } + .is-size-7-fullhd { + font-size: 0.85rem !important; + } +} +.has-text-centered { + text-align: center !important; +} +.has-text-justified { + text-align: justify !important; +} +.has-text-left { + text-align: left !important; +} +.has-text-right { + text-align: right !important; +} +@media screen and (max-width: 768px) { + .has-text-centered-mobile { + text-align: center !important; + } +} +@media screen and (min-width: 769px), print { + .has-text-centered-tablet { + text-align: center !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .has-text-centered-tablet-only { + text-align: center !important; + } +} +@media screen and (max-width: 1087px) { + .has-text-centered-touch { + text-align: center !important; + } +} +@media screen and (min-width: 1088px) { + .has-text-centered-desktop { + text-align: center !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .has-text-centered-desktop-only { + text-align: center !important; + } +} +@media screen and (min-width: 1280px) { + .has-text-centered-widescreen { + text-align: center !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .has-text-centered-widescreen-only { + text-align: center !important; + } +} +@media screen and (min-width: 1472px) { + .has-text-centered-fullhd { + text-align: center !important; + } +} +@media screen and (max-width: 768px) { + .has-text-justified-mobile { + text-align: justify !important; + } +} +@media screen and (min-width: 769px), print { + .has-text-justified-tablet { + text-align: justify !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .has-text-justified-tablet-only { + text-align: justify !important; + } +} +@media screen and (max-width: 1087px) { + .has-text-justified-touch { + text-align: justify !important; + } +} +@media screen and (min-width: 1088px) { + .has-text-justified-desktop { + text-align: justify !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .has-text-justified-desktop-only { + text-align: justify !important; + } +} +@media screen and (min-width: 1280px) { + .has-text-justified-widescreen { + text-align: justify !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .has-text-justified-widescreen-only { + text-align: justify !important; + } +} +@media screen and (min-width: 1472px) { + .has-text-justified-fullhd { + text-align: justify !important; + } +} +@media screen and (max-width: 768px) { + .has-text-left-mobile { + text-align: left !important; + } +} +@media screen and (min-width: 769px), print { + .has-text-left-tablet { + text-align: left !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .has-text-left-tablet-only { + text-align: left !important; + } +} +@media screen and (max-width: 1087px) { + .has-text-left-touch { + text-align: left !important; + } +} +@media screen and (min-width: 1088px) { + .has-text-left-desktop { + text-align: left !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .has-text-left-desktop-only { + text-align: left !important; + } +} +@media screen and (min-width: 1280px) { + .has-text-left-widescreen { + text-align: left !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .has-text-left-widescreen-only { + text-align: left !important; + } +} +@media screen and (min-width: 1472px) { + .has-text-left-fullhd { + text-align: left !important; + } +} +@media screen and (max-width: 768px) { + .has-text-right-mobile { + text-align: right !important; + } +} +@media screen and (min-width: 769px), print { + .has-text-right-tablet { + text-align: right !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .has-text-right-tablet-only { + text-align: right !important; + } +} +@media screen and (max-width: 1087px) { + .has-text-right-touch { + text-align: right !important; + } +} +@media screen and (min-width: 1088px) { + .has-text-right-desktop { + text-align: right !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .has-text-right-desktop-only { + text-align: right !important; + } +} +@media screen and (min-width: 1280px) { + .has-text-right-widescreen { + text-align: right !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .has-text-right-widescreen-only { + text-align: right !important; + } +} +@media screen and (min-width: 1472px) { + .has-text-right-fullhd { + text-align: right !important; + } +} +.is-capitalized { + text-transform: capitalize !important; +} +.is-lowercase { + text-transform: lowercase !important; +} +.is-uppercase, +article.media .categories { + text-transform: uppercase !important; +} +.is-italic { + font-style: italic !important; +} +.has-text-white { + color: #fff !important; +} +a.has-text-white:hover, +a.has-text-white:focus { + color: #e6e6e6 !important; +} +.has-background-white { + background-color: #fff !important; +} +.has-text-black { + color: #0a0a0a !important; +} +a.has-text-black:hover, +a.has-text-black:focus { + color: #000 !important; +} +.has-background-black { + background-color: #0a0a0a !important; +} +.has-text-light { + color: #f5f5f5 !important; +} +a.has-text-light:hover, +a.has-text-light:focus { + color: #dbdbdb !important; +} +.has-background-light { + background-color: #f5f5f5 !important; +} +.has-text-dark { + color: #363636 !important; +} +a.has-text-dark:hover, +a.has-text-dark:focus { + color: #1c1c1c !important; +} +.has-background-dark { + background-color: #363636 !important; +} +.has-text-primary { + color: #3273dc !important; +} +a.has-text-primary:hover, +a.has-text-primary:focus { + color: #205bbc !important; +} +.has-background-primary { + background-color: #3273dc !important; +} +.has-text-link { + color: #3273dc !important; +} +a.has-text-link:hover, +a.has-text-link:focus { + color: #205bbc !important; +} +.has-background-link { + background-color: #3273dc !important; +} +.has-text-info { + color: #3298dc !important; +} +a.has-text-info:hover, +a.has-text-info:focus { + color: #207dbc !important; +} +.has-background-info { + background-color: #3298dc !important; +} +.has-text-success { + color: #48c774 !important; +} +a.has-text-success:hover, +a.has-text-success:focus { + color: #34a85c !important; +} +.has-background-success { + background-color: #48c774 !important; +} +.has-text-warning { + color: #ffdd57 !important; +} +a.has-text-warning:hover, +a.has-text-warning:focus { + color: #ffd324 !important; +} +.has-background-warning { + background-color: #ffdd57 !important; +} +.has-text-danger { + color: #f14668 !important; +} +a.has-text-danger:hover, +a.has-text-danger:focus { + color: #ee1742 !important; +} +.has-background-danger { + background-color: #f14668 !important; +} +.has-text-grey-lightest { + color: #ededed !important; +} +a.has-text-grey-lightest:hover, +a.has-text-grey-lightest:focus { + color: #d4d4d4 !important; +} +.has-background-grey-lightest { + background-color: #ededed !important; +} +.has-text-black-bis { + color: #121212 !important; +} +.has-background-black-bis { + background-color: #121212 !important; +} +.has-text-black-ter { + color: #242424 !important; +} +.has-background-black-ter { + background-color: #242424 !important; +} +.has-text-grey-darker { + color: #363636 !important; +} +.has-background-grey-darker { + background-color: #363636 !important; +} +.has-text-grey-dark { + color: #4a4a4a !important; +} +.has-background-grey-dark { + background-color: #4a4a4a !important; +} +.has-text-grey, +.article-licensing .licensing-title a { + color: #7a7a7a !important; +} +.has-background-grey { + background-color: #7a7a7a !important; +} +.has-text-grey-light { + color: #b5b5b5 !important; +} +.has-background-grey-light { + background-color: #b5b5b5 !important; +} +.has-text-grey-lighter { + color: #dbdbdb !important; +} +.has-background-grey-lighter { + background-color: #dbdbdb !important; +} +.has-text-white-ter { + color: #f5f5f5 !important; +} +.has-background-white-ter { + background-color: #f5f5f5 !important; +} +.has-text-white-bis { + color: #fafafa !important; +} +.has-background-white-bis { + background-color: #fafafa !important; +} +.has-text-weight-light { + font-weight: 300 !important; +} +.has-text-weight-normal { + font-weight: 400 !important; +} +.has-text-weight-medium { + font-weight: 500 !important; +} +.has-text-weight-semibold { + font-weight: 600 !important; +} +.has-text-weight-bold { + font-weight: 700 !important; +} +.is-family-primary { + font-family: Ubuntu, Roboto, 'Open Sans', 'Microsoft YaHei', sans-serif !important; +} +.is-family-secondary { + font-family: Ubuntu, Roboto, 'Open Sans', 'Microsoft YaHei', sans-serif !important; +} +.is-family-sans-serif { + font-family: Ubuntu, Roboto, 'Open Sans', 'Microsoft YaHei', sans-serif !important; +} +.is-family-monospace { + font-family: monospace !important; +} +.is-family-code { + font-family: 'Source Code Pro', monospace, 'Microsoft YaHei' !important; +} +.is-block { + display: block !important; +} +@media screen and (max-width: 768px) { + .is-block-mobile { + display: block !important; + } +} +@media screen and (min-width: 769px), print { + .is-block-tablet { + display: block !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-block-tablet-only { + display: block !important; + } +} +@media screen and (max-width: 1087px) { + .is-block-touch { + display: block !important; + } +} +@media screen and (min-width: 1088px) { + .is-block-desktop { + display: block !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-block-desktop-only { + display: block !important; + } +} +@media screen and (min-width: 1280px) { + .is-block-widescreen { + display: block !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-block-widescreen-only { + display: block !important; + } +} +@media screen and (min-width: 1472px) { + .is-block-fullhd { + display: block !important; + } +} +.is-flex { + display: flex !important; +} +@media screen and (max-width: 768px) { + .is-flex-mobile { + display: flex !important; + } +} +@media screen and (min-width: 769px), print { + .is-flex-tablet { + display: flex !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-flex-tablet-only { + display: flex !important; + } +} +@media screen and (max-width: 1087px) { + .is-flex-touch { + display: flex !important; + } +} +@media screen and (min-width: 1088px) { + .is-flex-desktop { + display: flex !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-flex-desktop-only { + display: flex !important; + } +} +@media screen and (min-width: 1280px) { + .is-flex-widescreen { + display: flex !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-flex-widescreen-only { + display: flex !important; + } +} +@media screen and (min-width: 1472px) { + .is-flex-fullhd { + display: flex !important; + } +} +.is-inline { + display: inline !important; +} +@media screen and (max-width: 768px) { + .is-inline-mobile { + display: inline !important; + } +} +@media screen and (min-width: 769px), print { + .is-inline-tablet { + display: inline !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-inline-tablet-only { + display: inline !important; + } +} +@media screen and (max-width: 1087px) { + .is-inline-touch { + display: inline !important; + } +} +@media screen and (min-width: 1088px) { + .is-inline-desktop { + display: inline !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-inline-desktop-only { + display: inline !important; + } +} +@media screen and (min-width: 1280px) { + .is-inline-widescreen { + display: inline !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-inline-widescreen-only { + display: inline !important; + } +} +@media screen and (min-width: 1472px) { + .is-inline-fullhd { + display: inline !important; + } +} +.is-inline-block { + display: inline-block !important; +} +@media screen and (max-width: 768px) { + .is-inline-block-mobile { + display: inline-block !important; + } +} +@media screen and (min-width: 769px), print { + .is-inline-block-tablet { + display: inline-block !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-inline-block-tablet-only { + display: inline-block !important; + } +} +@media screen and (max-width: 1087px) { + .is-inline-block-touch { + display: inline-block !important; + } +} +@media screen and (min-width: 1088px) { + .is-inline-block-desktop { + display: inline-block !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-inline-block-desktop-only { + display: inline-block !important; + } +} +@media screen and (min-width: 1280px) { + .is-inline-block-widescreen { + display: inline-block !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-inline-block-widescreen-only { + display: inline-block !important; + } +} +@media screen and (min-width: 1472px) { + .is-inline-block-fullhd { + display: inline-block !important; + } +} +.is-inline-flex { + display: inline-flex !important; +} +@media screen and (max-width: 768px) { + .is-inline-flex-mobile { + display: inline-flex !important; + } +} +@media screen and (min-width: 769px), print { + .is-inline-flex-tablet { + display: inline-flex !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-inline-flex-tablet-only { + display: inline-flex !important; + } +} +@media screen and (max-width: 1087px) { + .is-inline-flex-touch { + display: inline-flex !important; + } +} +@media screen and (min-width: 1088px) { + .is-inline-flex-desktop { + display: inline-flex !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-inline-flex-desktop-only { + display: inline-flex !important; + } +} +@media screen and (min-width: 1280px) { + .is-inline-flex-widescreen { + display: inline-flex !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-inline-flex-widescreen-only { + display: inline-flex !important; + } +} +@media screen and (min-width: 1472px) { + .is-inline-flex-fullhd { + display: inline-flex !important; + } +} +.is-hidden { + display: none !important; +} +.is-sr-only { + border: none !important; + clip: rect(0, 0, 0, 0) !important; + height: 0.01em !important; + overflow: hidden !important; + padding: 0 !important; + position: absolute !important; + white-space: nowrap !important; + width: 0.01em !important; +} +@media screen and (max-width: 768px) { + .is-hidden-mobile { + display: none !important; + } +} +@media screen and (min-width: 769px), print { + .is-hidden-tablet { + display: none !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-hidden-tablet-only { + display: none !important; + } +} +@media screen and (max-width: 1087px) { + .is-hidden-touch { + display: none !important; + } +} +@media screen and (min-width: 1088px) { + .is-hidden-desktop { + display: none !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-hidden-desktop-only { + display: none !important; + } +} +@media screen and (min-width: 1280px) { + .is-hidden-widescreen { + display: none !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-hidden-widescreen-only { + display: none !important; + } +} +@media screen and (min-width: 1472px) { + .is-hidden-fullhd { + display: none !important; + } +} +.is-invisible { + visibility: hidden !important; +} +@media screen and (max-width: 768px) { + .is-invisible-mobile { + visibility: hidden !important; + } +} +@media screen and (min-width: 769px), print { + .is-invisible-tablet { + visibility: hidden !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-invisible-tablet-only { + visibility: hidden !important; + } +} +@media screen and (max-width: 1087px) { + .is-invisible-touch { + visibility: hidden !important; + } +} +@media screen and (min-width: 1088px) { + .is-invisible-desktop { + visibility: hidden !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-invisible-desktop-only { + visibility: hidden !important; + } +} +@media screen and (min-width: 1280px) { + .is-invisible-widescreen { + visibility: hidden !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-invisible-widescreen-only { + visibility: hidden !important; + } +} +@media screen and (min-width: 1472px) { + .is-invisible-fullhd { + visibility: hidden !important; + } +} +.is-marginless { + margin: 0 !important; +} +.is-paddingless { + padding: 0 !important; +} +.is-radiusless { + border-radius: 0 !important; +} +.is-shadowless { + box-shadow: none !important; +} +.is-relative { + position: relative !important; +} +.breadcrumb { + font-size: 1rem; + white-space: nowrap; +} +.breadcrumb a { + align-items: center; + color: #3273dc; + display: flex; + justify-content: center; + padding: 0 0.75em; +} +.breadcrumb a:hover { + color: #363636; +} +.breadcrumb li { + align-items: center; + display: flex; +} +.breadcrumb li:first-child a { + padding-left: 0; +} +.breadcrumb li.is-active a { + color: #363636; + cursor: default; + pointer-events: none; +} +.breadcrumb li + li::before { + color: #b5b5b5; + content: "\0002f"; +} +.breadcrumb ul, +.breadcrumb ol { + align-items: flex-start; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; +} +.breadcrumb .icon:first-child { + margin-right: 0.5em; +} +.breadcrumb .icon:last-child { + margin-left: 0.5em; +} +.breadcrumb.is-centered ol, +.breadcrumb.is-centered ul { + justify-content: center; +} +.breadcrumb.is-right ol, +.breadcrumb.is-right ul { + justify-content: flex-end; +} +.breadcrumb.is-small { + font-size: 0.75rem; +} +.breadcrumb.is-medium { + font-size: 1.25rem; +} +.breadcrumb.is-large { + font-size: 1.5rem; +} +.breadcrumb.has-arrow-separator li + li::before { + content: "\02192"; +} +.breadcrumb.has-bullet-separator li + li::before { + content: "\02022"; +} +.breadcrumb.has-dot-separator li + li::before { + content: "\000b7"; +} +.breadcrumb.has-succeeds-separator li + li::before { + content: "\0227B"; +} +.card { + background-color: #fff; + box-shadow: 0 4px 10px rgba(0,0,0,0.05), 0 0 1px rgba(0,0,0,0.1); + color: #4a4a4a; + max-width: 100%; + position: relative; +} +.card-header { + background-color: transparent; + align-items: stretch; + box-shadow: 0 0.125em 0.25em rgba(10,10,10,0.1); + display: flex; +} +.card-header-title { + align-items: center; + color: #363636; + display: flex; + flex-grow: 1; + font-weight: 700; + padding: 0.75rem 1rem; +} +.card-header-title.is-centered { + justify-content: center; +} +.card-header-icon { + align-items: center; + cursor: pointer; + display: flex; + justify-content: center; + padding: 0.75rem 1rem; +} +.card-image { + display: block; + position: relative; +} +.card-content { + background-color: transparent; + padding: 1.5rem; +} +.card-footer { + background-color: transparent; + border-top: 1px solid #ededed; + align-items: stretch; + display: flex; +} +.card-footer-item { + align-items: center; + display: flex; + flex-basis: 0; + flex-grow: 1; + flex-shrink: 0; + justify-content: center; + padding: 0.75rem; +} +.card-footer-item:not(:last-child) { + border-right: 1px solid #ededed; +} +.card .media:not(:last-child) { + margin-bottom: 0.75rem; +} +.dropdown { + display: inline-flex; + position: relative; + vertical-align: top; +} +.dropdown.is-active .dropdown-menu, +.dropdown.is-hoverable:hover .dropdown-menu { + display: block; +} +.dropdown.is-right .dropdown-menu { + left: auto; + right: 0; +} +.dropdown.is-up .dropdown-menu { + bottom: 100%; + padding-bottom: 4px; + padding-top: initial; + top: auto; +} +.dropdown-menu { + display: none; + left: 0; + min-width: 12rem; + padding-top: 4px; + position: absolute; + top: 100%; + z-index: 20; +} +.dropdown-content { + background-color: #fff; + border-radius: 4px; + box-shadow: 0 0.5em 1em -0.125em rgba(10,10,10,0.1), 0 0px 0 1px rgba(10,10,10,0.02); + padding-bottom: 0.5rem; + padding-top: 0.5rem; +} +.dropdown-item { + color: #4a4a4a; + display: block; + font-size: 0.875rem; + line-height: 1.5; + padding: 0.375rem 1rem; + position: relative; +} +a.dropdown-item, +button.dropdown-item { + padding-right: 3rem; + text-align: left; + white-space: nowrap; + width: 100%; +} +a.dropdown-item:hover, +button.dropdown-item:hover { + background-color: #f5f5f5; + color: #0a0a0a; +} +a.dropdown-item.is-active, +button.dropdown-item.is-active { + background-color: #3273dc; + color: #fff; +} +.dropdown-divider { + background-color: #ededed; + border: none; + display: block; + height: 1px; + margin: 0.5rem 0; +} +.level { + align-items: center; + justify-content: space-between; +} +.level code { + border-radius: 4px; +} +.level img { + display: inline-block; + vertical-align: top; +} +.level.is-mobile { + display: flex; +} +.level.is-mobile .level-left, +.level.is-mobile .level-right { + display: flex; +} +.level.is-mobile .level-left + .level-right { + margin-top: 0; +} +.level.is-mobile .level-item:not(:last-child) { + margin-bottom: 0; + margin-right: 0.75rem; +} +.level.is-mobile .level-item:not(.is-narrow) { + flex-grow: 1; +} +@media screen and (min-width: 769px), print { + .level { + display: flex; + } + .level > .level-item:not(.is-narrow) { + flex-grow: 1; + } +} +.level-item { + align-items: center; + display: flex; + flex-basis: auto; + flex-grow: 0; + flex-shrink: 0; + justify-content: center; +} +.level-item .title, +.level-item .subtitle { + margin-bottom: 0; +} +@media screen and (max-width: 768px) { + .level-item:not(:last-child) { + margin-bottom: 0.75rem; + } +} +.level-left, +.level-right { + flex-basis: auto; + flex-grow: 0; + flex-shrink: 0; +} +.level-left .level-item.is-flexible, +.level-right .level-item.is-flexible { + flex-grow: 1; +} +@media screen and (min-width: 769px), print { + .level-left .level-item:not(:last-child), + .level-right .level-item:not(:last-child) { + margin-right: 0.75rem; + } +} +.level-left { + align-items: center; + justify-content: flex-start; +} +@media screen and (max-width: 768px) { + .level-left + .level-right { + margin-top: 1.5rem; + } +} +@media screen and (min-width: 769px), print { + .level-left { + display: flex; + } +} +.level-right { + align-items: center; + justify-content: flex-end; +} +@media screen and (min-width: 769px), print { + .level-right { + display: flex; + } +} +.list { + background-color: #fff; + border-radius: 4px; + box-shadow: 0 2px 3px rgba(10,10,10,0.1), 0 0 0 1px rgba(10,10,10,0.1); +} +.list-item { + display: block; + padding: 0.5em 1em; +} +.list-item:not(a) { + color: #4a4a4a; +} +.list-item:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} +.list-item:last-child { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; +} +.list-item:not(:last-child) { + border-bottom: 1px solid #dbdbdb; +} +.list-item.is-active { + background-color: #3273dc; + color: #fff; +} +a.list-item { + background-color: #f5f5f5; + cursor: pointer; +} +.media { + align-items: flex-start; + display: flex; + text-align: left; +} +.media .content:not(:last-child) { + margin-bottom: 0.75rem; +} +.media .media { + border-top: 1px solid rgba(219,219,219,0.5); + display: flex; + padding-top: 0.75rem; +} +.media .media .content:not(:last-child), +.media .media .control:not(:last-child) { + margin-bottom: 0.5rem; +} +.media .media .media { + padding-top: 0.5rem; +} +.media .media .media + .media { + margin-top: 0.5rem; +} +.media + .media { + border-top: 1px solid rgba(219,219,219,0.5); + margin-top: 1rem; + padding-top: 1rem; +} +.media.is-large + .media { + margin-top: 1.5rem; + padding-top: 1.5rem; +} +.media-left, +.media-right { + flex-basis: auto; + flex-grow: 0; + flex-shrink: 0; +} +.media-left { + margin-right: 1rem; +} +.media-right { + margin-left: 1rem; +} +.media-content { + flex-basis: auto; + flex-grow: 1; + flex-shrink: 1; + text-align: left; +} +@media screen and (max-width: 768px) { + .media-content { + overflow-x: auto; + } +} +.menu { + font-size: 1rem; +} +.menu.is-small { + font-size: 0.75rem; +} +.menu.is-medium { + font-size: 1.25rem; +} +.menu.is-large { + font-size: 1.5rem; +} +.menu-list { + line-height: 1.25; +} +.menu-list a { + border-radius: 2px; + color: #4a4a4a; + display: block; + padding: 0.5em 0.75em; +} +.menu-list a:hover { + background-color: #f5f5f5; + color: #363636; +} +.menu-list a.is-active { + background-color: #eef3fc; + color: #3273dc; +} +.menu-list li ul { + border-left: 1px solid #dbdbdb; + margin: 0.75em; + padding-left: 0.75em; +} +.menu-label { + color: #7a7a7a; + font-size: 0.75em; + letter-spacing: 0.1em; + text-transform: uppercase; +} +.menu-label:not(:first-child) { + margin-top: 1em; +} +.menu-label:not(:last-child) { + margin-bottom: 1em; +} +.message { + background-color: #f5f5f5; + border-radius: 4px; + font-size: 1rem; +} +.message strong { + color: currentColor; +} +.message a:not(.button):not(.tag):not(.dropdown-item) { + color: currentColor; + text-decoration: underline; +} +.message.is-small { + font-size: 0.75rem; +} +.message.is-medium { + font-size: 1.25rem; +} +.message.is-large { + font-size: 1.5rem; +} +.message.is-white { + background-color: #fff; +} +.message.is-white .message-header { + background-color: #fff; + color: #0a0a0a; +} +.message.is-white .message-body { + border-color: #fff; +} +.message.is-black { + background-color: #fafafa; +} +.message.is-black .message-header { + background-color: #0a0a0a; + color: #fff; +} +.message.is-black .message-body { + border-color: #0a0a0a; +} +.message.is-light { + background-color: #fafafa; +} +.message.is-light .message-header { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.message.is-light .message-body { + border-color: #f5f5f5; +} +.message.is-dark { + background-color: #fafafa; +} +.message.is-dark .message-header { + background-color: #363636; + color: #fff; +} +.message.is-dark .message-body { + border-color: #363636; +} +.message.is-primary { + background-color: #eef3fc; +} +.message.is-primary .message-header { + background-color: #3273dc; + color: #fff; +} +.message.is-primary .message-body { + border-color: #3273dc; + color: #2160c4; +} +.message.is-link { + background-color: #eef3fc; +} +.message.is-link .message-header { + background-color: #3273dc; + color: #fff; +} +.message.is-link .message-body { + border-color: #3273dc; + color: #2160c4; +} +.message.is-info { + background-color: #eef6fc; +} +.message.is-info .message-header { + background-color: #3298dc; + color: #fff; +} +.message.is-info .message-body { + border-color: #3298dc; + color: #1d72aa; +} +.message.is-success { + background-color: #effaf3; +} +.message.is-success .message-header { + background-color: #48c774; + color: #fff; +} +.message.is-success .message-body { + border-color: #48c774; + color: #257942; +} +.message.is-warning { + background-color: #fffbeb; +} +.message.is-warning .message-header { + background-color: #ffdd57; + color: rgba(0,0,0,0.7); +} +.message.is-warning .message-body { + border-color: #ffdd57; + color: #947600; +} +.message.is-danger { + background-color: #feecf0; +} +.message.is-danger .message-header { + background-color: #f14668; + color: #fff; +} +.message.is-danger .message-body { + border-color: #f14668; + color: #cc0f35; +} +.message.is-grey-lightest { + background-color: #fafafa; +} +.message.is-grey-lightest .message-header { + background-color: #ededed; + color: #363636; +} +.message.is-grey-lightest .message-body { + border-color: #ededed; +} +.message-header { + align-items: center; + background-color: #4a4a4a; + border-radius: 4px 4px 0 0; + color: #fff; + display: flex; + font-weight: 700; + justify-content: space-between; + line-height: 1.25; + padding: 0.75em 1em; + position: relative; +} +.message-header .delete { + flex-grow: 0; + flex-shrink: 0; + margin-left: 0.75em; +} +.message-header + .message-body { + border-width: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.message-body { + border-color: #dbdbdb; + border-radius: 4px; + border-style: solid; + border-width: 0 0 0 4px; + color: #4a4a4a; + padding: 1.25em 1.5em; +} +.message-body code, +.message-body pre { + background-color: #fff; +} +.message-body pre code { + background-color: transparent; +} +.modal { + align-items: center; + display: none; + flex-direction: column; + justify-content: center; + overflow: hidden; + position: fixed; + z-index: 40; +} +.modal.is-active { + display: flex; +} +.modal-background { + background-color: rgba(10,10,10,0.86); +} +.modal-content, +.modal-card { + margin: 0 20px; + max-height: calc(100vh - 160px); + overflow: auto; + position: relative; + width: 100%; +} +@media screen and (min-width: 769px), print { + .modal-content, + .modal-card { + margin: 0 auto; + max-height: calc(100vh - 40px); + width: 640px; + } +} +.modal-close { + background: none; + height: 40px; + position: fixed; + right: 20px; + top: 20px; + width: 40px; +} +.modal-card { + display: flex; + flex-direction: column; + max-height: calc(100vh - 40px); + overflow: hidden; + -ms-overflow-y: visible; +} +.modal-card-head, +.modal-card-foot { + align-items: center; + background-color: #f5f5f5; + display: flex; + flex-shrink: 0; + justify-content: flex-start; + padding: 20px; + position: relative; +} +.modal-card-head { + border-bottom: 1px solid #dbdbdb; + border-top-left-radius: 6px; + border-top-right-radius: 6px; +} +.modal-card-title { + color: #363636; + flex-grow: 1; + flex-shrink: 0; + font-size: 1.5rem; + line-height: 1; +} +.modal-card-foot { + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + border-top: 1px solid #dbdbdb; +} +.modal-card-foot .button:not(:last-child) { + margin-right: 0.5em; +} +.modal-card-body { + -webkit-overflow-scrolling: touch; + background-color: #fff; + flex-grow: 1; + flex-shrink: 1; + overflow: auto; + padding: 20px; +} +.navbar { + background-color: #fff; + min-height: 3.25rem; + position: relative; + z-index: 30; +} +.navbar.is-white { + background-color: #fff; + color: #0a0a0a; +} +.navbar.is-white .navbar-brand > .navbar-item, +.navbar.is-white .navbar-brand .navbar-link { + color: #0a0a0a; +} +.navbar.is-white .navbar-brand > a.navbar-item:focus, +.navbar.is-white .navbar-brand .navbar-link:focus, +.navbar.is-white .navbar-brand > a.navbar-item:hover, +.navbar.is-white .navbar-brand .navbar-link:hover, +.navbar.is-white .navbar-brand > a.navbar-item.is-active, +.navbar.is-white .navbar-brand .navbar-link.is-active { + background-color: #f2f2f2; + color: #0a0a0a; +} +.navbar.is-white .navbar-brand .navbar-link::after { + border-color: #0a0a0a; +} +.navbar.is-white .navbar-burger { + color: #0a0a0a; +} +@media screen and (min-width: 1088px) { + .navbar.is-white .navbar-start > .navbar-item, + .navbar.is-white .navbar-end > .navbar-item, + .navbar.is-white .navbar-start .navbar-link, + .navbar.is-white .navbar-end .navbar-link { + color: #0a0a0a; + } + .navbar.is-white .navbar-start > a.navbar-item:focus, + .navbar.is-white .navbar-end > a.navbar-item:focus, + .navbar.is-white .navbar-start .navbar-link:focus, + .navbar.is-white .navbar-end .navbar-link:focus, + .navbar.is-white .navbar-start > a.navbar-item:hover, + .navbar.is-white .navbar-end > a.navbar-item:hover, + .navbar.is-white .navbar-start .navbar-link:hover, + .navbar.is-white .navbar-end .navbar-link:hover, + .navbar.is-white .navbar-start > a.navbar-item.is-active, + .navbar.is-white .navbar-end > a.navbar-item.is-active, + .navbar.is-white .navbar-start .navbar-link.is-active, + .navbar.is-white .navbar-end .navbar-link.is-active { + background-color: #f2f2f2; + color: #0a0a0a; + } + .navbar.is-white .navbar-start .navbar-link::after, + .navbar.is-white .navbar-end .navbar-link::after { + border-color: #0a0a0a; + } + .navbar.is-white .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-white .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-white .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #f2f2f2; + color: #0a0a0a; + } + .navbar.is-white .navbar-dropdown a.navbar-item.is-active { + background-color: #fff; + color: #0a0a0a; + } +} +.navbar.is-black { + background-color: #0a0a0a; + color: #fff; +} +.navbar.is-black .navbar-brand > .navbar-item, +.navbar.is-black .navbar-brand .navbar-link { + color: #fff; +} +.navbar.is-black .navbar-brand > a.navbar-item:focus, +.navbar.is-black .navbar-brand .navbar-link:focus, +.navbar.is-black .navbar-brand > a.navbar-item:hover, +.navbar.is-black .navbar-brand .navbar-link:hover, +.navbar.is-black .navbar-brand > a.navbar-item.is-active, +.navbar.is-black .navbar-brand .navbar-link.is-active { + background-color: #000; + color: #fff; +} +.navbar.is-black .navbar-brand .navbar-link::after { + border-color: #fff; +} +.navbar.is-black .navbar-burger { + color: #fff; +} +@media screen and (min-width: 1088px) { + .navbar.is-black .navbar-start > .navbar-item, + .navbar.is-black .navbar-end > .navbar-item, + .navbar.is-black .navbar-start .navbar-link, + .navbar.is-black .navbar-end .navbar-link { + color: #fff; + } + .navbar.is-black .navbar-start > a.navbar-item:focus, + .navbar.is-black .navbar-end > a.navbar-item:focus, + .navbar.is-black .navbar-start .navbar-link:focus, + .navbar.is-black .navbar-end .navbar-link:focus, + .navbar.is-black .navbar-start > a.navbar-item:hover, + .navbar.is-black .navbar-end > a.navbar-item:hover, + .navbar.is-black .navbar-start .navbar-link:hover, + .navbar.is-black .navbar-end .navbar-link:hover, + .navbar.is-black .navbar-start > a.navbar-item.is-active, + .navbar.is-black .navbar-end > a.navbar-item.is-active, + .navbar.is-black .navbar-start .navbar-link.is-active, + .navbar.is-black .navbar-end .navbar-link.is-active { + background-color: #000; + color: #fff; + } + .navbar.is-black .navbar-start .navbar-link::after, + .navbar.is-black .navbar-end .navbar-link::after { + border-color: #fff; + } + .navbar.is-black .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-black .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-black .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #000; + color: #fff; + } + .navbar.is-black .navbar-dropdown a.navbar-item.is-active { + background-color: #0a0a0a; + color: #fff; + } +} +.navbar.is-light { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.navbar.is-light .navbar-brand > .navbar-item, +.navbar.is-light .navbar-brand .navbar-link { + color: rgba(0,0,0,0.7); +} +.navbar.is-light .navbar-brand > a.navbar-item:focus, +.navbar.is-light .navbar-brand .navbar-link:focus, +.navbar.is-light .navbar-brand > a.navbar-item:hover, +.navbar.is-light .navbar-brand .navbar-link:hover, +.navbar.is-light .navbar-brand > a.navbar-item.is-active, +.navbar.is-light .navbar-brand .navbar-link.is-active { + background-color: #e8e8e8; + color: rgba(0,0,0,0.7); +} +.navbar.is-light .navbar-brand .navbar-link::after { + border-color: rgba(0,0,0,0.7); +} +.navbar.is-light .navbar-burger { + color: rgba(0,0,0,0.7); +} +@media screen and (min-width: 1088px) { + .navbar.is-light .navbar-start > .navbar-item, + .navbar.is-light .navbar-end > .navbar-item, + .navbar.is-light .navbar-start .navbar-link, + .navbar.is-light .navbar-end .navbar-link { + color: rgba(0,0,0,0.7); + } + .navbar.is-light .navbar-start > a.navbar-item:focus, + .navbar.is-light .navbar-end > a.navbar-item:focus, + .navbar.is-light .navbar-start .navbar-link:focus, + .navbar.is-light .navbar-end .navbar-link:focus, + .navbar.is-light .navbar-start > a.navbar-item:hover, + .navbar.is-light .navbar-end > a.navbar-item:hover, + .navbar.is-light .navbar-start .navbar-link:hover, + .navbar.is-light .navbar-end .navbar-link:hover, + .navbar.is-light .navbar-start > a.navbar-item.is-active, + .navbar.is-light .navbar-end > a.navbar-item.is-active, + .navbar.is-light .navbar-start .navbar-link.is-active, + .navbar.is-light .navbar-end .navbar-link.is-active { + background-color: #e8e8e8; + color: rgba(0,0,0,0.7); + } + .navbar.is-light .navbar-start .navbar-link::after, + .navbar.is-light .navbar-end .navbar-link::after { + border-color: rgba(0,0,0,0.7); + } + .navbar.is-light .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-light .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-light .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #e8e8e8; + color: rgba(0,0,0,0.7); + } + .navbar.is-light .navbar-dropdown a.navbar-item.is-active { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); + } +} +.navbar.is-dark { + background-color: #363636; + color: #fff; +} +.navbar.is-dark .navbar-brand > .navbar-item, +.navbar.is-dark .navbar-brand .navbar-link { + color: #fff; +} +.navbar.is-dark .navbar-brand > a.navbar-item:focus, +.navbar.is-dark .navbar-brand .navbar-link:focus, +.navbar.is-dark .navbar-brand > a.navbar-item:hover, +.navbar.is-dark .navbar-brand .navbar-link:hover, +.navbar.is-dark .navbar-brand > a.navbar-item.is-active, +.navbar.is-dark .navbar-brand .navbar-link.is-active { + background-color: #292929; + color: #fff; +} +.navbar.is-dark .navbar-brand .navbar-link::after { + border-color: #fff; +} +.navbar.is-dark .navbar-burger { + color: #fff; +} +@media screen and (min-width: 1088px) { + .navbar.is-dark .navbar-start > .navbar-item, + .navbar.is-dark .navbar-end > .navbar-item, + .navbar.is-dark .navbar-start .navbar-link, + .navbar.is-dark .navbar-end .navbar-link { + color: #fff; + } + .navbar.is-dark .navbar-start > a.navbar-item:focus, + .navbar.is-dark .navbar-end > a.navbar-item:focus, + .navbar.is-dark .navbar-start .navbar-link:focus, + .navbar.is-dark .navbar-end .navbar-link:focus, + .navbar.is-dark .navbar-start > a.navbar-item:hover, + .navbar.is-dark .navbar-end > a.navbar-item:hover, + .navbar.is-dark .navbar-start .navbar-link:hover, + .navbar.is-dark .navbar-end .navbar-link:hover, + .navbar.is-dark .navbar-start > a.navbar-item.is-active, + .navbar.is-dark .navbar-end > a.navbar-item.is-active, + .navbar.is-dark .navbar-start .navbar-link.is-active, + .navbar.is-dark .navbar-end .navbar-link.is-active { + background-color: #292929; + color: #fff; + } + .navbar.is-dark .navbar-start .navbar-link::after, + .navbar.is-dark .navbar-end .navbar-link::after { + border-color: #fff; + } + .navbar.is-dark .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-dark .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-dark .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #292929; + color: #fff; + } + .navbar.is-dark .navbar-dropdown a.navbar-item.is-active { + background-color: #363636; + color: #fff; + } +} +.navbar.is-primary { + background-color: #3273dc; + color: #fff; +} +.navbar.is-primary .navbar-brand > .navbar-item, +.navbar.is-primary .navbar-brand .navbar-link { + color: #fff; +} +.navbar.is-primary .navbar-brand > a.navbar-item:focus, +.navbar.is-primary .navbar-brand .navbar-link:focus, +.navbar.is-primary .navbar-brand > a.navbar-item:hover, +.navbar.is-primary .navbar-brand .navbar-link:hover, +.navbar.is-primary .navbar-brand > a.navbar-item.is-active, +.navbar.is-primary .navbar-brand .navbar-link.is-active { + background-color: #2366d1; + color: #fff; +} +.navbar.is-primary .navbar-brand .navbar-link::after { + border-color: #fff; +} +.navbar.is-primary .navbar-burger { + color: #fff; +} +@media screen and (min-width: 1088px) { + .navbar.is-primary .navbar-start > .navbar-item, + .navbar.is-primary .navbar-end > .navbar-item, + .navbar.is-primary .navbar-start .navbar-link, + .navbar.is-primary .navbar-end .navbar-link { + color: #fff; + } + .navbar.is-primary .navbar-start > a.navbar-item:focus, + .navbar.is-primary .navbar-end > a.navbar-item:focus, + .navbar.is-primary .navbar-start .navbar-link:focus, + .navbar.is-primary .navbar-end .navbar-link:focus, + .navbar.is-primary .navbar-start > a.navbar-item:hover, + .navbar.is-primary .navbar-end > a.navbar-item:hover, + .navbar.is-primary .navbar-start .navbar-link:hover, + .navbar.is-primary .navbar-end .navbar-link:hover, + .navbar.is-primary .navbar-start > a.navbar-item.is-active, + .navbar.is-primary .navbar-end > a.navbar-item.is-active, + .navbar.is-primary .navbar-start .navbar-link.is-active, + .navbar.is-primary .navbar-end .navbar-link.is-active { + background-color: #2366d1; + color: #fff; + } + .navbar.is-primary .navbar-start .navbar-link::after, + .navbar.is-primary .navbar-end .navbar-link::after { + border-color: #fff; + } + .navbar.is-primary .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-primary .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-primary .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #2366d1; + color: #fff; + } + .navbar.is-primary .navbar-dropdown a.navbar-item.is-active { + background-color: #3273dc; + color: #fff; + } +} +.navbar.is-link { + background-color: #3273dc; + color: #fff; +} +.navbar.is-link .navbar-brand > .navbar-item, +.navbar.is-link .navbar-brand .navbar-link { + color: #fff; +} +.navbar.is-link .navbar-brand > a.navbar-item:focus, +.navbar.is-link .navbar-brand .navbar-link:focus, +.navbar.is-link .navbar-brand > a.navbar-item:hover, +.navbar.is-link .navbar-brand .navbar-link:hover, +.navbar.is-link .navbar-brand > a.navbar-item.is-active, +.navbar.is-link .navbar-brand .navbar-link.is-active { + background-color: #2366d1; + color: #fff; +} +.navbar.is-link .navbar-brand .navbar-link::after { + border-color: #fff; +} +.navbar.is-link .navbar-burger { + color: #fff; +} +@media screen and (min-width: 1088px) { + .navbar.is-link .navbar-start > .navbar-item, + .navbar.is-link .navbar-end > .navbar-item, + .navbar.is-link .navbar-start .navbar-link, + .navbar.is-link .navbar-end .navbar-link { + color: #fff; + } + .navbar.is-link .navbar-start > a.navbar-item:focus, + .navbar.is-link .navbar-end > a.navbar-item:focus, + .navbar.is-link .navbar-start .navbar-link:focus, + .navbar.is-link .navbar-end .navbar-link:focus, + .navbar.is-link .navbar-start > a.navbar-item:hover, + .navbar.is-link .navbar-end > a.navbar-item:hover, + .navbar.is-link .navbar-start .navbar-link:hover, + .navbar.is-link .navbar-end .navbar-link:hover, + .navbar.is-link .navbar-start > a.navbar-item.is-active, + .navbar.is-link .navbar-end > a.navbar-item.is-active, + .navbar.is-link .navbar-start .navbar-link.is-active, + .navbar.is-link .navbar-end .navbar-link.is-active { + background-color: #2366d1; + color: #fff; + } + .navbar.is-link .navbar-start .navbar-link::after, + .navbar.is-link .navbar-end .navbar-link::after { + border-color: #fff; + } + .navbar.is-link .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-link .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-link .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #2366d1; + color: #fff; + } + .navbar.is-link .navbar-dropdown a.navbar-item.is-active { + background-color: #3273dc; + color: #fff; + } +} +.navbar.is-info { + background-color: #3298dc; + color: #fff; +} +.navbar.is-info .navbar-brand > .navbar-item, +.navbar.is-info .navbar-brand .navbar-link { + color: #fff; +} +.navbar.is-info .navbar-brand > a.navbar-item:focus, +.navbar.is-info .navbar-brand .navbar-link:focus, +.navbar.is-info .navbar-brand > a.navbar-item:hover, +.navbar.is-info .navbar-brand .navbar-link:hover, +.navbar.is-info .navbar-brand > a.navbar-item.is-active, +.navbar.is-info .navbar-brand .navbar-link.is-active { + background-color: #238cd1; + color: #fff; +} +.navbar.is-info .navbar-brand .navbar-link::after { + border-color: #fff; +} +.navbar.is-info .navbar-burger { + color: #fff; +} +@media screen and (min-width: 1088px) { + .navbar.is-info .navbar-start > .navbar-item, + .navbar.is-info .navbar-end > .navbar-item, + .navbar.is-info .navbar-start .navbar-link, + .navbar.is-info .navbar-end .navbar-link { + color: #fff; + } + .navbar.is-info .navbar-start > a.navbar-item:focus, + .navbar.is-info .navbar-end > a.navbar-item:focus, + .navbar.is-info .navbar-start .navbar-link:focus, + .navbar.is-info .navbar-end .navbar-link:focus, + .navbar.is-info .navbar-start > a.navbar-item:hover, + .navbar.is-info .navbar-end > a.navbar-item:hover, + .navbar.is-info .navbar-start .navbar-link:hover, + .navbar.is-info .navbar-end .navbar-link:hover, + .navbar.is-info .navbar-start > a.navbar-item.is-active, + .navbar.is-info .navbar-end > a.navbar-item.is-active, + .navbar.is-info .navbar-start .navbar-link.is-active, + .navbar.is-info .navbar-end .navbar-link.is-active { + background-color: #238cd1; + color: #fff; + } + .navbar.is-info .navbar-start .navbar-link::after, + .navbar.is-info .navbar-end .navbar-link::after { + border-color: #fff; + } + .navbar.is-info .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-info .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-info .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #238cd1; + color: #fff; + } + .navbar.is-info .navbar-dropdown a.navbar-item.is-active { + background-color: #3298dc; + color: #fff; + } +} +.navbar.is-success { + background-color: #48c774; + color: #fff; +} +.navbar.is-success .navbar-brand > .navbar-item, +.navbar.is-success .navbar-brand .navbar-link { + color: #fff; +} +.navbar.is-success .navbar-brand > a.navbar-item:focus, +.navbar.is-success .navbar-brand .navbar-link:focus, +.navbar.is-success .navbar-brand > a.navbar-item:hover, +.navbar.is-success .navbar-brand .navbar-link:hover, +.navbar.is-success .navbar-brand > a.navbar-item.is-active, +.navbar.is-success .navbar-brand .navbar-link.is-active { + background-color: #3abb67; + color: #fff; +} +.navbar.is-success .navbar-brand .navbar-link::after { + border-color: #fff; +} +.navbar.is-success .navbar-burger { + color: #fff; +} +@media screen and (min-width: 1088px) { + .navbar.is-success .navbar-start > .navbar-item, + .navbar.is-success .navbar-end > .navbar-item, + .navbar.is-success .navbar-start .navbar-link, + .navbar.is-success .navbar-end .navbar-link { + color: #fff; + } + .navbar.is-success .navbar-start > a.navbar-item:focus, + .navbar.is-success .navbar-end > a.navbar-item:focus, + .navbar.is-success .navbar-start .navbar-link:focus, + .navbar.is-success .navbar-end .navbar-link:focus, + .navbar.is-success .navbar-start > a.navbar-item:hover, + .navbar.is-success .navbar-end > a.navbar-item:hover, + .navbar.is-success .navbar-start .navbar-link:hover, + .navbar.is-success .navbar-end .navbar-link:hover, + .navbar.is-success .navbar-start > a.navbar-item.is-active, + .navbar.is-success .navbar-end > a.navbar-item.is-active, + .navbar.is-success .navbar-start .navbar-link.is-active, + .navbar.is-success .navbar-end .navbar-link.is-active { + background-color: #3abb67; + color: #fff; + } + .navbar.is-success .navbar-start .navbar-link::after, + .navbar.is-success .navbar-end .navbar-link::after { + border-color: #fff; + } + .navbar.is-success .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-success .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-success .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #3abb67; + color: #fff; + } + .navbar.is-success .navbar-dropdown a.navbar-item.is-active { + background-color: #48c774; + color: #fff; + } +} +.navbar.is-warning { + background-color: #ffdd57; + color: rgba(0,0,0,0.7); +} +.navbar.is-warning .navbar-brand > .navbar-item, +.navbar.is-warning .navbar-brand .navbar-link { + color: rgba(0,0,0,0.7); +} +.navbar.is-warning .navbar-brand > a.navbar-item:focus, +.navbar.is-warning .navbar-brand .navbar-link:focus, +.navbar.is-warning .navbar-brand > a.navbar-item:hover, +.navbar.is-warning .navbar-brand .navbar-link:hover, +.navbar.is-warning .navbar-brand > a.navbar-item.is-active, +.navbar.is-warning .navbar-brand .navbar-link.is-active { + background-color: #ffd83d; + color: rgba(0,0,0,0.7); +} +.navbar.is-warning .navbar-brand .navbar-link::after { + border-color: rgba(0,0,0,0.7); +} +.navbar.is-warning .navbar-burger { + color: rgba(0,0,0,0.7); +} +@media screen and (min-width: 1088px) { + .navbar.is-warning .navbar-start > .navbar-item, + .navbar.is-warning .navbar-end > .navbar-item, + .navbar.is-warning .navbar-start .navbar-link, + .navbar.is-warning .navbar-end .navbar-link { + color: rgba(0,0,0,0.7); + } + .navbar.is-warning .navbar-start > a.navbar-item:focus, + .navbar.is-warning .navbar-end > a.navbar-item:focus, + .navbar.is-warning .navbar-start .navbar-link:focus, + .navbar.is-warning .navbar-end .navbar-link:focus, + .navbar.is-warning .navbar-start > a.navbar-item:hover, + .navbar.is-warning .navbar-end > a.navbar-item:hover, + .navbar.is-warning .navbar-start .navbar-link:hover, + .navbar.is-warning .navbar-end .navbar-link:hover, + .navbar.is-warning .navbar-start > a.navbar-item.is-active, + .navbar.is-warning .navbar-end > a.navbar-item.is-active, + .navbar.is-warning .navbar-start .navbar-link.is-active, + .navbar.is-warning .navbar-end .navbar-link.is-active { + background-color: #ffd83d; + color: rgba(0,0,0,0.7); + } + .navbar.is-warning .navbar-start .navbar-link::after, + .navbar.is-warning .navbar-end .navbar-link::after { + border-color: rgba(0,0,0,0.7); + } + .navbar.is-warning .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-warning .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-warning .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #ffd83d; + color: rgba(0,0,0,0.7); + } + .navbar.is-warning .navbar-dropdown a.navbar-item.is-active { + background-color: #ffdd57; + color: rgba(0,0,0,0.7); + } +} +.navbar.is-danger { + background-color: #f14668; + color: #fff; +} +.navbar.is-danger .navbar-brand > .navbar-item, +.navbar.is-danger .navbar-brand .navbar-link { + color: #fff; +} +.navbar.is-danger .navbar-brand > a.navbar-item:focus, +.navbar.is-danger .navbar-brand .navbar-link:focus, +.navbar.is-danger .navbar-brand > a.navbar-item:hover, +.navbar.is-danger .navbar-brand .navbar-link:hover, +.navbar.is-danger .navbar-brand > a.navbar-item.is-active, +.navbar.is-danger .navbar-brand .navbar-link.is-active { + background-color: #ef2e55; + color: #fff; +} +.navbar.is-danger .navbar-brand .navbar-link::after { + border-color: #fff; +} +.navbar.is-danger .navbar-burger { + color: #fff; +} +@media screen and (min-width: 1088px) { + .navbar.is-danger .navbar-start > .navbar-item, + .navbar.is-danger .navbar-end > .navbar-item, + .navbar.is-danger .navbar-start .navbar-link, + .navbar.is-danger .navbar-end .navbar-link { + color: #fff; + } + .navbar.is-danger .navbar-start > a.navbar-item:focus, + .navbar.is-danger .navbar-end > a.navbar-item:focus, + .navbar.is-danger .navbar-start .navbar-link:focus, + .navbar.is-danger .navbar-end .navbar-link:focus, + .navbar.is-danger .navbar-start > a.navbar-item:hover, + .navbar.is-danger .navbar-end > a.navbar-item:hover, + .navbar.is-danger .navbar-start .navbar-link:hover, + .navbar.is-danger .navbar-end .navbar-link:hover, + .navbar.is-danger .navbar-start > a.navbar-item.is-active, + .navbar.is-danger .navbar-end > a.navbar-item.is-active, + .navbar.is-danger .navbar-start .navbar-link.is-active, + .navbar.is-danger .navbar-end .navbar-link.is-active { + background-color: #ef2e55; + color: #fff; + } + .navbar.is-danger .navbar-start .navbar-link::after, + .navbar.is-danger .navbar-end .navbar-link::after { + border-color: #fff; + } + .navbar.is-danger .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-danger .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-danger .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #ef2e55; + color: #fff; + } + .navbar.is-danger .navbar-dropdown a.navbar-item.is-active { + background-color: #f14668; + color: #fff; + } +} +.navbar.is-grey-lightest { + background-color: #ededed; + color: #363636; +} +.navbar.is-grey-lightest .navbar-brand > .navbar-item, +.navbar.is-grey-lightest .navbar-brand .navbar-link { + color: #363636; +} +.navbar.is-grey-lightest .navbar-brand > a.navbar-item:focus, +.navbar.is-grey-lightest .navbar-brand .navbar-link:focus, +.navbar.is-grey-lightest .navbar-brand > a.navbar-item:hover, +.navbar.is-grey-lightest .navbar-brand .navbar-link:hover, +.navbar.is-grey-lightest .navbar-brand > a.navbar-item.is-active, +.navbar.is-grey-lightest .navbar-brand .navbar-link.is-active { + background-color: #e0e0e0; + color: #363636; +} +.navbar.is-grey-lightest .navbar-brand .navbar-link::after { + border-color: #363636; +} +.navbar.is-grey-lightest .navbar-burger { + color: #363636; +} +@media screen and (min-width: 1088px) { + .navbar.is-grey-lightest .navbar-start > .navbar-item, + .navbar.is-grey-lightest .navbar-end > .navbar-item, + .navbar.is-grey-lightest .navbar-start .navbar-link, + .navbar.is-grey-lightest .navbar-end .navbar-link { + color: #363636; + } + .navbar.is-grey-lightest .navbar-start > a.navbar-item:focus, + .navbar.is-grey-lightest .navbar-end > a.navbar-item:focus, + .navbar.is-grey-lightest .navbar-start .navbar-link:focus, + .navbar.is-grey-lightest .navbar-end .navbar-link:focus, + .navbar.is-grey-lightest .navbar-start > a.navbar-item:hover, + .navbar.is-grey-lightest .navbar-end > a.navbar-item:hover, + .navbar.is-grey-lightest .navbar-start .navbar-link:hover, + .navbar.is-grey-lightest .navbar-end .navbar-link:hover, + .navbar.is-grey-lightest .navbar-start > a.navbar-item.is-active, + .navbar.is-grey-lightest .navbar-end > a.navbar-item.is-active, + .navbar.is-grey-lightest .navbar-start .navbar-link.is-active, + .navbar.is-grey-lightest .navbar-end .navbar-link.is-active { + background-color: #e0e0e0; + color: #363636; + } + .navbar.is-grey-lightest .navbar-start .navbar-link::after, + .navbar.is-grey-lightest .navbar-end .navbar-link::after { + border-color: #363636; + } + .navbar.is-grey-lightest .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-grey-lightest .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-grey-lightest .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #e0e0e0; + color: #363636; + } + .navbar.is-grey-lightest .navbar-dropdown a.navbar-item.is-active { + background-color: #ededed; + color: #363636; + } +} +.navbar > .container { + align-items: stretch; + display: flex; + min-height: 3.25rem; + width: 100%; +} +.navbar.has-shadow { + box-shadow: 0 2px 0 0 #f5f5f5; +} +.navbar.is-fixed-bottom, +.navbar.is-fixed-top { + left: 0; + position: fixed; + right: 0; + z-index: 30; +} +.navbar.is-fixed-bottom { + bottom: 0; +} +.navbar.is-fixed-bottom.has-shadow { + box-shadow: 0 -2px 0 0 #f5f5f5; +} +.navbar.is-fixed-top { + top: 0; +} +html.has-navbar-fixed-top, +body.has-navbar-fixed-top { + padding-top: 3.25rem; +} +html.has-navbar-fixed-bottom, +body.has-navbar-fixed-bottom { + padding-bottom: 3.25rem; +} +.navbar-brand, +.navbar-tabs { + align-items: stretch; + display: flex; + flex-shrink: 0; + min-height: 3.25rem; +} +.navbar-brand a.navbar-item:focus, +.navbar-brand a.navbar-item:hover { + background-color: transparent; +} +.navbar-tabs { + -webkit-overflow-scrolling: touch; + max-width: 100vw; + overflow-x: auto; + overflow-y: hidden; +} +.navbar-burger { + color: #4a4a4a; + cursor: pointer; + display: block; + height: 3.25rem; + position: relative; + width: 3.25rem; + margin-left: auto; +} +.navbar-burger span { + background-color: currentColor; + display: block; + height: 1px; + left: calc(50% - 8px); + position: absolute; + transform-origin: center; + transition-duration: 86ms; + transition-property: background-color, opacity, transform; + transition-timing-function: ease-out; + width: 16px; +} +.navbar-burger span:nth-child(1) { + top: calc(50% - 6px); +} +.navbar-burger span:nth-child(2) { + top: calc(50% - 1px); +} +.navbar-burger span:nth-child(3) { + top: calc(50% + 4px); +} +.navbar-burger:hover { + background-color: rgba(0,0,0,0.05); +} +.navbar-burger.is-active span:nth-child(1) { + transform: translateY(5px) rotate(45deg); +} +.navbar-burger.is-active span:nth-child(2) { + opacity: 0; +} +.navbar-burger.is-active span:nth-child(3) { + transform: translateY(-5px) rotate(-45deg); +} +.navbar-menu { + display: none; +} +.navbar-item, +.navbar-link { + color: #4a4a4a; + display: block; + line-height: 1.5; + padding: 0.5rem 0.75rem; + position: relative; +} +.navbar-item .icon:only-child, +.navbar-link .icon:only-child { + margin-left: -0.25rem; + margin-right: -0.25rem; +} +a.navbar-item, +.navbar-link { + cursor: pointer; +} +a.navbar-item:focus, +.navbar-link:focus, +a.navbar-item:focus-within, +.navbar-link:focus-within, +a.navbar-item:hover, +.navbar-link:hover, +a.navbar-item.is-active, +.navbar-link.is-active { + background-color: #fafafa; + color: #3273dc; +} +.navbar-item { + display: block; + flex-grow: 0; + flex-shrink: 0; +} +.navbar-item img { + max-height: 1.75rem; +} +.navbar-item.has-dropdown { + padding: 0; +} +.navbar-item.is-expanded { + flex-grow: 1; + flex-shrink: 1; +} +.navbar-item.is-tab { + border-bottom: 1px solid transparent; + min-height: 3.25rem; + padding-bottom: calc(0.5rem - 1px); +} +.navbar-item.is-tab:focus, +.navbar-item.is-tab:hover { + background-color: transparent; + border-bottom-color: #3273dc; +} +.navbar-item.is-tab.is-active { + background-color: transparent; + border-bottom-color: #3273dc; + border-bottom-style: solid; + border-bottom-width: 3px; + color: #3273dc; + padding-bottom: calc(0.5rem - 3px); +} +.navbar-content { + flex-grow: 1; + flex-shrink: 1; +} +.navbar-link:not(.is-arrowless) { + padding-right: 2.5em; +} +.navbar-link:not(.is-arrowless)::after { + border-color: #3273dc; + margin-top: -0.375em; + right: 1.125em; +} +.navbar-dropdown { + font-size: 0.875rem; + padding-bottom: 0.5rem; + padding-top: 0.5rem; +} +.navbar-dropdown .navbar-item { + padding-left: 1.5rem; + padding-right: 1.5rem; +} +.navbar-divider { + background-color: #f5f5f5; + border: none; + display: none; + height: 2px; + margin: 0.5rem 0; +} +@media screen and (max-width: 1087px) { + .navbar > .container { + display: block; + } + .navbar-brand .navbar-item, + .navbar-tabs .navbar-item { + align-items: center; + display: flex; + } + .navbar-link::after { + display: none; + } + .navbar-menu { + background-color: #fff; + box-shadow: 0 8px 16px rgba(10,10,10,0.1); + padding: 0.5rem 0; + } + .navbar-menu.is-active { + display: block; + } + .navbar.is-fixed-bottom-touch, + .navbar.is-fixed-top-touch { + left: 0; + position: fixed; + right: 0; + z-index: 30; + } + .navbar.is-fixed-bottom-touch { + bottom: 0; + } + .navbar.is-fixed-bottom-touch.has-shadow { + box-shadow: 0 -2px 3px rgba(10,10,10,0.1); + } + .navbar.is-fixed-top-touch { + top: 0; + } + .navbar.is-fixed-top .navbar-menu, + .navbar.is-fixed-top-touch .navbar-menu { + -webkit-overflow-scrolling: touch; + max-height: calc(100vh - 3.25rem); + overflow: auto; + } + html.has-navbar-fixed-top-touch, + body.has-navbar-fixed-top-touch { + padding-top: 3.25rem; + } + html.has-navbar-fixed-bottom-touch, + body.has-navbar-fixed-bottom-touch { + padding-bottom: 3.25rem; + } +} +@media screen and (min-width: 1088px) { + .navbar, + .navbar-menu, + .navbar-start, + .navbar-end { + align-items: stretch; + display: flex; + } + .navbar { + min-height: 3.25rem; + } + .navbar.is-spaced { + padding: 1rem 2rem; + } + .navbar.is-spaced .navbar-start, + .navbar.is-spaced .navbar-end { + align-items: center; + } + .navbar.is-spaced a.navbar-item, + .navbar.is-spaced .navbar-link { + border-radius: 4px; + } + .navbar.is-transparent a.navbar-item:focus, + .navbar.is-transparent .navbar-link:focus, + .navbar.is-transparent a.navbar-item:hover, + .navbar.is-transparent .navbar-link:hover, + .navbar.is-transparent a.navbar-item.is-active, + .navbar.is-transparent .navbar-link.is-active { + background-color: transparent !important; + } + .navbar.is-transparent .navbar-item.has-dropdown.is-active .navbar-link, + .navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:focus .navbar-link, + .navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:focus-within .navbar-link, + .navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:hover .navbar-link { + background-color: transparent !important; + } + .navbar.is-transparent .navbar-dropdown a.navbar-item:focus, + .navbar.is-transparent .navbar-dropdown a.navbar-item:hover { + background-color: #f5f5f5; + color: #0a0a0a; + } + .navbar.is-transparent .navbar-dropdown a.navbar-item.is-active { + background-color: #f5f5f5; + color: #3273dc; + } + .navbar-burger { + display: none; + } + .navbar-item, + .navbar-link { + align-items: center; + display: flex; + } + .navbar-item { + display: flex; + } + .navbar-item.has-dropdown { + align-items: stretch; + } + .navbar-item.has-dropdown-up .navbar-link::after { + transform: rotate(135deg) translate(0.25em, -0.25em); + } + .navbar-item.has-dropdown-up .navbar-dropdown { + border-bottom: 2px solid #dbdbdb; + border-radius: 6px 6px 0 0; + border-top: none; + bottom: 100%; + box-shadow: 0 -8px 8px rgba(10,10,10,0.1); + top: auto; + } + .navbar-item.is-active .navbar-dropdown, + .navbar-item.is-hoverable:focus .navbar-dropdown, + .navbar-item.is-hoverable:focus-within .navbar-dropdown, + .navbar-item.is-hoverable:hover .navbar-dropdown { + display: block; + } + .navbar.is-spaced .navbar-item.is-active .navbar-dropdown, + .navbar.is-spaced .navbar-item.is-hoverable:focus .navbar-dropdown, + .navbar.is-spaced .navbar-item.is-hoverable:focus-within .navbar-dropdown, + .navbar.is-spaced .navbar-item.is-hoverable:hover .navbar-dropdown, + .navbar-item.is-active .navbar-dropdown.is-boxed, + .navbar-item.is-hoverable:focus .navbar-dropdown.is-boxed, + .navbar-item.is-hoverable:focus-within .navbar-dropdown.is-boxed, + .navbar-item.is-hoverable:hover .navbar-dropdown.is-boxed { + opacity: 1; + pointer-events: auto; + transform: translateY(0); + } + .navbar-menu { + flex-grow: 1; + flex-shrink: 0; + } + .navbar-start { + justify-content: flex-start; + margin-right: auto; + } + .navbar-end { + justify-content: flex-end; + margin-left: auto; + } + .navbar-dropdown { + background-color: #fff; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + border-top: 2px solid #dbdbdb; + box-shadow: 0 8px 8px rgba(10,10,10,0.1); + display: none; + font-size: 0.875rem; + left: 0; + min-width: 100%; + position: absolute; + top: 100%; + z-index: 20; + } + .navbar-dropdown .navbar-item { + padding: 0.375rem 1rem; + white-space: nowrap; + } + .navbar-dropdown a.navbar-item { + padding-right: 3rem; + } + .navbar-dropdown a.navbar-item:focus, + .navbar-dropdown a.navbar-item:hover { + background-color: #f5f5f5; + color: #0a0a0a; + } + .navbar-dropdown a.navbar-item.is-active { + background-color: #f5f5f5; + color: #3273dc; + } + .navbar.is-spaced .navbar-dropdown, + .navbar-dropdown.is-boxed { + border-radius: 6px; + border-top: none; + box-shadow: 0 8px 8px rgba(10,10,10,0.1), 0 0 0 1px rgba(10,10,10,0.1); + display: block; + opacity: 0; + pointer-events: none; + top: calc(100% + -4px); + transform: translateY(-5px); + transition-duration: 86ms; + transition-property: opacity, transform; + } + .navbar-dropdown.is-right { + left: auto; + right: 0; + } + .navbar-divider { + display: block; + } + .navbar > .container .navbar-brand, + .container > .navbar .navbar-brand { + margin-left: -0.75rem; + } + .navbar > .container .navbar-menu, + .container > .navbar .navbar-menu { + margin-right: -0.75rem; + } + .navbar.is-fixed-bottom-desktop, + .navbar.is-fixed-top-desktop { + left: 0; + position: fixed; + right: 0; + z-index: 30; + } + .navbar.is-fixed-bottom-desktop { + bottom: 0; + } + .navbar.is-fixed-bottom-desktop.has-shadow { + box-shadow: 0 -2px 3px rgba(10,10,10,0.1); + } + .navbar.is-fixed-top-desktop { + top: 0; + } + html.has-navbar-fixed-top-desktop, + body.has-navbar-fixed-top-desktop { + padding-top: 3.25rem; + } + html.has-navbar-fixed-bottom-desktop, + body.has-navbar-fixed-bottom-desktop { + padding-bottom: 3.25rem; + } + html.has-spaced-navbar-fixed-top, + body.has-spaced-navbar-fixed-top { + padding-top: 5.25rem; + } + html.has-spaced-navbar-fixed-bottom, + body.has-spaced-navbar-fixed-bottom { + padding-bottom: 5.25rem; + } + a.navbar-item.is-active, + .navbar-link.is-active { + color: #3273dc; + } + a.navbar-item.is-active:not(:focus):not(:hover), + .navbar-link.is-active:not(:focus):not(:hover) { + background-color: transparent; + } + .navbar-item.has-dropdown:focus .navbar-link, + .navbar-item.has-dropdown:hover .navbar-link, + .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #fafafa; + } +} +.hero.is-fullheight-with-navbar { + min-height: calc(100vh - 3.25rem); +} +.pagination { + font-size: 1rem; + margin: -0.25rem; +} +.pagination.is-small { + font-size: 0.75rem; +} +.pagination.is-medium { + font-size: 1.25rem; +} +.pagination.is-large { + font-size: 1.5rem; +} +.pagination.is-rounded .pagination-previous, +.pagination.is-rounded .pagination-next { + padding-left: 1em; + padding-right: 1em; + border-radius: 290486px; +} +.pagination.is-rounded .pagination-link { + border-radius: 290486px; +} +.pagination, +.pagination-list { + align-items: center; + display: flex; + justify-content: center; + text-align: center; +} +.pagination-previous, +.pagination-next, +.pagination-link, +.pagination-ellipsis { + font-size: 1em; + justify-content: center; + margin: 0.25rem; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: center; +} +.pagination-previous, +.pagination-next, +.pagination-link { + border-color: #dbdbdb; + color: #363636; + min-width: 2.25em; +} +.pagination-previous:hover, +.pagination-next:hover, +.pagination-link:hover { + border-color: #b5b5b5; + color: #363636; +} +.pagination-previous:focus, +.pagination-next:focus, +.pagination-link:focus { + border-color: #3273dc; +} +.pagination-previous:active, +.pagination-next:active, +.pagination-link:active { + box-shadow: inset 0 1px 2px rgba(10,10,10,0.2); +} +.pagination-previous[disabled], +.pagination-next[disabled], +.pagination-link[disabled] { + background-color: #dbdbdb; + border-color: #dbdbdb; + box-shadow: none; + color: #7a7a7a; + opacity: 0.5; +} +.pagination-previous, +.pagination-next { + padding-left: 0.75em; + padding-right: 0.75em; + white-space: nowrap; +} +.pagination-link.is-current { + background-color: #3273dc; + border-color: #3273dc; + color: #fff; +} +.pagination-ellipsis { + color: #b5b5b5; + pointer-events: none; +} +.pagination-list { + flex-wrap: wrap; +} +@media screen and (max-width: 768px) { + .pagination { + flex-wrap: wrap; + } + .pagination-previous, + .pagination-next { + flex-grow: 1; + flex-shrink: 1; + } + .pagination-list li { + flex-grow: 1; + flex-shrink: 1; + } +} +@media screen and (min-width: 769px), print { + .pagination-list { + flex-grow: 1; + flex-shrink: 1; + justify-content: flex-start; + order: 1; + } + .pagination-previous { + order: 2; + } + .pagination-next { + order: 3; + } + .pagination { + justify-content: space-between; + } + .pagination.is-centered .pagination-previous, + .pagination .pagination-previous { + order: 1; + } + .pagination.is-centered .pagination-list, + .pagination .pagination-list { + justify-content: center; + order: 2; + } + .pagination.is-centered .pagination-next, + .pagination .pagination-next { + order: 3; + } + .pagination.is-right .pagination-previous { + order: 1; + } + .pagination.is-right .pagination-next { + order: 2; + } + .pagination.is-right .pagination-list { + justify-content: flex-end; + order: 3; + } +} +.panel { + border-radius: 6px; + box-shadow: 0 0.5em 1em -0.125em rgba(10,10,10,0.1), 0 0px 0 1px rgba(10,10,10,0.02); + font-size: 1rem; +} +.panel:not(:last-child) { + margin-bottom: 1.5rem; +} +.panel.is-white .panel-heading { + background-color: #fff; + color: #0a0a0a; +} +.panel.is-white .panel-tabs a.is-active { + border-bottom-color: #fff; +} +.panel.is-white .panel-block.is-active .panel-icon { + color: #fff; +} +.panel.is-black .panel-heading { + background-color: #0a0a0a; + color: #fff; +} +.panel.is-black .panel-tabs a.is-active { + border-bottom-color: #0a0a0a; +} +.panel.is-black .panel-block.is-active .panel-icon { + color: #0a0a0a; +} +.panel.is-light .panel-heading { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.panel.is-light .panel-tabs a.is-active { + border-bottom-color: #f5f5f5; +} +.panel.is-light .panel-block.is-active .panel-icon { + color: #f5f5f5; +} +.panel.is-dark .panel-heading { + background-color: #363636; + color: #fff; +} +.panel.is-dark .panel-tabs a.is-active { + border-bottom-color: #363636; +} +.panel.is-dark .panel-block.is-active .panel-icon { + color: #363636; +} +.panel.is-primary .panel-heading { + background-color: #3273dc; + color: #fff; +} +.panel.is-primary .panel-tabs a.is-active { + border-bottom-color: #3273dc; +} +.panel.is-primary .panel-block.is-active .panel-icon { + color: #3273dc; +} +.panel.is-link .panel-heading { + background-color: #3273dc; + color: #fff; +} +.panel.is-link .panel-tabs a.is-active { + border-bottom-color: #3273dc; +} +.panel.is-link .panel-block.is-active .panel-icon { + color: #3273dc; +} +.panel.is-info .panel-heading { + background-color: #3298dc; + color: #fff; +} +.panel.is-info .panel-tabs a.is-active { + border-bottom-color: #3298dc; +} +.panel.is-info .panel-block.is-active .panel-icon { + color: #3298dc; +} +.panel.is-success .panel-heading { + background-color: #48c774; + color: #fff; +} +.panel.is-success .panel-tabs a.is-active { + border-bottom-color: #48c774; +} +.panel.is-success .panel-block.is-active .panel-icon { + color: #48c774; +} +.panel.is-warning .panel-heading { + background-color: #ffdd57; + color: rgba(0,0,0,0.7); +} +.panel.is-warning .panel-tabs a.is-active { + border-bottom-color: #ffdd57; +} +.panel.is-warning .panel-block.is-active .panel-icon { + color: #ffdd57; +} +.panel.is-danger .panel-heading { + background-color: #f14668; + color: #fff; +} +.panel.is-danger .panel-tabs a.is-active { + border-bottom-color: #f14668; +} +.panel.is-danger .panel-block.is-active .panel-icon { + color: #f14668; +} +.panel.is-grey-lightest .panel-heading { + background-color: #ededed; + color: #363636; +} +.panel.is-grey-lightest .panel-tabs a.is-active { + border-bottom-color: #ededed; +} +.panel.is-grey-lightest .panel-block.is-active .panel-icon { + color: #ededed; +} +.panel-tabs:not(:last-child), +.panel-block:not(:last-child) { + border-bottom: 1px solid #ededed; +} +.panel-heading { + background-color: #ededed; + border-radius: 6px 6px 0 0; + color: #363636; + font-size: 1.25em; + font-weight: 700; + line-height: 1.25; + padding: 0.75em 1em; +} +.panel-tabs { + align-items: flex-end; + display: flex; + font-size: 0.875em; + justify-content: center; +} +.panel-tabs a { + border-bottom: 1px solid #dbdbdb; + margin-bottom: -1px; + padding: 0.5em; +} +.panel-tabs a.is-active { + border-bottom-color: #4a4a4a; + color: #363636; +} +.panel-list a { + color: #4a4a4a; +} +.panel-list a:hover { + color: #3273dc; +} +.panel-block { + align-items: center; + color: #363636; + display: flex; + justify-content: flex-start; + padding: 0.5em 0.75em; +} +.panel-block input[type="checkbox"] { + margin-right: 0.75em; +} +.panel-block > .control { + flex-grow: 1; + flex-shrink: 1; + width: 100%; +} +.panel-block.is-wrapped { + flex-wrap: wrap; +} +.panel-block.is-active { + border-left-color: #3273dc; + color: #363636; +} +.panel-block.is-active .panel-icon { + color: #3273dc; +} +.panel-block:last-child { + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; +} +a.panel-block, +label.panel-block { + cursor: pointer; +} +a.panel-block:hover, +label.panel-block:hover { + background-color: #f5f5f5; +} +.panel-icon { + display: inline-block; + font-size: 14px; + height: 1em; + line-height: 1em; + text-align: center; + vertical-align: top; + width: 1em; + color: #7a7a7a; + margin-right: 0.75em; +} +.panel-icon .fa { + font-size: inherit; + line-height: inherit; +} +.tabs { + -webkit-overflow-scrolling: touch; + align-items: stretch; + display: flex; + font-size: 1rem; + justify-content: space-between; + overflow: hidden; + overflow-x: auto; + white-space: nowrap; +} +.tabs a { + align-items: center; + border-bottom-color: #dbdbdb; + border-bottom-style: solid; + border-bottom-width: 1px; + color: #4a4a4a; + display: flex; + justify-content: center; + margin-bottom: -1px; + padding: 0.5em 1em; + vertical-align: top; +} +.tabs a:hover { + border-bottom-color: #363636; + color: #363636; +} +.tabs li { + display: block; +} +.tabs li.is-active a { + border-bottom-color: #3273dc; + color: #3273dc; +} +.tabs ul { + align-items: center; + border-bottom-color: #dbdbdb; + border-bottom-style: solid; + border-bottom-width: 1px; + display: flex; + flex-grow: 1; + flex-shrink: 0; + justify-content: flex-start; +} +.tabs ul.is-left { + padding-right: 0.75em; +} +.tabs ul.is-center { + flex: none; + justify-content: center; + padding-left: 0.75em; + padding-right: 0.75em; +} +.tabs ul.is-right { + justify-content: flex-end; + padding-left: 0.75em; +} +.tabs .icon:first-child { + margin-right: 0.5em; +} +.tabs .icon:last-child { + margin-left: 0.5em; +} +.tabs.is-centered ul { + justify-content: center; +} +.tabs.is-right ul { + justify-content: flex-end; +} +.tabs.is-boxed a { + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.tabs.is-boxed a:hover { + background-color: #f5f5f5; + border-bottom-color: #dbdbdb; +} +.tabs.is-boxed li.is-active a { + background-color: #fff; + border-color: #dbdbdb; + border-bottom-color: transparent !important; +} +.tabs.is-fullwidth li { + flex-grow: 1; + flex-shrink: 0; +} +.tabs.is-toggle a { + border-color: #dbdbdb; + border-style: solid; + border-width: 1px; + margin-bottom: 0; + position: relative; +} +.tabs.is-toggle a:hover { + background-color: #f5f5f5; + border-color: #b5b5b5; + z-index: 2; +} +.tabs.is-toggle li + li { + margin-left: -1px; +} +.tabs.is-toggle li:first-child a { + border-radius: 4px 0 0 4px; +} +.tabs.is-toggle li:last-child a { + border-radius: 0 4px 4px 0; +} +.tabs.is-toggle li.is-active a { + background-color: #3273dc; + border-color: #3273dc; + color: #fff; + z-index: 1; +} +.tabs.is-toggle ul { + border-bottom: none; +} +.tabs.is-toggle.is-toggle-rounded li:first-child a { + border-bottom-left-radius: 290486px; + border-top-left-radius: 290486px; + padding-left: 1.25em; +} +.tabs.is-toggle.is-toggle-rounded li:last-child a { + border-bottom-right-radius: 290486px; + border-top-right-radius: 290486px; + padding-right: 1.25em; +} +.tabs.is-small { + font-size: 0.75rem; +} +.tabs.is-medium { + font-size: 1.25rem; +} +.tabs.is-large { + font-size: 1.5rem; +} +.box { + background-color: #fff; + border-radius: 6px; + box-shadow: 0 0.5em 1em -0.125em rgba(10,10,10,0.1), 0 0px 0 1px rgba(10,10,10,0.02); + color: #4a4a4a; + display: block; + padding: 1.25rem; +} +a.box:hover, +a.box:focus { + box-shadow: 0 0.5em 1em -0.125em rgba(10,10,10,0.1), 0 0 0 1px #3273dc; +} +a.box:active { + box-shadow: inset 0 1px 2px rgba(10,10,10,0.2), 0 0 0 1px #3273dc; +} +.button { + background-color: #fff; + border-color: #dbdbdb; + border-width: 1px; + color: #363636; + cursor: pointer; + justify-content: center; + padding-bottom: calc(0.375em - 1px); + padding-left: 1em; + padding-right: 1em; + padding-top: calc(0.375em - 1px); + text-align: center; + white-space: nowrap; +} +.button strong { + color: inherit; +} +.button .icon, +.button .icon.is-small, +.button .icon.is-medium, +.button .icon.is-large { + height: 1.5em; + width: 1.5em; +} +.button .icon:first-child:not(:last-child) { + margin-left: calc(-0.5em - 1px); + margin-right: 0.25em; +} +.button .icon:last-child:not(:first-child) { + margin-left: 0.25em; + margin-right: calc(-0.5em - 1px); +} +.button .icon:first-child:last-child { + margin-left: calc(-0.5em - 1px); + margin-right: calc(-0.5em - 1px); +} +.button:hover, +.button.is-hovered { + border-color: #b5b5b5; + color: #363636; +} +.button:focus, +.button.is-focused { + border-color: #3273dc; + color: #363636; +} +.button:focus:not(:active), +.button.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(50,115,220,0.25); +} +.button:active, +.button.is-active { + border-color: #4a4a4a; + color: #363636; +} +.button.is-text { + background-color: transparent; + border-color: transparent; + color: #4a4a4a; + text-decoration: underline; +} +.button.is-text:hover, +.button.is-text.is-hovered, +.button.is-text:focus, +.button.is-text.is-focused { + background-color: #f5f5f5; + color: #363636; +} +.button.is-text:active, +.button.is-text.is-active { + background-color: #e8e8e8; + color: #363636; +} +.button.is-text[disabled], +fieldset[disabled] .button.is-text { + background-color: transparent; + border-color: transparent; + box-shadow: none; +} +.button.is-white { + background-color: #fff; + border-color: transparent; + color: #0a0a0a; +} +.button.is-white:hover, +.button.is-white.is-hovered { + background-color: #f9f9f9; + border-color: transparent; + color: #0a0a0a; +} +.button.is-white:focus, +.button.is-white.is-focused { + border-color: transparent; + color: #0a0a0a; +} +.button.is-white:focus:not(:active), +.button.is-white.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(255,255,255,0.25); +} +.button.is-white:active, +.button.is-white.is-active { + background-color: #f2f2f2; + border-color: transparent; + color: #0a0a0a; +} +.button.is-white[disabled], +fieldset[disabled] .button.is-white { + background-color: #fff; + border-color: transparent; + box-shadow: none; +} +.button.is-white.is-inverted { + background-color: #0a0a0a; + color: #fff; +} +.button.is-white.is-inverted:hover, +.button.is-white.is-inverted.is-hovered { + background-color: #000; +} +.button.is-white.is-inverted[disabled], +fieldset[disabled] .button.is-white.is-inverted { + background-color: #0a0a0a; + border-color: transparent; + box-shadow: none; + color: #fff; +} +.button.is-white.is-loading::after { + border-color: transparent transparent #0a0a0a #0a0a0a !important; +} +.button.is-white.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; +} +.button.is-white.is-outlined:hover, +.button.is-white.is-outlined.is-hovered, +.button.is-white.is-outlined:focus, +.button.is-white.is-outlined.is-focused { + background-color: #fff; + border-color: #fff; + color: #0a0a0a; +} +.button.is-white.is-outlined.is-loading::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-white.is-outlined.is-loading:hover::after, +.button.is-white.is-outlined.is-loading.is-hovered::after, +.button.is-white.is-outlined.is-loading:focus::after, +.button.is-white.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #0a0a0a #0a0a0a !important; +} +.button.is-white.is-outlined[disabled], +fieldset[disabled] .button.is-white.is-outlined { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; +} +.button.is-white.is-inverted.is-outlined { + background-color: transparent; + border-color: #0a0a0a; + color: #0a0a0a; +} +.button.is-white.is-inverted.is-outlined:hover, +.button.is-white.is-inverted.is-outlined.is-hovered, +.button.is-white.is-inverted.is-outlined:focus, +.button.is-white.is-inverted.is-outlined.is-focused { + background-color: #0a0a0a; + color: #fff; +} +.button.is-white.is-inverted.is-outlined.is-loading:hover::after, +.button.is-white.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-white.is-inverted.is-outlined.is-loading:focus::after, +.button.is-white.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-white.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-white.is-inverted.is-outlined { + background-color: transparent; + border-color: #0a0a0a; + box-shadow: none; + color: #0a0a0a; +} +.button.is-black { + background-color: #0a0a0a; + border-color: transparent; + color: #fff; +} +.button.is-black:hover, +.button.is-black.is-hovered { + background-color: #040404; + border-color: transparent; + color: #fff; +} +.button.is-black:focus, +.button.is-black.is-focused { + border-color: transparent; + color: #fff; +} +.button.is-black:focus:not(:active), +.button.is-black.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(10,10,10,0.25); +} +.button.is-black:active, +.button.is-black.is-active { + background-color: #000; + border-color: transparent; + color: #fff; +} +.button.is-black[disabled], +fieldset[disabled] .button.is-black { + background-color: #0a0a0a; + border-color: transparent; + box-shadow: none; +} +.button.is-black.is-inverted { + background-color: #fff; + color: #0a0a0a; +} +.button.is-black.is-inverted:hover, +.button.is-black.is-inverted.is-hovered { + background-color: #f2f2f2; +} +.button.is-black.is-inverted[disabled], +fieldset[disabled] .button.is-black.is-inverted { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #0a0a0a; +} +.button.is-black.is-loading::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-black.is-outlined { + background-color: transparent; + border-color: #0a0a0a; + color: #0a0a0a; +} +.button.is-black.is-outlined:hover, +.button.is-black.is-outlined.is-hovered, +.button.is-black.is-outlined:focus, +.button.is-black.is-outlined.is-focused { + background-color: #0a0a0a; + border-color: #0a0a0a; + color: #fff; +} +.button.is-black.is-outlined.is-loading::after { + border-color: transparent transparent #0a0a0a #0a0a0a !important; +} +.button.is-black.is-outlined.is-loading:hover::after, +.button.is-black.is-outlined.is-loading.is-hovered::after, +.button.is-black.is-outlined.is-loading:focus::after, +.button.is-black.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-black.is-outlined[disabled], +fieldset[disabled] .button.is-black.is-outlined { + background-color: transparent; + border-color: #0a0a0a; + box-shadow: none; + color: #0a0a0a; +} +.button.is-black.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; +} +.button.is-black.is-inverted.is-outlined:hover, +.button.is-black.is-inverted.is-outlined.is-hovered, +.button.is-black.is-inverted.is-outlined:focus, +.button.is-black.is-inverted.is-outlined.is-focused { + background-color: #fff; + color: #0a0a0a; +} +.button.is-black.is-inverted.is-outlined.is-loading:hover::after, +.button.is-black.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-black.is-inverted.is-outlined.is-loading:focus::after, +.button.is-black.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #0a0a0a #0a0a0a !important; +} +.button.is-black.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-black.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; +} +.button.is-light, +article.article .article-more { + background-color: #f5f5f5; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-light:hover, +article.article .article-more:hover, +.button.is-light.is-hovered, +article.article .article-more.is-hovered { + background-color: #eee; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-light:focus, +article.article .article-more:focus, +.button.is-light.is-focused, +article.article .article-more.is-focused { + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-light:focus:not(:active), +article.article .article-more:focus:not(:active), +.button.is-light.is-focused:not(:active), +article.article .article-more.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(245,245,245,0.25); +} +.button.is-light:active, +article.article .article-more:active, +.button.is-light.is-active, +article.article .article-more.is-active { + background-color: #e8e8e8; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-light[disabled], +article.article .article-more[disabled], +fieldset[disabled] .button.is-light, +fieldset[disabled] article.article .article-more { + background-color: #f5f5f5; + border-color: transparent; + box-shadow: none; +} +.button.is-light.is-inverted, +article.article .article-more.is-inverted { + background-color: rgba(0,0,0,0.7); + color: #f5f5f5; +} +.button.is-light.is-inverted:hover, +article.article .article-more.is-inverted:hover, +.button.is-light.is-inverted.is-hovered, +article.article .article-more.is-inverted.is-hovered { + background-color: rgba(0,0,0,0.7); +} +.button.is-light.is-inverted[disabled], +article.article .article-more.is-inverted[disabled], +fieldset[disabled] .button.is-light.is-inverted, +fieldset[disabled] article.article .article-more.is-inverted { + background-color: rgba(0,0,0,0.7); + border-color: transparent; + box-shadow: none; + color: #f5f5f5; +} +.button.is-light.is-loading::after, +article.article .article-more.is-loading::after { + border-color: transparent transparent rgba(0,0,0,0.7) rgba(0,0,0,0.7) !important; +} +.button.is-light.is-outlined, +article.article .article-more.is-outlined { + background-color: transparent; + border-color: #f5f5f5; + color: #f5f5f5; +} +.button.is-light.is-outlined:hover, +article.article .article-more.is-outlined:hover, +.button.is-light.is-outlined.is-hovered, +article.article .article-more.is-outlined.is-hovered, +.button.is-light.is-outlined:focus, +article.article .article-more.is-outlined:focus, +.button.is-light.is-outlined.is-focused, +article.article .article-more.is-outlined.is-focused { + background-color: #f5f5f5; + border-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.button.is-light.is-outlined.is-loading::after, +article.article .article-more.is-outlined.is-loading::after { + border-color: transparent transparent #f5f5f5 #f5f5f5 !important; +} +.button.is-light.is-outlined.is-loading:hover::after, +article.article .article-more.is-outlined.is-loading:hover::after, +.button.is-light.is-outlined.is-loading.is-hovered::after, +article.article .article-more.is-outlined.is-loading.is-hovered::after, +.button.is-light.is-outlined.is-loading:focus::after, +article.article .article-more.is-outlined.is-loading:focus::after, +.button.is-light.is-outlined.is-loading.is-focused::after, +article.article .article-more.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent rgba(0,0,0,0.7) rgba(0,0,0,0.7) !important; +} +.button.is-light.is-outlined[disabled], +article.article .article-more.is-outlined[disabled], +fieldset[disabled] .button.is-light.is-outlined, +fieldset[disabled] article.article .article-more.is-outlined { + background-color: transparent; + border-color: #f5f5f5; + box-shadow: none; + color: #f5f5f5; +} +.button.is-light.is-inverted.is-outlined, +article.article .article-more.is-inverted.is-outlined { + background-color: transparent; + border-color: rgba(0,0,0,0.7); + color: rgba(0,0,0,0.7); +} +.button.is-light.is-inverted.is-outlined:hover, +article.article .article-more.is-inverted.is-outlined:hover, +.button.is-light.is-inverted.is-outlined.is-hovered, +article.article .article-more.is-inverted.is-outlined.is-hovered, +.button.is-light.is-inverted.is-outlined:focus, +article.article .article-more.is-inverted.is-outlined:focus, +.button.is-light.is-inverted.is-outlined.is-focused, +article.article .article-more.is-inverted.is-outlined.is-focused { + background-color: rgba(0,0,0,0.7); + color: #f5f5f5; +} +.button.is-light.is-inverted.is-outlined.is-loading:hover::after, +article.article .article-more.is-inverted.is-outlined.is-loading:hover::after, +.button.is-light.is-inverted.is-outlined.is-loading.is-hovered::after, +article.article .article-more.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-light.is-inverted.is-outlined.is-loading:focus::after, +article.article .article-more.is-inverted.is-outlined.is-loading:focus::after, +.button.is-light.is-inverted.is-outlined.is-loading.is-focused::after, +article.article .article-more.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #f5f5f5 #f5f5f5 !important; +} +.button.is-light.is-inverted.is-outlined[disabled], +article.article .article-more.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-light.is-inverted.is-outlined, +fieldset[disabled] article.article .article-more.is-inverted.is-outlined { + background-color: transparent; + border-color: rgba(0,0,0,0.7); + box-shadow: none; + color: rgba(0,0,0,0.7); +} +.button.is-dark { + background-color: #363636; + border-color: transparent; + color: #fff; +} +.button.is-dark:hover, +.button.is-dark.is-hovered { + background-color: #2f2f2f; + border-color: transparent; + color: #fff; +} +.button.is-dark:focus, +.button.is-dark.is-focused { + border-color: transparent; + color: #fff; +} +.button.is-dark:focus:not(:active), +.button.is-dark.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(54,54,54,0.25); +} +.button.is-dark:active, +.button.is-dark.is-active { + background-color: #292929; + border-color: transparent; + color: #fff; +} +.button.is-dark[disabled], +fieldset[disabled] .button.is-dark { + background-color: #363636; + border-color: transparent; + box-shadow: none; +} +.button.is-dark.is-inverted { + background-color: #fff; + color: #363636; +} +.button.is-dark.is-inverted:hover, +.button.is-dark.is-inverted.is-hovered { + background-color: #f2f2f2; +} +.button.is-dark.is-inverted[disabled], +fieldset[disabled] .button.is-dark.is-inverted { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #363636; +} +.button.is-dark.is-loading::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-dark.is-outlined { + background-color: transparent; + border-color: #363636; + color: #363636; +} +.button.is-dark.is-outlined:hover, +.button.is-dark.is-outlined.is-hovered, +.button.is-dark.is-outlined:focus, +.button.is-dark.is-outlined.is-focused { + background-color: #363636; + border-color: #363636; + color: #fff; +} +.button.is-dark.is-outlined.is-loading::after { + border-color: transparent transparent #363636 #363636 !important; +} +.button.is-dark.is-outlined.is-loading:hover::after, +.button.is-dark.is-outlined.is-loading.is-hovered::after, +.button.is-dark.is-outlined.is-loading:focus::after, +.button.is-dark.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-dark.is-outlined[disabled], +fieldset[disabled] .button.is-dark.is-outlined { + background-color: transparent; + border-color: #363636; + box-shadow: none; + color: #363636; +} +.button.is-dark.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; +} +.button.is-dark.is-inverted.is-outlined:hover, +.button.is-dark.is-inverted.is-outlined.is-hovered, +.button.is-dark.is-inverted.is-outlined:focus, +.button.is-dark.is-inverted.is-outlined.is-focused { + background-color: #fff; + color: #363636; +} +.button.is-dark.is-inverted.is-outlined.is-loading:hover::after, +.button.is-dark.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-dark.is-inverted.is-outlined.is-loading:focus::after, +.button.is-dark.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #363636 #363636 !important; +} +.button.is-dark.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-dark.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; +} +.button.is-primary { + background-color: #3273dc; + border-color: transparent; + color: #fff; +} +.button.is-primary:hover, +.button.is-primary.is-hovered { + background-color: #276cda; + border-color: transparent; + color: #fff; +} +.button.is-primary:focus, +.button.is-primary.is-focused { + border-color: transparent; + color: #fff; +} +.button.is-primary:focus:not(:active), +.button.is-primary.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(50,115,220,0.25); +} +.button.is-primary:active, +.button.is-primary.is-active { + background-color: #2366d1; + border-color: transparent; + color: #fff; +} +.button.is-primary[disabled], +fieldset[disabled] .button.is-primary { + background-color: #3273dc; + border-color: transparent; + box-shadow: none; +} +.button.is-primary.is-inverted { + background-color: #fff; + color: #3273dc; +} +.button.is-primary.is-inverted:hover, +.button.is-primary.is-inverted.is-hovered { + background-color: #f2f2f2; +} +.button.is-primary.is-inverted[disabled], +fieldset[disabled] .button.is-primary.is-inverted { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #3273dc; +} +.button.is-primary.is-loading::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-primary.is-outlined { + background-color: transparent; + border-color: #3273dc; + color: #3273dc; +} +.button.is-primary.is-outlined:hover, +.button.is-primary.is-outlined.is-hovered, +.button.is-primary.is-outlined:focus, +.button.is-primary.is-outlined.is-focused { + background-color: #3273dc; + border-color: #3273dc; + color: #fff; +} +.button.is-primary.is-outlined.is-loading::after { + border-color: transparent transparent #3273dc #3273dc !important; +} +.button.is-primary.is-outlined.is-loading:hover::after, +.button.is-primary.is-outlined.is-loading.is-hovered::after, +.button.is-primary.is-outlined.is-loading:focus::after, +.button.is-primary.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-primary.is-outlined[disabled], +fieldset[disabled] .button.is-primary.is-outlined { + background-color: transparent; + border-color: #3273dc; + box-shadow: none; + color: #3273dc; +} +.button.is-primary.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; +} +.button.is-primary.is-inverted.is-outlined:hover, +.button.is-primary.is-inverted.is-outlined.is-hovered, +.button.is-primary.is-inverted.is-outlined:focus, +.button.is-primary.is-inverted.is-outlined.is-focused { + background-color: #fff; + color: #3273dc; +} +.button.is-primary.is-inverted.is-outlined.is-loading:hover::after, +.button.is-primary.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-primary.is-inverted.is-outlined.is-loading:focus::after, +.button.is-primary.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #3273dc #3273dc !important; +} +.button.is-primary.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-primary.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; +} +.button.is-primary.is-light { + background-color: #eef3fc; + color: #2160c4; +} +.button.is-primary.is-light:hover, +.button.is-primary.is-light.is-hovered { + background-color: #e3ecfa; + border-color: transparent; + color: #2160c4; +} +.button.is-primary.is-light:active, +.button.is-primary.is-light.is-active { + background-color: #d8e4f8; + border-color: transparent; + color: #2160c4; +} +.button.is-link { + background-color: #3273dc; + border-color: transparent; + color: #fff; +} +.button.is-link:hover, +.button.is-link.is-hovered { + background-color: #276cda; + border-color: transparent; + color: #fff; +} +.button.is-link:focus, +.button.is-link.is-focused { + border-color: transparent; + color: #fff; +} +.button.is-link:focus:not(:active), +.button.is-link.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(50,115,220,0.25); +} +.button.is-link:active, +.button.is-link.is-active { + background-color: #2366d1; + border-color: transparent; + color: #fff; +} +.button.is-link[disabled], +fieldset[disabled] .button.is-link { + background-color: #3273dc; + border-color: transparent; + box-shadow: none; +} +.button.is-link.is-inverted { + background-color: #fff; + color: #3273dc; +} +.button.is-link.is-inverted:hover, +.button.is-link.is-inverted.is-hovered { + background-color: #f2f2f2; +} +.button.is-link.is-inverted[disabled], +fieldset[disabled] .button.is-link.is-inverted { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #3273dc; +} +.button.is-link.is-loading::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-link.is-outlined { + background-color: transparent; + border-color: #3273dc; + color: #3273dc; +} +.button.is-link.is-outlined:hover, +.button.is-link.is-outlined.is-hovered, +.button.is-link.is-outlined:focus, +.button.is-link.is-outlined.is-focused { + background-color: #3273dc; + border-color: #3273dc; + color: #fff; +} +.button.is-link.is-outlined.is-loading::after { + border-color: transparent transparent #3273dc #3273dc !important; +} +.button.is-link.is-outlined.is-loading:hover::after, +.button.is-link.is-outlined.is-loading.is-hovered::after, +.button.is-link.is-outlined.is-loading:focus::after, +.button.is-link.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-link.is-outlined[disabled], +fieldset[disabled] .button.is-link.is-outlined { + background-color: transparent; + border-color: #3273dc; + box-shadow: none; + color: #3273dc; +} +.button.is-link.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; +} +.button.is-link.is-inverted.is-outlined:hover, +.button.is-link.is-inverted.is-outlined.is-hovered, +.button.is-link.is-inverted.is-outlined:focus, +.button.is-link.is-inverted.is-outlined.is-focused { + background-color: #fff; + color: #3273dc; +} +.button.is-link.is-inverted.is-outlined.is-loading:hover::after, +.button.is-link.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-link.is-inverted.is-outlined.is-loading:focus::after, +.button.is-link.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #3273dc #3273dc !important; +} +.button.is-link.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-link.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; +} +.button.is-link.is-light { + background-color: #eef3fc; + color: #2160c4; +} +.button.is-link.is-light:hover, +.button.is-link.is-light.is-hovered { + background-color: #e3ecfa; + border-color: transparent; + color: #2160c4; +} +.button.is-link.is-light:active, +.button.is-link.is-light.is-active { + background-color: #d8e4f8; + border-color: transparent; + color: #2160c4; +} +.button.is-info { + background-color: #3298dc; + border-color: transparent; + color: #fff; +} +.button.is-info:hover, +.button.is-info.is-hovered { + background-color: #2793da; + border-color: transparent; + color: #fff; +} +.button.is-info:focus, +.button.is-info.is-focused { + border-color: transparent; + color: #fff; +} +.button.is-info:focus:not(:active), +.button.is-info.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(50,152,220,0.25); +} +.button.is-info:active, +.button.is-info.is-active { + background-color: #238cd1; + border-color: transparent; + color: #fff; +} +.button.is-info[disabled], +fieldset[disabled] .button.is-info { + background-color: #3298dc; + border-color: transparent; + box-shadow: none; +} +.button.is-info.is-inverted { + background-color: #fff; + color: #3298dc; +} +.button.is-info.is-inverted:hover, +.button.is-info.is-inverted.is-hovered { + background-color: #f2f2f2; +} +.button.is-info.is-inverted[disabled], +fieldset[disabled] .button.is-info.is-inverted { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #3298dc; +} +.button.is-info.is-loading::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-info.is-outlined { + background-color: transparent; + border-color: #3298dc; + color: #3298dc; +} +.button.is-info.is-outlined:hover, +.button.is-info.is-outlined.is-hovered, +.button.is-info.is-outlined:focus, +.button.is-info.is-outlined.is-focused { + background-color: #3298dc; + border-color: #3298dc; + color: #fff; +} +.button.is-info.is-outlined.is-loading::after { + border-color: transparent transparent #3298dc #3298dc !important; +} +.button.is-info.is-outlined.is-loading:hover::after, +.button.is-info.is-outlined.is-loading.is-hovered::after, +.button.is-info.is-outlined.is-loading:focus::after, +.button.is-info.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-info.is-outlined[disabled], +fieldset[disabled] .button.is-info.is-outlined { + background-color: transparent; + border-color: #3298dc; + box-shadow: none; + color: #3298dc; +} +.button.is-info.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; +} +.button.is-info.is-inverted.is-outlined:hover, +.button.is-info.is-inverted.is-outlined.is-hovered, +.button.is-info.is-inverted.is-outlined:focus, +.button.is-info.is-inverted.is-outlined.is-focused { + background-color: #fff; + color: #3298dc; +} +.button.is-info.is-inverted.is-outlined.is-loading:hover::after, +.button.is-info.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-info.is-inverted.is-outlined.is-loading:focus::after, +.button.is-info.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #3298dc #3298dc !important; +} +.button.is-info.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-info.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; +} +.button.is-info.is-light { + background-color: #eef6fc; + color: #1d72aa; +} +.button.is-info.is-light:hover, +.button.is-info.is-light.is-hovered { + background-color: #e3f1fa; + border-color: transparent; + color: #1d72aa; +} +.button.is-info.is-light:active, +.button.is-info.is-light.is-active { + background-color: #d8ebf8; + border-color: transparent; + color: #1d72aa; +} +.button.is-success { + background-color: #48c774; + border-color: transparent; + color: #fff; +} +.button.is-success:hover, +.button.is-success.is-hovered { + background-color: #3ec46d; + border-color: transparent; + color: #fff; +} +.button.is-success:focus, +.button.is-success.is-focused { + border-color: transparent; + color: #fff; +} +.button.is-success:focus:not(:active), +.button.is-success.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(72,199,116,0.25); +} +.button.is-success:active, +.button.is-success.is-active { + background-color: #3abb67; + border-color: transparent; + color: #fff; +} +.button.is-success[disabled], +fieldset[disabled] .button.is-success { + background-color: #48c774; + border-color: transparent; + box-shadow: none; +} +.button.is-success.is-inverted { + background-color: #fff; + color: #48c774; +} +.button.is-success.is-inverted:hover, +.button.is-success.is-inverted.is-hovered { + background-color: #f2f2f2; +} +.button.is-success.is-inverted[disabled], +fieldset[disabled] .button.is-success.is-inverted { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #48c774; +} +.button.is-success.is-loading::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-success.is-outlined { + background-color: transparent; + border-color: #48c774; + color: #48c774; +} +.button.is-success.is-outlined:hover, +.button.is-success.is-outlined.is-hovered, +.button.is-success.is-outlined:focus, +.button.is-success.is-outlined.is-focused { + background-color: #48c774; + border-color: #48c774; + color: #fff; +} +.button.is-success.is-outlined.is-loading::after { + border-color: transparent transparent #48c774 #48c774 !important; +} +.button.is-success.is-outlined.is-loading:hover::after, +.button.is-success.is-outlined.is-loading.is-hovered::after, +.button.is-success.is-outlined.is-loading:focus::after, +.button.is-success.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-success.is-outlined[disabled], +fieldset[disabled] .button.is-success.is-outlined { + background-color: transparent; + border-color: #48c774; + box-shadow: none; + color: #48c774; +} +.button.is-success.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; +} +.button.is-success.is-inverted.is-outlined:hover, +.button.is-success.is-inverted.is-outlined.is-hovered, +.button.is-success.is-inverted.is-outlined:focus, +.button.is-success.is-inverted.is-outlined.is-focused { + background-color: #fff; + color: #48c774; +} +.button.is-success.is-inverted.is-outlined.is-loading:hover::after, +.button.is-success.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-success.is-inverted.is-outlined.is-loading:focus::after, +.button.is-success.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #48c774 #48c774 !important; +} +.button.is-success.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-success.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; +} +.button.is-success.is-light { + background-color: #effaf3; + color: #257942; +} +.button.is-success.is-light:hover, +.button.is-success.is-light.is-hovered { + background-color: #e6f7ec; + border-color: transparent; + color: #257942; +} +.button.is-success.is-light:active, +.button.is-success.is-light.is-active { + background-color: #dcf4e4; + border-color: transparent; + color: #257942; +} +.button.is-warning { + background-color: #ffdd57; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-warning:hover, +.button.is-warning.is-hovered { + background-color: #ffdb4a; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-warning:focus, +.button.is-warning.is-focused { + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-warning:focus:not(:active), +.button.is-warning.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(255,221,87,0.25); +} +.button.is-warning:active, +.button.is-warning.is-active { + background-color: #ffd83d; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-warning[disabled], +fieldset[disabled] .button.is-warning { + background-color: #ffdd57; + border-color: transparent; + box-shadow: none; +} +.button.is-warning.is-inverted { + background-color: rgba(0,0,0,0.7); + color: #ffdd57; +} +.button.is-warning.is-inverted:hover, +.button.is-warning.is-inverted.is-hovered { + background-color: rgba(0,0,0,0.7); +} +.button.is-warning.is-inverted[disabled], +fieldset[disabled] .button.is-warning.is-inverted { + background-color: rgba(0,0,0,0.7); + border-color: transparent; + box-shadow: none; + color: #ffdd57; +} +.button.is-warning.is-loading::after { + border-color: transparent transparent rgba(0,0,0,0.7) rgba(0,0,0,0.7) !important; +} +.button.is-warning.is-outlined { + background-color: transparent; + border-color: #ffdd57; + color: #ffdd57; +} +.button.is-warning.is-outlined:hover, +.button.is-warning.is-outlined.is-hovered, +.button.is-warning.is-outlined:focus, +.button.is-warning.is-outlined.is-focused { + background-color: #ffdd57; + border-color: #ffdd57; + color: rgba(0,0,0,0.7); +} +.button.is-warning.is-outlined.is-loading::after { + border-color: transparent transparent #ffdd57 #ffdd57 !important; +} +.button.is-warning.is-outlined.is-loading:hover::after, +.button.is-warning.is-outlined.is-loading.is-hovered::after, +.button.is-warning.is-outlined.is-loading:focus::after, +.button.is-warning.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent rgba(0,0,0,0.7) rgba(0,0,0,0.7) !important; +} +.button.is-warning.is-outlined[disabled], +fieldset[disabled] .button.is-warning.is-outlined { + background-color: transparent; + border-color: #ffdd57; + box-shadow: none; + color: #ffdd57; +} +.button.is-warning.is-inverted.is-outlined { + background-color: transparent; + border-color: rgba(0,0,0,0.7); + color: rgba(0,0,0,0.7); +} +.button.is-warning.is-inverted.is-outlined:hover, +.button.is-warning.is-inverted.is-outlined.is-hovered, +.button.is-warning.is-inverted.is-outlined:focus, +.button.is-warning.is-inverted.is-outlined.is-focused { + background-color: rgba(0,0,0,0.7); + color: #ffdd57; +} +.button.is-warning.is-inverted.is-outlined.is-loading:hover::after, +.button.is-warning.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-warning.is-inverted.is-outlined.is-loading:focus::after, +.button.is-warning.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #ffdd57 #ffdd57 !important; +} +.button.is-warning.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-warning.is-inverted.is-outlined { + background-color: transparent; + border-color: rgba(0,0,0,0.7); + box-shadow: none; + color: rgba(0,0,0,0.7); +} +.button.is-warning.is-light { + background-color: #fffbeb; + color: #947600; +} +.button.is-warning.is-light:hover, +.button.is-warning.is-light.is-hovered { + background-color: #fff8de; + border-color: transparent; + color: #947600; +} +.button.is-warning.is-light:active, +.button.is-warning.is-light.is-active { + background-color: #fff6d1; + border-color: transparent; + color: #947600; +} +.button.is-danger { + background-color: #f14668; + border-color: transparent; + color: #fff; +} +.button.is-danger:hover, +.button.is-danger.is-hovered { + background-color: #f03a5f; + border-color: transparent; + color: #fff; +} +.button.is-danger:focus, +.button.is-danger.is-focused { + border-color: transparent; + color: #fff; +} +.button.is-danger:focus:not(:active), +.button.is-danger.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(241,70,104,0.25); +} +.button.is-danger:active, +.button.is-danger.is-active { + background-color: #ef2e55; + border-color: transparent; + color: #fff; +} +.button.is-danger[disabled], +fieldset[disabled] .button.is-danger { + background-color: #f14668; + border-color: transparent; + box-shadow: none; +} +.button.is-danger.is-inverted { + background-color: #fff; + color: #f14668; +} +.button.is-danger.is-inverted:hover, +.button.is-danger.is-inverted.is-hovered { + background-color: #f2f2f2; +} +.button.is-danger.is-inverted[disabled], +fieldset[disabled] .button.is-danger.is-inverted { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #f14668; +} +.button.is-danger.is-loading::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-danger.is-outlined { + background-color: transparent; + border-color: #f14668; + color: #f14668; +} +.button.is-danger.is-outlined:hover, +.button.is-danger.is-outlined.is-hovered, +.button.is-danger.is-outlined:focus, +.button.is-danger.is-outlined.is-focused { + background-color: #f14668; + border-color: #f14668; + color: #fff; +} +.button.is-danger.is-outlined.is-loading::after { + border-color: transparent transparent #f14668 #f14668 !important; +} +.button.is-danger.is-outlined.is-loading:hover::after, +.button.is-danger.is-outlined.is-loading.is-hovered::after, +.button.is-danger.is-outlined.is-loading:focus::after, +.button.is-danger.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-danger.is-outlined[disabled], +fieldset[disabled] .button.is-danger.is-outlined { + background-color: transparent; + border-color: #f14668; + box-shadow: none; + color: #f14668; +} +.button.is-danger.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; +} +.button.is-danger.is-inverted.is-outlined:hover, +.button.is-danger.is-inverted.is-outlined.is-hovered, +.button.is-danger.is-inverted.is-outlined:focus, +.button.is-danger.is-inverted.is-outlined.is-focused { + background-color: #fff; + color: #f14668; +} +.button.is-danger.is-inverted.is-outlined.is-loading:hover::after, +.button.is-danger.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-danger.is-inverted.is-outlined.is-loading:focus::after, +.button.is-danger.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #f14668 #f14668 !important; +} +.button.is-danger.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-danger.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; +} +.button.is-danger.is-light { + background-color: #feecf0; + color: #cc0f35; +} +.button.is-danger.is-light:hover, +.button.is-danger.is-light.is-hovered { + background-color: #fde0e6; + border-color: transparent; + color: #cc0f35; +} +.button.is-danger.is-light:active, +.button.is-danger.is-light.is-active { + background-color: #fcd4dc; + border-color: transparent; + color: #cc0f35; +} +.button.is-grey-lightest { + background-color: #ededed; + border-color: transparent; + color: #363636; +} +.button.is-grey-lightest:hover, +.button.is-grey-lightest.is-hovered { + background-color: #e7e7e7; + border-color: transparent; + color: #363636; +} +.button.is-grey-lightest:focus, +.button.is-grey-lightest.is-focused { + border-color: transparent; + color: #363636; +} +.button.is-grey-lightest:focus:not(:active), +.button.is-grey-lightest.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(237,237,237,0.25); +} +.button.is-grey-lightest:active, +.button.is-grey-lightest.is-active { + background-color: #e0e0e0; + border-color: transparent; + color: #363636; +} +.button.is-grey-lightest[disabled], +fieldset[disabled] .button.is-grey-lightest { + background-color: #ededed; + border-color: transparent; + box-shadow: none; +} +.button.is-grey-lightest.is-inverted { + background-color: #363636; + color: #ededed; +} +.button.is-grey-lightest.is-inverted:hover, +.button.is-grey-lightest.is-inverted.is-hovered { + background-color: #292929; +} +.button.is-grey-lightest.is-inverted[disabled], +fieldset[disabled] .button.is-grey-lightest.is-inverted { + background-color: #363636; + border-color: transparent; + box-shadow: none; + color: #ededed; +} +.button.is-grey-lightest.is-loading::after { + border-color: transparent transparent #363636 #363636 !important; +} +.button.is-grey-lightest.is-outlined { + background-color: transparent; + border-color: #ededed; + color: #ededed; +} +.button.is-grey-lightest.is-outlined:hover, +.button.is-grey-lightest.is-outlined.is-hovered, +.button.is-grey-lightest.is-outlined:focus, +.button.is-grey-lightest.is-outlined.is-focused { + background-color: #ededed; + border-color: #ededed; + color: #363636; +} +.button.is-grey-lightest.is-outlined.is-loading::after { + border-color: transparent transparent #ededed #ededed !important; +} +.button.is-grey-lightest.is-outlined.is-loading:hover::after, +.button.is-grey-lightest.is-outlined.is-loading.is-hovered::after, +.button.is-grey-lightest.is-outlined.is-loading:focus::after, +.button.is-grey-lightest.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #363636 #363636 !important; +} +.button.is-grey-lightest.is-outlined[disabled], +fieldset[disabled] .button.is-grey-lightest.is-outlined { + background-color: transparent; + border-color: #ededed; + box-shadow: none; + color: #ededed; +} +.button.is-grey-lightest.is-inverted.is-outlined { + background-color: transparent; + border-color: #363636; + color: #363636; +} +.button.is-grey-lightest.is-inverted.is-outlined:hover, +.button.is-grey-lightest.is-inverted.is-outlined.is-hovered, +.button.is-grey-lightest.is-inverted.is-outlined:focus, +.button.is-grey-lightest.is-inverted.is-outlined.is-focused { + background-color: #363636; + color: #ededed; +} +.button.is-grey-lightest.is-inverted.is-outlined.is-loading:hover::after, +.button.is-grey-lightest.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-grey-lightest.is-inverted.is-outlined.is-loading:focus::after, +.button.is-grey-lightest.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #ededed #ededed !important; +} +.button.is-grey-lightest.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-grey-lightest.is-inverted.is-outlined { + background-color: transparent; + border-color: #363636; + box-shadow: none; + color: #363636; +} +.button.is-small { + border-radius: 2px; + font-size: 0.75rem; +} +.button.is-normal { + font-size: 1rem; +} +.button.is-medium { + font-size: 1.25rem; +} +.button.is-large { + font-size: 1.5rem; +} +.button[disabled], +fieldset[disabled] .button { + background-color: #fff; + border-color: #dbdbdb; + box-shadow: none; + opacity: 0.5; +} +.button.is-fullwidth { + display: flex; + width: 100%; +} +.button.is-loading { + color: transparent !important; + pointer-events: none; +} +.button.is-loading::after { + position: absolute; + left: calc(50% - (1em / 2)); + top: calc(50% - (1em / 2)); + position: absolute !important; +} +.button.is-static { + background-color: #f5f5f5; + border-color: #dbdbdb; + color: #7a7a7a; + box-shadow: none; + pointer-events: none; +} +.button.is-rounded { + border-radius: 290486px; + padding-left: calc(1em + 0.25em); + padding-right: calc(1em + 0.25em); +} +.buttons { + align-items: center; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; +} +.buttons .button { + margin-bottom: 0.5rem; +} +.buttons .button:not(:last-child):not(.is-fullwidth) { + margin-right: 0.5rem; +} +.buttons:last-child { + margin-bottom: -0.5rem; +} +.buttons:not(:last-child) { + margin-bottom: 1rem; +} +.buttons.are-small .button:not(.is-normal):not(.is-medium):not(.is-large) { + border-radius: 2px; + font-size: 0.75rem; +} +.buttons.are-medium .button:not(.is-small):not(.is-normal):not(.is-large) { + font-size: 1.25rem; +} +.buttons.are-large .button:not(.is-small):not(.is-normal):not(.is-medium) { + font-size: 1.5rem; +} +.buttons.has-addons .button:not(:first-child) { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.buttons.has-addons .button:not(:last-child) { + border-bottom-right-radius: 0; + border-top-right-radius: 0; + margin-right: -1px; +} +.buttons.has-addons .button:last-child { + margin-right: 0; +} +.buttons.has-addons .button:hover, +.buttons.has-addons .button.is-hovered { + z-index: 2; +} +.buttons.has-addons .button:focus, +.buttons.has-addons .button.is-focused, +.buttons.has-addons .button:active, +.buttons.has-addons .button.is-active, +.buttons.has-addons .button.is-selected { + z-index: 3; +} +.buttons.has-addons .button:focus:hover, +.buttons.has-addons .button.is-focused:hover, +.buttons.has-addons .button:active:hover, +.buttons.has-addons .button.is-active:hover, +.buttons.has-addons .button.is-selected:hover { + z-index: 4; +} +.buttons.has-addons .button.is-expanded { + flex-grow: 1; + flex-shrink: 1; +} +.buttons.is-centered { + justify-content: center; +} +.buttons.is-centered:not(.has-addons) .button:not(.is-fullwidth) { + margin-left: 0.25rem; + margin-right: 0.25rem; +} +.buttons.is-right { + justify-content: flex-end; +} +.buttons.is-right:not(.has-addons) .button:not(.is-fullwidth) { + margin-left: 0.25rem; + margin-right: 0.25rem; +} +.container { + flex-grow: 1; + margin: 0 auto; + position: relative; + width: auto; +} +.container.is-fluid { + max-width: none; + padding-left: 64px; + padding-right: 64px; + width: 100%; +} +@media screen and (min-width: 1088px) { + .container { + max-width: 960px; + } +} +@media screen and (max-width: 1279px) { + .container.is-widescreen { + max-width: 1152px; + } +} +@media screen and (max-width: 1471px) { + .container.is-fullhd { + max-width: 1344px; + } +} +@media screen and (min-width: 1280px) { + .container { + max-width: 1152px; + } +} +@media screen and (min-width: 1472px) { + .container { + max-width: 1344px; + } +} +.content li + li { + margin-top: 0.25em; +} +.content p:not(:last-child), +.content dl:not(:last-child), +.content ol:not(:last-child), +.content ul:not(:last-child), +.content blockquote:not(:last-child), +.content pre:not(:last-child), +.content table:not(:last-child) { + margin-bottom: 1em; +} +.content h1, +.content h2, +.content h3, +.content h4, +.content h5, +.content h6 { + color: #363636; + font-weight: 400; + line-height: 1.125; +} +.content h1 { + font-size: 2em; + margin-bottom: 0.5em; +} +.content h1:not(:first-child) { + margin-top: 1em; +} +.content h2 { + font-size: 1.75em; + margin-bottom: 0.5714em; +} +.content h2:not(:first-child) { + margin-top: 1.1428em; +} +.content h3 { + font-size: 1.5em; + margin-bottom: 0.6666em; +} +.content h3:not(:first-child) { + margin-top: 1.3333em; +} +.content h4 { + font-size: 1.25em; + margin-bottom: 0.8em; +} +.content h5 { + font-size: 1.125em; + margin-bottom: 0.8888em; +} +.content h6 { + font-size: 1em; + margin-bottom: 1em; +} +.content blockquote { + background-color: #f5f5f5; + border-left: 5px solid #dbdbdb; + padding: 1.25em 1.5em; +} +.content ol { + list-style-position: outside; + margin-left: 2em; + margin-top: 1em; +} +.content ol:not([type]) { + list-style-type: decimal; +} +.content ol:not([type]).is-lower-alpha { + list-style-type: lower-alpha; +} +.content ol:not([type]).is-lower-roman { + list-style-type: lower-roman; +} +.content ol:not([type]).is-upper-alpha { + list-style-type: upper-alpha; +} +.content ol:not([type]).is-upper-roman { + list-style-type: upper-roman; +} +.content ul { + list-style: disc outside; + margin-left: 2em; + margin-top: 1em; +} +.content ul ul { + list-style-type: circle; + margin-top: 0.5em; +} +.content ul ul ul { + list-style-type: square; +} +.content dd { + margin-left: 2em; +} +.content figure { + margin-left: 2em; + margin-right: 2em; + text-align: center; +} +.content figure:not(:first-child) { + margin-top: 2em; +} +.content figure:not(:last-child) { + margin-bottom: 2em; +} +.content figure img { + display: inline-block; +} +.content figure figcaption { + font-style: italic; +} +.content pre { + -webkit-overflow-scrolling: touch; + overflow-x: auto; + padding: 1.25em 1.5em; + white-space: pre; + word-wrap: normal; +} +.content sup, +.content sub { + font-size: 75%; +} +.content table { + width: 100%; +} +.content table td, +.content table th { + border: 1px solid #dbdbdb; + border-width: 0 0 1px; + padding: 0.5em 0.75em; + vertical-align: top; +} +.content table th { + color: #363636; +} +.content table th:not([align]) { + text-align: left; +} +.content table thead td, +.content table thead th { + border-width: 0 0 2px; + color: #363636; +} +.content table tfoot td, +.content table tfoot th { + border-width: 2px 0 0; + color: #363636; +} +.content table tbody tr:last-child td, +.content table tbody tr:last-child th { + border-bottom-width: 0; +} +.content .tabs li + li { + margin-top: 0; +} +.content.is-small { + font-size: 0.75rem; +} +.content.is-medium { + font-size: 1.25rem; +} +.content.is-large { + font-size: 1.5rem; +} +.icon { + align-items: center; + display: inline-flex; + justify-content: center; + height: 1.5rem; + width: 1.5rem; +} +.icon.is-small { + height: 1rem; + width: 1rem; +} +.icon.is-medium { + height: 2rem; + width: 2rem; +} +.icon.is-large { + height: 3rem; + width: 3rem; +} +.image { + display: block; + position: relative; +} +.image img { + display: block; + height: auto; + width: 100%; +} +.image img.is-rounded { + border-radius: 290486px; +} +.image.is-fullwidth { + width: 100%; +} +.image.is-square img, +.image.is-1by1 img, +.image.is-5by4 img, +.image.is-4by3 img, +.image.is-3by2 img, +.image.is-5by3 img, +.image.is-16by9 img, +.image.is-2by1 img, +.image.is-3by1 img, +.image.is-4by5 img, +.image.is-3by4 img, +.image.is-2by3 img, +.image.is-3by5 img, +.image.is-9by16 img, +.image.is-1by2 img, +.image.is-1by3 img, +.image.is-square .has-ratio, +.image.is-1by1 .has-ratio, +.image.is-5by4 .has-ratio, +.image.is-4by3 .has-ratio, +.image.is-3by2 .has-ratio, +.image.is-5by3 .has-ratio, +.image.is-16by9 .has-ratio, +.image.is-2by1 .has-ratio, +.image.is-3by1 .has-ratio, +.image.is-4by5 .has-ratio, +.image.is-3by4 .has-ratio, +.image.is-2by3 .has-ratio, +.image.is-3by5 .has-ratio, +.image.is-9by16 .has-ratio, +.image.is-1by2 .has-ratio, +.image.is-1by3 .has-ratio { + height: 100%; + width: 100%; +} +.image.is-square, +.image.is-1by1 { + padding-top: 100%; +} +.image.is-5by4 { + padding-top: 80%; +} +.image.is-4by3 { + padding-top: 75%; +} +.image.is-3by2 { + padding-top: 66.6666%; +} +.image.is-5by3 { + padding-top: 60%; +} +.image.is-16by9 { + padding-top: 56.25%; +} +.image.is-2by1 { + padding-top: 50%; +} +.image.is-3by1 { + padding-top: 33.3333%; +} +.image.is-4by5 { + padding-top: 125%; +} +.image.is-3by4 { + padding-top: 133.3333%; +} +.image.is-2by3 { + padding-top: 150%; +} +.image.is-3by5 { + padding-top: 166.6666%; +} +.image.is-9by16 { + padding-top: 177.7777%; +} +.image.is-1by2 { + padding-top: 200%; +} +.image.is-1by3 { + padding-top: 300%; +} +.image.is-16x16 { + height: 16px; + width: 16px; +} +.image.is-24x24 { + height: 24px; + width: 24px; +} +.image.is-32x32 { + height: 32px; + width: 32px; +} +.image.is-48x48 { + height: 48px; + width: 48px; +} +.image.is-64x64 { + height: 64px; + width: 64px; +} +.image.is-96x96 { + height: 96px; + width: 96px; +} +.image.is-128x128 { + height: 128px; + width: 128px; +} +.notification { + background-color: #f5f5f5; + border-radius: 4px; + padding: 1.25rem 2.5rem 1.25rem 1.5rem; + position: relative; +} +.notification a:not(.button):not(.dropdown-item) { + color: currentColor; + text-decoration: underline; +} +.notification strong { + color: currentColor; +} +.notification code, +.notification pre { + background: #fff; +} +.notification pre code { + background: transparent; +} +.notification > .delete { + position: absolute; + right: 0.5rem; + top: 0.5rem; +} +.notification .title, +.notification .subtitle, +.notification .content { + color: currentColor; +} +.notification.is-white { + background-color: #fff; + color: #0a0a0a; +} +.notification.is-black { + background-color: #0a0a0a; + color: #fff; +} +.notification.is-light { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.notification.is-dark { + background-color: #363636; + color: #fff; +} +.notification.is-primary { + background-color: #3273dc; + color: #fff; +} +.notification.is-link { + background-color: #3273dc; + color: #fff; +} +.notification.is-info { + background-color: #3298dc; + color: #fff; +} +.notification.is-success { + background-color: #48c774; + color: #fff; +} +.notification.is-warning { + background-color: #ffdd57; + color: rgba(0,0,0,0.7); +} +.notification.is-danger { + background-color: #f14668; + color: #fff; +} +.notification.is-grey-lightest { + background-color: #ededed; + color: #363636; +} +.progress { + -moz-appearance: none; + -webkit-appearance: none; + border: none; + border-radius: 290486px; + display: block; + height: 1rem; + overflow: hidden; + padding: 0; + width: 100%; +} +.progress::-webkit-progress-bar { + background-color: #ededed; +} +.progress::-webkit-progress-value { + background-color: #4a4a4a; +} +.progress::-moz-progress-bar { + background-color: #4a4a4a; +} +.progress::-ms-fill { + background-color: #4a4a4a; + border: none; +} +.progress.is-white::-webkit-progress-value { + background-color: #fff; +} +.progress.is-white::-moz-progress-bar { + background-color: #fff; +} +.progress.is-white::-ms-fill { + background-color: #fff; +} +.progress.is-white:indeterminate { + background-image: linear-gradient(to right, #fff 30%, #ededed 30%); +} +.progress.is-black::-webkit-progress-value { + background-color: #0a0a0a; +} +.progress.is-black::-moz-progress-bar { + background-color: #0a0a0a; +} +.progress.is-black::-ms-fill { + background-color: #0a0a0a; +} +.progress.is-black:indeterminate { + background-image: linear-gradient(to right, #0a0a0a 30%, #ededed 30%); +} +.progress.is-light::-webkit-progress-value { + background-color: #f5f5f5; +} +.progress.is-light::-moz-progress-bar { + background-color: #f5f5f5; +} +.progress.is-light::-ms-fill { + background-color: #f5f5f5; +} +.progress.is-light:indeterminate { + background-image: linear-gradient(to right, #f5f5f5 30%, #ededed 30%); +} +.progress.is-dark::-webkit-progress-value { + background-color: #363636; +} +.progress.is-dark::-moz-progress-bar { + background-color: #363636; +} +.progress.is-dark::-ms-fill { + background-color: #363636; +} +.progress.is-dark:indeterminate { + background-image: linear-gradient(to right, #363636 30%, #ededed 30%); +} +.progress.is-primary::-webkit-progress-value { + background-color: #3273dc; +} +.progress.is-primary::-moz-progress-bar { + background-color: #3273dc; +} +.progress.is-primary::-ms-fill { + background-color: #3273dc; +} +.progress.is-primary:indeterminate { + background-image: linear-gradient(to right, #3273dc 30%, #ededed 30%); +} +.progress.is-link::-webkit-progress-value { + background-color: #3273dc; +} +.progress.is-link::-moz-progress-bar { + background-color: #3273dc; +} +.progress.is-link::-ms-fill { + background-color: #3273dc; +} +.progress.is-link:indeterminate { + background-image: linear-gradient(to right, #3273dc 30%, #ededed 30%); +} +.progress.is-info::-webkit-progress-value { + background-color: #3298dc; +} +.progress.is-info::-moz-progress-bar { + background-color: #3298dc; +} +.progress.is-info::-ms-fill { + background-color: #3298dc; +} +.progress.is-info:indeterminate { + background-image: linear-gradient(to right, #3298dc 30%, #ededed 30%); +} +.progress.is-success::-webkit-progress-value { + background-color: #48c774; +} +.progress.is-success::-moz-progress-bar { + background-color: #48c774; +} +.progress.is-success::-ms-fill { + background-color: #48c774; +} +.progress.is-success:indeterminate { + background-image: linear-gradient(to right, #48c774 30%, #ededed 30%); +} +.progress.is-warning::-webkit-progress-value { + background-color: #ffdd57; +} +.progress.is-warning::-moz-progress-bar { + background-color: #ffdd57; +} +.progress.is-warning::-ms-fill { + background-color: #ffdd57; +} +.progress.is-warning:indeterminate { + background-image: linear-gradient(to right, #ffdd57 30%, #ededed 30%); +} +.progress.is-danger::-webkit-progress-value { + background-color: #f14668; +} +.progress.is-danger::-moz-progress-bar { + background-color: #f14668; +} +.progress.is-danger::-ms-fill { + background-color: #f14668; +} +.progress.is-danger:indeterminate { + background-image: linear-gradient(to right, #f14668 30%, #ededed 30%); +} +.progress.is-grey-lightest::-webkit-progress-value { + background-color: #ededed; +} +.progress.is-grey-lightest::-moz-progress-bar { + background-color: #ededed; +} +.progress.is-grey-lightest::-ms-fill { + background-color: #ededed; +} +.progress.is-grey-lightest:indeterminate { + background-image: linear-gradient(to right, #ededed 30%, #ededed 30%); +} +.progress:indeterminate { + animation-duration: 1.5s; + animation-iteration-count: infinite; + animation-name: moveIndeterminate; + animation-timing-function: linear; + background-color: #ededed; + background-image: linear-gradient(to right, #4a4a4a 30%, #ededed 30%); + background-position: top left; + background-repeat: no-repeat; + background-size: 150% 150%; +} +.progress:indeterminate::-webkit-progress-bar { + background-color: transparent; +} +.progress:indeterminate::-moz-progress-bar { + background-color: transparent; +} +.progress.is-small { + height: 0.75rem; +} +.progress.is-medium { + height: 1.25rem; +} +.progress.is-large { + height: 1.5rem; +} +@-moz-keyframes moveIndeterminate { + from { + background-position: 200% 0; + } + to { + background-position: -200% 0; + } +} +@-webkit-keyframes moveIndeterminate { + from { + background-position: 200% 0; + } + to { + background-position: -200% 0; + } +} +@-o-keyframes moveIndeterminate { + from { + background-position: 200% 0; + } + to { + background-position: -200% 0; + } +} +@keyframes moveIndeterminate { + from { + background-position: 200% 0; + } + to { + background-position: -200% 0; + } +} +.table { + background-color: #fff; + color: #363636; +} +.table td, +.table th { + border: 1px solid #dbdbdb; + border-width: 0 0 1px; + padding: 0.5em 0.75em; + vertical-align: top; +} +.table td.is-white, +.table th.is-white { + background-color: #fff; + border-color: #fff; + color: #0a0a0a; +} +.table td.is-black, +.table th.is-black { + background-color: #0a0a0a; + border-color: #0a0a0a; + color: #fff; +} +.table td.is-light, +.table th.is-light { + background-color: #f5f5f5; + border-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.table td.is-dark, +.table th.is-dark { + background-color: #363636; + border-color: #363636; + color: #fff; +} +.table td.is-primary, +.table th.is-primary { + background-color: #3273dc; + border-color: #3273dc; + color: #fff; +} +.table td.is-link, +.table th.is-link { + background-color: #3273dc; + border-color: #3273dc; + color: #fff; +} +.table td.is-info, +.table th.is-info { + background-color: #3298dc; + border-color: #3298dc; + color: #fff; +} +.table td.is-success, +.table th.is-success { + background-color: #48c774; + border-color: #48c774; + color: #fff; +} +.table td.is-warning, +.table th.is-warning { + background-color: #ffdd57; + border-color: #ffdd57; + color: rgba(0,0,0,0.7); +} +.table td.is-danger, +.table th.is-danger { + background-color: #f14668; + border-color: #f14668; + color: #fff; +} +.table td.is-grey-lightest, +.table th.is-grey-lightest { + background-color: #ededed; + border-color: #ededed; + color: #363636; +} +.table td.is-narrow, +.table th.is-narrow { + white-space: nowrap; + width: 1%; +} +.table td.is-selected, +.table th.is-selected { + background-color: #3273dc; + color: #fff; +} +.table td.is-selected a, +.table th.is-selected a, +.table td.is-selected strong, +.table th.is-selected strong { + color: currentColor; +} +.table th { + color: #363636; +} +.table th:not([align]) { + text-align: left; +} +.table tr.is-selected { + background-color: #3273dc; + color: #fff; +} +.table tr.is-selected a, +.table tr.is-selected strong { + color: currentColor; +} +.table tr.is-selected td, +.table tr.is-selected th { + border-color: #fff; + color: currentColor; +} +.table thead { + background-color: transparent; +} +.table thead td, +.table thead th { + border-width: 0 0 2px; + color: #363636; +} +.table tfoot { + background-color: transparent; +} +.table tfoot td, +.table tfoot th { + border-width: 2px 0 0; + color: #363636; +} +.table tbody { + background-color: transparent; +} +.table tbody tr:last-child td, +.table tbody tr:last-child th { + border-bottom-width: 0; +} +.table.is-bordered td, +.table.is-bordered th { + border-width: 1px; +} +.table.is-bordered tr:last-child td, +.table.is-bordered tr:last-child th { + border-bottom-width: 1px; +} +.table.is-fullwidth { + width: 100%; +} +.table.is-hoverable tbody tr:not(.is-selected):hover { + background-color: #fafafa; +} +.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover { + background-color: #fafafa; +} +.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover:nth-child(even) { + background-color: #f5f5f5; +} +.table.is-narrow td, +.table.is-narrow th { + padding: 0.25em 0.5em; +} +.table.is-striped tbody tr:not(.is-selected):nth-child(even) { + background-color: #fafafa; +} +.table-container { + -webkit-overflow-scrolling: touch; + overflow: auto; + overflow-y: hidden; + max-width: 100%; +} +.tags { + align-items: center; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; +} +.tags .tag { + margin-bottom: 0.5rem; +} +.tags .tag:not(:last-child) { + margin-right: 0.5rem; +} +.tags:last-child { + margin-bottom: -0.5rem; +} +.tags:not(:last-child) { + margin-bottom: 1rem; +} +.tags.are-medium .tag:not(.is-normal):not(.is-large) { + font-size: 1rem; +} +.tags.are-large .tag:not(.is-normal):not(.is-medium) { + font-size: 1.25rem; +} +.tags.is-centered { + justify-content: center; +} +.tags.is-centered .tag { + margin-right: 0.25rem; + margin-left: 0.25rem; +} +.tags.is-right { + justify-content: flex-end; +} +.tags.is-right .tag:not(:first-child) { + margin-left: 0.5rem; +} +.tags.is-right .tag:not(:last-child) { + margin-right: 0; +} +.tags.has-addons .tag { + margin-right: 0; +} +.tags.has-addons .tag:not(:first-child) { + margin-left: 0; + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.tags.has-addons .tag:not(:last-child) { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.tag:not(body) { + align-items: center; + background-color: #f5f5f5; + border-radius: 4px; + color: #4a4a4a; + display: inline-flex; + font-size: 0.75rem; + height: 2em; + justify-content: center; + line-height: 1.5; + padding-left: 0.75em; + padding-right: 0.75em; + white-space: nowrap; +} +.tag:not(body) .delete { + margin-left: 0.25rem; + margin-right: -0.375rem; +} +.tag:not(body).is-white { + background-color: #fff; + color: #0a0a0a; +} +.tag:not(body).is-black { + background-color: #0a0a0a; + color: #fff; +} +.tag:not(body).is-light { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.tag:not(body).is-dark { + background-color: #363636; + color: #fff; +} +.tag:not(body).is-primary { + background-color: #3273dc; + color: #fff; +} +.tag:not(body).is-primary.is-light { + background-color: #eef3fc; + color: #2160c4; +} +.tag:not(body).is-link { + background-color: #3273dc; + color: #fff; +} +.tag:not(body).is-link.is-light { + background-color: #eef3fc; + color: #2160c4; +} +.tag:not(body).is-info { + background-color: #3298dc; + color: #fff; +} +.tag:not(body).is-info.is-light { + background-color: #eef6fc; + color: #1d72aa; +} +.tag:not(body).is-success { + background-color: #48c774; + color: #fff; +} +.tag:not(body).is-success.is-light { + background-color: #effaf3; + color: #257942; +} +.tag:not(body).is-warning { + background-color: #ffdd57; + color: rgba(0,0,0,0.7); +} +.tag:not(body).is-warning.is-light { + background-color: #fffbeb; + color: #947600; +} +.tag:not(body).is-danger { + background-color: #f14668; + color: #fff; +} +.tag:not(body).is-danger.is-light { + background-color: #feecf0; + color: #cc0f35; +} +.tag:not(body).is-grey-lightest { + background-color: #ededed; + color: #363636; +} +.tag:not(body).is-normal { + font-size: 0.75rem; +} +.tag:not(body).is-medium { + font-size: 1rem; +} +.tag:not(body).is-large { + font-size: 1.25rem; +} +.tag:not(body) .icon:first-child:not(:last-child) { + margin-left: -0.375em; + margin-right: 0.1875em; +} +.tag:not(body) .icon:last-child:not(:first-child) { + margin-left: 0.1875em; + margin-right: -0.375em; +} +.tag:not(body) .icon:first-child:last-child { + margin-left: -0.375em; + margin-right: -0.375em; +} +.tag:not(body).is-delete { + margin-left: 1px; + padding: 0; + position: relative; + width: 2em; +} +.tag:not(body).is-delete::before, +.tag:not(body).is-delete::after { + background-color: currentColor; + content: ""; + display: block; + left: 50%; + position: absolute; + top: 50%; + transform: translateX(-50%) translateY(-50%) rotate(45deg); + transform-origin: center center; +} +.tag:not(body).is-delete::before { + height: 1px; + width: 50%; +} +.tag:not(body).is-delete::after { + height: 50%; + width: 1px; +} +.tag:not(body).is-delete:hover, +.tag:not(body).is-delete:focus { + background-color: #e8e8e8; +} +.tag:not(body).is-delete:active { + background-color: #dbdbdb; +} +.tag:not(body).is-rounded { + border-radius: 290486px; +} +a.tag:hover { + text-decoration: underline; +} +.title, +.subtitle { + word-break: break-word; +} +.title em, +.subtitle em, +.title span, +.subtitle span { + font-weight: inherit; +} +.title sub, +.subtitle sub { + font-size: 0.75em; +} +.title sup, +.subtitle sup { + font-size: 0.75em; +} +.title .tag, +.subtitle .tag { + vertical-align: middle; +} +.title { + color: #363636; + font-size: 2rem; + font-weight: 400; + line-height: 1.125; +} +.title strong { + color: inherit; + font-weight: inherit; +} +.title + .highlight { + margin-top: -0.75rem; +} +.title:not(.is-spaced) + .subtitle { + margin-top: -1.25rem; +} +.title.is-1 { + font-size: 3rem; +} +.title.is-2 { + font-size: 2.5rem; +} +.title.is-3 { + font-size: 2rem; +} +.title.is-4 { + font-size: 1.5rem; +} +.title.is-5 { + font-size: 1.25rem; +} +.title.is-6 { + font-size: 1rem; +} +.title.is-7 { + font-size: 0.85rem; +} +.subtitle { + color: #4a4a4a; + font-size: 1.25rem; + font-weight: 400; + line-height: 1.25; +} +.subtitle strong { + color: #363636; + font-weight: 600; +} +.subtitle:not(.is-spaced) + .title { + margin-top: -1.25rem; +} +.subtitle.is-1 { + font-size: 3rem; +} +.subtitle.is-2 { + font-size: 2.5rem; +} +.subtitle.is-3 { + font-size: 2rem; +} +.subtitle.is-4 { + font-size: 1.5rem; +} +.subtitle.is-5 { + font-size: 1.25rem; +} +.subtitle.is-6 { + font-size: 1rem; +} +.subtitle.is-7 { + font-size: 0.85rem; +} +.heading { + display: block; + font-size: 11px; + letter-spacing: 1px; + margin-bottom: 5px; + text-transform: uppercase; +} +.highlight { + font-weight: 400; + max-width: 100%; + overflow: hidden; + padding: 0; +} +.highlight pre { + overflow: auto; + max-width: 100%; +} +.number { + align-items: center; + background-color: #f5f5f5; + border-radius: 290486px; + display: inline-flex; + font-size: 1.25rem; + height: 2em; + justify-content: center; + margin-right: 1.5rem; + min-width: 2.5em; + padding: 0.25rem 0.5rem; + text-align: center; + vertical-align: top; +} +.input, +.textarea, +.select select { + background-color: #fff; + border-color: #dbdbdb; + border-radius: 4px; + color: #363636; +} +.input::-moz-placeholder, +.textarea::-moz-placeholder, +.select select::-moz-placeholder { + color: rgba(54,54,54,0.3); +} +.input::-webkit-input-placeholder, +.textarea::-webkit-input-placeholder, +.select select::-webkit-input-placeholder { + color: rgba(54,54,54,0.3); +} +.input:-moz-placeholder, +.textarea:-moz-placeholder, +.select select:-moz-placeholder { + color: rgba(54,54,54,0.3); +} +.input:-ms-input-placeholder, +.textarea:-ms-input-placeholder, +.select select:-ms-input-placeholder { + color: rgba(54,54,54,0.3); +} +.input:hover, +.textarea:hover, +.select select:hover, +.input.is-hovered, +.textarea.is-hovered, +.select select.is-hovered { + border-color: #b5b5b5; +} +.input:focus, +.textarea:focus, +.select select:focus, +.input.is-focused, +.textarea.is-focused, +.select select.is-focused, +.input:active, +.textarea:active, +.select select:active, +.input.is-active, +.textarea.is-active, +.select select.is-active { + border-color: #3273dc; + box-shadow: 0 0 0 0.125em rgba(50,115,220,0.25); +} +.input[disabled], +.textarea[disabled], +.select select[disabled], +fieldset[disabled] .input, +fieldset[disabled] .textarea, +fieldset[disabled] .select select { + background-color: #f5f5f5; + border-color: #f5f5f5; + box-shadow: none; + color: #7a7a7a; +} +.input[disabled]::-moz-placeholder, +.textarea[disabled]::-moz-placeholder, +.select select[disabled]::-moz-placeholder, +fieldset[disabled] .input::-moz-placeholder, +fieldset[disabled] .textarea::-moz-placeholder, +fieldset[disabled] .select select::-moz-placeholder { + color: rgba(122,122,122,0.3); +} +.input[disabled]::-webkit-input-placeholder, +.textarea[disabled]::-webkit-input-placeholder, +.select select[disabled]::-webkit-input-placeholder, +fieldset[disabled] .input::-webkit-input-placeholder, +fieldset[disabled] .textarea::-webkit-input-placeholder, +fieldset[disabled] .select select::-webkit-input-placeholder { + color: rgba(122,122,122,0.3); +} +.input[disabled]:-moz-placeholder, +.textarea[disabled]:-moz-placeholder, +.select select[disabled]:-moz-placeholder, +fieldset[disabled] .input:-moz-placeholder, +fieldset[disabled] .textarea:-moz-placeholder, +fieldset[disabled] .select select:-moz-placeholder { + color: rgba(122,122,122,0.3); +} +.input[disabled]:-ms-input-placeholder, +.textarea[disabled]:-ms-input-placeholder, +.select select[disabled]:-ms-input-placeholder, +fieldset[disabled] .input:-ms-input-placeholder, +fieldset[disabled] .textarea:-ms-input-placeholder, +fieldset[disabled] .select select:-ms-input-placeholder { + color: rgba(122,122,122,0.3); +} +.input, +.textarea { + box-shadow: inset 0 0.0625em 0.125em rgba(10,10,10,0.05); + max-width: 100%; + width: 100%; +} +.input[readonly], +.textarea[readonly] { + box-shadow: none; +} +.input.is-white, +.textarea.is-white { + border-color: #fff; +} +.input.is-white:focus, +.textarea.is-white:focus, +.input.is-white.is-focused, +.textarea.is-white.is-focused, +.input.is-white:active, +.textarea.is-white:active, +.input.is-white.is-active, +.textarea.is-white.is-active { + box-shadow: 0 0 0 0.125em rgba(255,255,255,0.25); +} +.input.is-black, +.textarea.is-black { + border-color: #0a0a0a; +} +.input.is-black:focus, +.textarea.is-black:focus, +.input.is-black.is-focused, +.textarea.is-black.is-focused, +.input.is-black:active, +.textarea.is-black:active, +.input.is-black.is-active, +.textarea.is-black.is-active { + box-shadow: 0 0 0 0.125em rgba(10,10,10,0.25); +} +.input.is-light, +.textarea.is-light { + border-color: #f5f5f5; +} +.input.is-light:focus, +.textarea.is-light:focus, +.input.is-light.is-focused, +.textarea.is-light.is-focused, +.input.is-light:active, +.textarea.is-light:active, +.input.is-light.is-active, +.textarea.is-light.is-active { + box-shadow: 0 0 0 0.125em rgba(245,245,245,0.25); +} +.input.is-dark, +.textarea.is-dark { + border-color: #363636; +} +.input.is-dark:focus, +.textarea.is-dark:focus, +.input.is-dark.is-focused, +.textarea.is-dark.is-focused, +.input.is-dark:active, +.textarea.is-dark:active, +.input.is-dark.is-active, +.textarea.is-dark.is-active { + box-shadow: 0 0 0 0.125em rgba(54,54,54,0.25); +} +.input.is-primary, +.textarea.is-primary { + border-color: #3273dc; +} +.input.is-primary:focus, +.textarea.is-primary:focus, +.input.is-primary.is-focused, +.textarea.is-primary.is-focused, +.input.is-primary:active, +.textarea.is-primary:active, +.input.is-primary.is-active, +.textarea.is-primary.is-active { + box-shadow: 0 0 0 0.125em rgba(50,115,220,0.25); +} +.input.is-link, +.textarea.is-link { + border-color: #3273dc; +} +.input.is-link:focus, +.textarea.is-link:focus, +.input.is-link.is-focused, +.textarea.is-link.is-focused, +.input.is-link:active, +.textarea.is-link:active, +.input.is-link.is-active, +.textarea.is-link.is-active { + box-shadow: 0 0 0 0.125em rgba(50,115,220,0.25); +} +.input.is-info, +.textarea.is-info { + border-color: #3298dc; +} +.input.is-info:focus, +.textarea.is-info:focus, +.input.is-info.is-focused, +.textarea.is-info.is-focused, +.input.is-info:active, +.textarea.is-info:active, +.input.is-info.is-active, +.textarea.is-info.is-active { + box-shadow: 0 0 0 0.125em rgba(50,152,220,0.25); +} +.input.is-success, +.textarea.is-success { + border-color: #48c774; +} +.input.is-success:focus, +.textarea.is-success:focus, +.input.is-success.is-focused, +.textarea.is-success.is-focused, +.input.is-success:active, +.textarea.is-success:active, +.input.is-success.is-active, +.textarea.is-success.is-active { + box-shadow: 0 0 0 0.125em rgba(72,199,116,0.25); +} +.input.is-warning, +.textarea.is-warning { + border-color: #ffdd57; +} +.input.is-warning:focus, +.textarea.is-warning:focus, +.input.is-warning.is-focused, +.textarea.is-warning.is-focused, +.input.is-warning:active, +.textarea.is-warning:active, +.input.is-warning.is-active, +.textarea.is-warning.is-active { + box-shadow: 0 0 0 0.125em rgba(255,221,87,0.25); +} +.input.is-danger, +.textarea.is-danger { + border-color: #f14668; +} +.input.is-danger:focus, +.textarea.is-danger:focus, +.input.is-danger.is-focused, +.textarea.is-danger.is-focused, +.input.is-danger:active, +.textarea.is-danger:active, +.input.is-danger.is-active, +.textarea.is-danger.is-active { + box-shadow: 0 0 0 0.125em rgba(241,70,104,0.25); +} +.input.is-grey-lightest, +.textarea.is-grey-lightest { + border-color: #ededed; +} +.input.is-grey-lightest:focus, +.textarea.is-grey-lightest:focus, +.input.is-grey-lightest.is-focused, +.textarea.is-grey-lightest.is-focused, +.input.is-grey-lightest:active, +.textarea.is-grey-lightest:active, +.input.is-grey-lightest.is-active, +.textarea.is-grey-lightest.is-active { + box-shadow: 0 0 0 0.125em rgba(237,237,237,0.25); +} +.input.is-small, +.textarea.is-small { + border-radius: 2px; + font-size: 0.75rem; +} +.input.is-medium, +.textarea.is-medium { + font-size: 1.25rem; +} +.input.is-large, +.textarea.is-large { + font-size: 1.5rem; +} +.input.is-fullwidth, +.textarea.is-fullwidth { + display: block; + width: 100%; +} +.input.is-inline, +.textarea.is-inline { + display: inline; + width: auto; +} +.input.is-rounded { + border-radius: 290486px; + padding-left: calc(calc(0.75em - 1px) + 0.375em); + padding-right: calc(calc(0.75em - 1px) + 0.375em); +} +.input.is-static { + background-color: transparent; + border-color: transparent; + box-shadow: none; + padding-left: 0; + padding-right: 0; +} +.textarea { + display: block; + max-width: 100%; + min-width: 100%; + padding: calc(0.75em - 1px); + resize: vertical; +} +.textarea:not([rows]) { + max-height: 40em; + min-height: 8em; +} +.textarea[rows] { + height: initial; +} +.textarea.has-fixed-size { + resize: none; +} +.checkbox, +.radio { + cursor: pointer; + display: inline-block; + line-height: 1.25; + position: relative; +} +.checkbox input, +.radio input { + cursor: pointer; +} +.checkbox:hover, +.radio:hover { + color: #363636; +} +.checkbox[disabled], +.radio[disabled], +fieldset[disabled] .checkbox, +fieldset[disabled] .radio { + color: #7a7a7a; + cursor: not-allowed; +} +.radio + .radio { + margin-left: 0.5em; +} +.select { + display: inline-block; + max-width: 100%; + position: relative; + vertical-align: top; +} +.select:not(.is-multiple) { + height: 2.25em; +} +.select:not(.is-multiple):not(.is-loading)::after { + border-color: #3273dc; + right: 1.125em; + z-index: 4; +} +.select.is-rounded select { + border-radius: 290486px; + padding-left: 1em; +} +.select select { + cursor: pointer; + display: block; + font-size: 1em; + max-width: 100%; + outline: none; +} +.select select::-ms-expand { + display: none; +} +.select select[disabled]:hover, +fieldset[disabled] .select select:hover { + border-color: #f5f5f5; +} +.select select:not([multiple]) { + padding-right: 2.5em; +} +.select select[multiple] { + height: auto; + padding: 0; +} +.select select[multiple] option { + padding: 0.5em 1em; +} +.select:not(.is-multiple):not(.is-loading):hover::after { + border-color: #363636; +} +.select.is-white:not(:hover)::after { + border-color: #fff; +} +.select.is-white select { + border-color: #fff; +} +.select.is-white select:hover, +.select.is-white select.is-hovered { + border-color: #f2f2f2; +} +.select.is-white select:focus, +.select.is-white select.is-focused, +.select.is-white select:active, +.select.is-white select.is-active { + box-shadow: 0 0 0 0.125em rgba(255,255,255,0.25); +} +.select.is-black:not(:hover)::after { + border-color: #0a0a0a; +} +.select.is-black select { + border-color: #0a0a0a; +} +.select.is-black select:hover, +.select.is-black select.is-hovered { + border-color: #000; +} +.select.is-black select:focus, +.select.is-black select.is-focused, +.select.is-black select:active, +.select.is-black select.is-active { + box-shadow: 0 0 0 0.125em rgba(10,10,10,0.25); +} +.select.is-light:not(:hover)::after { + border-color: #f5f5f5; +} +.select.is-light select { + border-color: #f5f5f5; +} +.select.is-light select:hover, +.select.is-light select.is-hovered { + border-color: #e8e8e8; +} +.select.is-light select:focus, +.select.is-light select.is-focused, +.select.is-light select:active, +.select.is-light select.is-active { + box-shadow: 0 0 0 0.125em rgba(245,245,245,0.25); +} +.select.is-dark:not(:hover)::after { + border-color: #363636; +} +.select.is-dark select { + border-color: #363636; +} +.select.is-dark select:hover, +.select.is-dark select.is-hovered { + border-color: #292929; +} +.select.is-dark select:focus, +.select.is-dark select.is-focused, +.select.is-dark select:active, +.select.is-dark select.is-active { + box-shadow: 0 0 0 0.125em rgba(54,54,54,0.25); +} +.select.is-primary:not(:hover)::after { + border-color: #3273dc; +} +.select.is-primary select { + border-color: #3273dc; +} +.select.is-primary select:hover, +.select.is-primary select.is-hovered { + border-color: #2366d1; +} +.select.is-primary select:focus, +.select.is-primary select.is-focused, +.select.is-primary select:active, +.select.is-primary select.is-active { + box-shadow: 0 0 0 0.125em rgba(50,115,220,0.25); +} +.select.is-link:not(:hover)::after { + border-color: #3273dc; +} +.select.is-link select { + border-color: #3273dc; +} +.select.is-link select:hover, +.select.is-link select.is-hovered { + border-color: #2366d1; +} +.select.is-link select:focus, +.select.is-link select.is-focused, +.select.is-link select:active, +.select.is-link select.is-active { + box-shadow: 0 0 0 0.125em rgba(50,115,220,0.25); +} +.select.is-info:not(:hover)::after { + border-color: #3298dc; +} +.select.is-info select { + border-color: #3298dc; +} +.select.is-info select:hover, +.select.is-info select.is-hovered { + border-color: #238cd1; +} +.select.is-info select:focus, +.select.is-info select.is-focused, +.select.is-info select:active, +.select.is-info select.is-active { + box-shadow: 0 0 0 0.125em rgba(50,152,220,0.25); +} +.select.is-success:not(:hover)::after { + border-color: #48c774; +} +.select.is-success select { + border-color: #48c774; +} +.select.is-success select:hover, +.select.is-success select.is-hovered { + border-color: #3abb67; +} +.select.is-success select:focus, +.select.is-success select.is-focused, +.select.is-success select:active, +.select.is-success select.is-active { + box-shadow: 0 0 0 0.125em rgba(72,199,116,0.25); +} +.select.is-warning:not(:hover)::after { + border-color: #ffdd57; +} +.select.is-warning select { + border-color: #ffdd57; +} +.select.is-warning select:hover, +.select.is-warning select.is-hovered { + border-color: #ffd83d; +} +.select.is-warning select:focus, +.select.is-warning select.is-focused, +.select.is-warning select:active, +.select.is-warning select.is-active { + box-shadow: 0 0 0 0.125em rgba(255,221,87,0.25); +} +.select.is-danger:not(:hover)::after { + border-color: #f14668; +} +.select.is-danger select { + border-color: #f14668; +} +.select.is-danger select:hover, +.select.is-danger select.is-hovered { + border-color: #ef2e55; +} +.select.is-danger select:focus, +.select.is-danger select.is-focused, +.select.is-danger select:active, +.select.is-danger select.is-active { + box-shadow: 0 0 0 0.125em rgba(241,70,104,0.25); +} +.select.is-grey-lightest:not(:hover)::after { + border-color: #ededed; +} +.select.is-grey-lightest select { + border-color: #ededed; +} +.select.is-grey-lightest select:hover, +.select.is-grey-lightest select.is-hovered { + border-color: #e0e0e0; +} +.select.is-grey-lightest select:focus, +.select.is-grey-lightest select.is-focused, +.select.is-grey-lightest select:active, +.select.is-grey-lightest select.is-active { + box-shadow: 0 0 0 0.125em rgba(237,237,237,0.25); +} +.select.is-small { + border-radius: 2px; + font-size: 0.75rem; +} +.select.is-medium { + font-size: 1.25rem; +} +.select.is-large { + font-size: 1.5rem; +} +.select.is-disabled::after { + border-color: #7a7a7a; +} +.select.is-fullwidth { + width: 100%; +} +.select.is-fullwidth select { + width: 100%; +} +.select.is-loading::after { + margin-top: 0; + position: absolute; + right: 0.625em; + top: 0.625em; + transform: none; +} +.select.is-loading.is-small:after { + font-size: 0.75rem; +} +.select.is-loading.is-medium:after { + font-size: 1.25rem; +} +.select.is-loading.is-large:after { + font-size: 1.5rem; +} +.file { + align-items: stretch; + display: flex; + justify-content: flex-start; + position: relative; +} +.file.is-white .file-cta { + background-color: #fff; + border-color: transparent; + color: #0a0a0a; +} +.file.is-white:hover .file-cta, +.file.is-white.is-hovered .file-cta { + background-color: #f9f9f9; + border-color: transparent; + color: #0a0a0a; +} +.file.is-white:focus .file-cta, +.file.is-white.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(255,255,255,0.25); + color: #0a0a0a; +} +.file.is-white:active .file-cta, +.file.is-white.is-active .file-cta { + background-color: #f2f2f2; + border-color: transparent; + color: #0a0a0a; +} +.file.is-black .file-cta { + background-color: #0a0a0a; + border-color: transparent; + color: #fff; +} +.file.is-black:hover .file-cta, +.file.is-black.is-hovered .file-cta { + background-color: #040404; + border-color: transparent; + color: #fff; +} +.file.is-black:focus .file-cta, +.file.is-black.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(10,10,10,0.25); + color: #fff; +} +.file.is-black:active .file-cta, +.file.is-black.is-active .file-cta { + background-color: #000; + border-color: transparent; + color: #fff; +} +.file.is-light .file-cta { + background-color: #f5f5f5; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.file.is-light:hover .file-cta, +.file.is-light.is-hovered .file-cta { + background-color: #eee; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.file.is-light:focus .file-cta, +.file.is-light.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(245,245,245,0.25); + color: rgba(0,0,0,0.7); +} +.file.is-light:active .file-cta, +.file.is-light.is-active .file-cta { + background-color: #e8e8e8; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.file.is-dark .file-cta { + background-color: #363636; + border-color: transparent; + color: #fff; +} +.file.is-dark:hover .file-cta, +.file.is-dark.is-hovered .file-cta { + background-color: #2f2f2f; + border-color: transparent; + color: #fff; +} +.file.is-dark:focus .file-cta, +.file.is-dark.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(54,54,54,0.25); + color: #fff; +} +.file.is-dark:active .file-cta, +.file.is-dark.is-active .file-cta { + background-color: #292929; + border-color: transparent; + color: #fff; +} +.file.is-primary .file-cta { + background-color: #3273dc; + border-color: transparent; + color: #fff; +} +.file.is-primary:hover .file-cta, +.file.is-primary.is-hovered .file-cta { + background-color: #276cda; + border-color: transparent; + color: #fff; +} +.file.is-primary:focus .file-cta, +.file.is-primary.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(50,115,220,0.25); + color: #fff; +} +.file.is-primary:active .file-cta, +.file.is-primary.is-active .file-cta { + background-color: #2366d1; + border-color: transparent; + color: #fff; +} +.file.is-link .file-cta { + background-color: #3273dc; + border-color: transparent; + color: #fff; +} +.file.is-link:hover .file-cta, +.file.is-link.is-hovered .file-cta { + background-color: #276cda; + border-color: transparent; + color: #fff; +} +.file.is-link:focus .file-cta, +.file.is-link.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(50,115,220,0.25); + color: #fff; +} +.file.is-link:active .file-cta, +.file.is-link.is-active .file-cta { + background-color: #2366d1; + border-color: transparent; + color: #fff; +} +.file.is-info .file-cta { + background-color: #3298dc; + border-color: transparent; + color: #fff; +} +.file.is-info:hover .file-cta, +.file.is-info.is-hovered .file-cta { + background-color: #2793da; + border-color: transparent; + color: #fff; +} +.file.is-info:focus .file-cta, +.file.is-info.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(50,152,220,0.25); + color: #fff; +} +.file.is-info:active .file-cta, +.file.is-info.is-active .file-cta { + background-color: #238cd1; + border-color: transparent; + color: #fff; +} +.file.is-success .file-cta { + background-color: #48c774; + border-color: transparent; + color: #fff; +} +.file.is-success:hover .file-cta, +.file.is-success.is-hovered .file-cta { + background-color: #3ec46d; + border-color: transparent; + color: #fff; +} +.file.is-success:focus .file-cta, +.file.is-success.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(72,199,116,0.25); + color: #fff; +} +.file.is-success:active .file-cta, +.file.is-success.is-active .file-cta { + background-color: #3abb67; + border-color: transparent; + color: #fff; +} +.file.is-warning .file-cta { + background-color: #ffdd57; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.file.is-warning:hover .file-cta, +.file.is-warning.is-hovered .file-cta { + background-color: #ffdb4a; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.file.is-warning:focus .file-cta, +.file.is-warning.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(255,221,87,0.25); + color: rgba(0,0,0,0.7); +} +.file.is-warning:active .file-cta, +.file.is-warning.is-active .file-cta { + background-color: #ffd83d; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.file.is-danger .file-cta { + background-color: #f14668; + border-color: transparent; + color: #fff; +} +.file.is-danger:hover .file-cta, +.file.is-danger.is-hovered .file-cta { + background-color: #f03a5f; + border-color: transparent; + color: #fff; +} +.file.is-danger:focus .file-cta, +.file.is-danger.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(241,70,104,0.25); + color: #fff; +} +.file.is-danger:active .file-cta, +.file.is-danger.is-active .file-cta { + background-color: #ef2e55; + border-color: transparent; + color: #fff; +} +.file.is-grey-lightest .file-cta { + background-color: #ededed; + border-color: transparent; + color: #363636; +} +.file.is-grey-lightest:hover .file-cta, +.file.is-grey-lightest.is-hovered .file-cta { + background-color: #e7e7e7; + border-color: transparent; + color: #363636; +} +.file.is-grey-lightest:focus .file-cta, +.file.is-grey-lightest.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(237,237,237,0.25); + color: #363636; +} +.file.is-grey-lightest:active .file-cta, +.file.is-grey-lightest.is-active .file-cta { + background-color: #e0e0e0; + border-color: transparent; + color: #363636; +} +.file.is-small { + font-size: 0.75rem; +} +.file.is-medium { + font-size: 1.25rem; +} +.file.is-medium .file-icon .fa { + font-size: 21px; +} +.file.is-large { + font-size: 1.5rem; +} +.file.is-large .file-icon .fa { + font-size: 28px; +} +.file.has-name .file-cta { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.file.has-name .file-name { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.file.has-name.is-empty .file-cta { + border-radius: 4px; +} +.file.has-name.is-empty .file-name { + display: none; +} +.file.is-boxed .file-label { + flex-direction: column; +} +.file.is-boxed .file-cta { + flex-direction: column; + height: auto; + padding: 1em 3em; +} +.file.is-boxed .file-name { + border-width: 0 1px 1px; +} +.file.is-boxed .file-icon { + height: 1.5em; + width: 1.5em; +} +.file.is-boxed .file-icon .fa { + font-size: 21px; +} +.file.is-boxed.is-small .file-icon .fa { + font-size: 14px; +} +.file.is-boxed.is-medium .file-icon .fa { + font-size: 28px; +} +.file.is-boxed.is-large .file-icon .fa { + font-size: 35px; +} +.file.is-boxed.has-name .file-cta { + border-radius: 4px 4px 0 0; +} +.file.is-boxed.has-name .file-name { + border-radius: 0 0 4px 4px; + border-width: 0 1px 1px; +} +.file.is-centered { + justify-content: center; +} +.file.is-fullwidth .file-label { + width: 100%; +} +.file.is-fullwidth .file-name { + flex-grow: 1; + max-width: none; +} +.file.is-right { + justify-content: flex-end; +} +.file.is-right .file-cta { + border-radius: 0 4px 4px 0; +} +.file.is-right .file-name { + border-radius: 4px 0 0 4px; + border-width: 1px 0 1px 1px; + order: -1; +} +.file-label { + align-items: stretch; + display: flex; + cursor: pointer; + justify-content: flex-start; + overflow: hidden; + position: relative; +} +.file-label:hover .file-cta { + background-color: #eee; + color: #363636; +} +.file-label:hover .file-name { + border-color: #d5d5d5; +} +.file-label:active .file-cta { + background-color: #e8e8e8; + color: #363636; +} +.file-label:active .file-name { + border-color: #cfcfcf; +} +.file-input { + height: 100%; + left: 0; + opacity: 0; + outline: none; + position: absolute; + top: 0; + width: 100%; +} +.file-cta, +.file-name { + border-color: #dbdbdb; + border-radius: 4px; + font-size: 1em; + padding-left: 1em; + padding-right: 1em; + white-space: nowrap; +} +.file-cta { + background-color: #f5f5f5; + color: #4a4a4a; +} +.file-name { + border-color: #dbdbdb; + border-style: solid; + border-width: 1px 1px 1px 0; + display: block; + max-width: 16em; + overflow: hidden; + text-align: left; + text-overflow: ellipsis; +} +.file-icon { + align-items: center; + display: flex; + height: 1em; + justify-content: center; + margin-right: 0.5em; + width: 1em; +} +.file-icon .fa { + font-size: 14px; +} +.label { + color: #363636; + display: block; + font-size: 1rem; + font-weight: 700; +} +.label:not(:last-child) { + margin-bottom: 0.5em; +} +.label.is-small { + font-size: 0.75rem; +} +.label.is-medium { + font-size: 1.25rem; +} +.label.is-large { + font-size: 1.5rem; +} +.help { + display: block; + font-size: 0.75rem; + margin-top: 0.25rem; +} +.help.is-white { + color: #fff; +} +.help.is-black { + color: #0a0a0a; +} +.help.is-light { + color: #f5f5f5; +} +.help.is-dark { + color: #363636; +} +.help.is-primary { + color: #3273dc; +} +.help.is-link { + color: #3273dc; +} +.help.is-info { + color: #3298dc; +} +.help.is-success { + color: #48c774; +} +.help.is-warning { + color: #ffdd57; +} +.help.is-danger { + color: #f14668; +} +.help.is-grey-lightest { + color: #ededed; +} +.field:not(:last-child) { + margin-bottom: 0.75rem; +} +.field.has-addons { + display: flex; + justify-content: flex-start; +} +.field.has-addons .control:not(:last-child) { + margin-right: -1px; +} +.field.has-addons .control:not(:first-child):not(:last-child) .button, +.field.has-addons .control:not(:first-child):not(:last-child) .input, +.field.has-addons .control:not(:first-child):not(:last-child) .select select { + border-radius: 0; +} +.field.has-addons .control:first-child:not(:only-child) .button, +.field.has-addons .control:first-child:not(:only-child) .input, +.field.has-addons .control:first-child:not(:only-child) .select select { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.field.has-addons .control:last-child:not(:only-child) .button, +.field.has-addons .control:last-child:not(:only-child) .input, +.field.has-addons .control:last-child:not(:only-child) .select select { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.field.has-addons .control .button:not([disabled]):hover, +.field.has-addons .control .input:not([disabled]):hover, +.field.has-addons .control .select select:not([disabled]):hover, +.field.has-addons .control .button:not([disabled]).is-hovered, +.field.has-addons .control .input:not([disabled]).is-hovered, +.field.has-addons .control .select select:not([disabled]).is-hovered { + z-index: 2; +} +.field.has-addons .control .button:not([disabled]):focus, +.field.has-addons .control .input:not([disabled]):focus, +.field.has-addons .control .select select:not([disabled]):focus, +.field.has-addons .control .button:not([disabled]).is-focused, +.field.has-addons .control .input:not([disabled]).is-focused, +.field.has-addons .control .select select:not([disabled]).is-focused, +.field.has-addons .control .button:not([disabled]):active, +.field.has-addons .control .input:not([disabled]):active, +.field.has-addons .control .select select:not([disabled]):active, +.field.has-addons .control .button:not([disabled]).is-active, +.field.has-addons .control .input:not([disabled]).is-active, +.field.has-addons .control .select select:not([disabled]).is-active { + z-index: 3; +} +.field.has-addons .control .button:not([disabled]):focus:hover, +.field.has-addons .control .input:not([disabled]):focus:hover, +.field.has-addons .control .select select:not([disabled]):focus:hover, +.field.has-addons .control .button:not([disabled]).is-focused:hover, +.field.has-addons .control .input:not([disabled]).is-focused:hover, +.field.has-addons .control .select select:not([disabled]).is-focused:hover, +.field.has-addons .control .button:not([disabled]):active:hover, +.field.has-addons .control .input:not([disabled]):active:hover, +.field.has-addons .control .select select:not([disabled]):active:hover, +.field.has-addons .control .button:not([disabled]).is-active:hover, +.field.has-addons .control .input:not([disabled]).is-active:hover, +.field.has-addons .control .select select:not([disabled]).is-active:hover { + z-index: 4; +} +.field.has-addons .control.is-expanded { + flex-grow: 1; + flex-shrink: 1; +} +.field.has-addons.has-addons-centered { + justify-content: center; +} +.field.has-addons.has-addons-right { + justify-content: flex-end; +} +.field.has-addons.has-addons-fullwidth .control { + flex-grow: 1; + flex-shrink: 0; +} +.field.is-grouped { + display: flex; + justify-content: flex-start; +} +.field.is-grouped > .control { + flex-shrink: 0; +} +.field.is-grouped > .control:not(:last-child) { + margin-bottom: 0; + margin-right: 0.75rem; +} +.field.is-grouped > .control.is-expanded { + flex-grow: 1; + flex-shrink: 1; +} +.field.is-grouped.is-grouped-centered { + justify-content: center; +} +.field.is-grouped.is-grouped-right { + justify-content: flex-end; +} +.field.is-grouped.is-grouped-multiline { + flex-wrap: wrap; +} +.field.is-grouped.is-grouped-multiline > .control:last-child, +.field.is-grouped.is-grouped-multiline > .control:not(:last-child) { + margin-bottom: 0.75rem; +} +.field.is-grouped.is-grouped-multiline:last-child { + margin-bottom: -0.75rem; +} +.field.is-grouped.is-grouped-multiline:not(:last-child) { + margin-bottom: 0; +} +@media screen and (min-width: 769px), print { + .field.is-horizontal { + display: flex; + } +} +.field-label .label { + font-size: inherit; +} +@media screen and (max-width: 768px) { + .field-label { + margin-bottom: 0.5rem; + } +} +@media screen and (min-width: 769px), print { + .field-label { + flex-basis: 0; + flex-grow: 1; + flex-shrink: 0; + margin-right: 1.5rem; + text-align: right; + } + .field-label.is-small { + font-size: 0.75rem; + padding-top: 0.375em; + } + .field-label.is-normal { + padding-top: 0.375em; + } + .field-label.is-medium { + font-size: 1.25rem; + padding-top: 0.375em; + } + .field-label.is-large { + font-size: 1.5rem; + padding-top: 0.375em; + } +} +.field-body .field .field { + margin-bottom: 0; +} +@media screen and (min-width: 769px), print { + .field-body { + display: flex; + flex-basis: 0; + flex-grow: 5; + flex-shrink: 1; + } + .field-body .field { + margin-bottom: 0; + } + .field-body > .field { + flex-shrink: 1; + } + .field-body > .field:not(.is-narrow) { + flex-grow: 1; + } + .field-body > .field:not(:last-child) { + margin-right: 0.75rem; + } +} +.control { + box-sizing: border-box; + clear: both; + font-size: 1rem; + position: relative; + text-align: left; +} +.control.has-icons-left .input:focus ~ .icon, +.control.has-icons-right .input:focus ~ .icon, +.control.has-icons-left .select:focus ~ .icon, +.control.has-icons-right .select:focus ~ .icon { + color: #4a4a4a; +} +.control.has-icons-left .input.is-small ~ .icon, +.control.has-icons-right .input.is-small ~ .icon, +.control.has-icons-left .select.is-small ~ .icon, +.control.has-icons-right .select.is-small ~ .icon { + font-size: 0.75rem; +} +.control.has-icons-left .input.is-medium ~ .icon, +.control.has-icons-right .input.is-medium ~ .icon, +.control.has-icons-left .select.is-medium ~ .icon, +.control.has-icons-right .select.is-medium ~ .icon { + font-size: 1.25rem; +} +.control.has-icons-left .input.is-large ~ .icon, +.control.has-icons-right .input.is-large ~ .icon, +.control.has-icons-left .select.is-large ~ .icon, +.control.has-icons-right .select.is-large ~ .icon { + font-size: 1.5rem; +} +.control.has-icons-left .icon, +.control.has-icons-right .icon { + color: #dbdbdb; + height: 2.25em; + pointer-events: none; + position: absolute; + top: 0; + width: 2.25em; + z-index: 4; +} +.control.has-icons-left .input, +.control.has-icons-left .select select { + padding-left: 2.25em; +} +.control.has-icons-left .icon.is-left { + left: 0; +} +.control.has-icons-right .input, +.control.has-icons-right .select select { + padding-right: 2.25em; +} +.control.has-icons-right .icon.is-right { + right: 0; +} +.control.is-loading::after { + position: absolute !important; + right: 0.625em; + top: 0.625em; + z-index: 4; +} +.control.is-loading.is-small:after { + font-size: 0.75rem; +} +.control.is-loading.is-medium:after { + font-size: 1.25rem; +} +.control.is-loading.is-large:after { + font-size: 1.5rem; +} +.column { + display: block; + flex-basis: 0; + flex-grow: 1; + flex-shrink: 1; + padding: 0.75rem; +} +.columns.is-mobile > .column.is-narrow { + flex: none; +} +.columns.is-mobile > .column.is-full { + flex: none; + width: 100%; +} +.columns.is-mobile > .column.is-three-quarters { + flex: none; + width: 75%; +} +.columns.is-mobile > .column.is-two-thirds { + flex: none; + width: 66.6666%; +} +.columns.is-mobile > .column.is-half { + flex: none; + width: 50%; +} +.columns.is-mobile > .column.is-one-third { + flex: none; + width: 33.3333%; +} +.columns.is-mobile > .column.is-one-quarter { + flex: none; + width: 25%; +} +.columns.is-mobile > .column.is-one-fifth { + flex: none; + width: 20%; +} +.columns.is-mobile > .column.is-two-fifths { + flex: none; + width: 40%; +} +.columns.is-mobile > .column.is-three-fifths { + flex: none; + width: 60%; +} +.columns.is-mobile > .column.is-four-fifths { + flex: none; + width: 80%; +} +.columns.is-mobile > .column.is-offset-three-quarters { + margin-left: 75%; +} +.columns.is-mobile > .column.is-offset-two-thirds { + margin-left: 66.6666%; +} +.columns.is-mobile > .column.is-offset-half { + margin-left: 50%; +} +.columns.is-mobile > .column.is-offset-one-third { + margin-left: 33.3333%; +} +.columns.is-mobile > .column.is-offset-one-quarter { + margin-left: 25%; +} +.columns.is-mobile > .column.is-offset-one-fifth { + margin-left: 20%; +} +.columns.is-mobile > .column.is-offset-two-fifths { + margin-left: 40%; +} +.columns.is-mobile > .column.is-offset-three-fifths { + margin-left: 60%; +} +.columns.is-mobile > .column.is-offset-four-fifths { + margin-left: 80%; +} +.columns.is-mobile > .column.is-0 { + flex: none; + width: 0%; +} +.columns.is-mobile > .column.is-offset-0 { + margin-left: 0%; +} +.columns.is-mobile > .column.is-1 { + flex: none; + width: 8.33333%; +} +.columns.is-mobile > .column.is-offset-1 { + margin-left: 8.33333%; +} +.columns.is-mobile > .column.is-2 { + flex: none; + width: 16.66667%; +} +.columns.is-mobile > .column.is-offset-2 { + margin-left: 16.66667%; +} +.columns.is-mobile > .column.is-3 { + flex: none; + width: 25%; +} +.columns.is-mobile > .column.is-offset-3 { + margin-left: 25%; +} +.columns.is-mobile > .column.is-4 { + flex: none; + width: 33.33333%; +} +.columns.is-mobile > .column.is-offset-4 { + margin-left: 33.33333%; +} +.columns.is-mobile > .column.is-5 { + flex: none; + width: 41.66667%; +} +.columns.is-mobile > .column.is-offset-5 { + margin-left: 41.66667%; +} +.columns.is-mobile > .column.is-6 { + flex: none; + width: 50%; +} +.columns.is-mobile > .column.is-offset-6 { + margin-left: 50%; +} +.columns.is-mobile > .column.is-7 { + flex: none; + width: 58.33333%; +} +.columns.is-mobile > .column.is-offset-7 { + margin-left: 58.33333%; +} +.columns.is-mobile > .column.is-8 { + flex: none; + width: 66.66667%; +} +.columns.is-mobile > .column.is-offset-8 { + margin-left: 66.66667%; +} +.columns.is-mobile > .column.is-9 { + flex: none; + width: 75%; +} +.columns.is-mobile > .column.is-offset-9 { + margin-left: 75%; +} +.columns.is-mobile > .column.is-10 { + flex: none; + width: 83.33333%; +} +.columns.is-mobile > .column.is-offset-10 { + margin-left: 83.33333%; +} +.columns.is-mobile > .column.is-11 { + flex: none; + width: 91.66667%; +} +.columns.is-mobile > .column.is-offset-11 { + margin-left: 91.66667%; +} +.columns.is-mobile > .column.is-12 { + flex: none; + width: 100%; +} +.columns.is-mobile > .column.is-offset-12 { + margin-left: 100%; +} +@media screen and (max-width: 768px) { + .column.is-narrow-mobile { + flex: none; + } + .column.is-full-mobile { + flex: none; + width: 100%; + } + .column.is-three-quarters-mobile { + flex: none; + width: 75%; + } + .column.is-two-thirds-mobile { + flex: none; + width: 66.6666%; + } + .column.is-half-mobile { + flex: none; + width: 50%; + } + .column.is-one-third-mobile { + flex: none; + width: 33.3333%; + } + .column.is-one-quarter-mobile { + flex: none; + width: 25%; + } + .column.is-one-fifth-mobile { + flex: none; + width: 20%; + } + .column.is-two-fifths-mobile { + flex: none; + width: 40%; + } + .column.is-three-fifths-mobile { + flex: none; + width: 60%; + } + .column.is-four-fifths-mobile { + flex: none; + width: 80%; + } + .column.is-offset-three-quarters-mobile { + margin-left: 75%; + } + .column.is-offset-two-thirds-mobile { + margin-left: 66.6666%; + } + .column.is-offset-half-mobile { + margin-left: 50%; + } + .column.is-offset-one-third-mobile { + margin-left: 33.3333%; + } + .column.is-offset-one-quarter-mobile { + margin-left: 25%; + } + .column.is-offset-one-fifth-mobile { + margin-left: 20%; + } + .column.is-offset-two-fifths-mobile { + margin-left: 40%; + } + .column.is-offset-three-fifths-mobile { + margin-left: 60%; + } + .column.is-offset-four-fifths-mobile { + margin-left: 80%; + } + .column.is-0-mobile { + flex: none; + width: 0%; + } + .column.is-offset-0-mobile { + margin-left: 0%; + } + .column.is-1-mobile { + flex: none; + width: 8.33333%; + } + .column.is-offset-1-mobile { + margin-left: 8.33333%; + } + .column.is-2-mobile { + flex: none; + width: 16.66667%; + } + .column.is-offset-2-mobile { + margin-left: 16.66667%; + } + .column.is-3-mobile { + flex: none; + width: 25%; + } + .column.is-offset-3-mobile { + margin-left: 25%; + } + .column.is-4-mobile { + flex: none; + width: 33.33333%; + } + .column.is-offset-4-mobile { + margin-left: 33.33333%; + } + .column.is-5-mobile { + flex: none; + width: 41.66667%; + } + .column.is-offset-5-mobile { + margin-left: 41.66667%; + } + .column.is-6-mobile { + flex: none; + width: 50%; + } + .column.is-offset-6-mobile { + margin-left: 50%; + } + .column.is-7-mobile { + flex: none; + width: 58.33333%; + } + .column.is-offset-7-mobile { + margin-left: 58.33333%; + } + .column.is-8-mobile { + flex: none; + width: 66.66667%; + } + .column.is-offset-8-mobile { + margin-left: 66.66667%; + } + .column.is-9-mobile { + flex: none; + width: 75%; + } + .column.is-offset-9-mobile { + margin-left: 75%; + } + .column.is-10-mobile { + flex: none; + width: 83.33333%; + } + .column.is-offset-10-mobile { + margin-left: 83.33333%; + } + .column.is-11-mobile { + flex: none; + width: 91.66667%; + } + .column.is-offset-11-mobile { + margin-left: 91.66667%; + } + .column.is-12-mobile { + flex: none; + width: 100%; + } + .column.is-offset-12-mobile { + margin-left: 100%; + } +} +@media screen and (min-width: 769px), print { + .column.is-narrow, + .column.is-narrow-tablet { + flex: none; + } + .column.is-full, + .column.is-full-tablet { + flex: none; + width: 100%; + } + .column.is-three-quarters, + .column.is-three-quarters-tablet { + flex: none; + width: 75%; + } + .column.is-two-thirds, + .column.is-two-thirds-tablet { + flex: none; + width: 66.6666%; + } + .column.is-half, + .column.is-half-tablet { + flex: none; + width: 50%; + } + .column.is-one-third, + .column.is-one-third-tablet { + flex: none; + width: 33.3333%; + } + .column.is-one-quarter, + .column.is-one-quarter-tablet { + flex: none; + width: 25%; + } + .column.is-one-fifth, + .column.is-one-fifth-tablet { + flex: none; + width: 20%; + } + .column.is-two-fifths, + .column.is-two-fifths-tablet { + flex: none; + width: 40%; + } + .column.is-three-fifths, + .column.is-three-fifths-tablet { + flex: none; + width: 60%; + } + .column.is-four-fifths, + .column.is-four-fifths-tablet { + flex: none; + width: 80%; + } + .column.is-offset-three-quarters, + .column.is-offset-three-quarters-tablet { + margin-left: 75%; + } + .column.is-offset-two-thirds, + .column.is-offset-two-thirds-tablet { + margin-left: 66.6666%; + } + .column.is-offset-half, + .column.is-offset-half-tablet { + margin-left: 50%; + } + .column.is-offset-one-third, + .column.is-offset-one-third-tablet { + margin-left: 33.3333%; + } + .column.is-offset-one-quarter, + .column.is-offset-one-quarter-tablet { + margin-left: 25%; + } + .column.is-offset-one-fifth, + .column.is-offset-one-fifth-tablet { + margin-left: 20%; + } + .column.is-offset-two-fifths, + .column.is-offset-two-fifths-tablet { + margin-left: 40%; + } + .column.is-offset-three-fifths, + .column.is-offset-three-fifths-tablet { + margin-left: 60%; + } + .column.is-offset-four-fifths, + .column.is-offset-four-fifths-tablet { + margin-left: 80%; + } + .column.is-0, + .column.is-0-tablet { + flex: none; + width: 0%; + } + .column.is-offset-0, + .column.is-offset-0-tablet { + margin-left: 0%; + } + .column.is-1, + .column.is-1-tablet { + flex: none; + width: 8.33333%; + } + .column.is-offset-1, + .column.is-offset-1-tablet { + margin-left: 8.33333%; + } + .column.is-2, + .column.is-2-tablet { + flex: none; + width: 16.66667%; + } + .column.is-offset-2, + .column.is-offset-2-tablet { + margin-left: 16.66667%; + } + .column.is-3, + .column.is-3-tablet { + flex: none; + width: 25%; + } + .column.is-offset-3, + .column.is-offset-3-tablet { + margin-left: 25%; + } + .column.is-4, + .column.is-4-tablet { + flex: none; + width: 33.33333%; + } + .column.is-offset-4, + .column.is-offset-4-tablet { + margin-left: 33.33333%; + } + .column.is-5, + .column.is-5-tablet { + flex: none; + width: 41.66667%; + } + .column.is-offset-5, + .column.is-offset-5-tablet { + margin-left: 41.66667%; + } + .column.is-6, + .column.is-6-tablet { + flex: none; + width: 50%; + } + .column.is-offset-6, + .column.is-offset-6-tablet { + margin-left: 50%; + } + .column.is-7, + .column.is-7-tablet { + flex: none; + width: 58.33333%; + } + .column.is-offset-7, + .column.is-offset-7-tablet { + margin-left: 58.33333%; + } + .column.is-8, + .column.is-8-tablet { + flex: none; + width: 66.66667%; + } + .column.is-offset-8, + .column.is-offset-8-tablet { + margin-left: 66.66667%; + } + .column.is-9, + .column.is-9-tablet { + flex: none; + width: 75%; + } + .column.is-offset-9, + .column.is-offset-9-tablet { + margin-left: 75%; + } + .column.is-10, + .column.is-10-tablet { + flex: none; + width: 83.33333%; + } + .column.is-offset-10, + .column.is-offset-10-tablet { + margin-left: 83.33333%; + } + .column.is-11, + .column.is-11-tablet { + flex: none; + width: 91.66667%; + } + .column.is-offset-11, + .column.is-offset-11-tablet { + margin-left: 91.66667%; + } + .column.is-12, + .column.is-12-tablet { + flex: none; + width: 100%; + } + .column.is-offset-12, + .column.is-offset-12-tablet { + margin-left: 100%; + } +} +@media screen and (max-width: 1087px) { + .column.is-narrow-touch { + flex: none; + } + .column.is-full-touch { + flex: none; + width: 100%; + } + .column.is-three-quarters-touch { + flex: none; + width: 75%; + } + .column.is-two-thirds-touch { + flex: none; + width: 66.6666%; + } + .column.is-half-touch { + flex: none; + width: 50%; + } + .column.is-one-third-touch { + flex: none; + width: 33.3333%; + } + .column.is-one-quarter-touch { + flex: none; + width: 25%; + } + .column.is-one-fifth-touch { + flex: none; + width: 20%; + } + .column.is-two-fifths-touch { + flex: none; + width: 40%; + } + .column.is-three-fifths-touch { + flex: none; + width: 60%; + } + .column.is-four-fifths-touch { + flex: none; + width: 80%; + } + .column.is-offset-three-quarters-touch { + margin-left: 75%; + } + .column.is-offset-two-thirds-touch { + margin-left: 66.6666%; + } + .column.is-offset-half-touch { + margin-left: 50%; + } + .column.is-offset-one-third-touch { + margin-left: 33.3333%; + } + .column.is-offset-one-quarter-touch { + margin-left: 25%; + } + .column.is-offset-one-fifth-touch { + margin-left: 20%; + } + .column.is-offset-two-fifths-touch { + margin-left: 40%; + } + .column.is-offset-three-fifths-touch { + margin-left: 60%; + } + .column.is-offset-four-fifths-touch { + margin-left: 80%; + } + .column.is-0-touch { + flex: none; + width: 0%; + } + .column.is-offset-0-touch { + margin-left: 0%; + } + .column.is-1-touch { + flex: none; + width: 8.33333%; + } + .column.is-offset-1-touch { + margin-left: 8.33333%; + } + .column.is-2-touch { + flex: none; + width: 16.66667%; + } + .column.is-offset-2-touch { + margin-left: 16.66667%; + } + .column.is-3-touch { + flex: none; + width: 25%; + } + .column.is-offset-3-touch { + margin-left: 25%; + } + .column.is-4-touch { + flex: none; + width: 33.33333%; + } + .column.is-offset-4-touch { + margin-left: 33.33333%; + } + .column.is-5-touch { + flex: none; + width: 41.66667%; + } + .column.is-offset-5-touch { + margin-left: 41.66667%; + } + .column.is-6-touch { + flex: none; + width: 50%; + } + .column.is-offset-6-touch { + margin-left: 50%; + } + .column.is-7-touch { + flex: none; + width: 58.33333%; + } + .column.is-offset-7-touch { + margin-left: 58.33333%; + } + .column.is-8-touch { + flex: none; + width: 66.66667%; + } + .column.is-offset-8-touch { + margin-left: 66.66667%; + } + .column.is-9-touch { + flex: none; + width: 75%; + } + .column.is-offset-9-touch { + margin-left: 75%; + } + .column.is-10-touch { + flex: none; + width: 83.33333%; + } + .column.is-offset-10-touch { + margin-left: 83.33333%; + } + .column.is-11-touch { + flex: none; + width: 91.66667%; + } + .column.is-offset-11-touch { + margin-left: 91.66667%; + } + .column.is-12-touch { + flex: none; + width: 100%; + } + .column.is-offset-12-touch { + margin-left: 100%; + } +} +@media screen and (min-width: 1088px) { + .column.is-narrow-desktop { + flex: none; + } + .column.is-full-desktop { + flex: none; + width: 100%; + } + .column.is-three-quarters-desktop { + flex: none; + width: 75%; + } + .column.is-two-thirds-desktop { + flex: none; + width: 66.6666%; + } + .column.is-half-desktop { + flex: none; + width: 50%; + } + .column.is-one-third-desktop { + flex: none; + width: 33.3333%; + } + .column.is-one-quarter-desktop { + flex: none; + width: 25%; + } + .column.is-one-fifth-desktop { + flex: none; + width: 20%; + } + .column.is-two-fifths-desktop { + flex: none; + width: 40%; + } + .column.is-three-fifths-desktop { + flex: none; + width: 60%; + } + .column.is-four-fifths-desktop { + flex: none; + width: 80%; + } + .column.is-offset-three-quarters-desktop { + margin-left: 75%; + } + .column.is-offset-two-thirds-desktop { + margin-left: 66.6666%; + } + .column.is-offset-half-desktop { + margin-left: 50%; + } + .column.is-offset-one-third-desktop { + margin-left: 33.3333%; + } + .column.is-offset-one-quarter-desktop { + margin-left: 25%; + } + .column.is-offset-one-fifth-desktop { + margin-left: 20%; + } + .column.is-offset-two-fifths-desktop { + margin-left: 40%; + } + .column.is-offset-three-fifths-desktop { + margin-left: 60%; + } + .column.is-offset-four-fifths-desktop { + margin-left: 80%; + } + .column.is-0-desktop { + flex: none; + width: 0%; + } + .column.is-offset-0-desktop { + margin-left: 0%; + } + .column.is-1-desktop { + flex: none; + width: 8.33333%; + } + .column.is-offset-1-desktop { + margin-left: 8.33333%; + } + .column.is-2-desktop { + flex: none; + width: 16.66667%; + } + .column.is-offset-2-desktop { + margin-left: 16.66667%; + } + .column.is-3-desktop { + flex: none; + width: 25%; + } + .column.is-offset-3-desktop { + margin-left: 25%; + } + .column.is-4-desktop { + flex: none; + width: 33.33333%; + } + .column.is-offset-4-desktop { + margin-left: 33.33333%; + } + .column.is-5-desktop { + flex: none; + width: 41.66667%; + } + .column.is-offset-5-desktop { + margin-left: 41.66667%; + } + .column.is-6-desktop { + flex: none; + width: 50%; + } + .column.is-offset-6-desktop { + margin-left: 50%; + } + .column.is-7-desktop { + flex: none; + width: 58.33333%; + } + .column.is-offset-7-desktop { + margin-left: 58.33333%; + } + .column.is-8-desktop { + flex: none; + width: 66.66667%; + } + .column.is-offset-8-desktop { + margin-left: 66.66667%; + } + .column.is-9-desktop { + flex: none; + width: 75%; + } + .column.is-offset-9-desktop { + margin-left: 75%; + } + .column.is-10-desktop { + flex: none; + width: 83.33333%; + } + .column.is-offset-10-desktop { + margin-left: 83.33333%; + } + .column.is-11-desktop { + flex: none; + width: 91.66667%; + } + .column.is-offset-11-desktop { + margin-left: 91.66667%; + } + .column.is-12-desktop { + flex: none; + width: 100%; + } + .column.is-offset-12-desktop { + margin-left: 100%; + } +} +@media screen and (min-width: 1280px) { + .column.is-narrow-widescreen { + flex: none; + } + .column.is-full-widescreen { + flex: none; + width: 100%; + } + .column.is-three-quarters-widescreen { + flex: none; + width: 75%; + } + .column.is-two-thirds-widescreen { + flex: none; + width: 66.6666%; + } + .column.is-half-widescreen { + flex: none; + width: 50%; + } + .column.is-one-third-widescreen { + flex: none; + width: 33.3333%; + } + .column.is-one-quarter-widescreen { + flex: none; + width: 25%; + } + .column.is-one-fifth-widescreen { + flex: none; + width: 20%; + } + .column.is-two-fifths-widescreen { + flex: none; + width: 40%; + } + .column.is-three-fifths-widescreen { + flex: none; + width: 60%; + } + .column.is-four-fifths-widescreen { + flex: none; + width: 80%; + } + .column.is-offset-three-quarters-widescreen { + margin-left: 75%; + } + .column.is-offset-two-thirds-widescreen { + margin-left: 66.6666%; + } + .column.is-offset-half-widescreen { + margin-left: 50%; + } + .column.is-offset-one-third-widescreen { + margin-left: 33.3333%; + } + .column.is-offset-one-quarter-widescreen { + margin-left: 25%; + } + .column.is-offset-one-fifth-widescreen { + margin-left: 20%; + } + .column.is-offset-two-fifths-widescreen { + margin-left: 40%; + } + .column.is-offset-three-fifths-widescreen { + margin-left: 60%; + } + .column.is-offset-four-fifths-widescreen { + margin-left: 80%; + } + .column.is-0-widescreen { + flex: none; + width: 0%; + } + .column.is-offset-0-widescreen { + margin-left: 0%; + } + .column.is-1-widescreen { + flex: none; + width: 8.33333%; + } + .column.is-offset-1-widescreen { + margin-left: 8.33333%; + } + .column.is-2-widescreen { + flex: none; + width: 16.66667%; + } + .column.is-offset-2-widescreen { + margin-left: 16.66667%; + } + .column.is-3-widescreen { + flex: none; + width: 25%; + } + .column.is-offset-3-widescreen { + margin-left: 25%; + } + .column.is-4-widescreen { + flex: none; + width: 33.33333%; + } + .column.is-offset-4-widescreen { + margin-left: 33.33333%; + } + .column.is-5-widescreen { + flex: none; + width: 41.66667%; + } + .column.is-offset-5-widescreen { + margin-left: 41.66667%; + } + .column.is-6-widescreen { + flex: none; + width: 50%; + } + .column.is-offset-6-widescreen { + margin-left: 50%; + } + .column.is-7-widescreen { + flex: none; + width: 58.33333%; + } + .column.is-offset-7-widescreen { + margin-left: 58.33333%; + } + .column.is-8-widescreen { + flex: none; + width: 66.66667%; + } + .column.is-offset-8-widescreen { + margin-left: 66.66667%; + } + .column.is-9-widescreen { + flex: none; + width: 75%; + } + .column.is-offset-9-widescreen { + margin-left: 75%; + } + .column.is-10-widescreen { + flex: none; + width: 83.33333%; + } + .column.is-offset-10-widescreen { + margin-left: 83.33333%; + } + .column.is-11-widescreen { + flex: none; + width: 91.66667%; + } + .column.is-offset-11-widescreen { + margin-left: 91.66667%; + } + .column.is-12-widescreen { + flex: none; + width: 100%; + } + .column.is-offset-12-widescreen { + margin-left: 100%; + } +} +@media screen and (min-width: 1472px) { + .column.is-narrow-fullhd { + flex: none; + } + .column.is-full-fullhd { + flex: none; + width: 100%; + } + .column.is-three-quarters-fullhd { + flex: none; + width: 75%; + } + .column.is-two-thirds-fullhd { + flex: none; + width: 66.6666%; + } + .column.is-half-fullhd { + flex: none; + width: 50%; + } + .column.is-one-third-fullhd { + flex: none; + width: 33.3333%; + } + .column.is-one-quarter-fullhd { + flex: none; + width: 25%; + } + .column.is-one-fifth-fullhd { + flex: none; + width: 20%; + } + .column.is-two-fifths-fullhd { + flex: none; + width: 40%; + } + .column.is-three-fifths-fullhd { + flex: none; + width: 60%; + } + .column.is-four-fifths-fullhd { + flex: none; + width: 80%; + } + .column.is-offset-three-quarters-fullhd { + margin-left: 75%; + } + .column.is-offset-two-thirds-fullhd { + margin-left: 66.6666%; + } + .column.is-offset-half-fullhd { + margin-left: 50%; + } + .column.is-offset-one-third-fullhd { + margin-left: 33.3333%; + } + .column.is-offset-one-quarter-fullhd { + margin-left: 25%; + } + .column.is-offset-one-fifth-fullhd { + margin-left: 20%; + } + .column.is-offset-two-fifths-fullhd { + margin-left: 40%; + } + .column.is-offset-three-fifths-fullhd { + margin-left: 60%; + } + .column.is-offset-four-fifths-fullhd { + margin-left: 80%; + } + .column.is-0-fullhd { + flex: none; + width: 0%; + } + .column.is-offset-0-fullhd { + margin-left: 0%; + } + .column.is-1-fullhd { + flex: none; + width: 8.33333%; + } + .column.is-offset-1-fullhd { + margin-left: 8.33333%; + } + .column.is-2-fullhd { + flex: none; + width: 16.66667%; + } + .column.is-offset-2-fullhd { + margin-left: 16.66667%; + } + .column.is-3-fullhd { + flex: none; + width: 25%; + } + .column.is-offset-3-fullhd { + margin-left: 25%; + } + .column.is-4-fullhd { + flex: none; + width: 33.33333%; + } + .column.is-offset-4-fullhd { + margin-left: 33.33333%; + } + .column.is-5-fullhd { + flex: none; + width: 41.66667%; + } + .column.is-offset-5-fullhd { + margin-left: 41.66667%; + } + .column.is-6-fullhd { + flex: none; + width: 50%; + } + .column.is-offset-6-fullhd { + margin-left: 50%; + } + .column.is-7-fullhd { + flex: none; + width: 58.33333%; + } + .column.is-offset-7-fullhd { + margin-left: 58.33333%; + } + .column.is-8-fullhd { + flex: none; + width: 66.66667%; + } + .column.is-offset-8-fullhd { + margin-left: 66.66667%; + } + .column.is-9-fullhd { + flex: none; + width: 75%; + } + .column.is-offset-9-fullhd { + margin-left: 75%; + } + .column.is-10-fullhd { + flex: none; + width: 83.33333%; + } + .column.is-offset-10-fullhd { + margin-left: 83.33333%; + } + .column.is-11-fullhd { + flex: none; + width: 91.66667%; + } + .column.is-offset-11-fullhd { + margin-left: 91.66667%; + } + .column.is-12-fullhd { + flex: none; + width: 100%; + } + .column.is-offset-12-fullhd { + margin-left: 100%; + } +} +.columns { + margin-left: -0.75rem; + margin-right: -0.75rem; + margin-top: -0.75rem; +} +.columns:last-child { + margin-bottom: -0.75rem; +} +.columns:not(:last-child) { + margin-bottom: calc(1.5rem - 0.75rem); +} +.columns.is-centered { + justify-content: center; +} +.columns.is-gapless { + margin-left: 0; + margin-right: 0; + margin-top: 0; +} +.columns.is-gapless > .column { + margin: 0; + padding: 0 !important; +} +.columns.is-gapless:not(:last-child) { + margin-bottom: 1.5rem; +} +.columns.is-gapless:last-child { + margin-bottom: 0; +} +.columns.is-mobile { + display: flex; +} +.columns.is-multiline { + flex-wrap: wrap; +} +.columns.is-vcentered { + align-items: center; +} +@media screen and (min-width: 769px), print { + .columns:not(.is-desktop) { + display: flex; + } +} +@media screen and (min-width: 1088px) { + .columns.is-desktop { + display: flex; + } +} +.columns.is-variable { + --columnGap: 0.75rem; + margin-left: calc(-1 * var(--columnGap)); + margin-right: calc(-1 * var(--columnGap)); +} +.columns.is-variable .column { + padding-left: var(--columnGap); + padding-right: var(--columnGap); +} +.columns.is-variable.is-0 { + --columnGap: 0rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-0-mobile { + --columnGap: 0rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-0-tablet { + --columnGap: 0rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-0-tablet-only { + --columnGap: 0rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-0-touch { + --columnGap: 0rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-0-desktop { + --columnGap: 0rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-0-desktop-only { + --columnGap: 0rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-0-widescreen { + --columnGap: 0rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-0-widescreen-only { + --columnGap: 0rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-0-fullhd { + --columnGap: 0rem; + } +} +.columns.is-variable.is-1 { + --columnGap: 0.25rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-1-mobile { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-1-tablet { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-1-tablet-only { + --columnGap: 0.25rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-1-touch { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-1-desktop { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-1-desktop-only { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-1-widescreen { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-1-widescreen-only { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-1-fullhd { + --columnGap: 0.25rem; + } +} +.columns.is-variable.is-2 { + --columnGap: 0.5rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-2-mobile { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-2-tablet { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-2-tablet-only { + --columnGap: 0.5rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-2-touch { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-2-desktop { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-2-desktop-only { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-2-widescreen { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-2-widescreen-only { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-2-fullhd { + --columnGap: 0.5rem; + } +} +.columns.is-variable.is-3 { + --columnGap: 0.75rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-3-mobile { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-3-tablet { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-3-tablet-only { + --columnGap: 0.75rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-3-touch { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-3-desktop { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-3-desktop-only { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-3-widescreen { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-3-widescreen-only { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-3-fullhd { + --columnGap: 0.75rem; + } +} +.columns.is-variable.is-4 { + --columnGap: 1rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-4-mobile { + --columnGap: 1rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-4-tablet { + --columnGap: 1rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-4-tablet-only { + --columnGap: 1rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-4-touch { + --columnGap: 1rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-4-desktop { + --columnGap: 1rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-4-desktop-only { + --columnGap: 1rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-4-widescreen { + --columnGap: 1rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-4-widescreen-only { + --columnGap: 1rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-4-fullhd { + --columnGap: 1rem; + } +} +.columns.is-variable.is-5 { + --columnGap: 1.25rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-5-mobile { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-5-tablet { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-5-tablet-only { + --columnGap: 1.25rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-5-touch { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-5-desktop { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-5-desktop-only { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-5-widescreen { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-5-widescreen-only { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-5-fullhd { + --columnGap: 1.25rem; + } +} +.columns.is-variable.is-6 { + --columnGap: 1.5rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-6-mobile { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-6-tablet { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-6-tablet-only { + --columnGap: 1.5rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-6-touch { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-6-desktop { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-6-desktop-only { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-6-widescreen { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-6-widescreen-only { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-6-fullhd { + --columnGap: 1.5rem; + } +} +.columns.is-variable.is-7 { + --columnGap: 1.75rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-7-mobile { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-7-tablet { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-7-tablet-only { + --columnGap: 1.75rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-7-touch { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-7-desktop { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-7-desktop-only { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-7-widescreen { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-7-widescreen-only { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-7-fullhd { + --columnGap: 1.75rem; + } +} +.columns.is-variable.is-8 { + --columnGap: 2rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-8-mobile { + --columnGap: 2rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-8-tablet { + --columnGap: 2rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-8-tablet-only { + --columnGap: 2rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-8-touch { + --columnGap: 2rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-8-desktop { + --columnGap: 2rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-8-desktop-only { + --columnGap: 2rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-8-widescreen { + --columnGap: 2rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-8-widescreen-only { + --columnGap: 2rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-8-fullhd { + --columnGap: 2rem; + } +} +.tile { + align-items: stretch; + display: block; + flex-basis: 0; + flex-grow: 1; + flex-shrink: 1; + min-height: min-content; +} +.tile.is-ancestor { + margin-left: -0.75rem; + margin-right: -0.75rem; + margin-top: -0.75rem; +} +.tile.is-ancestor:last-child { + margin-bottom: -0.75rem; +} +.tile.is-ancestor:not(:last-child) { + margin-bottom: 0.75rem; +} +.tile.is-child { + margin: 0 !important; +} +.tile.is-parent { + padding: 0.75rem; +} +.tile.is-vertical { + flex-direction: column; +} +.tile.is-vertical > .tile.is-child:not(:last-child) { + margin-bottom: 1.5rem !important; +} +@media screen and (min-width: 769px), print { + .tile:not(.is-child) { + display: flex; + } + .tile.is-1 { + flex: none; + width: 8.33333%; + } + .tile.is-2 { + flex: none; + width: 16.66667%; + } + .tile.is-3 { + flex: none; + width: 25%; + } + .tile.is-4 { + flex: none; + width: 33.33333%; + } + .tile.is-5 { + flex: none; + width: 41.66667%; + } + .tile.is-6 { + flex: none; + width: 50%; + } + .tile.is-7 { + flex: none; + width: 58.33333%; + } + .tile.is-8 { + flex: none; + width: 66.66667%; + } + .tile.is-9 { + flex: none; + width: 75%; + } + .tile.is-10 { + flex: none; + width: 83.33333%; + } + .tile.is-11 { + flex: none; + width: 91.66667%; + } + .tile.is-12 { + flex: none; + width: 100%; + } +} +.hero { + align-items: stretch; + display: flex; + flex-direction: column; + justify-content: space-between; +} +.hero .navbar { + background: none; +} +.hero .tabs ul { + border-bottom: none; +} +.hero.is-white { + background-color: #fff; + color: #0a0a0a; +} +.hero.is-white a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-white strong { + color: inherit; +} +.hero.is-white .title { + color: #0a0a0a; +} +.hero.is-white .subtitle { + color: rgba(10,10,10,0.9); +} +.hero.is-white .subtitle a:not(.button), +.hero.is-white .subtitle strong { + color: #0a0a0a; +} +@media screen and (max-width: 1087px) { + .hero.is-white .navbar-menu { + background-color: #fff; + } +} +.hero.is-white .navbar-item, +.hero.is-white .navbar-link { + color: rgba(10,10,10,0.7); +} +.hero.is-white a.navbar-item:hover, +.hero.is-white .navbar-link:hover, +.hero.is-white a.navbar-item.is-active, +.hero.is-white .navbar-link.is-active { + background-color: #f2f2f2; + color: #0a0a0a; +} +.hero.is-white .tabs a { + color: #0a0a0a; + opacity: 0.9; +} +.hero.is-white .tabs a:hover { + opacity: 1; +} +.hero.is-white .tabs li.is-active a { + opacity: 1; +} +.hero.is-white .tabs.is-boxed a, +.hero.is-white .tabs.is-toggle a { + color: #0a0a0a; +} +.hero.is-white .tabs.is-boxed a:hover, +.hero.is-white .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-white .tabs.is-boxed li.is-active a, +.hero.is-white .tabs.is-toggle li.is-active a, +.hero.is-white .tabs.is-boxed li.is-active a:hover, +.hero.is-white .tabs.is-toggle li.is-active a:hover { + background-color: #0a0a0a; + border-color: #0a0a0a; + color: #fff; +} +.hero.is-white.is-bold { + background-image: linear-gradient(141deg, #e8e3e3 0%, #fff 71%, #fff 100%); +} +@media screen and (max-width: 768px) { + .hero.is-white.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #e8e3e3 0%, #fff 71%, #fff 100%); + } +} +.hero.is-black { + background-color: #0a0a0a; + color: #fff; +} +.hero.is-black a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-black strong { + color: inherit; +} +.hero.is-black .title { + color: #fff; +} +.hero.is-black .subtitle { + color: rgba(255,255,255,0.9); +} +.hero.is-black .subtitle a:not(.button), +.hero.is-black .subtitle strong { + color: #fff; +} +@media screen and (max-width: 1087px) { + .hero.is-black .navbar-menu { + background-color: #0a0a0a; + } +} +.hero.is-black .navbar-item, +.hero.is-black .navbar-link { + color: rgba(255,255,255,0.7); +} +.hero.is-black a.navbar-item:hover, +.hero.is-black .navbar-link:hover, +.hero.is-black a.navbar-item.is-active, +.hero.is-black .navbar-link.is-active { + background-color: #000; + color: #fff; +} +.hero.is-black .tabs a { + color: #fff; + opacity: 0.9; +} +.hero.is-black .tabs a:hover { + opacity: 1; +} +.hero.is-black .tabs li.is-active a { + opacity: 1; +} +.hero.is-black .tabs.is-boxed a, +.hero.is-black .tabs.is-toggle a { + color: #fff; +} +.hero.is-black .tabs.is-boxed a:hover, +.hero.is-black .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-black .tabs.is-boxed li.is-active a, +.hero.is-black .tabs.is-toggle li.is-active a, +.hero.is-black .tabs.is-boxed li.is-active a:hover, +.hero.is-black .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #0a0a0a; +} +.hero.is-black.is-bold { + background-image: linear-gradient(141deg, #000 0%, #0a0a0a 71%, #181616 100%); +} +@media screen and (max-width: 768px) { + .hero.is-black.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #000 0%, #0a0a0a 71%, #181616 100%); + } +} +.hero.is-light { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.hero.is-light a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-light strong { + color: inherit; +} +.hero.is-light .title { + color: rgba(0,0,0,0.7); +} +.hero.is-light .subtitle { + color: rgba(0,0,0,0.9); +} +.hero.is-light .subtitle a:not(.button), +.hero.is-light .subtitle strong { + color: rgba(0,0,0,0.7); +} +@media screen and (max-width: 1087px) { + .hero.is-light .navbar-menu { + background-color: #f5f5f5; + } +} +.hero.is-light .navbar-item, +.hero.is-light .navbar-link { + color: rgba(0,0,0,0.7); +} +.hero.is-light a.navbar-item:hover, +.hero.is-light .navbar-link:hover, +.hero.is-light a.navbar-item.is-active, +.hero.is-light .navbar-link.is-active { + background-color: #e8e8e8; + color: rgba(0,0,0,0.7); +} +.hero.is-light .tabs a { + color: rgba(0,0,0,0.7); + opacity: 0.9; +} +.hero.is-light .tabs a:hover { + opacity: 1; +} +.hero.is-light .tabs li.is-active a { + opacity: 1; +} +.hero.is-light .tabs.is-boxed a, +.hero.is-light .tabs.is-toggle a { + color: rgba(0,0,0,0.7); +} +.hero.is-light .tabs.is-boxed a:hover, +.hero.is-light .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-light .tabs.is-boxed li.is-active a, +.hero.is-light .tabs.is-toggle li.is-active a, +.hero.is-light .tabs.is-boxed li.is-active a:hover, +.hero.is-light .tabs.is-toggle li.is-active a:hover { + background-color: rgba(0,0,0,0.7); + border-color: rgba(0,0,0,0.7); + color: #f5f5f5; +} +.hero.is-light.is-bold { + background-image: linear-gradient(141deg, #dfd8d8 0%, #f5f5f5 71%, #fff 100%); +} +@media screen and (max-width: 768px) { + .hero.is-light.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #dfd8d8 0%, #f5f5f5 71%, #fff 100%); + } +} +.hero.is-dark { + background-color: #363636; + color: #fff; +} +.hero.is-dark a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-dark strong { + color: inherit; +} +.hero.is-dark .title { + color: #fff; +} +.hero.is-dark .subtitle { + color: rgba(255,255,255,0.9); +} +.hero.is-dark .subtitle a:not(.button), +.hero.is-dark .subtitle strong { + color: #fff; +} +@media screen and (max-width: 1087px) { + .hero.is-dark .navbar-menu { + background-color: #363636; + } +} +.hero.is-dark .navbar-item, +.hero.is-dark .navbar-link { + color: rgba(255,255,255,0.7); +} +.hero.is-dark a.navbar-item:hover, +.hero.is-dark .navbar-link:hover, +.hero.is-dark a.navbar-item.is-active, +.hero.is-dark .navbar-link.is-active { + background-color: #292929; + color: #fff; +} +.hero.is-dark .tabs a { + color: #fff; + opacity: 0.9; +} +.hero.is-dark .tabs a:hover { + opacity: 1; +} +.hero.is-dark .tabs li.is-active a { + opacity: 1; +} +.hero.is-dark .tabs.is-boxed a, +.hero.is-dark .tabs.is-toggle a { + color: #fff; +} +.hero.is-dark .tabs.is-boxed a:hover, +.hero.is-dark .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-dark .tabs.is-boxed li.is-active a, +.hero.is-dark .tabs.is-toggle li.is-active a, +.hero.is-dark .tabs.is-boxed li.is-active a:hover, +.hero.is-dark .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #363636; +} +.hero.is-dark.is-bold { + background-image: linear-gradient(141deg, #1f1a1a 0%, #363636 71%, #463f3f 100%); +} +@media screen and (max-width: 768px) { + .hero.is-dark.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #1f1a1a 0%, #363636 71%, #463f3f 100%); + } +} +.hero.is-primary { + background-color: #3273dc; + color: #fff; +} +.hero.is-primary a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-primary strong { + color: inherit; +} +.hero.is-primary .title { + color: #fff; +} +.hero.is-primary .subtitle { + color: rgba(255,255,255,0.9); +} +.hero.is-primary .subtitle a:not(.button), +.hero.is-primary .subtitle strong { + color: #fff; +} +@media screen and (max-width: 1087px) { + .hero.is-primary .navbar-menu { + background-color: #3273dc; + } +} +.hero.is-primary .navbar-item, +.hero.is-primary .navbar-link { + color: rgba(255,255,255,0.7); +} +.hero.is-primary a.navbar-item:hover, +.hero.is-primary .navbar-link:hover, +.hero.is-primary a.navbar-item.is-active, +.hero.is-primary .navbar-link.is-active { + background-color: #2366d1; + color: #fff; +} +.hero.is-primary .tabs a { + color: #fff; + opacity: 0.9; +} +.hero.is-primary .tabs a:hover { + opacity: 1; +} +.hero.is-primary .tabs li.is-active a { + opacity: 1; +} +.hero.is-primary .tabs.is-boxed a, +.hero.is-primary .tabs.is-toggle a { + color: #fff; +} +.hero.is-primary .tabs.is-boxed a:hover, +.hero.is-primary .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-primary .tabs.is-boxed li.is-active a, +.hero.is-primary .tabs.is-toggle li.is-active a, +.hero.is-primary .tabs.is-boxed li.is-active a:hover, +.hero.is-primary .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #3273dc; +} +.hero.is-primary.is-bold { + background-image: linear-gradient(141deg, #1576c6 0%, #3273dc 71%, #4266e5 100%); +} +@media screen and (max-width: 768px) { + .hero.is-primary.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #1576c6 0%, #3273dc 71%, #4266e5 100%); + } +} +.hero.is-link { + background-color: #3273dc; + color: #fff; +} +.hero.is-link a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-link strong { + color: inherit; +} +.hero.is-link .title { + color: #fff; +} +.hero.is-link .subtitle { + color: rgba(255,255,255,0.9); +} +.hero.is-link .subtitle a:not(.button), +.hero.is-link .subtitle strong { + color: #fff; +} +@media screen and (max-width: 1087px) { + .hero.is-link .navbar-menu { + background-color: #3273dc; + } +} +.hero.is-link .navbar-item, +.hero.is-link .navbar-link { + color: rgba(255,255,255,0.7); +} +.hero.is-link a.navbar-item:hover, +.hero.is-link .navbar-link:hover, +.hero.is-link a.navbar-item.is-active, +.hero.is-link .navbar-link.is-active { + background-color: #2366d1; + color: #fff; +} +.hero.is-link .tabs a { + color: #fff; + opacity: 0.9; +} +.hero.is-link .tabs a:hover { + opacity: 1; +} +.hero.is-link .tabs li.is-active a { + opacity: 1; +} +.hero.is-link .tabs.is-boxed a, +.hero.is-link .tabs.is-toggle a { + color: #fff; +} +.hero.is-link .tabs.is-boxed a:hover, +.hero.is-link .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-link .tabs.is-boxed li.is-active a, +.hero.is-link .tabs.is-toggle li.is-active a, +.hero.is-link .tabs.is-boxed li.is-active a:hover, +.hero.is-link .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #3273dc; +} +.hero.is-link.is-bold { + background-image: linear-gradient(141deg, #1576c6 0%, #3273dc 71%, #4266e5 100%); +} +@media screen and (max-width: 768px) { + .hero.is-link.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #1576c6 0%, #3273dc 71%, #4266e5 100%); + } +} +.hero.is-info { + background-color: #3298dc; + color: #fff; +} +.hero.is-info a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-info strong { + color: inherit; +} +.hero.is-info .title { + color: #fff; +} +.hero.is-info .subtitle { + color: rgba(255,255,255,0.9); +} +.hero.is-info .subtitle a:not(.button), +.hero.is-info .subtitle strong { + color: #fff; +} +@media screen and (max-width: 1087px) { + .hero.is-info .navbar-menu { + background-color: #3298dc; + } +} +.hero.is-info .navbar-item, +.hero.is-info .navbar-link { + color: rgba(255,255,255,0.7); +} +.hero.is-info a.navbar-item:hover, +.hero.is-info .navbar-link:hover, +.hero.is-info a.navbar-item.is-active, +.hero.is-info .navbar-link.is-active { + background-color: #238cd1; + color: #fff; +} +.hero.is-info .tabs a { + color: #fff; + opacity: 0.9; +} +.hero.is-info .tabs a:hover { + opacity: 1; +} +.hero.is-info .tabs li.is-active a { + opacity: 1; +} +.hero.is-info .tabs.is-boxed a, +.hero.is-info .tabs.is-toggle a { + color: #fff; +} +.hero.is-info .tabs.is-boxed a:hover, +.hero.is-info .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-info .tabs.is-boxed li.is-active a, +.hero.is-info .tabs.is-toggle li.is-active a, +.hero.is-info .tabs.is-boxed li.is-active a:hover, +.hero.is-info .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #3298dc; +} +.hero.is-info.is-bold { + background-image: linear-gradient(141deg, #159cc6 0%, #3298dc 71%, #4289e5 100%); +} +@media screen and (max-width: 768px) { + .hero.is-info.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #159cc6 0%, #3298dc 71%, #4289e5 100%); + } +} +.hero.is-success { + background-color: #48c774; + color: #fff; +} +.hero.is-success a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-success strong { + color: inherit; +} +.hero.is-success .title { + color: #fff; +} +.hero.is-success .subtitle { + color: rgba(255,255,255,0.9); +} +.hero.is-success .subtitle a:not(.button), +.hero.is-success .subtitle strong { + color: #fff; +} +@media screen and (max-width: 1087px) { + .hero.is-success .navbar-menu { + background-color: #48c774; + } +} +.hero.is-success .navbar-item, +.hero.is-success .navbar-link { + color: rgba(255,255,255,0.7); +} +.hero.is-success a.navbar-item:hover, +.hero.is-success .navbar-link:hover, +.hero.is-success a.navbar-item.is-active, +.hero.is-success .navbar-link.is-active { + background-color: #3abb67; + color: #fff; +} +.hero.is-success .tabs a { + color: #fff; + opacity: 0.9; +} +.hero.is-success .tabs a:hover { + opacity: 1; +} +.hero.is-success .tabs li.is-active a { + opacity: 1; +} +.hero.is-success .tabs.is-boxed a, +.hero.is-success .tabs.is-toggle a { + color: #fff; +} +.hero.is-success .tabs.is-boxed a:hover, +.hero.is-success .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-success .tabs.is-boxed li.is-active a, +.hero.is-success .tabs.is-toggle li.is-active a, +.hero.is-success .tabs.is-boxed li.is-active a:hover, +.hero.is-success .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #48c774; +} +.hero.is-success.is-bold { + background-image: linear-gradient(141deg, #29b342 0%, #48c774 71%, #56d296 100%); +} +@media screen and (max-width: 768px) { + .hero.is-success.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #29b342 0%, #48c774 71%, #56d296 100%); + } +} +.hero.is-warning { + background-color: #ffdd57; + color: rgba(0,0,0,0.7); +} +.hero.is-warning a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-warning strong { + color: inherit; +} +.hero.is-warning .title { + color: rgba(0,0,0,0.7); +} +.hero.is-warning .subtitle { + color: rgba(0,0,0,0.9); +} +.hero.is-warning .subtitle a:not(.button), +.hero.is-warning .subtitle strong { + color: rgba(0,0,0,0.7); +} +@media screen and (max-width: 1087px) { + .hero.is-warning .navbar-menu { + background-color: #ffdd57; + } +} +.hero.is-warning .navbar-item, +.hero.is-warning .navbar-link { + color: rgba(0,0,0,0.7); +} +.hero.is-warning a.navbar-item:hover, +.hero.is-warning .navbar-link:hover, +.hero.is-warning a.navbar-item.is-active, +.hero.is-warning .navbar-link.is-active { + background-color: #ffd83d; + color: rgba(0,0,0,0.7); +} +.hero.is-warning .tabs a { + color: rgba(0,0,0,0.7); + opacity: 0.9; +} +.hero.is-warning .tabs a:hover { + opacity: 1; +} +.hero.is-warning .tabs li.is-active a { + opacity: 1; +} +.hero.is-warning .tabs.is-boxed a, +.hero.is-warning .tabs.is-toggle a { + color: rgba(0,0,0,0.7); +} +.hero.is-warning .tabs.is-boxed a:hover, +.hero.is-warning .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-warning .tabs.is-boxed li.is-active a, +.hero.is-warning .tabs.is-toggle li.is-active a, +.hero.is-warning .tabs.is-boxed li.is-active a:hover, +.hero.is-warning .tabs.is-toggle li.is-active a:hover { + background-color: rgba(0,0,0,0.7); + border-color: rgba(0,0,0,0.7); + color: #ffdd57; +} +.hero.is-warning.is-bold { + background-image: linear-gradient(141deg, #ffae24 0%, #ffdd57 71%, #fffa71 100%); +} +@media screen and (max-width: 768px) { + .hero.is-warning.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #ffae24 0%, #ffdd57 71%, #fffa71 100%); + } +} +.hero.is-danger { + background-color: #f14668; + color: #fff; +} +.hero.is-danger a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-danger strong { + color: inherit; +} +.hero.is-danger .title { + color: #fff; +} +.hero.is-danger .subtitle { + color: rgba(255,255,255,0.9); +} +.hero.is-danger .subtitle a:not(.button), +.hero.is-danger .subtitle strong { + color: #fff; +} +@media screen and (max-width: 1087px) { + .hero.is-danger .navbar-menu { + background-color: #f14668; + } +} +.hero.is-danger .navbar-item, +.hero.is-danger .navbar-link { + color: rgba(255,255,255,0.7); +} +.hero.is-danger a.navbar-item:hover, +.hero.is-danger .navbar-link:hover, +.hero.is-danger a.navbar-item.is-active, +.hero.is-danger .navbar-link.is-active { + background-color: #ef2e55; + color: #fff; +} +.hero.is-danger .tabs a { + color: #fff; + opacity: 0.9; +} +.hero.is-danger .tabs a:hover { + opacity: 1; +} +.hero.is-danger .tabs li.is-active a { + opacity: 1; +} +.hero.is-danger .tabs.is-boxed a, +.hero.is-danger .tabs.is-toggle a { + color: #fff; +} +.hero.is-danger .tabs.is-boxed a:hover, +.hero.is-danger .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-danger .tabs.is-boxed li.is-active a, +.hero.is-danger .tabs.is-toggle li.is-active a, +.hero.is-danger .tabs.is-boxed li.is-active a:hover, +.hero.is-danger .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #f14668; +} +.hero.is-danger.is-bold { + background-image: linear-gradient(141deg, #fa0a61 0%, #f14668 71%, #f7595f 100%); +} +@media screen and (max-width: 768px) { + .hero.is-danger.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #fa0a61 0%, #f14668 71%, #f7595f 100%); + } +} +.hero.is-grey-lightest { + background-color: #ededed; + color: #363636; +} +.hero.is-grey-lightest a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-grey-lightest strong { + color: inherit; +} +.hero.is-grey-lightest .title { + color: #363636; +} +.hero.is-grey-lightest .subtitle { + color: rgba(54,54,54,0.9); +} +.hero.is-grey-lightest .subtitle a:not(.button), +.hero.is-grey-lightest .subtitle strong { + color: #363636; +} +@media screen and (max-width: 1087px) { + .hero.is-grey-lightest .navbar-menu { + background-color: #ededed; + } +} +.hero.is-grey-lightest .navbar-item, +.hero.is-grey-lightest .navbar-link { + color: rgba(54,54,54,0.7); +} +.hero.is-grey-lightest a.navbar-item:hover, +.hero.is-grey-lightest .navbar-link:hover, +.hero.is-grey-lightest a.navbar-item.is-active, +.hero.is-grey-lightest .navbar-link.is-active { + background-color: #e0e0e0; + color: #363636; +} +.hero.is-grey-lightest .tabs a { + color: #363636; + opacity: 0.9; +} +.hero.is-grey-lightest .tabs a:hover { + opacity: 1; +} +.hero.is-grey-lightest .tabs li.is-active a { + opacity: 1; +} +.hero.is-grey-lightest .tabs.is-boxed a, +.hero.is-grey-lightest .tabs.is-toggle a { + color: #363636; +} +.hero.is-grey-lightest .tabs.is-boxed a:hover, +.hero.is-grey-lightest .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-grey-lightest .tabs.is-boxed li.is-active a, +.hero.is-grey-lightest .tabs.is-toggle li.is-active a, +.hero.is-grey-lightest .tabs.is-boxed li.is-active a:hover, +.hero.is-grey-lightest .tabs.is-toggle li.is-active a:hover { + background-color: #363636; + border-color: #363636; + color: #ededed; +} +.hero.is-grey-lightest.is-bold { + background-image: linear-gradient(141deg, #d8cfcf 0%, #ededed 71%, #faf9f9 100%); +} +@media screen and (max-width: 768px) { + .hero.is-grey-lightest.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #d8cfcf 0%, #ededed 71%, #faf9f9 100%); + } +} +.hero.is-small .hero-body { + padding-bottom: 1.5rem; + padding-top: 1.5rem; +} +@media screen and (min-width: 769px), print { + .hero.is-medium .hero-body { + padding-bottom: 9rem; + padding-top: 9rem; + } +} +@media screen and (min-width: 769px), print { + .hero.is-large .hero-body { + padding-bottom: 18rem; + padding-top: 18rem; + } +} +.hero.is-halfheight .hero-body, +.hero.is-fullheight .hero-body, +.hero.is-fullheight-with-navbar .hero-body { + align-items: center; + display: flex; +} +.hero.is-halfheight .hero-body > .container, +.hero.is-fullheight .hero-body > .container, +.hero.is-fullheight-with-navbar .hero-body > .container { + flex-grow: 1; + flex-shrink: 1; +} +.hero.is-halfheight { + min-height: 50vh; +} +.hero.is-fullheight { + min-height: 100vh; +} +.hero-video { + overflow: hidden; +} +.hero-video video { + left: 50%; + min-height: 100%; + min-width: 100%; + position: absolute; + top: 50%; + transform: translate3d(-50%, -50%, 0); +} +.hero-video.is-transparent { + opacity: 0.3; +} +@media screen and (max-width: 768px) { + .hero-video { + display: none; + } +} +.hero-buttons { + margin-top: 1.5rem; +} +@media screen and (max-width: 768px) { + .hero-buttons .button { + display: flex; + } + .hero-buttons .button:not(:last-child) { + margin-bottom: 0.75rem; + } +} +@media screen and (min-width: 769px), print { + .hero-buttons { + display: flex; + justify-content: center; + } + .hero-buttons .button:not(:last-child) { + margin-right: 1.5rem; + } +} +.hero-head, +.hero-foot { + flex-grow: 0; + flex-shrink: 0; +} +.hero-body { + flex-grow: 1; + flex-shrink: 0; + padding: 3rem 1.5rem; +} +.section { + padding: 3rem 1.5rem; +} +@media screen and (min-width: 1088px) { + .section.is-medium { + padding: 9rem 1.5rem; + } + .section.is-large { + padding: 18rem 1.5rem; + } +} +.footer { + background-color: #fff; + padding: 3rem 1.5rem 6rem; +} +html { + height: 100%; + -webkit-text-size-adjust: 100%; + -moz-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + text-size-adjust: 100%; +} +body { + min-height: 100%; + display: flex; + flex-direction: column; +} +body > .section { + flex-grow: 1; +} +@media screen and (min-width: 1088px) { + ::-webkit-scrollbar { + width: 8px; + height: 8px; + } + ::-webkit-scrollbar-track { + border-radius: 3px; + background: rgba(0,0,0,0.06); + box-shadow: inset 0 0 5px rgba(0,0,0,0.1); + } + ::-webkit-scrollbar-thumb { + border-radius: 3px; + background: rgba(0,0,0,0.12); + box-shadow: inset 0 0 10px rgba(0,0,0,0.2); + } + ::-webkit-scrollbar-thumb:hover { + background: rgba(0,0,0,0.24); + } +} +.ml-0, +.mx-0 { + margin-left: 0 !important; +} +.mr-0, +.mx-0 { + margin-right: 0 !important; +} +.ml-n0, +.mx-n0 { + margin-left: 0 !important; +} +.mr-n0, +.mx-n0 { + margin-right: 0 !important; +} +.mt-0, +.my-0 { + margin-top: 0 !important; +} +.mb-0, +.my-0 { + margin-bottom: 0 !important; +} +.mt-n0, +.my-n0 { + margin-top: 0 !important; +} +.mb-n0, +.my-n0 { + margin-bottom: 0 !important; +} +.pl-0, +.px-0 { + padding-left: 0 !important; +} +.pr-0, +.px-0 { + padding-right: 0 !important; +} +.pl-n0, +.px-n0 { + padding-left: 0 !important; +} +.pr-n0, +.px-n0 { + padding-right: 0 !important; +} +.pt-0, +.py-0 { + padding-top: 0 !important; +} +.pb-0, +.py-0 { + padding-bottom: 0 !important; +} +.pt-n0, +.py-n0 { + padding-top: 0 !important; +} +.pb-n0, +.py-n0 { + padding-bottom: 0 !important; +} +.ml-1, +.mx-1 { + margin-left: 0.25rem !important; +} +.mr-1, +.mx-1, +.article-licensing .licensing-meta .icon { + margin-right: 0.25rem !important; +} +.ml-n1, +.mx-n1 { + margin-left: -0.25rem !important; +} +.mr-n1, +.mx-n1 { + margin-right: -0.25rem !important; +} +.mt-1, +.my-1 { + margin-top: 0.25rem !important; +} +.mb-1, +.my-1, +.article-licensing .licensing-title p:not(:last-child) { + margin-bottom: 0.25rem !important; +} +.mt-n1, +.my-n1 { + margin-top: -0.25rem !important; +} +.mb-n1, +.my-n1 { + margin-bottom: -0.25rem !important; +} +.pl-1, +.px-1 { + padding-left: 0.25rem !important; +} +.pr-1, +.px-1 { + padding-right: 0.25rem !important; +} +.pl-n1, +.px-n1 { + padding-left: -0.25rem !important; +} +.pr-n1, +.px-n1 { + padding-right: -0.25rem !important; +} +.pt-1, +.py-1 { + padding-top: 0.25rem !important; +} +.pb-1, +.py-1 { + padding-bottom: 0.25rem !important; +} +.pt-n1, +.py-n1 { + padding-top: -0.25rem !important; +} +.pb-n1, +.py-n1 { + padding-bottom: -0.25rem !important; +} +.ml-2, +.mx-2 { + margin-left: 0.5rem !important; +} +.mr-2, +.mx-2 { + margin-right: 0.5rem !important; +} +.ml-n2, +.mx-n2 { + margin-left: -0.5rem !important; +} +.mr-n2, +.mx-n2 { + margin-right: -0.5rem !important; +} +.mt-2, +.my-2 { + margin-top: 0.5rem !important; +} +.mb-2, +.my-2 { + margin-bottom: 0.5rem !important; +} +.mt-n2, +.my-n2 { + margin-top: -0.5rem !important; +} +.mb-n2, +.my-n2 { + margin-bottom: -0.5rem !important; +} +.pl-2, +.px-2 { + padding-left: 0.5rem !important; +} +.pr-2, +.px-2 { + padding-right: 0.5rem !important; +} +.pl-n2, +.px-n2 { + padding-left: -0.5rem !important; +} +.pr-n2, +.px-n2 { + padding-right: -0.5rem !important; +} +.pt-2, +.py-2 { + padding-top: 0.5rem !important; +} +.pb-2, +.py-2 { + padding-bottom: 0.5rem !important; +} +.pt-n2, +.py-n2 { + padding-top: -0.5rem !important; +} +.pb-n2, +.py-n2 { + padding-bottom: -0.5rem !important; +} +.ml-3, +.mx-3 { + margin-left: 1rem !important; +} +.mr-3, +.mx-3 { + margin-right: 1rem !important; +} +.ml-n3, +.mx-n3 { + margin-left: -1rem !important; +} +.mr-n3, +.mx-n3 { + margin-right: -1rem !important; +} +.mt-3, +.my-3 { + margin-top: 1rem !important; +} +.mb-3, +.my-3, +.article-licensing .licensing-title { + margin-bottom: 1rem !important; +} +.mt-n3, +.my-n3 { + margin-top: -1rem !important; +} +.mb-n3, +.my-n3 { + margin-bottom: -1rem !important; +} +.pl-3, +.px-3 { + padding-left: 1rem !important; +} +.pr-3, +.px-3 { + padding-right: 1rem !important; +} +.pl-n3, +.px-n3 { + padding-left: -1rem !important; +} +.pr-n3, +.px-n3 { + padding-right: -1rem !important; +} +.pt-3, +.py-3 { + padding-top: 1rem !important; +} +.pb-3, +.py-3 { + padding-bottom: 1rem !important; +} +.pt-n3, +.py-n3 { + padding-top: -1rem !important; +} +.pb-n3, +.py-n3 { + padding-bottom: -1rem !important; +} +.ml-4, +.mx-4 { + margin-left: 1.5rem !important; +} +.mr-4, +.mx-4, +.article-licensing .licensing-meta .level-item { + margin-right: 1.5rem !important; +} +.ml-n4, +.mx-n4 { + margin-left: -1.5rem !important; +} +.mr-n4, +.mx-n4 { + margin-right: -1.5rem !important; +} +.mt-4, +.my-4 { + margin-top: 1.5rem !important; +} +.mb-4, +.my-4 { + margin-bottom: 1.5rem !important; +} +.mt-n4, +.my-n4 { + margin-top: -1.5rem !important; +} +.mb-n4, +.my-n4 { + margin-bottom: -1.5rem !important; +} +.pl-4, +.px-4 { + padding-left: 1.5rem !important; +} +.pr-4, +.px-4 { + padding-right: 1.5rem !important; +} +.pl-n4, +.px-n4 { + padding-left: -1.5rem !important; +} +.pr-n4, +.px-n4 { + padding-right: -1.5rem !important; +} +.pt-4, +.py-4 { + padding-top: 1.5rem !important; +} +.pb-4, +.py-4 { + padding-bottom: 1.5rem !important; +} +.pt-n4, +.py-n4 { + padding-top: -1.5rem !important; +} +.pb-n4, +.py-n4 { + padding-bottom: -1.5rem !important; +} +.ml-5, +.mx-5 { + margin-left: 3rem !important; +} +.mr-5, +.mx-5 { + margin-right: 3rem !important; +} +.ml-n5, +.mx-n5 { + margin-left: -3rem !important; +} +.mr-n5, +.mx-n5 { + margin-right: -3rem !important; +} +.mt-5, +.my-5 { + margin-top: 3rem !important; +} +.mb-5, +.my-5 { + margin-bottom: 3rem !important; +} +.mt-n5, +.my-n5 { + margin-top: -3rem !important; +} +.mb-n5, +.my-n5 { + margin-bottom: -3rem !important; +} +.pl-5, +.px-5 { + padding-left: 3rem !important; +} +.pr-5, +.px-5 { + padding-right: 3rem !important; +} +.pl-n5, +.px-n5 { + padding-left: -3rem !important; +} +.pr-n5, +.px-n5 { + padding-right: -3rem !important; +} +.pt-5, +.py-5 { + padding-top: 3rem !important; +} +.pb-5, +.py-5 { + padding-bottom: 3rem !important; +} +.pt-n5, +.py-n5 { + padding-top: -3rem !important; +} +.pb-n5, +.py-n5 { + padding-bottom: -3rem !important; +} +.ml-auto, +.mx-auto { + margin-left: auto !important; +} +.mr-auto, +.mx-auto { + margin-right: auto !important; +} +.mt-auto, +.my-auto { + margin-top: auto !important; +} +.mb-auto, +.my-auto { + margin-bottom: auto !important; +} +.pl-auto, +.px-auto { + margin-left: auto !important; +} +.pr-auto, +.px-auto { + margin-right: auto !important; +} +.pt-auto, +.py-auto { + margin-top: auto !important; +} +.pb-auto, +.py-auto { + margin-bottom: auto !important; +} +.order-0 { + order: 0 !important; +} +.order-1 { + order: 1 !important; +} +.order-2 { + order: 2 !important; +} +.order-3 { + order: 3 !important; +} +.order-4 { + order: 4 !important; +} +.order-5 { + order: 5 !important; +} +.justify-content-start { + justify-content: start !important; +} +.justify-content-center { + justify-content: center !important; +} +.flex-shrink-1 { + flex-shrink: 1 !important; +} +.link-muted { + color: inherit; +} +.link-muted:hover { + color: #3273dc !important; +} +.image.is-7by3 { + padding-top: 42.8%; +} +.image.is-7by3 img { + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; +} +.image .avatar { + height: 100%; + object-fit: cover; +} +.image .fill { + object-fit: cover; + width: 100% !important; + height: 100% !important; +} +.button.is-transparent { + color: inherit; + background: transparent; + border-color: transparent; +} +.card { + overflow: visible; + border-radius: 4px; +} +.card + .card, +.card + .column-right-shadow { + margin-top: 1.5rem; +} +.card .card-image { + overflow: hidden; + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} +.card .media + .media { + border: none; + margin-top: 0; +} +article.media { + color: #7a7a7a; +} +article.media a { + color: inherit; +} +article.media a:hover { + color: #3273dc; +} +article.media .image { + width: 64px; + height: 64px; +} +article.media .image img { + object-fit: cover; + width: 100%; + height: 100%; +} +article.media .title { + margin-bottom: 0.25em; +} +article.media .media-content { + color: #7a7a7a; +} +article.media .media-content .title { + margin: 0; + line-height: inherit; +} +article.article .article-meta, +article.article .article-tags { + color: #7a7a7a; +} +article.article .article-meta { + overflow-x: auto; + margin-bottom: 0.5rem; +} +article.article .content { + word-wrap: break-word; + font-size: 1.1rem; +} +article.article .content h1 { + font-size: 1.75em; +} +article.article .content h2 { + font-size: 1.5em; +} +article.article .content h3 { + font-size: 1.25em; +} +article.article .content h4 { + font-size: 1.125em; +} +article.article .content h5 { + font-size: 1em; +} +article.article .content pre { + font-size: 0.85em; +} +article.article .content code { + padding: 0; + background: transparent; + overflow-wrap: break-word; +} +article.article .content blockquote.pullquote { + float: right; + max-width: 50%; + font-size: 1.15rem; + position: relative; +} +article.article .content blockquote footer strong + cite { + margin-left: 0.5em; +} +article.article .content .message.message-immersive { + border-radius: 0; + margin: 0 -1.5rem 1.5rem -1.5rem; +} +article.article .content .message.message-immersive .message-body { + border: none; +} +.rtl { + direction: rtl; +} +.rtl .level .level-item:not(:last-child), +.rtl .level.is-mobile .level-item:not(:last-child) { + margin-left: 0.75rem; + margin-right: 0; +} +.table-overflow { + overflow-x: auto; +} +.table-overflow table { + width: auto !important; +} +.table-overflow table th { + word-break: keep-all; +} +.video-container { + position: relative; + padding-bottom: 56.25%; + padding-top: 25px; + height: 0; +} +.video-container iframe { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.article-licensing { + position: relative; + z-index: 1; + box-shadow: none; + background: #f5f5f5; + border-radius: 4px; + overflow: hidden; +} +.article-licensing:after { + position: absolute; + z-index: -1; + right: -50px; + top: -87.87px; + content: '\f25e'; + font-size: 200px; + font-family: 'Font Awesome 5 Brands'; + opacity: 0.1; +} +.article-licensing .level-left { + flex-wrap: wrap; + max-width: 100%; +} +.article-licensing .licensing-title { + line-height: 1.2; +} +.article-licensing .licensing-meta .icon { + width: 1.2em; + height: 1.2em; + font-size: 1.2em; + vertical-align: bottom; +} +.article-licensing .licensing-meta a { + color: inherit; +} +a.article-nav-prev span { + text-align: left; + flex-shrink: 1; + word-wrap: break-word; + white-space: normal; +} +a.article-nav-next span { + text-align: right; + flex-shrink: 1; + word-wrap: break-word; + white-space: normal; +} +.navbar-main { + box-shadow: 0 4px 10px rgba(0,0,0,0.05); +} +.navbar-main .navbar-menu, +.navbar-main .navbar-start, +.navbar-main .navbar-end { + align-items: stretch; + display: flex; + padding: 0; + flex-shrink: 0; +} +.navbar-main .navbar-menu { + flex-grow: 1; + flex-shrink: 0; + overflow-x: auto; +} +.navbar-main .navbar-start { + justify-content: flex-start; + margin-right: auto; +} +.navbar-main .navbar-end { + justify-content: flex-end; + margin-left: auto; +} +.navbar-main .navbar-item { + display: flex; + align-items: center; + padding: 1.25rem 0.75rem; + margin: 0 0; +} +.navbar-main .navbar-item.is-active { + background-color: transparent; +} +@media screen and (max-width: 1087px) { + .navbar-main .navbar-menu { + justify-content: center; + box-shadow: none; + } + .navbar-main .navbar-start { + margin-right: 0; + } + .navbar-main .navbar-end { + margin-left: 0; + } +} +.navbar-logo img { + max-height: 1.75rem; +} +@media screen and (max-width: 768px) { + footer.footer .level-start { + text-align: center; + } +} +footer.footer .level-end .field { + flex-wrap: wrap; + align-items: center; +} +@media screen and (max-width: 768px) { + footer.footer .level-end .field { + justify-content: center; + margin-top: 1rem; + } +} +.footer-logo img { + max-height: 1.75rem; +} +.pagination { + margin-top: 1.5rem; +} +.pagination .pagination-link a, +.pagination .pagination-ellipsis a, +.pagination .pagination-previous a, +.pagination .pagination-next a { + color: #363636; +} +.pagination .pagination-link, +.pagination .pagination-previous, +.pagination .pagination-next { + border: none; + background: #fff; + box-shadow: 0 4px 10px rgba(0,0,0,0.05), 0 0 1px rgba(0,0,0,0.1); +} +.pagination .pagination-link.is-current { + background: #3273dc; +} +.post-navigation { + color: #7a7a7a; + flex-wrap: wrap; + justify-content: space-around; +} +.post-navigation .level-item { + margin-bottom: 0; +} +.timeline { + margin-left: 1rem; + padding: 1rem 0 0 1.5rem; + border-left: 1px solid #dbdbdb; +} +.timeline .media { + position: relative; +} +.timeline .media:before, +.timeline .media:last-child:after { + content: ''; + display: block; + position: absolute; + left: calc(-0.375rem - 1.5rem - 0.25px); +} +.timeline .media:before { + width: 0.75rem; + height: 0.75rem; + top: calc(1rem + 1.5 * 0.85rem / 2 - 0.75rem / 2); + background: #dbdbdb; + border-radius: 50%; +} +.timeline .media:first-child:before { + top: calc(1.5 * 0.85rem / 2 - 0.75rem / 2); +} +.timeline .media:last-child:after { + width: 0.75rem; + top: calc(1rem + 1.5 * 0.85rem / 2 + 0.75rem / 2); + bottom: 0; + background: #fff; +} +.timeline .media:first-child:last-child:after { + top: calc(1.5 * 0.85rem / 2 + 0.75rem / 2); +} +.searchbox { + display: none; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 100; + font-size: 1rem; + line-height: 0; + background: rgba(10,10,10,0.86); +} +.searchbox.show { + display: flex; +} +.searchbox a, +.searchbox a:hover { + color: inherit; + text-decoration: none; +} +.searchbox input { + font-size: 1rem; + border: none; + outline: none; + box-shadow: none; + border-radius: 0; +} +.searchbox, +.searchbox .searchbox-container { + position: fixed; + align-items: center; + flex-direction: column; + line-height: 1.25em; +} +.searchbox .searchbox-container { + z-index: 101; + display: flex; + overflow: hidden; + box-shadow: 0 4px 10px rgba(0,0,0,0.05), 0 0 1px rgba(0,0,0,0.1); + border-radius: 4px; + background-color: #f5f5f5; + width: 540px; + top: 100px; + bottom: 100px; +} +.searchbox .searchbox-header, +.searchbox .searchbox-body, +.searchbox .searchbox-footer { + width: 100%; +} +.searchbox .searchbox-header { + display: flex; + flex-direction: row; + line-height: 1.5em; + font-weight: normal; + background-color: #fff; + min-height: 3rem; +} +.searchbox .searchbox-input-container { + display: flex; + flex-grow: 1; +} +.searchbox .searchbox-input { + flex-grow: 1; + color: inherit; + box-sizing: border-box; + padding: 0.75em 0 0.75em 1.25em; + background: #fff; +} +.searchbox .searchbox-close { + display: inline-block; + font-size: 1.5em; + padding: 0.5em 0.75em; + cursor: pointer; +} +.searchbox .searchbox-close:hover { + background: #f5f5f5; +} +.searchbox .searchbox-close:active { + background: #dbdbdb; +} +.searchbox .searchbox-body { + flex-grow: 1; + overflow-y: auto; + border-top: 1px solid #dbdbdb; +} +.searchbox .searchbox-result-section header, +.searchbox .searchbox-result-item { + padding: 0.75em 1em; +} +.searchbox .searchbox-result-section { + border-bottom: 1px solid #dbdbdb; +} +.searchbox .searchbox-result-section header { + color: #b5b5b5; +} +.searchbox .searchbox-result-item { + display: flex; + flex-direction: row; +} +.searchbox .searchbox-result-item:not(.disabled):not(.active):not(:active):hover { + background-color: #fff; +} +.searchbox .searchbox-result-item:active, +.searchbox .searchbox-result-item.active { + color: #fff; + background-color: #3273dc; +} +.searchbox .searchbox-result-item em { + font-style: normal; + background: #ffdd57; +} +.searchbox .searchbox-result-icon { + margin-right: 1em; +} +.searchbox .searchbox-result-content { + overflow: hidden; +} +.searchbox .searchbox-result-title, +.searchbox .searchbox-result-preview { + display: block; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.searchbox .searchbox-result-title-secondary { + color: #b5b5b5; +} +.searchbox .searchbox-result-preview { + margin-top: 0.25em; +} +.searchbox .searchbox-result-item:not(:active):not(.active) .searchbox-result-preview { + color: #b5b5b5; +} +.searchbox .searchbox-footer { + padding: 0.5em 1em; +} +.searchbox .searchbox-pagination { + margin: 0; + padding: 0; + list-style: none; + text-align: center; +} +.searchbox .searchbox-pagination .searchbox-pagination-item { + margin: 0 0.25rem; +} +.searchbox .searchbox-pagination .searchbox-pagination-item, +.searchbox .searchbox-pagination .searchbox-pagination-link { + display: inline-block; +} +.searchbox .searchbox-pagination .searchbox-pagination-link { + overflow: hidden; + padding: 0.5em 0.8em; + box-shadow: 0 4px 10px rgba(0,0,0,0.05), 0 0 1px rgba(0,0,0,0.1); + border-radius: 4px; + background-color: #fff; +} +.searchbox .searchbox-pagination .searchbox-pagination-item.active .searchbox-pagination-link { + color: #fff; + background-color: #3273dc; +} +.searchbox .searchbox-pagination .searchbox-pagination-item.disabled .searchbox-pagination-link { + cursor: not-allowed; + background-color: #f5f5f5; +} +.searchbox .searchbox-pagination .searchbox-pagination-item:not(.active):not(.disabled) .searchbox-pagination-link:hover { + background-color: #f5f5f5; +} +@media screen and (max-width: 559px), screen and (max-height: 479px) { + .searchbox .searchbox-container { + top: 0; + left: 0; + width: 100%; + height: 100%; + border-radius: 0; + } +} +figure.highlight { + padding: 0; + width: 100%; + position: relative; + margin: 1em 0 1em !important; + border-radius: 4px; +} +figure.highlight.folded .highlight-body { + height: 0; +} +figure.highlight .copy { + opacity: 0.7; +} +figure.highlight pre, +figure.highlight table tr:hover { + color: inherit; + background: transparent; +} +figure.highlight table { + width: auto; +} +figure.highlight table tr td { + border: none; +} +figure.highlight table tr:not(:first-child) td { + padding-top: 0; +} +figure.highlight table tr:not(:last-child) td { + padding-bottom: 0; +} +figure.highlight pre { + padding: 0; + overflow: visible; +} +figure.highlight pre .line, +figure.highlight pre code .hljs { + line-height: 1.5rem; +} +figure.highlight figcaption, +figure.highlight .gutter { + background: rgba(200,200,200,0.15); +} +figure.highlight figcaption { + margin: 0 !important; + padding: 0.3em 0em 0.3em 0.75em; + font-style: normal; + font-size: 0.8em; +} +figure.highlight figcaption * { + color: inherit; +} +figure.highlight figcaption span { + font-weight: 500; + font-family: 'Source Code Pro', monospace, 'Microsoft YaHei'; +} +figure.highlight figcaption .level-left *:not(:last-child) { + margin-right: 0.5em; +} +figure.highlight figcaption .level-right *:not(:first-child) { + margin-left: 0.5em; +} +figure.highlight figcaption .fold { + cursor: pointer; +} +figure.highlight figcaption.level { + overflow: auto; +} +figure.highlight figcaption.level .level-right a { + padding: 0em 0.75em; +} +figure.highlight .highlight-body { + overflow: auto; +} +figure.highlight .gutter { + text-align: right; +} +figure.highlight .tag, +figure.highlight .title, +figure.highlight .number, +figure.highlight .section { + display: inherit; + font: inherit; + margin: inherit; + padding: inherit; + background: inherit; + height: inherit; + text-align: inherit; + vertical-align: inherit; + min-width: inherit; + border-radius: inherit; +} +.gist table tr:hover { + background: transparent; +} +.gist table td { + border: none; +} +.gist .file { + all: initial; +} +.widget .menu-list li ul { + margin-right: 0; +} +.widget .menu-list .level { + margin-bottom: 0; +} +.widget .menu-list .level .level-left, +.widget .menu-list .level .level-right, +.widget .menu-list .level .level-item { + flex-shrink: 1; +} +.widget .menu-list .level .level-left, +.widget .menu-list .level .level-right { + align-items: flex-start; +} +.widget .menu-list .tag { + background: $light-grey; + color: $white-invert; +} +.widget .tags .tag:first-child { + background: #3273dc; + color: #fff; +} +.widget .tags .tag:last-child { + background: $light-grey; + color: $white-invert; +} +.level.is-multiline { + flex-wrap: wrap; +} +.donate { + position: relative; +} +.donate .qrcode { + display: none; + position: absolute; + z-index: 99; + bottom: 2.5em; + line-height: 0; + overflow: hidden; + box-shadow: 0 4px 10px rgba(0,0,0,0.05), 0 0 1px rgba(0,0,0,0.1); + border-radius: 4px; +} +.donate .qrcode img { + max-width: 280px; +} +.donate:hover .qrcode { + display: block; +} +.donate:first-child:not(:last-child) .qrcode { + left: -0.75rem; +} +.donate:last-child:not(:first-child) .qrcode { + right: -0.75rem; +} +.donate[data-type="afdian"] { + color: #fff; + background-color: #885fd9; + border-color: transparent; +} +.donate[data-type="afdian"]:active { + background-color: #794ad4; +} +.donate[data-type="afdian"]:hover { + background-color: #8055d7; +} +.donate[data-type="afdian"]:focus:not(:active) { + box-shadow: 0 0 0 0.125em rgba(136,95,217,0.25); +} +.donate[data-type="alipay"] { + color: #fff; + background-color: #00a0e8; + border-color: transparent; +} +.donate[data-type="alipay"]:active { + background-color: #008ecf; +} +.donate[data-type="alipay"]:hover { + background-color: #0097db; +} +.donate[data-type="alipay"]:focus:not(:active) { + box-shadow: 0 0 0 0.125em rgba(0,160,232,0.25); +} +.donate[data-type="buymeacoffee"] { + color: rgba(0,0,0,0.7); + background-color: #fd0; + border-color: transparent; +} +.donate[data-type="buymeacoffee"]:active { + background-color: #e6c700; +} +.donate[data-type="buymeacoffee"]:hover { + background-color: #f2d200; +} +.donate[data-type="buymeacoffee"]:focus:not(:active) { + box-shadow: 0 0 0 0.125em rgba(255,221,0,0.25); +} +.donate[data-type="paypal"] { + color: rgba(0,0,0,0.7); + background-color: #feb700; + border-color: transparent; +} +.donate[data-type="paypal"]:active { + background-color: #e5a500; +} +.donate[data-type="paypal"]:hover { + background-color: #f1ae00; +} +.donate[data-type="paypal"]:focus:not(:active) { + box-shadow: 0 0 0 0.125em rgba(254,183,0,0.25); +} +.donate[data-type="patreon"] { + color: #fff; + background-color: #ff424d; + border-color: transparent; +} +.donate[data-type="patreon"]:active { + background-color: #ff2835; +} +.donate[data-type="patreon"]:hover { + background-color: #ff3541; +} +.donate[data-type="patreon"]:focus:not(:active) { + box-shadow: 0 0 0 0.125em rgba(255,66,77,0.25); +} +.donate[data-type="wechat"] { + color: #fff; + background-color: #1aad19; + border-color: transparent; +} +.donate[data-type="wechat"]:active { + background-color: #179716; +} +.donate[data-type="wechat"]:hover { + background-color: #18a217; +} +.donate[data-type="wechat"]:focus:not(:active) { + box-shadow: 0 0 0 0.125em rgba(26,173,25,0.25); +} +#back-to-top { + position: fixed; + opacity: 0; + outline: none; + padding: 8px 0; + line-height: 24px; + border-radius: 4px; + transform: translateY(120px); + transition: 0.4s ease opacity, 0.4s ease width, 0.4s ease transform, 0.4s ease border-radius; +} +#back-to-top.is-rounded { + border-radius: 50%; +} +#back-to-top.fade-in { + opacity: 1; +} +#back-to-top.rise-up { + transform: translateY(0); +} +.gallery-item .caption { + color: #7a7a7a; +} +@media screen and (max-width: 768px) { + #toc { + display: none; + position: fixed; + margin: 1rem; + left: 0; + right: 0; + bottom: 0; + z-index: 100; + max-height: calc(100vh - 2rem); + overflow-y: auto; + } + #toc-mask { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 99; + background: rgba(0,0,0,0.7); + } + #toc.is-active, + #toc-mask.is-active { + display: block; + } +} +.pace { + user-select: none; + pointer-events: none; +} +.pace .pace-progress { + top: 0; + right: 100%; + width: 100%; + height: 2px; + z-index: 2000; + position: fixed; + background: #3273dc; +} +.pace-inactive { + display: none; +} +.fa, +.fab, +.fal, +.far, +.fas { + line-height: inherit; +} +.MathJax, +.katex-display { + overflow-x: auto; + overflow-y: hidden; +} +.katex { + white-space: nowrap; +} +.katex-display { + margin-top: -1em !important; +} +.katex-html { + padding-top: 1em; +} +.katex-html .tag { + align-items: unset; + background-color: unset; + border-radius: unset; + color: unset; + display: unset; + font-size: unset; + height: unset; + justify-content: unset; + line-height: unset; + padding-left: unset; + padding-right: unset; + white-space: unset; +} +.cc-window, +.cc-revoke { + font-size: 1.1rem !important; + font-family: Ubuntu, Roboto, 'Open Sans', 'Microsoft YaHei', sans-serif !important; +} +.cc-window { + color: #4a4a4a !important; + background-color: #fff !important; +} +.cc-window.cc-floating { + border-radius: 4px; + box-shadow: 0 4px 10px rgba(0,0,0,0.05), 0 0 1px rgba(0,0,0,0.1); +} +.cc-window.cc-banner { + background-color: #f9f9f9 !important; +} +.cc-window.cc-theme-block .cc-compliance > .cc-btn, +.cc-window.cc-theme-classic .cc-compliance > .cc-btn { + border-radius: 290486px; +} +.cc-window .cc-compliance > .cc-btn { + font-weight: 400; + border: none; + color: #fff; + background-color: #3273dc; +} +.cc-window .cc-compliance > .cc-btn:hover, +.cc-window .cc-compliance > .cc-btn:focus { + background-color: #276cda; +} +.cc-window .cc-compliance > .cc-btn.cc-deny:hover { + color: #3273dc; + text-decoration: none; +} +.cc-revoke { + padding: 0.5rem 1rem !important; + color: #fff !important; + background-color: #3273dc !important; +} +.cc-revoke:hover { + text-decoration: none !important; + background-color: #276cda; +} +@media screen and (min-width: 1280px) { + .is-1-column .container, + .is-2-column .container { + max-width: 960px; + width: 960px; + } +} +@media screen and (min-width: 1472px) { + .is-2-column .container { + max-width: 1152px; + width: 1152px; + } + .is-1-column .container { + max-width: 960px; + width: 960px; + } +} +@media screen and (min-width: 769px), print { + .is-sticky { + position: -webkit-sticky; + position: sticky; + top: 1.5rem; + z-index: 99; + } + .column-main.is-sticky, + .column-left.is-sticky, + .column-right.is-sticky, + .column-right-shadow.is-sticky { + top: 0.75rem; + align-self: flex-start; + } +} +@media screen and (max-width: 768px) { + .section { + padding: 1.5rem 1rem; + } +} diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..245d44a --- /dev/null +++ b/css/style.css @@ -0,0 +1,11654 @@ +@-moz-keyframes spinAround { + from { + transform: rotate(0deg); + } + to { + transform: rotate(359deg); + } +} +@-webkit-keyframes spinAround { + from { + transform: rotate(0deg); + } + to { + transform: rotate(359deg); + } +} +@-o-keyframes spinAround { + from { + transform: rotate(0deg); + } + to { + transform: rotate(359deg); + } +} +@keyframes spinAround { + from { + transform: rotate(0deg); + } + to { + transform: rotate(359deg); + } +} +.is-unselectable, +.breadcrumb, +.modal-close, +.pagination-previous, +.pagination-next, +.pagination-link, +.pagination-ellipsis, +.tabs, +.button, +.delete, +.file { + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.navbar-link:not(.is-arrowless)::after, +.select:not(.is-multiple):not(.is-loading)::after { + border: 3px solid transparent; + border-radius: 2px; + border-right: 0; + border-top: 0; + content: " "; + display: block; + height: 0.625em; + margin-top: -0.4375em; + pointer-events: none; + position: absolute; + top: 50%; + transform: rotate(-45deg); + transform-origin: center; + width: 0.625em; +} +.breadcrumb:not(:last-child), +.level:not(:last-child), +.list:not(:last-child), +.message:not(:last-child), +.pagination:not(:last-child), +.tabs:not(:last-child), +.box:not(:last-child), +.content:not(:last-child), +.notification:not(:last-child), +.progress:not(:last-child), +.table:not(:last-child), +.table-container:not(:last-child), +.title:not(:last-child), +.subtitle:not(:last-child), +.block:not(:last-child), +.highlight:not(:last-child) { + margin-bottom: 1.5rem; +} +.modal-close, +.delete { + -moz-appearance: none; + -webkit-appearance: none; + background-color: rgba(10,10,10,0.2); + border: none; + border-radius: 290486px; + cursor: pointer; + pointer-events: auto; + display: inline-block; + flex-grow: 0; + flex-shrink: 0; + font-size: 0; + height: 20px; + max-height: 20px; + max-width: 20px; + min-height: 20px; + min-width: 20px; + outline: none; + position: relative; + vertical-align: top; + width: 20px; +} +.modal-close::before, +.delete::before, +.modal-close::after, +.delete::after { + background-color: #fff; + content: ""; + display: block; + left: 50%; + position: absolute; + top: 50%; + transform: translateX(-50%) translateY(-50%) rotate(45deg); + transform-origin: center center; +} +.modal-close::before, +.delete::before { + height: 2px; + width: 50%; +} +.modal-close::after, +.delete::after { + height: 50%; + width: 2px; +} +.modal-close:hover, +.delete:hover, +.modal-close:focus, +.delete:focus { + background-color: rgba(10,10,10,0.3); +} +.modal-close:active, +.delete:active { + background-color: rgba(10,10,10,0.4); +} +.modal-close.is-small, +.delete.is-small { + height: 16px; + max-height: 16px; + max-width: 16px; + min-height: 16px; + min-width: 16px; + width: 16px; +} +.modal-close.is-medium, +.delete.is-medium { + height: 24px; + max-height: 24px; + max-width: 24px; + min-height: 24px; + min-width: 24px; + width: 24px; +} +.modal-close.is-large, +.delete.is-large { + height: 32px; + max-height: 32px; + max-width: 32px; + min-height: 32px; + min-width: 32px; + width: 32px; +} +.button.is-loading::after, +.loader, +.select.is-loading::after, +.control.is-loading::after { + animation: spinAround 500ms infinite linear; + border: 2px solid #dbdbdb; + border-radius: 290486px; + border-right-color: transparent; + border-top-color: transparent; + content: ""; + display: block; + height: 1em; + position: relative; + width: 1em; +} +.is-overlay, +.modal, +.modal-background, +.image.is-square img, +.image.is-1by1 img, +.image.is-5by4 img, +.image.is-4by3 img, +.image.is-3by2 img, +.image.is-5by3 img, +.image.is-16by9 img, +.image.is-2by1 img, +.image.is-3by1 img, +.image.is-4by5 img, +.image.is-3by4 img, +.image.is-2by3 img, +.image.is-3by5 img, +.image.is-9by16 img, +.image.is-1by2 img, +.image.is-1by3 img, +.image.is-square .has-ratio, +.image.is-1by1 .has-ratio, +.image.is-5by4 .has-ratio, +.image.is-4by3 .has-ratio, +.image.is-3by2 .has-ratio, +.image.is-5by3 .has-ratio, +.image.is-16by9 .has-ratio, +.image.is-2by1 .has-ratio, +.image.is-3by1 .has-ratio, +.image.is-4by5 .has-ratio, +.image.is-3by4 .has-ratio, +.image.is-2by3 .has-ratio, +.image.is-3by5 .has-ratio, +.image.is-9by16 .has-ratio, +.image.is-1by2 .has-ratio, +.image.is-1by3 .has-ratio, +.hero-video { + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; +} +.pagination-previous, +.pagination-next, +.pagination-link, +.pagination-ellipsis, +.button, +.input, +.textarea, +.select select, +.file-cta, +.file-name { + -moz-appearance: none; + -webkit-appearance: none; + align-items: center; + border: 1px solid transparent; + border-radius: 4px; + box-shadow: none; + display: inline-flex; + font-size: 1rem; + height: 2.25em; + justify-content: flex-start; + line-height: 1.5; + padding-bottom: calc(0.5em - 1px); + padding-left: calc(0.75em - 1px); + padding-right: calc(0.75em - 1px); + padding-top: calc(0.5em - 1px); + position: relative; + vertical-align: top; +} +.pagination-previous:focus, +.pagination-next:focus, +.pagination-link:focus, +.pagination-ellipsis:focus, +.button:focus, +.input:focus, +.textarea:focus, +.select select:focus, +.file-cta:focus, +.file-name:focus, +.pagination-previous.is-focused, +.pagination-next.is-focused, +.pagination-link.is-focused, +.pagination-ellipsis.is-focused, +.button.is-focused, +.input.is-focused, +.textarea.is-focused, +.select select.is-focused, +.file-cta.is-focused, +.file-name.is-focused, +.pagination-previous:active, +.pagination-next:active, +.pagination-link:active, +.pagination-ellipsis:active, +.button:active, +.input:active, +.textarea:active, +.select select:active, +.file-cta:active, +.file-name:active, +.pagination-previous.is-active, +.pagination-next.is-active, +.pagination-link.is-active, +.pagination-ellipsis.is-active, +.button.is-active, +.input.is-active, +.textarea.is-active, +.select select.is-active, +.file-cta.is-active, +.file-name.is-active { + outline: none; +} +.pagination-previous[disabled], +.pagination-next[disabled], +.pagination-link[disabled], +.pagination-ellipsis[disabled], +.button[disabled], +.input[disabled], +.textarea[disabled], +.select select[disabled], +.file-cta[disabled], +.file-name[disabled], +fieldset[disabled] .pagination-previous, +fieldset[disabled] .pagination-next, +fieldset[disabled] .pagination-link, +fieldset[disabled] .pagination-ellipsis, +fieldset[disabled] .button, +fieldset[disabled] .input, +fieldset[disabled] .textarea, +fieldset[disabled] .select select, +fieldset[disabled] .file-cta, +fieldset[disabled] .file-name { + cursor: not-allowed; +} +/* minireset.css v0.0.6 | MIT License | github.com/jgthms/minireset.css */ +html, +body, +p, +ol, +ul, +li, +dl, +dt, +dd, +blockquote, +figure, +fieldset, +legend, +textarea, +pre, +iframe, +hr, +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 0; + padding: 0; +} +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: 100%; + font-weight: normal; +} +ul { + list-style: none; +} +button, +input, +select, +textarea { + margin: 0; +} +html { + box-sizing: border-box; +} +*, +*::before, +*::after { + box-sizing: inherit; +} +img, +video { + height: auto; + max-width: 100%; +} +iframe { + border: 0; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +td, +th { + padding: 0; +} +td:not([align]), +th:not([align]) { + text-align: left; +} +html { + background-color: #f7f7f7; + font-size: 14px; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + min-width: 300px; + overflow-x: hidden; + overflow-y: scroll; + text-rendering: optimizeLegibility; + text-size-adjust: 100%; +} +article, +aside, +figure, +footer, +header, +hgroup, +section { + display: block; +} +body, +button, +input, +select, +textarea { + font-family: Ubuntu, Roboto, 'Open Sans', 'Microsoft YaHei', sans-serif; +} +code, +pre { + -moz-osx-font-smoothing: auto; + -webkit-font-smoothing: auto; + font-family: 'Source Code Pro', monospace, 'Microsoft YaHei'; +} +body { + color: #4a4a4a; + font-size: 1em; + font-weight: 400; + line-height: 1.5; +} +a { + color: #3273dc; + cursor: pointer; + text-decoration: none; +} +a strong { + color: currentColor; +} +a:hover { + color: #363636; +} +code { + background-color: #f5f5f5; + color: #f14668; + font-size: 0.875em; + font-weight: normal; + padding: 0.25em 0.5em 0.25em; +} +hr { + background-color: #f5f5f5; + border: none; + display: block; + height: 2px; + margin: 1.5rem 0; +} +img { + height: auto; + max-width: 100%; +} +input[type="checkbox"], +input[type="radio"] { + vertical-align: baseline; +} +small { + font-size: 0.875em; +} +span { + font-style: inherit; + font-weight: inherit; +} +strong { + color: #363636; + font-weight: 700; +} +fieldset { + border: none; +} +pre { + -webkit-overflow-scrolling: touch; + background-color: #f5f5f5; + color: #4a4a4a; + font-size: 0.875em; + overflow-x: auto; + padding: 1.25rem 1.5rem; + white-space: pre; + word-wrap: normal; +} +pre code { + background-color: transparent; + color: currentColor; + font-size: 1em; + padding: 0; +} +table td, +table th { + vertical-align: top; +} +table td:not([align]), +table th:not([align]) { + text-align: left; +} +table th { + color: #363636; +} +.is-clearfix::after { + clear: both; + content: " "; + display: table; +} +.is-pulled-left { + float: left !important; +} +.is-pulled-right { + float: right !important; +} +.is-clipped { + overflow: hidden !important; +} +.is-size-1 { + font-size: 3rem !important; +} +.is-size-2 { + font-size: 2.5rem !important; +} +.is-size-3 { + font-size: 2rem !important; +} +.is-size-4 { + font-size: 1.5rem !important; +} +.is-size-5 { + font-size: 1.25rem !important; +} +.is-size-6, +article.media .title { + font-size: 1rem !important; +} +.is-size-7, +article.media .date, +article.media .categories, +.article-licensing .licensing-title a, +.article-licensing .licensing-meta h6 { + font-size: 0.85rem !important; +} +@media screen and (max-width: 768px) { + .is-size-1-mobile { + font-size: 3rem !important; + } + .is-size-2-mobile { + font-size: 2.5rem !important; + } + .is-size-3-mobile { + font-size: 2rem !important; + } + .is-size-4-mobile { + font-size: 1.5rem !important; + } + .is-size-5-mobile { + font-size: 1.25rem !important; + } + .is-size-6-mobile { + font-size: 1rem !important; + } + .is-size-7-mobile { + font-size: 0.85rem !important; + } +} +@media screen and (min-width: 769px), print { + .is-size-1-tablet { + font-size: 3rem !important; + } + .is-size-2-tablet { + font-size: 2.5rem !important; + } + .is-size-3-tablet { + font-size: 2rem !important; + } + .is-size-4-tablet { + font-size: 1.5rem !important; + } + .is-size-5-tablet { + font-size: 1.25rem !important; + } + .is-size-6-tablet { + font-size: 1rem !important; + } + .is-size-7-tablet { + font-size: 0.85rem !important; + } +} +@media screen and (max-width: 1087px) { + .is-size-1-touch { + font-size: 3rem !important; + } + .is-size-2-touch { + font-size: 2.5rem !important; + } + .is-size-3-touch { + font-size: 2rem !important; + } + .is-size-4-touch { + font-size: 1.5rem !important; + } + .is-size-5-touch { + font-size: 1.25rem !important; + } + .is-size-6-touch { + font-size: 1rem !important; + } + .is-size-7-touch { + font-size: 0.85rem !important; + } +} +@media screen and (min-width: 1088px) { + .is-size-1-desktop { + font-size: 3rem !important; + } + .is-size-2-desktop { + font-size: 2.5rem !important; + } + .is-size-3-desktop { + font-size: 2rem !important; + } + .is-size-4-desktop { + font-size: 1.5rem !important; + } + .is-size-5-desktop { + font-size: 1.25rem !important; + } + .is-size-6-desktop { + font-size: 1rem !important; + } + .is-size-7-desktop { + font-size: 0.85rem !important; + } +} +@media screen and (min-width: 1280px) { + .is-size-1-widescreen { + font-size: 3rem !important; + } + .is-size-2-widescreen { + font-size: 2.5rem !important; + } + .is-size-3-widescreen { + font-size: 2rem !important; + } + .is-size-4-widescreen { + font-size: 1.5rem !important; + } + .is-size-5-widescreen { + font-size: 1.25rem !important; + } + .is-size-6-widescreen { + font-size: 1rem !important; + } + .is-size-7-widescreen { + font-size: 0.85rem !important; + } +} +@media screen and (min-width: 1472px) { + .is-size-1-fullhd { + font-size: 3rem !important; + } + .is-size-2-fullhd { + font-size: 2.5rem !important; + } + .is-size-3-fullhd { + font-size: 2rem !important; + } + .is-size-4-fullhd { + font-size: 1.5rem !important; + } + .is-size-5-fullhd { + font-size: 1.25rem !important; + } + .is-size-6-fullhd { + font-size: 1rem !important; + } + .is-size-7-fullhd { + font-size: 0.85rem !important; + } +} +.has-text-centered { + text-align: center !important; +} +.has-text-justified { + text-align: justify !important; +} +.has-text-left { + text-align: left !important; +} +.has-text-right { + text-align: right !important; +} +@media screen and (max-width: 768px) { + .has-text-centered-mobile { + text-align: center !important; + } +} +@media screen and (min-width: 769px), print { + .has-text-centered-tablet { + text-align: center !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .has-text-centered-tablet-only { + text-align: center !important; + } +} +@media screen and (max-width: 1087px) { + .has-text-centered-touch { + text-align: center !important; + } +} +@media screen and (min-width: 1088px) { + .has-text-centered-desktop { + text-align: center !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .has-text-centered-desktop-only { + text-align: center !important; + } +} +@media screen and (min-width: 1280px) { + .has-text-centered-widescreen { + text-align: center !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .has-text-centered-widescreen-only { + text-align: center !important; + } +} +@media screen and (min-width: 1472px) { + .has-text-centered-fullhd { + text-align: center !important; + } +} +@media screen and (max-width: 768px) { + .has-text-justified-mobile { + text-align: justify !important; + } +} +@media screen and (min-width: 769px), print { + .has-text-justified-tablet { + text-align: justify !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .has-text-justified-tablet-only { + text-align: justify !important; + } +} +@media screen and (max-width: 1087px) { + .has-text-justified-touch { + text-align: justify !important; + } +} +@media screen and (min-width: 1088px) { + .has-text-justified-desktop { + text-align: justify !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .has-text-justified-desktop-only { + text-align: justify !important; + } +} +@media screen and (min-width: 1280px) { + .has-text-justified-widescreen { + text-align: justify !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .has-text-justified-widescreen-only { + text-align: justify !important; + } +} +@media screen and (min-width: 1472px) { + .has-text-justified-fullhd { + text-align: justify !important; + } +} +@media screen and (max-width: 768px) { + .has-text-left-mobile { + text-align: left !important; + } +} +@media screen and (min-width: 769px), print { + .has-text-left-tablet { + text-align: left !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .has-text-left-tablet-only { + text-align: left !important; + } +} +@media screen and (max-width: 1087px) { + .has-text-left-touch { + text-align: left !important; + } +} +@media screen and (min-width: 1088px) { + .has-text-left-desktop { + text-align: left !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .has-text-left-desktop-only { + text-align: left !important; + } +} +@media screen and (min-width: 1280px) { + .has-text-left-widescreen { + text-align: left !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .has-text-left-widescreen-only { + text-align: left !important; + } +} +@media screen and (min-width: 1472px) { + .has-text-left-fullhd { + text-align: left !important; + } +} +@media screen and (max-width: 768px) { + .has-text-right-mobile { + text-align: right !important; + } +} +@media screen and (min-width: 769px), print { + .has-text-right-tablet { + text-align: right !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .has-text-right-tablet-only { + text-align: right !important; + } +} +@media screen and (max-width: 1087px) { + .has-text-right-touch { + text-align: right !important; + } +} +@media screen and (min-width: 1088px) { + .has-text-right-desktop { + text-align: right !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .has-text-right-desktop-only { + text-align: right !important; + } +} +@media screen and (min-width: 1280px) { + .has-text-right-widescreen { + text-align: right !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .has-text-right-widescreen-only { + text-align: right !important; + } +} +@media screen and (min-width: 1472px) { + .has-text-right-fullhd { + text-align: right !important; + } +} +.is-capitalized { + text-transform: capitalize !important; +} +.is-lowercase { + text-transform: lowercase !important; +} +.is-uppercase, +article.media .categories { + text-transform: uppercase !important; +} +.is-italic { + font-style: italic !important; +} +.has-text-white { + color: #fff !important; +} +a.has-text-white:hover, +a.has-text-white:focus { + color: #e6e6e6 !important; +} +.has-background-white { + background-color: #fff !important; +} +.has-text-black { + color: #0a0a0a !important; +} +a.has-text-black:hover, +a.has-text-black:focus { + color: #000 !important; +} +.has-background-black { + background-color: #0a0a0a !important; +} +.has-text-light { + color: #f5f5f5 !important; +} +a.has-text-light:hover, +a.has-text-light:focus { + color: #dbdbdb !important; +} +.has-background-light { + background-color: #f5f5f5 !important; +} +.has-text-dark { + color: #363636 !important; +} +a.has-text-dark:hover, +a.has-text-dark:focus { + color: #1c1c1c !important; +} +.has-background-dark { + background-color: #363636 !important; +} +.has-text-primary { + color: #3273dc !important; +} +a.has-text-primary:hover, +a.has-text-primary:focus { + color: #205bbc !important; +} +.has-background-primary { + background-color: #3273dc !important; +} +.has-text-link { + color: #3273dc !important; +} +a.has-text-link:hover, +a.has-text-link:focus { + color: #205bbc !important; +} +.has-background-link { + background-color: #3273dc !important; +} +.has-text-info { + color: #3298dc !important; +} +a.has-text-info:hover, +a.has-text-info:focus { + color: #207dbc !important; +} +.has-background-info { + background-color: #3298dc !important; +} +.has-text-success { + color: #48c774 !important; +} +a.has-text-success:hover, +a.has-text-success:focus { + color: #34a85c !important; +} +.has-background-success { + background-color: #48c774 !important; +} +.has-text-warning { + color: #ffdd57 !important; +} +a.has-text-warning:hover, +a.has-text-warning:focus { + color: #ffd324 !important; +} +.has-background-warning { + background-color: #ffdd57 !important; +} +.has-text-danger { + color: #f14668 !important; +} +a.has-text-danger:hover, +a.has-text-danger:focus { + color: #ee1742 !important; +} +.has-background-danger { + background-color: #f14668 !important; +} +.has-text-grey-lightest { + color: #ededed !important; +} +a.has-text-grey-lightest:hover, +a.has-text-grey-lightest:focus { + color: #d4d4d4 !important; +} +.has-background-grey-lightest { + background-color: #ededed !important; +} +.has-text-black-bis { + color: #121212 !important; +} +.has-background-black-bis { + background-color: #121212 !important; +} +.has-text-black-ter { + color: #242424 !important; +} +.has-background-black-ter { + background-color: #242424 !important; +} +.has-text-grey-darker { + color: #363636 !important; +} +.has-background-grey-darker { + background-color: #363636 !important; +} +.has-text-grey-dark { + color: #4a4a4a !important; +} +.has-background-grey-dark { + background-color: #4a4a4a !important; +} +.has-text-grey, +.article-licensing .licensing-title a { + color: #7a7a7a !important; +} +.has-background-grey { + background-color: #7a7a7a !important; +} +.has-text-grey-light { + color: #b5b5b5 !important; +} +.has-background-grey-light { + background-color: #b5b5b5 !important; +} +.has-text-grey-lighter { + color: #dbdbdb !important; +} +.has-background-grey-lighter { + background-color: #dbdbdb !important; +} +.has-text-white-ter { + color: #f5f5f5 !important; +} +.has-background-white-ter { + background-color: #f5f5f5 !important; +} +.has-text-white-bis { + color: #fafafa !important; +} +.has-background-white-bis { + background-color: #fafafa !important; +} +.has-text-weight-light { + font-weight: 300 !important; +} +.has-text-weight-normal { + font-weight: 400 !important; +} +.has-text-weight-medium { + font-weight: 500 !important; +} +.has-text-weight-semibold { + font-weight: 600 !important; +} +.has-text-weight-bold { + font-weight: 700 !important; +} +.is-family-primary { + font-family: Ubuntu, Roboto, 'Open Sans', 'Microsoft YaHei', sans-serif !important; +} +.is-family-secondary { + font-family: Ubuntu, Roboto, 'Open Sans', 'Microsoft YaHei', sans-serif !important; +} +.is-family-sans-serif { + font-family: Ubuntu, Roboto, 'Open Sans', 'Microsoft YaHei', sans-serif !important; +} +.is-family-monospace { + font-family: monospace !important; +} +.is-family-code { + font-family: 'Source Code Pro', monospace, 'Microsoft YaHei' !important; +} +.is-block { + display: block !important; +} +@media screen and (max-width: 768px) { + .is-block-mobile { + display: block !important; + } +} +@media screen and (min-width: 769px), print { + .is-block-tablet { + display: block !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-block-tablet-only { + display: block !important; + } +} +@media screen and (max-width: 1087px) { + .is-block-touch { + display: block !important; + } +} +@media screen and (min-width: 1088px) { + .is-block-desktop { + display: block !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-block-desktop-only { + display: block !important; + } +} +@media screen and (min-width: 1280px) { + .is-block-widescreen { + display: block !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-block-widescreen-only { + display: block !important; + } +} +@media screen and (min-width: 1472px) { + .is-block-fullhd { + display: block !important; + } +} +.is-flex { + display: flex !important; +} +@media screen and (max-width: 768px) { + .is-flex-mobile { + display: flex !important; + } +} +@media screen and (min-width: 769px), print { + .is-flex-tablet { + display: flex !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-flex-tablet-only { + display: flex !important; + } +} +@media screen and (max-width: 1087px) { + .is-flex-touch { + display: flex !important; + } +} +@media screen and (min-width: 1088px) { + .is-flex-desktop { + display: flex !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-flex-desktop-only { + display: flex !important; + } +} +@media screen and (min-width: 1280px) { + .is-flex-widescreen { + display: flex !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-flex-widescreen-only { + display: flex !important; + } +} +@media screen and (min-width: 1472px) { + .is-flex-fullhd { + display: flex !important; + } +} +.is-inline { + display: inline !important; +} +@media screen and (max-width: 768px) { + .is-inline-mobile { + display: inline !important; + } +} +@media screen and (min-width: 769px), print { + .is-inline-tablet { + display: inline !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-inline-tablet-only { + display: inline !important; + } +} +@media screen and (max-width: 1087px) { + .is-inline-touch { + display: inline !important; + } +} +@media screen and (min-width: 1088px) { + .is-inline-desktop { + display: inline !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-inline-desktop-only { + display: inline !important; + } +} +@media screen and (min-width: 1280px) { + .is-inline-widescreen { + display: inline !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-inline-widescreen-only { + display: inline !important; + } +} +@media screen and (min-width: 1472px) { + .is-inline-fullhd { + display: inline !important; + } +} +.is-inline-block { + display: inline-block !important; +} +@media screen and (max-width: 768px) { + .is-inline-block-mobile { + display: inline-block !important; + } +} +@media screen and (min-width: 769px), print { + .is-inline-block-tablet { + display: inline-block !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-inline-block-tablet-only { + display: inline-block !important; + } +} +@media screen and (max-width: 1087px) { + .is-inline-block-touch { + display: inline-block !important; + } +} +@media screen and (min-width: 1088px) { + .is-inline-block-desktop { + display: inline-block !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-inline-block-desktop-only { + display: inline-block !important; + } +} +@media screen and (min-width: 1280px) { + .is-inline-block-widescreen { + display: inline-block !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-inline-block-widescreen-only { + display: inline-block !important; + } +} +@media screen and (min-width: 1472px) { + .is-inline-block-fullhd { + display: inline-block !important; + } +} +.is-inline-flex { + display: inline-flex !important; +} +@media screen and (max-width: 768px) { + .is-inline-flex-mobile { + display: inline-flex !important; + } +} +@media screen and (min-width: 769px), print { + .is-inline-flex-tablet { + display: inline-flex !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-inline-flex-tablet-only { + display: inline-flex !important; + } +} +@media screen and (max-width: 1087px) { + .is-inline-flex-touch { + display: inline-flex !important; + } +} +@media screen and (min-width: 1088px) { + .is-inline-flex-desktop { + display: inline-flex !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-inline-flex-desktop-only { + display: inline-flex !important; + } +} +@media screen and (min-width: 1280px) { + .is-inline-flex-widescreen { + display: inline-flex !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-inline-flex-widescreen-only { + display: inline-flex !important; + } +} +@media screen and (min-width: 1472px) { + .is-inline-flex-fullhd { + display: inline-flex !important; + } +} +.is-hidden { + display: none !important; +} +.is-sr-only { + border: none !important; + clip: rect(0, 0, 0, 0) !important; + height: 0.01em !important; + overflow: hidden !important; + padding: 0 !important; + position: absolute !important; + white-space: nowrap !important; + width: 0.01em !important; +} +@media screen and (max-width: 768px) { + .is-hidden-mobile { + display: none !important; + } +} +@media screen and (min-width: 769px), print { + .is-hidden-tablet { + display: none !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-hidden-tablet-only { + display: none !important; + } +} +@media screen and (max-width: 1087px) { + .is-hidden-touch { + display: none !important; + } +} +@media screen and (min-width: 1088px) { + .is-hidden-desktop { + display: none !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-hidden-desktop-only { + display: none !important; + } +} +@media screen and (min-width: 1280px) { + .is-hidden-widescreen { + display: none !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-hidden-widescreen-only { + display: none !important; + } +} +@media screen and (min-width: 1472px) { + .is-hidden-fullhd { + display: none !important; + } +} +.is-invisible { + visibility: hidden !important; +} +@media screen and (max-width: 768px) { + .is-invisible-mobile { + visibility: hidden !important; + } +} +@media screen and (min-width: 769px), print { + .is-invisible-tablet { + visibility: hidden !important; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .is-invisible-tablet-only { + visibility: hidden !important; + } +} +@media screen and (max-width: 1087px) { + .is-invisible-touch { + visibility: hidden !important; + } +} +@media screen and (min-width: 1088px) { + .is-invisible-desktop { + visibility: hidden !important; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .is-invisible-desktop-only { + visibility: hidden !important; + } +} +@media screen and (min-width: 1280px) { + .is-invisible-widescreen { + visibility: hidden !important; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .is-invisible-widescreen-only { + visibility: hidden !important; + } +} +@media screen and (min-width: 1472px) { + .is-invisible-fullhd { + visibility: hidden !important; + } +} +.is-marginless { + margin: 0 !important; +} +.is-paddingless { + padding: 0 !important; +} +.is-radiusless { + border-radius: 0 !important; +} +.is-shadowless { + box-shadow: none !important; +} +.is-relative { + position: relative !important; +} +.breadcrumb { + font-size: 1rem; + white-space: nowrap; +} +.breadcrumb a { + align-items: center; + color: #3273dc; + display: flex; + justify-content: center; + padding: 0 0.75em; +} +.breadcrumb a:hover { + color: #363636; +} +.breadcrumb li { + align-items: center; + display: flex; +} +.breadcrumb li:first-child a { + padding-left: 0; +} +.breadcrumb li.is-active a { + color: #363636; + cursor: default; + pointer-events: none; +} +.breadcrumb li + li::before { + color: #b5b5b5; + content: "\0002f"; +} +.breadcrumb ul, +.breadcrumb ol { + align-items: flex-start; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; +} +.breadcrumb .icon:first-child { + margin-right: 0.5em; +} +.breadcrumb .icon:last-child { + margin-left: 0.5em; +} +.breadcrumb.is-centered ol, +.breadcrumb.is-centered ul { + justify-content: center; +} +.breadcrumb.is-right ol, +.breadcrumb.is-right ul { + justify-content: flex-end; +} +.breadcrumb.is-small { + font-size: 0.75rem; +} +.breadcrumb.is-medium { + font-size: 1.25rem; +} +.breadcrumb.is-large { + font-size: 1.5rem; +} +.breadcrumb.has-arrow-separator li + li::before { + content: "\02192"; +} +.breadcrumb.has-bullet-separator li + li::before { + content: "\02022"; +} +.breadcrumb.has-dot-separator li + li::before { + content: "\000b7"; +} +.breadcrumb.has-succeeds-separator li + li::before { + content: "\0227B"; +} +.card { + background-color: #fff; + box-shadow: 0 4px 10px rgba(0,0,0,0.05), 0 0 1px rgba(0,0,0,0.1); + color: #4a4a4a; + max-width: 100%; + position: relative; +} +.card-header { + background-color: transparent; + align-items: stretch; + box-shadow: 0 0.125em 0.25em rgba(10,10,10,0.1); + display: flex; +} +.card-header-title { + align-items: center; + color: #363636; + display: flex; + flex-grow: 1; + font-weight: 700; + padding: 0.75rem 1rem; +} +.card-header-title.is-centered { + justify-content: center; +} +.card-header-icon { + align-items: center; + cursor: pointer; + display: flex; + justify-content: center; + padding: 0.75rem 1rem; +} +.card-image { + display: block; + position: relative; +} +.card-content { + background-color: transparent; + padding: 1.5rem; +} +.card-footer { + background-color: transparent; + border-top: 1px solid #ededed; + align-items: stretch; + display: flex; +} +.card-footer-item { + align-items: center; + display: flex; + flex-basis: 0; + flex-grow: 1; + flex-shrink: 0; + justify-content: center; + padding: 0.75rem; +} +.card-footer-item:not(:last-child) { + border-right: 1px solid #ededed; +} +.card .media:not(:last-child) { + margin-bottom: 0.75rem; +} +.dropdown { + display: inline-flex; + position: relative; + vertical-align: top; +} +.dropdown.is-active .dropdown-menu, +.dropdown.is-hoverable:hover .dropdown-menu { + display: block; +} +.dropdown.is-right .dropdown-menu { + left: auto; + right: 0; +} +.dropdown.is-up .dropdown-menu { + bottom: 100%; + padding-bottom: 4px; + padding-top: initial; + top: auto; +} +.dropdown-menu { + display: none; + left: 0; + min-width: 12rem; + padding-top: 4px; + position: absolute; + top: 100%; + z-index: 20; +} +.dropdown-content { + background-color: #fff; + border-radius: 4px; + box-shadow: 0 0.5em 1em -0.125em rgba(10,10,10,0.1), 0 0px 0 1px rgba(10,10,10,0.02); + padding-bottom: 0.5rem; + padding-top: 0.5rem; +} +.dropdown-item { + color: #4a4a4a; + display: block; + font-size: 0.875rem; + line-height: 1.5; + padding: 0.375rem 1rem; + position: relative; +} +a.dropdown-item, +button.dropdown-item { + padding-right: 3rem; + text-align: left; + white-space: nowrap; + width: 100%; +} +a.dropdown-item:hover, +button.dropdown-item:hover { + background-color: #f5f5f5; + color: #0a0a0a; +} +a.dropdown-item.is-active, +button.dropdown-item.is-active { + background-color: #3273dc; + color: #fff; +} +.dropdown-divider { + background-color: #ededed; + border: none; + display: block; + height: 1px; + margin: 0.5rem 0; +} +.level { + align-items: center; + justify-content: space-between; +} +.level code { + border-radius: 4px; +} +.level img { + display: inline-block; + vertical-align: top; +} +.level.is-mobile { + display: flex; +} +.level.is-mobile .level-left, +.level.is-mobile .level-right { + display: flex; +} +.level.is-mobile .level-left + .level-right { + margin-top: 0; +} +.level.is-mobile .level-item:not(:last-child) { + margin-bottom: 0; + margin-right: 0.75rem; +} +.level.is-mobile .level-item:not(.is-narrow) { + flex-grow: 1; +} +@media screen and (min-width: 769px), print { + .level { + display: flex; + } + .level > .level-item:not(.is-narrow) { + flex-grow: 1; + } +} +.level-item { + align-items: center; + display: flex; + flex-basis: auto; + flex-grow: 0; + flex-shrink: 0; + justify-content: center; +} +.level-item .title, +.level-item .subtitle { + margin-bottom: 0; +} +@media screen and (max-width: 768px) { + .level-item:not(:last-child) { + margin-bottom: 0.75rem; + } +} +.level-left, +.level-right { + flex-basis: auto; + flex-grow: 0; + flex-shrink: 0; +} +.level-left .level-item.is-flexible, +.level-right .level-item.is-flexible { + flex-grow: 1; +} +@media screen and (min-width: 769px), print { + .level-left .level-item:not(:last-child), + .level-right .level-item:not(:last-child) { + margin-right: 0.75rem; + } +} +.level-left { + align-items: center; + justify-content: flex-start; +} +@media screen and (max-width: 768px) { + .level-left + .level-right { + margin-top: 1.5rem; + } +} +@media screen and (min-width: 769px), print { + .level-left { + display: flex; + } +} +.level-right { + align-items: center; + justify-content: flex-end; +} +@media screen and (min-width: 769px), print { + .level-right { + display: flex; + } +} +.list { + background-color: #fff; + border-radius: 4px; + box-shadow: 0 2px 3px rgba(10,10,10,0.1), 0 0 0 1px rgba(10,10,10,0.1); +} +.list-item { + display: block; + padding: 0.5em 1em; +} +.list-item:not(a) { + color: #4a4a4a; +} +.list-item:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} +.list-item:last-child { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; +} +.list-item:not(:last-child) { + border-bottom: 1px solid #dbdbdb; +} +.list-item.is-active { + background-color: #3273dc; + color: #fff; +} +a.list-item { + background-color: #f5f5f5; + cursor: pointer; +} +.media { + align-items: flex-start; + display: flex; + text-align: left; +} +.media .content:not(:last-child) { + margin-bottom: 0.75rem; +} +.media .media { + border-top: 1px solid rgba(219,219,219,0.5); + display: flex; + padding-top: 0.75rem; +} +.media .media .content:not(:last-child), +.media .media .control:not(:last-child) { + margin-bottom: 0.5rem; +} +.media .media .media { + padding-top: 0.5rem; +} +.media .media .media + .media { + margin-top: 0.5rem; +} +.media + .media { + border-top: 1px solid rgba(219,219,219,0.5); + margin-top: 1rem; + padding-top: 1rem; +} +.media.is-large + .media { + margin-top: 1.5rem; + padding-top: 1.5rem; +} +.media-left, +.media-right { + flex-basis: auto; + flex-grow: 0; + flex-shrink: 0; +} +.media-left { + margin-right: 1rem; +} +.media-right { + margin-left: 1rem; +} +.media-content { + flex-basis: auto; + flex-grow: 1; + flex-shrink: 1; + text-align: left; +} +@media screen and (max-width: 768px) { + .media-content { + overflow-x: auto; + } +} +.menu { + font-size: 1rem; +} +.menu.is-small { + font-size: 0.75rem; +} +.menu.is-medium { + font-size: 1.25rem; +} +.menu.is-large { + font-size: 1.5rem; +} +.menu-list { + line-height: 1.25; +} +.menu-list a { + border-radius: 2px; + color: #4a4a4a; + display: block; + padding: 0.5em 0.75em; +} +.menu-list a:hover { + background-color: #f5f5f5; + color: #363636; +} +.menu-list a.is-active { + background-color: #eef3fc; + color: #3273dc; +} +.menu-list li ul { + border-left: 1px solid #dbdbdb; + margin: 0.75em; + padding-left: 0.75em; +} +.menu-label { + color: #7a7a7a; + font-size: 0.75em; + letter-spacing: 0.1em; + text-transform: uppercase; +} +.menu-label:not(:first-child) { + margin-top: 1em; +} +.menu-label:not(:last-child) { + margin-bottom: 1em; +} +.message { + background-color: #f5f5f5; + border-radius: 4px; + font-size: 1rem; +} +.message strong { + color: currentColor; +} +.message a:not(.button):not(.tag):not(.dropdown-item) { + color: currentColor; + text-decoration: underline; +} +.message.is-small { + font-size: 0.75rem; +} +.message.is-medium { + font-size: 1.25rem; +} +.message.is-large { + font-size: 1.5rem; +} +.message.is-white { + background-color: #fff; +} +.message.is-white .message-header { + background-color: #fff; + color: #0a0a0a; +} +.message.is-white .message-body { + border-color: #fff; +} +.message.is-black { + background-color: #fafafa; +} +.message.is-black .message-header { + background-color: #0a0a0a; + color: #fff; +} +.message.is-black .message-body { + border-color: #0a0a0a; +} +.message.is-light { + background-color: #fafafa; +} +.message.is-light .message-header { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.message.is-light .message-body { + border-color: #f5f5f5; +} +.message.is-dark { + background-color: #fafafa; +} +.message.is-dark .message-header { + background-color: #363636; + color: #fff; +} +.message.is-dark .message-body { + border-color: #363636; +} +.message.is-primary { + background-color: #eef3fc; +} +.message.is-primary .message-header { + background-color: #3273dc; + color: #fff; +} +.message.is-primary .message-body { + border-color: #3273dc; + color: #2160c4; +} +.message.is-link { + background-color: #eef3fc; +} +.message.is-link .message-header { + background-color: #3273dc; + color: #fff; +} +.message.is-link .message-body { + border-color: #3273dc; + color: #2160c4; +} +.message.is-info { + background-color: #eef6fc; +} +.message.is-info .message-header { + background-color: #3298dc; + color: #fff; +} +.message.is-info .message-body { + border-color: #3298dc; + color: #1d72aa; +} +.message.is-success { + background-color: #effaf3; +} +.message.is-success .message-header { + background-color: #48c774; + color: #fff; +} +.message.is-success .message-body { + border-color: #48c774; + color: #257942; +} +.message.is-warning { + background-color: #fffbeb; +} +.message.is-warning .message-header { + background-color: #ffdd57; + color: rgba(0,0,0,0.7); +} +.message.is-warning .message-body { + border-color: #ffdd57; + color: #947600; +} +.message.is-danger { + background-color: #feecf0; +} +.message.is-danger .message-header { + background-color: #f14668; + color: #fff; +} +.message.is-danger .message-body { + border-color: #f14668; + color: #cc0f35; +} +.message.is-grey-lightest { + background-color: #fafafa; +} +.message.is-grey-lightest .message-header { + background-color: #ededed; + color: #363636; +} +.message.is-grey-lightest .message-body { + border-color: #ededed; +} +.message-header { + align-items: center; + background-color: #4a4a4a; + border-radius: 4px 4px 0 0; + color: #fff; + display: flex; + font-weight: 700; + justify-content: space-between; + line-height: 1.25; + padding: 0.75em 1em; + position: relative; +} +.message-header .delete { + flex-grow: 0; + flex-shrink: 0; + margin-left: 0.75em; +} +.message-header + .message-body { + border-width: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.message-body { + border-color: #dbdbdb; + border-radius: 4px; + border-style: solid; + border-width: 0 0 0 4px; + color: #4a4a4a; + padding: 1.25em 1.5em; +} +.message-body code, +.message-body pre { + background-color: #fff; +} +.message-body pre code { + background-color: transparent; +} +.modal { + align-items: center; + display: none; + flex-direction: column; + justify-content: center; + overflow: hidden; + position: fixed; + z-index: 40; +} +.modal.is-active { + display: flex; +} +.modal-background { + background-color: rgba(10,10,10,0.86); +} +.modal-content, +.modal-card { + margin: 0 20px; + max-height: calc(100vh - 160px); + overflow: auto; + position: relative; + width: 100%; +} +@media screen and (min-width: 769px), print { + .modal-content, + .modal-card { + margin: 0 auto; + max-height: calc(100vh - 40px); + width: 640px; + } +} +.modal-close { + background: none; + height: 40px; + position: fixed; + right: 20px; + top: 20px; + width: 40px; +} +.modal-card { + display: flex; + flex-direction: column; + max-height: calc(100vh - 40px); + overflow: hidden; + -ms-overflow-y: visible; +} +.modal-card-head, +.modal-card-foot { + align-items: center; + background-color: #f5f5f5; + display: flex; + flex-shrink: 0; + justify-content: flex-start; + padding: 20px; + position: relative; +} +.modal-card-head { + border-bottom: 1px solid #dbdbdb; + border-top-left-radius: 6px; + border-top-right-radius: 6px; +} +.modal-card-title { + color: #363636; + flex-grow: 1; + flex-shrink: 0; + font-size: 1.5rem; + line-height: 1; +} +.modal-card-foot { + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + border-top: 1px solid #dbdbdb; +} +.modal-card-foot .button:not(:last-child) { + margin-right: 0.5em; +} +.modal-card-body { + -webkit-overflow-scrolling: touch; + background-color: #fff; + flex-grow: 1; + flex-shrink: 1; + overflow: auto; + padding: 20px; +} +.navbar { + background-color: #fff; + min-height: 3.25rem; + position: relative; + z-index: 30; +} +.navbar.is-white { + background-color: #fff; + color: #0a0a0a; +} +.navbar.is-white .navbar-brand > .navbar-item, +.navbar.is-white .navbar-brand .navbar-link { + color: #0a0a0a; +} +.navbar.is-white .navbar-brand > a.navbar-item:focus, +.navbar.is-white .navbar-brand .navbar-link:focus, +.navbar.is-white .navbar-brand > a.navbar-item:hover, +.navbar.is-white .navbar-brand .navbar-link:hover, +.navbar.is-white .navbar-brand > a.navbar-item.is-active, +.navbar.is-white .navbar-brand .navbar-link.is-active { + background-color: #f2f2f2; + color: #0a0a0a; +} +.navbar.is-white .navbar-brand .navbar-link::after { + border-color: #0a0a0a; +} +.navbar.is-white .navbar-burger { + color: #0a0a0a; +} +@media screen and (min-width: 1088px) { + .navbar.is-white .navbar-start > .navbar-item, + .navbar.is-white .navbar-end > .navbar-item, + .navbar.is-white .navbar-start .navbar-link, + .navbar.is-white .navbar-end .navbar-link { + color: #0a0a0a; + } + .navbar.is-white .navbar-start > a.navbar-item:focus, + .navbar.is-white .navbar-end > a.navbar-item:focus, + .navbar.is-white .navbar-start .navbar-link:focus, + .navbar.is-white .navbar-end .navbar-link:focus, + .navbar.is-white .navbar-start > a.navbar-item:hover, + .navbar.is-white .navbar-end > a.navbar-item:hover, + .navbar.is-white .navbar-start .navbar-link:hover, + .navbar.is-white .navbar-end .navbar-link:hover, + .navbar.is-white .navbar-start > a.navbar-item.is-active, + .navbar.is-white .navbar-end > a.navbar-item.is-active, + .navbar.is-white .navbar-start .navbar-link.is-active, + .navbar.is-white .navbar-end .navbar-link.is-active { + background-color: #f2f2f2; + color: #0a0a0a; + } + .navbar.is-white .navbar-start .navbar-link::after, + .navbar.is-white .navbar-end .navbar-link::after { + border-color: #0a0a0a; + } + .navbar.is-white .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-white .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-white .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #f2f2f2; + color: #0a0a0a; + } + .navbar.is-white .navbar-dropdown a.navbar-item.is-active { + background-color: #fff; + color: #0a0a0a; + } +} +.navbar.is-black { + background-color: #0a0a0a; + color: #fff; +} +.navbar.is-black .navbar-brand > .navbar-item, +.navbar.is-black .navbar-brand .navbar-link { + color: #fff; +} +.navbar.is-black .navbar-brand > a.navbar-item:focus, +.navbar.is-black .navbar-brand .navbar-link:focus, +.navbar.is-black .navbar-brand > a.navbar-item:hover, +.navbar.is-black .navbar-brand .navbar-link:hover, +.navbar.is-black .navbar-brand > a.navbar-item.is-active, +.navbar.is-black .navbar-brand .navbar-link.is-active { + background-color: #000; + color: #fff; +} +.navbar.is-black .navbar-brand .navbar-link::after { + border-color: #fff; +} +.navbar.is-black .navbar-burger { + color: #fff; +} +@media screen and (min-width: 1088px) { + .navbar.is-black .navbar-start > .navbar-item, + .navbar.is-black .navbar-end > .navbar-item, + .navbar.is-black .navbar-start .navbar-link, + .navbar.is-black .navbar-end .navbar-link { + color: #fff; + } + .navbar.is-black .navbar-start > a.navbar-item:focus, + .navbar.is-black .navbar-end > a.navbar-item:focus, + .navbar.is-black .navbar-start .navbar-link:focus, + .navbar.is-black .navbar-end .navbar-link:focus, + .navbar.is-black .navbar-start > a.navbar-item:hover, + .navbar.is-black .navbar-end > a.navbar-item:hover, + .navbar.is-black .navbar-start .navbar-link:hover, + .navbar.is-black .navbar-end .navbar-link:hover, + .navbar.is-black .navbar-start > a.navbar-item.is-active, + .navbar.is-black .navbar-end > a.navbar-item.is-active, + .navbar.is-black .navbar-start .navbar-link.is-active, + .navbar.is-black .navbar-end .navbar-link.is-active { + background-color: #000; + color: #fff; + } + .navbar.is-black .navbar-start .navbar-link::after, + .navbar.is-black .navbar-end .navbar-link::after { + border-color: #fff; + } + .navbar.is-black .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-black .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-black .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #000; + color: #fff; + } + .navbar.is-black .navbar-dropdown a.navbar-item.is-active { + background-color: #0a0a0a; + color: #fff; + } +} +.navbar.is-light { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.navbar.is-light .navbar-brand > .navbar-item, +.navbar.is-light .navbar-brand .navbar-link { + color: rgba(0,0,0,0.7); +} +.navbar.is-light .navbar-brand > a.navbar-item:focus, +.navbar.is-light .navbar-brand .navbar-link:focus, +.navbar.is-light .navbar-brand > a.navbar-item:hover, +.navbar.is-light .navbar-brand .navbar-link:hover, +.navbar.is-light .navbar-brand > a.navbar-item.is-active, +.navbar.is-light .navbar-brand .navbar-link.is-active { + background-color: #e8e8e8; + color: rgba(0,0,0,0.7); +} +.navbar.is-light .navbar-brand .navbar-link::after { + border-color: rgba(0,0,0,0.7); +} +.navbar.is-light .navbar-burger { + color: rgba(0,0,0,0.7); +} +@media screen and (min-width: 1088px) { + .navbar.is-light .navbar-start > .navbar-item, + .navbar.is-light .navbar-end > .navbar-item, + .navbar.is-light .navbar-start .navbar-link, + .navbar.is-light .navbar-end .navbar-link { + color: rgba(0,0,0,0.7); + } + .navbar.is-light .navbar-start > a.navbar-item:focus, + .navbar.is-light .navbar-end > a.navbar-item:focus, + .navbar.is-light .navbar-start .navbar-link:focus, + .navbar.is-light .navbar-end .navbar-link:focus, + .navbar.is-light .navbar-start > a.navbar-item:hover, + .navbar.is-light .navbar-end > a.navbar-item:hover, + .navbar.is-light .navbar-start .navbar-link:hover, + .navbar.is-light .navbar-end .navbar-link:hover, + .navbar.is-light .navbar-start > a.navbar-item.is-active, + .navbar.is-light .navbar-end > a.navbar-item.is-active, + .navbar.is-light .navbar-start .navbar-link.is-active, + .navbar.is-light .navbar-end .navbar-link.is-active { + background-color: #e8e8e8; + color: rgba(0,0,0,0.7); + } + .navbar.is-light .navbar-start .navbar-link::after, + .navbar.is-light .navbar-end .navbar-link::after { + border-color: rgba(0,0,0,0.7); + } + .navbar.is-light .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-light .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-light .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #e8e8e8; + color: rgba(0,0,0,0.7); + } + .navbar.is-light .navbar-dropdown a.navbar-item.is-active { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); + } +} +.navbar.is-dark { + background-color: #363636; + color: #fff; +} +.navbar.is-dark .navbar-brand > .navbar-item, +.navbar.is-dark .navbar-brand .navbar-link { + color: #fff; +} +.navbar.is-dark .navbar-brand > a.navbar-item:focus, +.navbar.is-dark .navbar-brand .navbar-link:focus, +.navbar.is-dark .navbar-brand > a.navbar-item:hover, +.navbar.is-dark .navbar-brand .navbar-link:hover, +.navbar.is-dark .navbar-brand > a.navbar-item.is-active, +.navbar.is-dark .navbar-brand .navbar-link.is-active { + background-color: #292929; + color: #fff; +} +.navbar.is-dark .navbar-brand .navbar-link::after { + border-color: #fff; +} +.navbar.is-dark .navbar-burger { + color: #fff; +} +@media screen and (min-width: 1088px) { + .navbar.is-dark .navbar-start > .navbar-item, + .navbar.is-dark .navbar-end > .navbar-item, + .navbar.is-dark .navbar-start .navbar-link, + .navbar.is-dark .navbar-end .navbar-link { + color: #fff; + } + .navbar.is-dark .navbar-start > a.navbar-item:focus, + .navbar.is-dark .navbar-end > a.navbar-item:focus, + .navbar.is-dark .navbar-start .navbar-link:focus, + .navbar.is-dark .navbar-end .navbar-link:focus, + .navbar.is-dark .navbar-start > a.navbar-item:hover, + .navbar.is-dark .navbar-end > a.navbar-item:hover, + .navbar.is-dark .navbar-start .navbar-link:hover, + .navbar.is-dark .navbar-end .navbar-link:hover, + .navbar.is-dark .navbar-start > a.navbar-item.is-active, + .navbar.is-dark .navbar-end > a.navbar-item.is-active, + .navbar.is-dark .navbar-start .navbar-link.is-active, + .navbar.is-dark .navbar-end .navbar-link.is-active { + background-color: #292929; + color: #fff; + } + .navbar.is-dark .navbar-start .navbar-link::after, + .navbar.is-dark .navbar-end .navbar-link::after { + border-color: #fff; + } + .navbar.is-dark .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-dark .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-dark .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #292929; + color: #fff; + } + .navbar.is-dark .navbar-dropdown a.navbar-item.is-active { + background-color: #363636; + color: #fff; + } +} +.navbar.is-primary { + background-color: #3273dc; + color: #fff; +} +.navbar.is-primary .navbar-brand > .navbar-item, +.navbar.is-primary .navbar-brand .navbar-link { + color: #fff; +} +.navbar.is-primary .navbar-brand > a.navbar-item:focus, +.navbar.is-primary .navbar-brand .navbar-link:focus, +.navbar.is-primary .navbar-brand > a.navbar-item:hover, +.navbar.is-primary .navbar-brand .navbar-link:hover, +.navbar.is-primary .navbar-brand > a.navbar-item.is-active, +.navbar.is-primary .navbar-brand .navbar-link.is-active { + background-color: #2366d1; + color: #fff; +} +.navbar.is-primary .navbar-brand .navbar-link::after { + border-color: #fff; +} +.navbar.is-primary .navbar-burger { + color: #fff; +} +@media screen and (min-width: 1088px) { + .navbar.is-primary .navbar-start > .navbar-item, + .navbar.is-primary .navbar-end > .navbar-item, + .navbar.is-primary .navbar-start .navbar-link, + .navbar.is-primary .navbar-end .navbar-link { + color: #fff; + } + .navbar.is-primary .navbar-start > a.navbar-item:focus, + .navbar.is-primary .navbar-end > a.navbar-item:focus, + .navbar.is-primary .navbar-start .navbar-link:focus, + .navbar.is-primary .navbar-end .navbar-link:focus, + .navbar.is-primary .navbar-start > a.navbar-item:hover, + .navbar.is-primary .navbar-end > a.navbar-item:hover, + .navbar.is-primary .navbar-start .navbar-link:hover, + .navbar.is-primary .navbar-end .navbar-link:hover, + .navbar.is-primary .navbar-start > a.navbar-item.is-active, + .navbar.is-primary .navbar-end > a.navbar-item.is-active, + .navbar.is-primary .navbar-start .navbar-link.is-active, + .navbar.is-primary .navbar-end .navbar-link.is-active { + background-color: #2366d1; + color: #fff; + } + .navbar.is-primary .navbar-start .navbar-link::after, + .navbar.is-primary .navbar-end .navbar-link::after { + border-color: #fff; + } + .navbar.is-primary .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-primary .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-primary .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #2366d1; + color: #fff; + } + .navbar.is-primary .navbar-dropdown a.navbar-item.is-active { + background-color: #3273dc; + color: #fff; + } +} +.navbar.is-link { + background-color: #3273dc; + color: #fff; +} +.navbar.is-link .navbar-brand > .navbar-item, +.navbar.is-link .navbar-brand .navbar-link { + color: #fff; +} +.navbar.is-link .navbar-brand > a.navbar-item:focus, +.navbar.is-link .navbar-brand .navbar-link:focus, +.navbar.is-link .navbar-brand > a.navbar-item:hover, +.navbar.is-link .navbar-brand .navbar-link:hover, +.navbar.is-link .navbar-brand > a.navbar-item.is-active, +.navbar.is-link .navbar-brand .navbar-link.is-active { + background-color: #2366d1; + color: #fff; +} +.navbar.is-link .navbar-brand .navbar-link::after { + border-color: #fff; +} +.navbar.is-link .navbar-burger { + color: #fff; +} +@media screen and (min-width: 1088px) { + .navbar.is-link .navbar-start > .navbar-item, + .navbar.is-link .navbar-end > .navbar-item, + .navbar.is-link .navbar-start .navbar-link, + .navbar.is-link .navbar-end .navbar-link { + color: #fff; + } + .navbar.is-link .navbar-start > a.navbar-item:focus, + .navbar.is-link .navbar-end > a.navbar-item:focus, + .navbar.is-link .navbar-start .navbar-link:focus, + .navbar.is-link .navbar-end .navbar-link:focus, + .navbar.is-link .navbar-start > a.navbar-item:hover, + .navbar.is-link .navbar-end > a.navbar-item:hover, + .navbar.is-link .navbar-start .navbar-link:hover, + .navbar.is-link .navbar-end .navbar-link:hover, + .navbar.is-link .navbar-start > a.navbar-item.is-active, + .navbar.is-link .navbar-end > a.navbar-item.is-active, + .navbar.is-link .navbar-start .navbar-link.is-active, + .navbar.is-link .navbar-end .navbar-link.is-active { + background-color: #2366d1; + color: #fff; + } + .navbar.is-link .navbar-start .navbar-link::after, + .navbar.is-link .navbar-end .navbar-link::after { + border-color: #fff; + } + .navbar.is-link .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-link .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-link .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #2366d1; + color: #fff; + } + .navbar.is-link .navbar-dropdown a.navbar-item.is-active { + background-color: #3273dc; + color: #fff; + } +} +.navbar.is-info { + background-color: #3298dc; + color: #fff; +} +.navbar.is-info .navbar-brand > .navbar-item, +.navbar.is-info .navbar-brand .navbar-link { + color: #fff; +} +.navbar.is-info .navbar-brand > a.navbar-item:focus, +.navbar.is-info .navbar-brand .navbar-link:focus, +.navbar.is-info .navbar-brand > a.navbar-item:hover, +.navbar.is-info .navbar-brand .navbar-link:hover, +.navbar.is-info .navbar-brand > a.navbar-item.is-active, +.navbar.is-info .navbar-brand .navbar-link.is-active { + background-color: #238cd1; + color: #fff; +} +.navbar.is-info .navbar-brand .navbar-link::after { + border-color: #fff; +} +.navbar.is-info .navbar-burger { + color: #fff; +} +@media screen and (min-width: 1088px) { + .navbar.is-info .navbar-start > .navbar-item, + .navbar.is-info .navbar-end > .navbar-item, + .navbar.is-info .navbar-start .navbar-link, + .navbar.is-info .navbar-end .navbar-link { + color: #fff; + } + .navbar.is-info .navbar-start > a.navbar-item:focus, + .navbar.is-info .navbar-end > a.navbar-item:focus, + .navbar.is-info .navbar-start .navbar-link:focus, + .navbar.is-info .navbar-end .navbar-link:focus, + .navbar.is-info .navbar-start > a.navbar-item:hover, + .navbar.is-info .navbar-end > a.navbar-item:hover, + .navbar.is-info .navbar-start .navbar-link:hover, + .navbar.is-info .navbar-end .navbar-link:hover, + .navbar.is-info .navbar-start > a.navbar-item.is-active, + .navbar.is-info .navbar-end > a.navbar-item.is-active, + .navbar.is-info .navbar-start .navbar-link.is-active, + .navbar.is-info .navbar-end .navbar-link.is-active { + background-color: #238cd1; + color: #fff; + } + .navbar.is-info .navbar-start .navbar-link::after, + .navbar.is-info .navbar-end .navbar-link::after { + border-color: #fff; + } + .navbar.is-info .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-info .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-info .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #238cd1; + color: #fff; + } + .navbar.is-info .navbar-dropdown a.navbar-item.is-active { + background-color: #3298dc; + color: #fff; + } +} +.navbar.is-success { + background-color: #48c774; + color: #fff; +} +.navbar.is-success .navbar-brand > .navbar-item, +.navbar.is-success .navbar-brand .navbar-link { + color: #fff; +} +.navbar.is-success .navbar-brand > a.navbar-item:focus, +.navbar.is-success .navbar-brand .navbar-link:focus, +.navbar.is-success .navbar-brand > a.navbar-item:hover, +.navbar.is-success .navbar-brand .navbar-link:hover, +.navbar.is-success .navbar-brand > a.navbar-item.is-active, +.navbar.is-success .navbar-brand .navbar-link.is-active { + background-color: #3abb67; + color: #fff; +} +.navbar.is-success .navbar-brand .navbar-link::after { + border-color: #fff; +} +.navbar.is-success .navbar-burger { + color: #fff; +} +@media screen and (min-width: 1088px) { + .navbar.is-success .navbar-start > .navbar-item, + .navbar.is-success .navbar-end > .navbar-item, + .navbar.is-success .navbar-start .navbar-link, + .navbar.is-success .navbar-end .navbar-link { + color: #fff; + } + .navbar.is-success .navbar-start > a.navbar-item:focus, + .navbar.is-success .navbar-end > a.navbar-item:focus, + .navbar.is-success .navbar-start .navbar-link:focus, + .navbar.is-success .navbar-end .navbar-link:focus, + .navbar.is-success .navbar-start > a.navbar-item:hover, + .navbar.is-success .navbar-end > a.navbar-item:hover, + .navbar.is-success .navbar-start .navbar-link:hover, + .navbar.is-success .navbar-end .navbar-link:hover, + .navbar.is-success .navbar-start > a.navbar-item.is-active, + .navbar.is-success .navbar-end > a.navbar-item.is-active, + .navbar.is-success .navbar-start .navbar-link.is-active, + .navbar.is-success .navbar-end .navbar-link.is-active { + background-color: #3abb67; + color: #fff; + } + .navbar.is-success .navbar-start .navbar-link::after, + .navbar.is-success .navbar-end .navbar-link::after { + border-color: #fff; + } + .navbar.is-success .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-success .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-success .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #3abb67; + color: #fff; + } + .navbar.is-success .navbar-dropdown a.navbar-item.is-active { + background-color: #48c774; + color: #fff; + } +} +.navbar.is-warning { + background-color: #ffdd57; + color: rgba(0,0,0,0.7); +} +.navbar.is-warning .navbar-brand > .navbar-item, +.navbar.is-warning .navbar-brand .navbar-link { + color: rgba(0,0,0,0.7); +} +.navbar.is-warning .navbar-brand > a.navbar-item:focus, +.navbar.is-warning .navbar-brand .navbar-link:focus, +.navbar.is-warning .navbar-brand > a.navbar-item:hover, +.navbar.is-warning .navbar-brand .navbar-link:hover, +.navbar.is-warning .navbar-brand > a.navbar-item.is-active, +.navbar.is-warning .navbar-brand .navbar-link.is-active { + background-color: #ffd83d; + color: rgba(0,0,0,0.7); +} +.navbar.is-warning .navbar-brand .navbar-link::after { + border-color: rgba(0,0,0,0.7); +} +.navbar.is-warning .navbar-burger { + color: rgba(0,0,0,0.7); +} +@media screen and (min-width: 1088px) { + .navbar.is-warning .navbar-start > .navbar-item, + .navbar.is-warning .navbar-end > .navbar-item, + .navbar.is-warning .navbar-start .navbar-link, + .navbar.is-warning .navbar-end .navbar-link { + color: rgba(0,0,0,0.7); + } + .navbar.is-warning .navbar-start > a.navbar-item:focus, + .navbar.is-warning .navbar-end > a.navbar-item:focus, + .navbar.is-warning .navbar-start .navbar-link:focus, + .navbar.is-warning .navbar-end .navbar-link:focus, + .navbar.is-warning .navbar-start > a.navbar-item:hover, + .navbar.is-warning .navbar-end > a.navbar-item:hover, + .navbar.is-warning .navbar-start .navbar-link:hover, + .navbar.is-warning .navbar-end .navbar-link:hover, + .navbar.is-warning .navbar-start > a.navbar-item.is-active, + .navbar.is-warning .navbar-end > a.navbar-item.is-active, + .navbar.is-warning .navbar-start .navbar-link.is-active, + .navbar.is-warning .navbar-end .navbar-link.is-active { + background-color: #ffd83d; + color: rgba(0,0,0,0.7); + } + .navbar.is-warning .navbar-start .navbar-link::after, + .navbar.is-warning .navbar-end .navbar-link::after { + border-color: rgba(0,0,0,0.7); + } + .navbar.is-warning .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-warning .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-warning .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #ffd83d; + color: rgba(0,0,0,0.7); + } + .navbar.is-warning .navbar-dropdown a.navbar-item.is-active { + background-color: #ffdd57; + color: rgba(0,0,0,0.7); + } +} +.navbar.is-danger { + background-color: #f14668; + color: #fff; +} +.navbar.is-danger .navbar-brand > .navbar-item, +.navbar.is-danger .navbar-brand .navbar-link { + color: #fff; +} +.navbar.is-danger .navbar-brand > a.navbar-item:focus, +.navbar.is-danger .navbar-brand .navbar-link:focus, +.navbar.is-danger .navbar-brand > a.navbar-item:hover, +.navbar.is-danger .navbar-brand .navbar-link:hover, +.navbar.is-danger .navbar-brand > a.navbar-item.is-active, +.navbar.is-danger .navbar-brand .navbar-link.is-active { + background-color: #ef2e55; + color: #fff; +} +.navbar.is-danger .navbar-brand .navbar-link::after { + border-color: #fff; +} +.navbar.is-danger .navbar-burger { + color: #fff; +} +@media screen and (min-width: 1088px) { + .navbar.is-danger .navbar-start > .navbar-item, + .navbar.is-danger .navbar-end > .navbar-item, + .navbar.is-danger .navbar-start .navbar-link, + .navbar.is-danger .navbar-end .navbar-link { + color: #fff; + } + .navbar.is-danger .navbar-start > a.navbar-item:focus, + .navbar.is-danger .navbar-end > a.navbar-item:focus, + .navbar.is-danger .navbar-start .navbar-link:focus, + .navbar.is-danger .navbar-end .navbar-link:focus, + .navbar.is-danger .navbar-start > a.navbar-item:hover, + .navbar.is-danger .navbar-end > a.navbar-item:hover, + .navbar.is-danger .navbar-start .navbar-link:hover, + .navbar.is-danger .navbar-end .navbar-link:hover, + .navbar.is-danger .navbar-start > a.navbar-item.is-active, + .navbar.is-danger .navbar-end > a.navbar-item.is-active, + .navbar.is-danger .navbar-start .navbar-link.is-active, + .navbar.is-danger .navbar-end .navbar-link.is-active { + background-color: #ef2e55; + color: #fff; + } + .navbar.is-danger .navbar-start .navbar-link::after, + .navbar.is-danger .navbar-end .navbar-link::after { + border-color: #fff; + } + .navbar.is-danger .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-danger .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-danger .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #ef2e55; + color: #fff; + } + .navbar.is-danger .navbar-dropdown a.navbar-item.is-active { + background-color: #f14668; + color: #fff; + } +} +.navbar.is-grey-lightest { + background-color: #ededed; + color: #363636; +} +.navbar.is-grey-lightest .navbar-brand > .navbar-item, +.navbar.is-grey-lightest .navbar-brand .navbar-link { + color: #363636; +} +.navbar.is-grey-lightest .navbar-brand > a.navbar-item:focus, +.navbar.is-grey-lightest .navbar-brand .navbar-link:focus, +.navbar.is-grey-lightest .navbar-brand > a.navbar-item:hover, +.navbar.is-grey-lightest .navbar-brand .navbar-link:hover, +.navbar.is-grey-lightest .navbar-brand > a.navbar-item.is-active, +.navbar.is-grey-lightest .navbar-brand .navbar-link.is-active { + background-color: #e0e0e0; + color: #363636; +} +.navbar.is-grey-lightest .navbar-brand .navbar-link::after { + border-color: #363636; +} +.navbar.is-grey-lightest .navbar-burger { + color: #363636; +} +@media screen and (min-width: 1088px) { + .navbar.is-grey-lightest .navbar-start > .navbar-item, + .navbar.is-grey-lightest .navbar-end > .navbar-item, + .navbar.is-grey-lightest .navbar-start .navbar-link, + .navbar.is-grey-lightest .navbar-end .navbar-link { + color: #363636; + } + .navbar.is-grey-lightest .navbar-start > a.navbar-item:focus, + .navbar.is-grey-lightest .navbar-end > a.navbar-item:focus, + .navbar.is-grey-lightest .navbar-start .navbar-link:focus, + .navbar.is-grey-lightest .navbar-end .navbar-link:focus, + .navbar.is-grey-lightest .navbar-start > a.navbar-item:hover, + .navbar.is-grey-lightest .navbar-end > a.navbar-item:hover, + .navbar.is-grey-lightest .navbar-start .navbar-link:hover, + .navbar.is-grey-lightest .navbar-end .navbar-link:hover, + .navbar.is-grey-lightest .navbar-start > a.navbar-item.is-active, + .navbar.is-grey-lightest .navbar-end > a.navbar-item.is-active, + .navbar.is-grey-lightest .navbar-start .navbar-link.is-active, + .navbar.is-grey-lightest .navbar-end .navbar-link.is-active { + background-color: #e0e0e0; + color: #363636; + } + .navbar.is-grey-lightest .navbar-start .navbar-link::after, + .navbar.is-grey-lightest .navbar-end .navbar-link::after { + border-color: #363636; + } + .navbar.is-grey-lightest .navbar-item.has-dropdown:focus .navbar-link, + .navbar.is-grey-lightest .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-grey-lightest .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #e0e0e0; + color: #363636; + } + .navbar.is-grey-lightest .navbar-dropdown a.navbar-item.is-active { + background-color: #ededed; + color: #363636; + } +} +.navbar > .container { + align-items: stretch; + display: flex; + min-height: 3.25rem; + width: 100%; +} +.navbar.has-shadow { + box-shadow: 0 2px 0 0 #f5f5f5; +} +.navbar.is-fixed-bottom, +.navbar.is-fixed-top { + left: 0; + position: fixed; + right: 0; + z-index: 30; +} +.navbar.is-fixed-bottom { + bottom: 0; +} +.navbar.is-fixed-bottom.has-shadow { + box-shadow: 0 -2px 0 0 #f5f5f5; +} +.navbar.is-fixed-top { + top: 0; +} +html.has-navbar-fixed-top, +body.has-navbar-fixed-top { + padding-top: 3.25rem; +} +html.has-navbar-fixed-bottom, +body.has-navbar-fixed-bottom { + padding-bottom: 3.25rem; +} +.navbar-brand, +.navbar-tabs { + align-items: stretch; + display: flex; + flex-shrink: 0; + min-height: 3.25rem; +} +.navbar-brand a.navbar-item:focus, +.navbar-brand a.navbar-item:hover { + background-color: transparent; +} +.navbar-tabs { + -webkit-overflow-scrolling: touch; + max-width: 100vw; + overflow-x: auto; + overflow-y: hidden; +} +.navbar-burger { + color: #4a4a4a; + cursor: pointer; + display: block; + height: 3.25rem; + position: relative; + width: 3.25rem; + margin-left: auto; +} +.navbar-burger span { + background-color: currentColor; + display: block; + height: 1px; + left: calc(50% - 8px); + position: absolute; + transform-origin: center; + transition-duration: 86ms; + transition-property: background-color, opacity, transform; + transition-timing-function: ease-out; + width: 16px; +} +.navbar-burger span:nth-child(1) { + top: calc(50% - 6px); +} +.navbar-burger span:nth-child(2) { + top: calc(50% - 1px); +} +.navbar-burger span:nth-child(3) { + top: calc(50% + 4px); +} +.navbar-burger:hover { + background-color: rgba(0,0,0,0.05); +} +.navbar-burger.is-active span:nth-child(1) { + transform: translateY(5px) rotate(45deg); +} +.navbar-burger.is-active span:nth-child(2) { + opacity: 0; +} +.navbar-burger.is-active span:nth-child(3) { + transform: translateY(-5px) rotate(-45deg); +} +.navbar-menu { + display: none; +} +.navbar-item, +.navbar-link { + color: #4a4a4a; + display: block; + line-height: 1.5; + padding: 0.5rem 0.75rem; + position: relative; +} +.navbar-item .icon:only-child, +.navbar-link .icon:only-child { + margin-left: -0.25rem; + margin-right: -0.25rem; +} +a.navbar-item, +.navbar-link { + cursor: pointer; +} +a.navbar-item:focus, +.navbar-link:focus, +a.navbar-item:focus-within, +.navbar-link:focus-within, +a.navbar-item:hover, +.navbar-link:hover, +a.navbar-item.is-active, +.navbar-link.is-active { + background-color: #fafafa; + color: #3273dc; +} +.navbar-item { + display: block; + flex-grow: 0; + flex-shrink: 0; +} +.navbar-item img { + max-height: 1.75rem; +} +.navbar-item.has-dropdown { + padding: 0; +} +.navbar-item.is-expanded { + flex-grow: 1; + flex-shrink: 1; +} +.navbar-item.is-tab { + border-bottom: 1px solid transparent; + min-height: 3.25rem; + padding-bottom: calc(0.5rem - 1px); +} +.navbar-item.is-tab:focus, +.navbar-item.is-tab:hover { + background-color: transparent; + border-bottom-color: #3273dc; +} +.navbar-item.is-tab.is-active { + background-color: transparent; + border-bottom-color: #3273dc; + border-bottom-style: solid; + border-bottom-width: 3px; + color: #3273dc; + padding-bottom: calc(0.5rem - 3px); +} +.navbar-content { + flex-grow: 1; + flex-shrink: 1; +} +.navbar-link:not(.is-arrowless) { + padding-right: 2.5em; +} +.navbar-link:not(.is-arrowless)::after { + border-color: #3273dc; + margin-top: -0.375em; + right: 1.125em; +} +.navbar-dropdown { + font-size: 0.875rem; + padding-bottom: 0.5rem; + padding-top: 0.5rem; +} +.navbar-dropdown .navbar-item { + padding-left: 1.5rem; + padding-right: 1.5rem; +} +.navbar-divider { + background-color: #f5f5f5; + border: none; + display: none; + height: 2px; + margin: 0.5rem 0; +} +@media screen and (max-width: 1087px) { + .navbar > .container { + display: block; + } + .navbar-brand .navbar-item, + .navbar-tabs .navbar-item { + align-items: center; + display: flex; + } + .navbar-link::after { + display: none; + } + .navbar-menu { + background-color: #fff; + box-shadow: 0 8px 16px rgba(10,10,10,0.1); + padding: 0.5rem 0; + } + .navbar-menu.is-active { + display: block; + } + .navbar.is-fixed-bottom-touch, + .navbar.is-fixed-top-touch { + left: 0; + position: fixed; + right: 0; + z-index: 30; + } + .navbar.is-fixed-bottom-touch { + bottom: 0; + } + .navbar.is-fixed-bottom-touch.has-shadow { + box-shadow: 0 -2px 3px rgba(10,10,10,0.1); + } + .navbar.is-fixed-top-touch { + top: 0; + } + .navbar.is-fixed-top .navbar-menu, + .navbar.is-fixed-top-touch .navbar-menu { + -webkit-overflow-scrolling: touch; + max-height: calc(100vh - 3.25rem); + overflow: auto; + } + html.has-navbar-fixed-top-touch, + body.has-navbar-fixed-top-touch { + padding-top: 3.25rem; + } + html.has-navbar-fixed-bottom-touch, + body.has-navbar-fixed-bottom-touch { + padding-bottom: 3.25rem; + } +} +@media screen and (min-width: 1088px) { + .navbar, + .navbar-menu, + .navbar-start, + .navbar-end { + align-items: stretch; + display: flex; + } + .navbar { + min-height: 3.25rem; + } + .navbar.is-spaced { + padding: 1rem 2rem; + } + .navbar.is-spaced .navbar-start, + .navbar.is-spaced .navbar-end { + align-items: center; + } + .navbar.is-spaced a.navbar-item, + .navbar.is-spaced .navbar-link { + border-radius: 4px; + } + .navbar.is-transparent a.navbar-item:focus, + .navbar.is-transparent .navbar-link:focus, + .navbar.is-transparent a.navbar-item:hover, + .navbar.is-transparent .navbar-link:hover, + .navbar.is-transparent a.navbar-item.is-active, + .navbar.is-transparent .navbar-link.is-active { + background-color: transparent !important; + } + .navbar.is-transparent .navbar-item.has-dropdown.is-active .navbar-link, + .navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:focus .navbar-link, + .navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:focus-within .navbar-link, + .navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:hover .navbar-link { + background-color: transparent !important; + } + .navbar.is-transparent .navbar-dropdown a.navbar-item:focus, + .navbar.is-transparent .navbar-dropdown a.navbar-item:hover { + background-color: #f5f5f5; + color: #0a0a0a; + } + .navbar.is-transparent .navbar-dropdown a.navbar-item.is-active { + background-color: #f5f5f5; + color: #3273dc; + } + .navbar-burger { + display: none; + } + .navbar-item, + .navbar-link { + align-items: center; + display: flex; + } + .navbar-item { + display: flex; + } + .navbar-item.has-dropdown { + align-items: stretch; + } + .navbar-item.has-dropdown-up .navbar-link::after { + transform: rotate(135deg) translate(0.25em, -0.25em); + } + .navbar-item.has-dropdown-up .navbar-dropdown { + border-bottom: 2px solid #dbdbdb; + border-radius: 6px 6px 0 0; + border-top: none; + bottom: 100%; + box-shadow: 0 -8px 8px rgba(10,10,10,0.1); + top: auto; + } + .navbar-item.is-active .navbar-dropdown, + .navbar-item.is-hoverable:focus .navbar-dropdown, + .navbar-item.is-hoverable:focus-within .navbar-dropdown, + .navbar-item.is-hoverable:hover .navbar-dropdown { + display: block; + } + .navbar.is-spaced .navbar-item.is-active .navbar-dropdown, + .navbar.is-spaced .navbar-item.is-hoverable:focus .navbar-dropdown, + .navbar.is-spaced .navbar-item.is-hoverable:focus-within .navbar-dropdown, + .navbar.is-spaced .navbar-item.is-hoverable:hover .navbar-dropdown, + .navbar-item.is-active .navbar-dropdown.is-boxed, + .navbar-item.is-hoverable:focus .navbar-dropdown.is-boxed, + .navbar-item.is-hoverable:focus-within .navbar-dropdown.is-boxed, + .navbar-item.is-hoverable:hover .navbar-dropdown.is-boxed { + opacity: 1; + pointer-events: auto; + transform: translateY(0); + } + .navbar-menu { + flex-grow: 1; + flex-shrink: 0; + } + .navbar-start { + justify-content: flex-start; + margin-right: auto; + } + .navbar-end { + justify-content: flex-end; + margin-left: auto; + } + .navbar-dropdown { + background-color: #fff; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + border-top: 2px solid #dbdbdb; + box-shadow: 0 8px 8px rgba(10,10,10,0.1); + display: none; + font-size: 0.875rem; + left: 0; + min-width: 100%; + position: absolute; + top: 100%; + z-index: 20; + } + .navbar-dropdown .navbar-item { + padding: 0.375rem 1rem; + white-space: nowrap; + } + .navbar-dropdown a.navbar-item { + padding-right: 3rem; + } + .navbar-dropdown a.navbar-item:focus, + .navbar-dropdown a.navbar-item:hover { + background-color: #f5f5f5; + color: #0a0a0a; + } + .navbar-dropdown a.navbar-item.is-active { + background-color: #f5f5f5; + color: #3273dc; + } + .navbar.is-spaced .navbar-dropdown, + .navbar-dropdown.is-boxed { + border-radius: 6px; + border-top: none; + box-shadow: 0 8px 8px rgba(10,10,10,0.1), 0 0 0 1px rgba(10,10,10,0.1); + display: block; + opacity: 0; + pointer-events: none; + top: calc(100% + -4px); + transform: translateY(-5px); + transition-duration: 86ms; + transition-property: opacity, transform; + } + .navbar-dropdown.is-right { + left: auto; + right: 0; + } + .navbar-divider { + display: block; + } + .navbar > .container .navbar-brand, + .container > .navbar .navbar-brand { + margin-left: -0.75rem; + } + .navbar > .container .navbar-menu, + .container > .navbar .navbar-menu { + margin-right: -0.75rem; + } + .navbar.is-fixed-bottom-desktop, + .navbar.is-fixed-top-desktop { + left: 0; + position: fixed; + right: 0; + z-index: 30; + } + .navbar.is-fixed-bottom-desktop { + bottom: 0; + } + .navbar.is-fixed-bottom-desktop.has-shadow { + box-shadow: 0 -2px 3px rgba(10,10,10,0.1); + } + .navbar.is-fixed-top-desktop { + top: 0; + } + html.has-navbar-fixed-top-desktop, + body.has-navbar-fixed-top-desktop { + padding-top: 3.25rem; + } + html.has-navbar-fixed-bottom-desktop, + body.has-navbar-fixed-bottom-desktop { + padding-bottom: 3.25rem; + } + html.has-spaced-navbar-fixed-top, + body.has-spaced-navbar-fixed-top { + padding-top: 5.25rem; + } + html.has-spaced-navbar-fixed-bottom, + body.has-spaced-navbar-fixed-bottom { + padding-bottom: 5.25rem; + } + a.navbar-item.is-active, + .navbar-link.is-active { + color: #3273dc; + } + a.navbar-item.is-active:not(:focus):not(:hover), + .navbar-link.is-active:not(:focus):not(:hover) { + background-color: transparent; + } + .navbar-item.has-dropdown:focus .navbar-link, + .navbar-item.has-dropdown:hover .navbar-link, + .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #fafafa; + } +} +.hero.is-fullheight-with-navbar { + min-height: calc(100vh - 3.25rem); +} +.pagination { + font-size: 1rem; + margin: -0.25rem; +} +.pagination.is-small { + font-size: 0.75rem; +} +.pagination.is-medium { + font-size: 1.25rem; +} +.pagination.is-large { + font-size: 1.5rem; +} +.pagination.is-rounded .pagination-previous, +.pagination.is-rounded .pagination-next { + padding-left: 1em; + padding-right: 1em; + border-radius: 290486px; +} +.pagination.is-rounded .pagination-link { + border-radius: 290486px; +} +.pagination, +.pagination-list { + align-items: center; + display: flex; + justify-content: center; + text-align: center; +} +.pagination-previous, +.pagination-next, +.pagination-link, +.pagination-ellipsis { + font-size: 1em; + justify-content: center; + margin: 0.25rem; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: center; +} +.pagination-previous, +.pagination-next, +.pagination-link { + border-color: #dbdbdb; + color: #363636; + min-width: 2.25em; +} +.pagination-previous:hover, +.pagination-next:hover, +.pagination-link:hover { + border-color: #b5b5b5; + color: #363636; +} +.pagination-previous:focus, +.pagination-next:focus, +.pagination-link:focus { + border-color: #3273dc; +} +.pagination-previous:active, +.pagination-next:active, +.pagination-link:active { + box-shadow: inset 0 1px 2px rgba(10,10,10,0.2); +} +.pagination-previous[disabled], +.pagination-next[disabled], +.pagination-link[disabled] { + background-color: #dbdbdb; + border-color: #dbdbdb; + box-shadow: none; + color: #7a7a7a; + opacity: 0.5; +} +.pagination-previous, +.pagination-next { + padding-left: 0.75em; + padding-right: 0.75em; + white-space: nowrap; +} +.pagination-link.is-current { + background-color: #3273dc; + border-color: #3273dc; + color: #fff; +} +.pagination-ellipsis { + color: #b5b5b5; + pointer-events: none; +} +.pagination-list { + flex-wrap: wrap; +} +@media screen and (max-width: 768px) { + .pagination { + flex-wrap: wrap; + } + .pagination-previous, + .pagination-next { + flex-grow: 1; + flex-shrink: 1; + } + .pagination-list li { + flex-grow: 1; + flex-shrink: 1; + } +} +@media screen and (min-width: 769px), print { + .pagination-list { + flex-grow: 1; + flex-shrink: 1; + justify-content: flex-start; + order: 1; + } + .pagination-previous { + order: 2; + } + .pagination-next { + order: 3; + } + .pagination { + justify-content: space-between; + } + .pagination.is-centered .pagination-previous, + .pagination .pagination-previous { + order: 1; + } + .pagination.is-centered .pagination-list, + .pagination .pagination-list { + justify-content: center; + order: 2; + } + .pagination.is-centered .pagination-next, + .pagination .pagination-next { + order: 3; + } + .pagination.is-right .pagination-previous { + order: 1; + } + .pagination.is-right .pagination-next { + order: 2; + } + .pagination.is-right .pagination-list { + justify-content: flex-end; + order: 3; + } +} +.panel { + border-radius: 6px; + box-shadow: 0 0.5em 1em -0.125em rgba(10,10,10,0.1), 0 0px 0 1px rgba(10,10,10,0.02); + font-size: 1rem; +} +.panel:not(:last-child) { + margin-bottom: 1.5rem; +} +.panel.is-white .panel-heading { + background-color: #fff; + color: #0a0a0a; +} +.panel.is-white .panel-tabs a.is-active { + border-bottom-color: #fff; +} +.panel.is-white .panel-block.is-active .panel-icon { + color: #fff; +} +.panel.is-black .panel-heading { + background-color: #0a0a0a; + color: #fff; +} +.panel.is-black .panel-tabs a.is-active { + border-bottom-color: #0a0a0a; +} +.panel.is-black .panel-block.is-active .panel-icon { + color: #0a0a0a; +} +.panel.is-light .panel-heading { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.panel.is-light .panel-tabs a.is-active { + border-bottom-color: #f5f5f5; +} +.panel.is-light .panel-block.is-active .panel-icon { + color: #f5f5f5; +} +.panel.is-dark .panel-heading { + background-color: #363636; + color: #fff; +} +.panel.is-dark .panel-tabs a.is-active { + border-bottom-color: #363636; +} +.panel.is-dark .panel-block.is-active .panel-icon { + color: #363636; +} +.panel.is-primary .panel-heading { + background-color: #3273dc; + color: #fff; +} +.panel.is-primary .panel-tabs a.is-active { + border-bottom-color: #3273dc; +} +.panel.is-primary .panel-block.is-active .panel-icon { + color: #3273dc; +} +.panel.is-link .panel-heading { + background-color: #3273dc; + color: #fff; +} +.panel.is-link .panel-tabs a.is-active { + border-bottom-color: #3273dc; +} +.panel.is-link .panel-block.is-active .panel-icon { + color: #3273dc; +} +.panel.is-info .panel-heading { + background-color: #3298dc; + color: #fff; +} +.panel.is-info .panel-tabs a.is-active { + border-bottom-color: #3298dc; +} +.panel.is-info .panel-block.is-active .panel-icon { + color: #3298dc; +} +.panel.is-success .panel-heading { + background-color: #48c774; + color: #fff; +} +.panel.is-success .panel-tabs a.is-active { + border-bottom-color: #48c774; +} +.panel.is-success .panel-block.is-active .panel-icon { + color: #48c774; +} +.panel.is-warning .panel-heading { + background-color: #ffdd57; + color: rgba(0,0,0,0.7); +} +.panel.is-warning .panel-tabs a.is-active { + border-bottom-color: #ffdd57; +} +.panel.is-warning .panel-block.is-active .panel-icon { + color: #ffdd57; +} +.panel.is-danger .panel-heading { + background-color: #f14668; + color: #fff; +} +.panel.is-danger .panel-tabs a.is-active { + border-bottom-color: #f14668; +} +.panel.is-danger .panel-block.is-active .panel-icon { + color: #f14668; +} +.panel.is-grey-lightest .panel-heading { + background-color: #ededed; + color: #363636; +} +.panel.is-grey-lightest .panel-tabs a.is-active { + border-bottom-color: #ededed; +} +.panel.is-grey-lightest .panel-block.is-active .panel-icon { + color: #ededed; +} +.panel-tabs:not(:last-child), +.panel-block:not(:last-child) { + border-bottom: 1px solid #ededed; +} +.panel-heading { + background-color: #ededed; + border-radius: 6px 6px 0 0; + color: #363636; + font-size: 1.25em; + font-weight: 700; + line-height: 1.25; + padding: 0.75em 1em; +} +.panel-tabs { + align-items: flex-end; + display: flex; + font-size: 0.875em; + justify-content: center; +} +.panel-tabs a { + border-bottom: 1px solid #dbdbdb; + margin-bottom: -1px; + padding: 0.5em; +} +.panel-tabs a.is-active { + border-bottom-color: #4a4a4a; + color: #363636; +} +.panel-list a { + color: #4a4a4a; +} +.panel-list a:hover { + color: #3273dc; +} +.panel-block { + align-items: center; + color: #363636; + display: flex; + justify-content: flex-start; + padding: 0.5em 0.75em; +} +.panel-block input[type="checkbox"] { + margin-right: 0.75em; +} +.panel-block > .control { + flex-grow: 1; + flex-shrink: 1; + width: 100%; +} +.panel-block.is-wrapped { + flex-wrap: wrap; +} +.panel-block.is-active { + border-left-color: #3273dc; + color: #363636; +} +.panel-block.is-active .panel-icon { + color: #3273dc; +} +.panel-block:last-child { + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; +} +a.panel-block, +label.panel-block { + cursor: pointer; +} +a.panel-block:hover, +label.panel-block:hover { + background-color: #f5f5f5; +} +.panel-icon { + display: inline-block; + font-size: 14px; + height: 1em; + line-height: 1em; + text-align: center; + vertical-align: top; + width: 1em; + color: #7a7a7a; + margin-right: 0.75em; +} +.panel-icon .fa { + font-size: inherit; + line-height: inherit; +} +.tabs { + -webkit-overflow-scrolling: touch; + align-items: stretch; + display: flex; + font-size: 1rem; + justify-content: space-between; + overflow: hidden; + overflow-x: auto; + white-space: nowrap; +} +.tabs a { + align-items: center; + border-bottom-color: #dbdbdb; + border-bottom-style: solid; + border-bottom-width: 1px; + color: #4a4a4a; + display: flex; + justify-content: center; + margin-bottom: -1px; + padding: 0.5em 1em; + vertical-align: top; +} +.tabs a:hover { + border-bottom-color: #363636; + color: #363636; +} +.tabs li { + display: block; +} +.tabs li.is-active a { + border-bottom-color: #3273dc; + color: #3273dc; +} +.tabs ul { + align-items: center; + border-bottom-color: #dbdbdb; + border-bottom-style: solid; + border-bottom-width: 1px; + display: flex; + flex-grow: 1; + flex-shrink: 0; + justify-content: flex-start; +} +.tabs ul.is-left { + padding-right: 0.75em; +} +.tabs ul.is-center { + flex: none; + justify-content: center; + padding-left: 0.75em; + padding-right: 0.75em; +} +.tabs ul.is-right { + justify-content: flex-end; + padding-left: 0.75em; +} +.tabs .icon:first-child { + margin-right: 0.5em; +} +.tabs .icon:last-child { + margin-left: 0.5em; +} +.tabs.is-centered ul { + justify-content: center; +} +.tabs.is-right ul { + justify-content: flex-end; +} +.tabs.is-boxed a { + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.tabs.is-boxed a:hover { + background-color: #f5f5f5; + border-bottom-color: #dbdbdb; +} +.tabs.is-boxed li.is-active a { + background-color: #fff; + border-color: #dbdbdb; + border-bottom-color: transparent !important; +} +.tabs.is-fullwidth li { + flex-grow: 1; + flex-shrink: 0; +} +.tabs.is-toggle a { + border-color: #dbdbdb; + border-style: solid; + border-width: 1px; + margin-bottom: 0; + position: relative; +} +.tabs.is-toggle a:hover { + background-color: #f5f5f5; + border-color: #b5b5b5; + z-index: 2; +} +.tabs.is-toggle li + li { + margin-left: -1px; +} +.tabs.is-toggle li:first-child a { + border-radius: 4px 0 0 4px; +} +.tabs.is-toggle li:last-child a { + border-radius: 0 4px 4px 0; +} +.tabs.is-toggle li.is-active a { + background-color: #3273dc; + border-color: #3273dc; + color: #fff; + z-index: 1; +} +.tabs.is-toggle ul { + border-bottom: none; +} +.tabs.is-toggle.is-toggle-rounded li:first-child a { + border-bottom-left-radius: 290486px; + border-top-left-radius: 290486px; + padding-left: 1.25em; +} +.tabs.is-toggle.is-toggle-rounded li:last-child a { + border-bottom-right-radius: 290486px; + border-top-right-radius: 290486px; + padding-right: 1.25em; +} +.tabs.is-small { + font-size: 0.75rem; +} +.tabs.is-medium { + font-size: 1.25rem; +} +.tabs.is-large { + font-size: 1.5rem; +} +.box { + background-color: #fff; + border-radius: 6px; + box-shadow: 0 0.5em 1em -0.125em rgba(10,10,10,0.1), 0 0px 0 1px rgba(10,10,10,0.02); + color: #4a4a4a; + display: block; + padding: 1.25rem; +} +a.box:hover, +a.box:focus { + box-shadow: 0 0.5em 1em -0.125em rgba(10,10,10,0.1), 0 0 0 1px #3273dc; +} +a.box:active { + box-shadow: inset 0 1px 2px rgba(10,10,10,0.2), 0 0 0 1px #3273dc; +} +.button { + background-color: #fff; + border-color: #dbdbdb; + border-width: 1px; + color: #363636; + cursor: pointer; + justify-content: center; + padding-bottom: calc(0.375em - 1px); + padding-left: 1em; + padding-right: 1em; + padding-top: calc(0.375em - 1px); + text-align: center; + white-space: nowrap; +} +.button strong { + color: inherit; +} +.button .icon, +.button .icon.is-small, +.button .icon.is-medium, +.button .icon.is-large { + height: 1.5em; + width: 1.5em; +} +.button .icon:first-child:not(:last-child) { + margin-left: calc(-0.5em - 1px); + margin-right: 0.25em; +} +.button .icon:last-child:not(:first-child) { + margin-left: 0.25em; + margin-right: calc(-0.5em - 1px); +} +.button .icon:first-child:last-child { + margin-left: calc(-0.5em - 1px); + margin-right: calc(-0.5em - 1px); +} +.button:hover, +.button.is-hovered { + border-color: #b5b5b5; + color: #363636; +} +.button:focus, +.button.is-focused { + border-color: #3273dc; + color: #363636; +} +.button:focus:not(:active), +.button.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(50,115,220,0.25); +} +.button:active, +.button.is-active { + border-color: #4a4a4a; + color: #363636; +} +.button.is-text { + background-color: transparent; + border-color: transparent; + color: #4a4a4a; + text-decoration: underline; +} +.button.is-text:hover, +.button.is-text.is-hovered, +.button.is-text:focus, +.button.is-text.is-focused { + background-color: #f5f5f5; + color: #363636; +} +.button.is-text:active, +.button.is-text.is-active { + background-color: #e8e8e8; + color: #363636; +} +.button.is-text[disabled], +fieldset[disabled] .button.is-text { + background-color: transparent; + border-color: transparent; + box-shadow: none; +} +.button.is-white { + background-color: #fff; + border-color: transparent; + color: #0a0a0a; +} +.button.is-white:hover, +.button.is-white.is-hovered { + background-color: #f9f9f9; + border-color: transparent; + color: #0a0a0a; +} +.button.is-white:focus, +.button.is-white.is-focused { + border-color: transparent; + color: #0a0a0a; +} +.button.is-white:focus:not(:active), +.button.is-white.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(255,255,255,0.25); +} +.button.is-white:active, +.button.is-white.is-active { + background-color: #f2f2f2; + border-color: transparent; + color: #0a0a0a; +} +.button.is-white[disabled], +fieldset[disabled] .button.is-white { + background-color: #fff; + border-color: transparent; + box-shadow: none; +} +.button.is-white.is-inverted { + background-color: #0a0a0a; + color: #fff; +} +.button.is-white.is-inverted:hover, +.button.is-white.is-inverted.is-hovered { + background-color: #000; +} +.button.is-white.is-inverted[disabled], +fieldset[disabled] .button.is-white.is-inverted { + background-color: #0a0a0a; + border-color: transparent; + box-shadow: none; + color: #fff; +} +.button.is-white.is-loading::after { + border-color: transparent transparent #0a0a0a #0a0a0a !important; +} +.button.is-white.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; +} +.button.is-white.is-outlined:hover, +.button.is-white.is-outlined.is-hovered, +.button.is-white.is-outlined:focus, +.button.is-white.is-outlined.is-focused { + background-color: #fff; + border-color: #fff; + color: #0a0a0a; +} +.button.is-white.is-outlined.is-loading::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-white.is-outlined.is-loading:hover::after, +.button.is-white.is-outlined.is-loading.is-hovered::after, +.button.is-white.is-outlined.is-loading:focus::after, +.button.is-white.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #0a0a0a #0a0a0a !important; +} +.button.is-white.is-outlined[disabled], +fieldset[disabled] .button.is-white.is-outlined { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; +} +.button.is-white.is-inverted.is-outlined { + background-color: transparent; + border-color: #0a0a0a; + color: #0a0a0a; +} +.button.is-white.is-inverted.is-outlined:hover, +.button.is-white.is-inverted.is-outlined.is-hovered, +.button.is-white.is-inverted.is-outlined:focus, +.button.is-white.is-inverted.is-outlined.is-focused { + background-color: #0a0a0a; + color: #fff; +} +.button.is-white.is-inverted.is-outlined.is-loading:hover::after, +.button.is-white.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-white.is-inverted.is-outlined.is-loading:focus::after, +.button.is-white.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-white.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-white.is-inverted.is-outlined { + background-color: transparent; + border-color: #0a0a0a; + box-shadow: none; + color: #0a0a0a; +} +.button.is-black { + background-color: #0a0a0a; + border-color: transparent; + color: #fff; +} +.button.is-black:hover, +.button.is-black.is-hovered { + background-color: #040404; + border-color: transparent; + color: #fff; +} +.button.is-black:focus, +.button.is-black.is-focused { + border-color: transparent; + color: #fff; +} +.button.is-black:focus:not(:active), +.button.is-black.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(10,10,10,0.25); +} +.button.is-black:active, +.button.is-black.is-active { + background-color: #000; + border-color: transparent; + color: #fff; +} +.button.is-black[disabled], +fieldset[disabled] .button.is-black { + background-color: #0a0a0a; + border-color: transparent; + box-shadow: none; +} +.button.is-black.is-inverted { + background-color: #fff; + color: #0a0a0a; +} +.button.is-black.is-inverted:hover, +.button.is-black.is-inverted.is-hovered { + background-color: #f2f2f2; +} +.button.is-black.is-inverted[disabled], +fieldset[disabled] .button.is-black.is-inverted { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #0a0a0a; +} +.button.is-black.is-loading::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-black.is-outlined { + background-color: transparent; + border-color: #0a0a0a; + color: #0a0a0a; +} +.button.is-black.is-outlined:hover, +.button.is-black.is-outlined.is-hovered, +.button.is-black.is-outlined:focus, +.button.is-black.is-outlined.is-focused { + background-color: #0a0a0a; + border-color: #0a0a0a; + color: #fff; +} +.button.is-black.is-outlined.is-loading::after { + border-color: transparent transparent #0a0a0a #0a0a0a !important; +} +.button.is-black.is-outlined.is-loading:hover::after, +.button.is-black.is-outlined.is-loading.is-hovered::after, +.button.is-black.is-outlined.is-loading:focus::after, +.button.is-black.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-black.is-outlined[disabled], +fieldset[disabled] .button.is-black.is-outlined { + background-color: transparent; + border-color: #0a0a0a; + box-shadow: none; + color: #0a0a0a; +} +.button.is-black.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; +} +.button.is-black.is-inverted.is-outlined:hover, +.button.is-black.is-inverted.is-outlined.is-hovered, +.button.is-black.is-inverted.is-outlined:focus, +.button.is-black.is-inverted.is-outlined.is-focused { + background-color: #fff; + color: #0a0a0a; +} +.button.is-black.is-inverted.is-outlined.is-loading:hover::after, +.button.is-black.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-black.is-inverted.is-outlined.is-loading:focus::after, +.button.is-black.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #0a0a0a #0a0a0a !important; +} +.button.is-black.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-black.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; +} +.button.is-light, +article.article .article-more { + background-color: #f5f5f5; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-light:hover, +article.article .article-more:hover, +.button.is-light.is-hovered, +article.article .article-more.is-hovered { + background-color: #eee; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-light:focus, +article.article .article-more:focus, +.button.is-light.is-focused, +article.article .article-more.is-focused { + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-light:focus:not(:active), +article.article .article-more:focus:not(:active), +.button.is-light.is-focused:not(:active), +article.article .article-more.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(245,245,245,0.25); +} +.button.is-light:active, +article.article .article-more:active, +.button.is-light.is-active, +article.article .article-more.is-active { + background-color: #e8e8e8; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-light[disabled], +article.article .article-more[disabled], +fieldset[disabled] .button.is-light, +fieldset[disabled] article.article .article-more { + background-color: #f5f5f5; + border-color: transparent; + box-shadow: none; +} +.button.is-light.is-inverted, +article.article .article-more.is-inverted { + background-color: rgba(0,0,0,0.7); + color: #f5f5f5; +} +.button.is-light.is-inverted:hover, +article.article .article-more.is-inverted:hover, +.button.is-light.is-inverted.is-hovered, +article.article .article-more.is-inverted.is-hovered { + background-color: rgba(0,0,0,0.7); +} +.button.is-light.is-inverted[disabled], +article.article .article-more.is-inverted[disabled], +fieldset[disabled] .button.is-light.is-inverted, +fieldset[disabled] article.article .article-more.is-inverted { + background-color: rgba(0,0,0,0.7); + border-color: transparent; + box-shadow: none; + color: #f5f5f5; +} +.button.is-light.is-loading::after, +article.article .article-more.is-loading::after { + border-color: transparent transparent rgba(0,0,0,0.7) rgba(0,0,0,0.7) !important; +} +.button.is-light.is-outlined, +article.article .article-more.is-outlined { + background-color: transparent; + border-color: #f5f5f5; + color: #f5f5f5; +} +.button.is-light.is-outlined:hover, +article.article .article-more.is-outlined:hover, +.button.is-light.is-outlined.is-hovered, +article.article .article-more.is-outlined.is-hovered, +.button.is-light.is-outlined:focus, +article.article .article-more.is-outlined:focus, +.button.is-light.is-outlined.is-focused, +article.article .article-more.is-outlined.is-focused { + background-color: #f5f5f5; + border-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.button.is-light.is-outlined.is-loading::after, +article.article .article-more.is-outlined.is-loading::after { + border-color: transparent transparent #f5f5f5 #f5f5f5 !important; +} +.button.is-light.is-outlined.is-loading:hover::after, +article.article .article-more.is-outlined.is-loading:hover::after, +.button.is-light.is-outlined.is-loading.is-hovered::after, +article.article .article-more.is-outlined.is-loading.is-hovered::after, +.button.is-light.is-outlined.is-loading:focus::after, +article.article .article-more.is-outlined.is-loading:focus::after, +.button.is-light.is-outlined.is-loading.is-focused::after, +article.article .article-more.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent rgba(0,0,0,0.7) rgba(0,0,0,0.7) !important; +} +.button.is-light.is-outlined[disabled], +article.article .article-more.is-outlined[disabled], +fieldset[disabled] .button.is-light.is-outlined, +fieldset[disabled] article.article .article-more.is-outlined { + background-color: transparent; + border-color: #f5f5f5; + box-shadow: none; + color: #f5f5f5; +} +.button.is-light.is-inverted.is-outlined, +article.article .article-more.is-inverted.is-outlined { + background-color: transparent; + border-color: rgba(0,0,0,0.7); + color: rgba(0,0,0,0.7); +} +.button.is-light.is-inverted.is-outlined:hover, +article.article .article-more.is-inverted.is-outlined:hover, +.button.is-light.is-inverted.is-outlined.is-hovered, +article.article .article-more.is-inverted.is-outlined.is-hovered, +.button.is-light.is-inverted.is-outlined:focus, +article.article .article-more.is-inverted.is-outlined:focus, +.button.is-light.is-inverted.is-outlined.is-focused, +article.article .article-more.is-inverted.is-outlined.is-focused { + background-color: rgba(0,0,0,0.7); + color: #f5f5f5; +} +.button.is-light.is-inverted.is-outlined.is-loading:hover::after, +article.article .article-more.is-inverted.is-outlined.is-loading:hover::after, +.button.is-light.is-inverted.is-outlined.is-loading.is-hovered::after, +article.article .article-more.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-light.is-inverted.is-outlined.is-loading:focus::after, +article.article .article-more.is-inverted.is-outlined.is-loading:focus::after, +.button.is-light.is-inverted.is-outlined.is-loading.is-focused::after, +article.article .article-more.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #f5f5f5 #f5f5f5 !important; +} +.button.is-light.is-inverted.is-outlined[disabled], +article.article .article-more.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-light.is-inverted.is-outlined, +fieldset[disabled] article.article .article-more.is-inverted.is-outlined { + background-color: transparent; + border-color: rgba(0,0,0,0.7); + box-shadow: none; + color: rgba(0,0,0,0.7); +} +.button.is-dark { + background-color: #363636; + border-color: transparent; + color: #fff; +} +.button.is-dark:hover, +.button.is-dark.is-hovered { + background-color: #2f2f2f; + border-color: transparent; + color: #fff; +} +.button.is-dark:focus, +.button.is-dark.is-focused { + border-color: transparent; + color: #fff; +} +.button.is-dark:focus:not(:active), +.button.is-dark.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(54,54,54,0.25); +} +.button.is-dark:active, +.button.is-dark.is-active { + background-color: #292929; + border-color: transparent; + color: #fff; +} +.button.is-dark[disabled], +fieldset[disabled] .button.is-dark { + background-color: #363636; + border-color: transparent; + box-shadow: none; +} +.button.is-dark.is-inverted { + background-color: #fff; + color: #363636; +} +.button.is-dark.is-inverted:hover, +.button.is-dark.is-inverted.is-hovered { + background-color: #f2f2f2; +} +.button.is-dark.is-inverted[disabled], +fieldset[disabled] .button.is-dark.is-inverted { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #363636; +} +.button.is-dark.is-loading::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-dark.is-outlined { + background-color: transparent; + border-color: #363636; + color: #363636; +} +.button.is-dark.is-outlined:hover, +.button.is-dark.is-outlined.is-hovered, +.button.is-dark.is-outlined:focus, +.button.is-dark.is-outlined.is-focused { + background-color: #363636; + border-color: #363636; + color: #fff; +} +.button.is-dark.is-outlined.is-loading::after { + border-color: transparent transparent #363636 #363636 !important; +} +.button.is-dark.is-outlined.is-loading:hover::after, +.button.is-dark.is-outlined.is-loading.is-hovered::after, +.button.is-dark.is-outlined.is-loading:focus::after, +.button.is-dark.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-dark.is-outlined[disabled], +fieldset[disabled] .button.is-dark.is-outlined { + background-color: transparent; + border-color: #363636; + box-shadow: none; + color: #363636; +} +.button.is-dark.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; +} +.button.is-dark.is-inverted.is-outlined:hover, +.button.is-dark.is-inverted.is-outlined.is-hovered, +.button.is-dark.is-inverted.is-outlined:focus, +.button.is-dark.is-inverted.is-outlined.is-focused { + background-color: #fff; + color: #363636; +} +.button.is-dark.is-inverted.is-outlined.is-loading:hover::after, +.button.is-dark.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-dark.is-inverted.is-outlined.is-loading:focus::after, +.button.is-dark.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #363636 #363636 !important; +} +.button.is-dark.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-dark.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; +} +.button.is-primary { + background-color: #3273dc; + border-color: transparent; + color: #fff; +} +.button.is-primary:hover, +.button.is-primary.is-hovered { + background-color: #276cda; + border-color: transparent; + color: #fff; +} +.button.is-primary:focus, +.button.is-primary.is-focused { + border-color: transparent; + color: #fff; +} +.button.is-primary:focus:not(:active), +.button.is-primary.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(50,115,220,0.25); +} +.button.is-primary:active, +.button.is-primary.is-active { + background-color: #2366d1; + border-color: transparent; + color: #fff; +} +.button.is-primary[disabled], +fieldset[disabled] .button.is-primary { + background-color: #3273dc; + border-color: transparent; + box-shadow: none; +} +.button.is-primary.is-inverted { + background-color: #fff; + color: #3273dc; +} +.button.is-primary.is-inverted:hover, +.button.is-primary.is-inverted.is-hovered { + background-color: #f2f2f2; +} +.button.is-primary.is-inverted[disabled], +fieldset[disabled] .button.is-primary.is-inverted { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #3273dc; +} +.button.is-primary.is-loading::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-primary.is-outlined { + background-color: transparent; + border-color: #3273dc; + color: #3273dc; +} +.button.is-primary.is-outlined:hover, +.button.is-primary.is-outlined.is-hovered, +.button.is-primary.is-outlined:focus, +.button.is-primary.is-outlined.is-focused { + background-color: #3273dc; + border-color: #3273dc; + color: #fff; +} +.button.is-primary.is-outlined.is-loading::after { + border-color: transparent transparent #3273dc #3273dc !important; +} +.button.is-primary.is-outlined.is-loading:hover::after, +.button.is-primary.is-outlined.is-loading.is-hovered::after, +.button.is-primary.is-outlined.is-loading:focus::after, +.button.is-primary.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-primary.is-outlined[disabled], +fieldset[disabled] .button.is-primary.is-outlined { + background-color: transparent; + border-color: #3273dc; + box-shadow: none; + color: #3273dc; +} +.button.is-primary.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; +} +.button.is-primary.is-inverted.is-outlined:hover, +.button.is-primary.is-inverted.is-outlined.is-hovered, +.button.is-primary.is-inverted.is-outlined:focus, +.button.is-primary.is-inverted.is-outlined.is-focused { + background-color: #fff; + color: #3273dc; +} +.button.is-primary.is-inverted.is-outlined.is-loading:hover::after, +.button.is-primary.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-primary.is-inverted.is-outlined.is-loading:focus::after, +.button.is-primary.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #3273dc #3273dc !important; +} +.button.is-primary.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-primary.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; +} +.button.is-primary.is-light { + background-color: #eef3fc; + color: #2160c4; +} +.button.is-primary.is-light:hover, +.button.is-primary.is-light.is-hovered { + background-color: #e3ecfa; + border-color: transparent; + color: #2160c4; +} +.button.is-primary.is-light:active, +.button.is-primary.is-light.is-active { + background-color: #d8e4f8; + border-color: transparent; + color: #2160c4; +} +.button.is-link { + background-color: #3273dc; + border-color: transparent; + color: #fff; +} +.button.is-link:hover, +.button.is-link.is-hovered { + background-color: #276cda; + border-color: transparent; + color: #fff; +} +.button.is-link:focus, +.button.is-link.is-focused { + border-color: transparent; + color: #fff; +} +.button.is-link:focus:not(:active), +.button.is-link.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(50,115,220,0.25); +} +.button.is-link:active, +.button.is-link.is-active { + background-color: #2366d1; + border-color: transparent; + color: #fff; +} +.button.is-link[disabled], +fieldset[disabled] .button.is-link { + background-color: #3273dc; + border-color: transparent; + box-shadow: none; +} +.button.is-link.is-inverted { + background-color: #fff; + color: #3273dc; +} +.button.is-link.is-inverted:hover, +.button.is-link.is-inverted.is-hovered { + background-color: #f2f2f2; +} +.button.is-link.is-inverted[disabled], +fieldset[disabled] .button.is-link.is-inverted { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #3273dc; +} +.button.is-link.is-loading::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-link.is-outlined { + background-color: transparent; + border-color: #3273dc; + color: #3273dc; +} +.button.is-link.is-outlined:hover, +.button.is-link.is-outlined.is-hovered, +.button.is-link.is-outlined:focus, +.button.is-link.is-outlined.is-focused { + background-color: #3273dc; + border-color: #3273dc; + color: #fff; +} +.button.is-link.is-outlined.is-loading::after { + border-color: transparent transparent #3273dc #3273dc !important; +} +.button.is-link.is-outlined.is-loading:hover::after, +.button.is-link.is-outlined.is-loading.is-hovered::after, +.button.is-link.is-outlined.is-loading:focus::after, +.button.is-link.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-link.is-outlined[disabled], +fieldset[disabled] .button.is-link.is-outlined { + background-color: transparent; + border-color: #3273dc; + box-shadow: none; + color: #3273dc; +} +.button.is-link.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; +} +.button.is-link.is-inverted.is-outlined:hover, +.button.is-link.is-inverted.is-outlined.is-hovered, +.button.is-link.is-inverted.is-outlined:focus, +.button.is-link.is-inverted.is-outlined.is-focused { + background-color: #fff; + color: #3273dc; +} +.button.is-link.is-inverted.is-outlined.is-loading:hover::after, +.button.is-link.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-link.is-inverted.is-outlined.is-loading:focus::after, +.button.is-link.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #3273dc #3273dc !important; +} +.button.is-link.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-link.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; +} +.button.is-link.is-light { + background-color: #eef3fc; + color: #2160c4; +} +.button.is-link.is-light:hover, +.button.is-link.is-light.is-hovered { + background-color: #e3ecfa; + border-color: transparent; + color: #2160c4; +} +.button.is-link.is-light:active, +.button.is-link.is-light.is-active { + background-color: #d8e4f8; + border-color: transparent; + color: #2160c4; +} +.button.is-info { + background-color: #3298dc; + border-color: transparent; + color: #fff; +} +.button.is-info:hover, +.button.is-info.is-hovered { + background-color: #2793da; + border-color: transparent; + color: #fff; +} +.button.is-info:focus, +.button.is-info.is-focused { + border-color: transparent; + color: #fff; +} +.button.is-info:focus:not(:active), +.button.is-info.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(50,152,220,0.25); +} +.button.is-info:active, +.button.is-info.is-active { + background-color: #238cd1; + border-color: transparent; + color: #fff; +} +.button.is-info[disabled], +fieldset[disabled] .button.is-info { + background-color: #3298dc; + border-color: transparent; + box-shadow: none; +} +.button.is-info.is-inverted { + background-color: #fff; + color: #3298dc; +} +.button.is-info.is-inverted:hover, +.button.is-info.is-inverted.is-hovered { + background-color: #f2f2f2; +} +.button.is-info.is-inverted[disabled], +fieldset[disabled] .button.is-info.is-inverted { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #3298dc; +} +.button.is-info.is-loading::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-info.is-outlined { + background-color: transparent; + border-color: #3298dc; + color: #3298dc; +} +.button.is-info.is-outlined:hover, +.button.is-info.is-outlined.is-hovered, +.button.is-info.is-outlined:focus, +.button.is-info.is-outlined.is-focused { + background-color: #3298dc; + border-color: #3298dc; + color: #fff; +} +.button.is-info.is-outlined.is-loading::after { + border-color: transparent transparent #3298dc #3298dc !important; +} +.button.is-info.is-outlined.is-loading:hover::after, +.button.is-info.is-outlined.is-loading.is-hovered::after, +.button.is-info.is-outlined.is-loading:focus::after, +.button.is-info.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-info.is-outlined[disabled], +fieldset[disabled] .button.is-info.is-outlined { + background-color: transparent; + border-color: #3298dc; + box-shadow: none; + color: #3298dc; +} +.button.is-info.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; +} +.button.is-info.is-inverted.is-outlined:hover, +.button.is-info.is-inverted.is-outlined.is-hovered, +.button.is-info.is-inverted.is-outlined:focus, +.button.is-info.is-inverted.is-outlined.is-focused { + background-color: #fff; + color: #3298dc; +} +.button.is-info.is-inverted.is-outlined.is-loading:hover::after, +.button.is-info.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-info.is-inverted.is-outlined.is-loading:focus::after, +.button.is-info.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #3298dc #3298dc !important; +} +.button.is-info.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-info.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; +} +.button.is-info.is-light { + background-color: #eef6fc; + color: #1d72aa; +} +.button.is-info.is-light:hover, +.button.is-info.is-light.is-hovered { + background-color: #e3f1fa; + border-color: transparent; + color: #1d72aa; +} +.button.is-info.is-light:active, +.button.is-info.is-light.is-active { + background-color: #d8ebf8; + border-color: transparent; + color: #1d72aa; +} +.button.is-success { + background-color: #48c774; + border-color: transparent; + color: #fff; +} +.button.is-success:hover, +.button.is-success.is-hovered { + background-color: #3ec46d; + border-color: transparent; + color: #fff; +} +.button.is-success:focus, +.button.is-success.is-focused { + border-color: transparent; + color: #fff; +} +.button.is-success:focus:not(:active), +.button.is-success.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(72,199,116,0.25); +} +.button.is-success:active, +.button.is-success.is-active { + background-color: #3abb67; + border-color: transparent; + color: #fff; +} +.button.is-success[disabled], +fieldset[disabled] .button.is-success { + background-color: #48c774; + border-color: transparent; + box-shadow: none; +} +.button.is-success.is-inverted { + background-color: #fff; + color: #48c774; +} +.button.is-success.is-inverted:hover, +.button.is-success.is-inverted.is-hovered { + background-color: #f2f2f2; +} +.button.is-success.is-inverted[disabled], +fieldset[disabled] .button.is-success.is-inverted { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #48c774; +} +.button.is-success.is-loading::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-success.is-outlined { + background-color: transparent; + border-color: #48c774; + color: #48c774; +} +.button.is-success.is-outlined:hover, +.button.is-success.is-outlined.is-hovered, +.button.is-success.is-outlined:focus, +.button.is-success.is-outlined.is-focused { + background-color: #48c774; + border-color: #48c774; + color: #fff; +} +.button.is-success.is-outlined.is-loading::after { + border-color: transparent transparent #48c774 #48c774 !important; +} +.button.is-success.is-outlined.is-loading:hover::after, +.button.is-success.is-outlined.is-loading.is-hovered::after, +.button.is-success.is-outlined.is-loading:focus::after, +.button.is-success.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-success.is-outlined[disabled], +fieldset[disabled] .button.is-success.is-outlined { + background-color: transparent; + border-color: #48c774; + box-shadow: none; + color: #48c774; +} +.button.is-success.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; +} +.button.is-success.is-inverted.is-outlined:hover, +.button.is-success.is-inverted.is-outlined.is-hovered, +.button.is-success.is-inverted.is-outlined:focus, +.button.is-success.is-inverted.is-outlined.is-focused { + background-color: #fff; + color: #48c774; +} +.button.is-success.is-inverted.is-outlined.is-loading:hover::after, +.button.is-success.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-success.is-inverted.is-outlined.is-loading:focus::after, +.button.is-success.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #48c774 #48c774 !important; +} +.button.is-success.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-success.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; +} +.button.is-success.is-light { + background-color: #effaf3; + color: #257942; +} +.button.is-success.is-light:hover, +.button.is-success.is-light.is-hovered { + background-color: #e6f7ec; + border-color: transparent; + color: #257942; +} +.button.is-success.is-light:active, +.button.is-success.is-light.is-active { + background-color: #dcf4e4; + border-color: transparent; + color: #257942; +} +.button.is-warning { + background-color: #ffdd57; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-warning:hover, +.button.is-warning.is-hovered { + background-color: #ffdb4a; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-warning:focus, +.button.is-warning.is-focused { + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-warning:focus:not(:active), +.button.is-warning.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(255,221,87,0.25); +} +.button.is-warning:active, +.button.is-warning.is-active { + background-color: #ffd83d; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.button.is-warning[disabled], +fieldset[disabled] .button.is-warning { + background-color: #ffdd57; + border-color: transparent; + box-shadow: none; +} +.button.is-warning.is-inverted { + background-color: rgba(0,0,0,0.7); + color: #ffdd57; +} +.button.is-warning.is-inverted:hover, +.button.is-warning.is-inverted.is-hovered { + background-color: rgba(0,0,0,0.7); +} +.button.is-warning.is-inverted[disabled], +fieldset[disabled] .button.is-warning.is-inverted { + background-color: rgba(0,0,0,0.7); + border-color: transparent; + box-shadow: none; + color: #ffdd57; +} +.button.is-warning.is-loading::after { + border-color: transparent transparent rgba(0,0,0,0.7) rgba(0,0,0,0.7) !important; +} +.button.is-warning.is-outlined { + background-color: transparent; + border-color: #ffdd57; + color: #ffdd57; +} +.button.is-warning.is-outlined:hover, +.button.is-warning.is-outlined.is-hovered, +.button.is-warning.is-outlined:focus, +.button.is-warning.is-outlined.is-focused { + background-color: #ffdd57; + border-color: #ffdd57; + color: rgba(0,0,0,0.7); +} +.button.is-warning.is-outlined.is-loading::after { + border-color: transparent transparent #ffdd57 #ffdd57 !important; +} +.button.is-warning.is-outlined.is-loading:hover::after, +.button.is-warning.is-outlined.is-loading.is-hovered::after, +.button.is-warning.is-outlined.is-loading:focus::after, +.button.is-warning.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent rgba(0,0,0,0.7) rgba(0,0,0,0.7) !important; +} +.button.is-warning.is-outlined[disabled], +fieldset[disabled] .button.is-warning.is-outlined { + background-color: transparent; + border-color: #ffdd57; + box-shadow: none; + color: #ffdd57; +} +.button.is-warning.is-inverted.is-outlined { + background-color: transparent; + border-color: rgba(0,0,0,0.7); + color: rgba(0,0,0,0.7); +} +.button.is-warning.is-inverted.is-outlined:hover, +.button.is-warning.is-inverted.is-outlined.is-hovered, +.button.is-warning.is-inverted.is-outlined:focus, +.button.is-warning.is-inverted.is-outlined.is-focused { + background-color: rgba(0,0,0,0.7); + color: #ffdd57; +} +.button.is-warning.is-inverted.is-outlined.is-loading:hover::after, +.button.is-warning.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-warning.is-inverted.is-outlined.is-loading:focus::after, +.button.is-warning.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #ffdd57 #ffdd57 !important; +} +.button.is-warning.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-warning.is-inverted.is-outlined { + background-color: transparent; + border-color: rgba(0,0,0,0.7); + box-shadow: none; + color: rgba(0,0,0,0.7); +} +.button.is-warning.is-light { + background-color: #fffbeb; + color: #947600; +} +.button.is-warning.is-light:hover, +.button.is-warning.is-light.is-hovered { + background-color: #fff8de; + border-color: transparent; + color: #947600; +} +.button.is-warning.is-light:active, +.button.is-warning.is-light.is-active { + background-color: #fff6d1; + border-color: transparent; + color: #947600; +} +.button.is-danger { + background-color: #f14668; + border-color: transparent; + color: #fff; +} +.button.is-danger:hover, +.button.is-danger.is-hovered { + background-color: #f03a5f; + border-color: transparent; + color: #fff; +} +.button.is-danger:focus, +.button.is-danger.is-focused { + border-color: transparent; + color: #fff; +} +.button.is-danger:focus:not(:active), +.button.is-danger.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(241,70,104,0.25); +} +.button.is-danger:active, +.button.is-danger.is-active { + background-color: #ef2e55; + border-color: transparent; + color: #fff; +} +.button.is-danger[disabled], +fieldset[disabled] .button.is-danger { + background-color: #f14668; + border-color: transparent; + box-shadow: none; +} +.button.is-danger.is-inverted { + background-color: #fff; + color: #f14668; +} +.button.is-danger.is-inverted:hover, +.button.is-danger.is-inverted.is-hovered { + background-color: #f2f2f2; +} +.button.is-danger.is-inverted[disabled], +fieldset[disabled] .button.is-danger.is-inverted { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #f14668; +} +.button.is-danger.is-loading::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-danger.is-outlined { + background-color: transparent; + border-color: #f14668; + color: #f14668; +} +.button.is-danger.is-outlined:hover, +.button.is-danger.is-outlined.is-hovered, +.button.is-danger.is-outlined:focus, +.button.is-danger.is-outlined.is-focused { + background-color: #f14668; + border-color: #f14668; + color: #fff; +} +.button.is-danger.is-outlined.is-loading::after { + border-color: transparent transparent #f14668 #f14668 !important; +} +.button.is-danger.is-outlined.is-loading:hover::after, +.button.is-danger.is-outlined.is-loading.is-hovered::after, +.button.is-danger.is-outlined.is-loading:focus::after, +.button.is-danger.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #fff #fff !important; +} +.button.is-danger.is-outlined[disabled], +fieldset[disabled] .button.is-danger.is-outlined { + background-color: transparent; + border-color: #f14668; + box-shadow: none; + color: #f14668; +} +.button.is-danger.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; +} +.button.is-danger.is-inverted.is-outlined:hover, +.button.is-danger.is-inverted.is-outlined.is-hovered, +.button.is-danger.is-inverted.is-outlined:focus, +.button.is-danger.is-inverted.is-outlined.is-focused { + background-color: #fff; + color: #f14668; +} +.button.is-danger.is-inverted.is-outlined.is-loading:hover::after, +.button.is-danger.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-danger.is-inverted.is-outlined.is-loading:focus::after, +.button.is-danger.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #f14668 #f14668 !important; +} +.button.is-danger.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-danger.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; +} +.button.is-danger.is-light { + background-color: #feecf0; + color: #cc0f35; +} +.button.is-danger.is-light:hover, +.button.is-danger.is-light.is-hovered { + background-color: #fde0e6; + border-color: transparent; + color: #cc0f35; +} +.button.is-danger.is-light:active, +.button.is-danger.is-light.is-active { + background-color: #fcd4dc; + border-color: transparent; + color: #cc0f35; +} +.button.is-grey-lightest { + background-color: #ededed; + border-color: transparent; + color: #363636; +} +.button.is-grey-lightest:hover, +.button.is-grey-lightest.is-hovered { + background-color: #e7e7e7; + border-color: transparent; + color: #363636; +} +.button.is-grey-lightest:focus, +.button.is-grey-lightest.is-focused { + border-color: transparent; + color: #363636; +} +.button.is-grey-lightest:focus:not(:active), +.button.is-grey-lightest.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(237,237,237,0.25); +} +.button.is-grey-lightest:active, +.button.is-grey-lightest.is-active { + background-color: #e0e0e0; + border-color: transparent; + color: #363636; +} +.button.is-grey-lightest[disabled], +fieldset[disabled] .button.is-grey-lightest { + background-color: #ededed; + border-color: transparent; + box-shadow: none; +} +.button.is-grey-lightest.is-inverted { + background-color: #363636; + color: #ededed; +} +.button.is-grey-lightest.is-inverted:hover, +.button.is-grey-lightest.is-inverted.is-hovered { + background-color: #292929; +} +.button.is-grey-lightest.is-inverted[disabled], +fieldset[disabled] .button.is-grey-lightest.is-inverted { + background-color: #363636; + border-color: transparent; + box-shadow: none; + color: #ededed; +} +.button.is-grey-lightest.is-loading::after { + border-color: transparent transparent #363636 #363636 !important; +} +.button.is-grey-lightest.is-outlined { + background-color: transparent; + border-color: #ededed; + color: #ededed; +} +.button.is-grey-lightest.is-outlined:hover, +.button.is-grey-lightest.is-outlined.is-hovered, +.button.is-grey-lightest.is-outlined:focus, +.button.is-grey-lightest.is-outlined.is-focused { + background-color: #ededed; + border-color: #ededed; + color: #363636; +} +.button.is-grey-lightest.is-outlined.is-loading::after { + border-color: transparent transparent #ededed #ededed !important; +} +.button.is-grey-lightest.is-outlined.is-loading:hover::after, +.button.is-grey-lightest.is-outlined.is-loading.is-hovered::after, +.button.is-grey-lightest.is-outlined.is-loading:focus::after, +.button.is-grey-lightest.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #363636 #363636 !important; +} +.button.is-grey-lightest.is-outlined[disabled], +fieldset[disabled] .button.is-grey-lightest.is-outlined { + background-color: transparent; + border-color: #ededed; + box-shadow: none; + color: #ededed; +} +.button.is-grey-lightest.is-inverted.is-outlined { + background-color: transparent; + border-color: #363636; + color: #363636; +} +.button.is-grey-lightest.is-inverted.is-outlined:hover, +.button.is-grey-lightest.is-inverted.is-outlined.is-hovered, +.button.is-grey-lightest.is-inverted.is-outlined:focus, +.button.is-grey-lightest.is-inverted.is-outlined.is-focused { + background-color: #363636; + color: #ededed; +} +.button.is-grey-lightest.is-inverted.is-outlined.is-loading:hover::after, +.button.is-grey-lightest.is-inverted.is-outlined.is-loading.is-hovered::after, +.button.is-grey-lightest.is-inverted.is-outlined.is-loading:focus::after, +.button.is-grey-lightest.is-inverted.is-outlined.is-loading.is-focused::after { + border-color: transparent transparent #ededed #ededed !important; +} +.button.is-grey-lightest.is-inverted.is-outlined[disabled], +fieldset[disabled] .button.is-grey-lightest.is-inverted.is-outlined { + background-color: transparent; + border-color: #363636; + box-shadow: none; + color: #363636; +} +.button.is-small { + border-radius: 2px; + font-size: 0.75rem; +} +.button.is-normal { + font-size: 1rem; +} +.button.is-medium { + font-size: 1.25rem; +} +.button.is-large { + font-size: 1.5rem; +} +.button[disabled], +fieldset[disabled] .button { + background-color: #fff; + border-color: #dbdbdb; + box-shadow: none; + opacity: 0.5; +} +.button.is-fullwidth { + display: flex; + width: 100%; +} +.button.is-loading { + color: transparent !important; + pointer-events: none; +} +.button.is-loading::after { + position: absolute; + left: calc(50% - (1em / 2)); + top: calc(50% - (1em / 2)); + position: absolute !important; +} +.button.is-static { + background-color: #f5f5f5; + border-color: #dbdbdb; + color: #7a7a7a; + box-shadow: none; + pointer-events: none; +} +.button.is-rounded { + border-radius: 290486px; + padding-left: calc(1em + 0.25em); + padding-right: calc(1em + 0.25em); +} +.buttons { + align-items: center; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; +} +.buttons .button { + margin-bottom: 0.5rem; +} +.buttons .button:not(:last-child):not(.is-fullwidth) { + margin-right: 0.5rem; +} +.buttons:last-child { + margin-bottom: -0.5rem; +} +.buttons:not(:last-child) { + margin-bottom: 1rem; +} +.buttons.are-small .button:not(.is-normal):not(.is-medium):not(.is-large) { + border-radius: 2px; + font-size: 0.75rem; +} +.buttons.are-medium .button:not(.is-small):not(.is-normal):not(.is-large) { + font-size: 1.25rem; +} +.buttons.are-large .button:not(.is-small):not(.is-normal):not(.is-medium) { + font-size: 1.5rem; +} +.buttons.has-addons .button:not(:first-child) { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.buttons.has-addons .button:not(:last-child) { + border-bottom-right-radius: 0; + border-top-right-radius: 0; + margin-right: -1px; +} +.buttons.has-addons .button:last-child { + margin-right: 0; +} +.buttons.has-addons .button:hover, +.buttons.has-addons .button.is-hovered { + z-index: 2; +} +.buttons.has-addons .button:focus, +.buttons.has-addons .button.is-focused, +.buttons.has-addons .button:active, +.buttons.has-addons .button.is-active, +.buttons.has-addons .button.is-selected { + z-index: 3; +} +.buttons.has-addons .button:focus:hover, +.buttons.has-addons .button.is-focused:hover, +.buttons.has-addons .button:active:hover, +.buttons.has-addons .button.is-active:hover, +.buttons.has-addons .button.is-selected:hover { + z-index: 4; +} +.buttons.has-addons .button.is-expanded { + flex-grow: 1; + flex-shrink: 1; +} +.buttons.is-centered { + justify-content: center; +} +.buttons.is-centered:not(.has-addons) .button:not(.is-fullwidth) { + margin-left: 0.25rem; + margin-right: 0.25rem; +} +.buttons.is-right { + justify-content: flex-end; +} +.buttons.is-right:not(.has-addons) .button:not(.is-fullwidth) { + margin-left: 0.25rem; + margin-right: 0.25rem; +} +.container { + flex-grow: 1; + margin: 0 auto; + position: relative; + width: auto; +} +.container.is-fluid { + max-width: none; + padding-left: 64px; + padding-right: 64px; + width: 100%; +} +@media screen and (min-width: 1088px) { + .container { + max-width: 960px; + } +} +@media screen and (max-width: 1279px) { + .container.is-widescreen { + max-width: 1152px; + } +} +@media screen and (max-width: 1471px) { + .container.is-fullhd { + max-width: 1344px; + } +} +@media screen and (min-width: 1280px) { + .container { + max-width: 1152px; + } +} +@media screen and (min-width: 1472px) { + .container { + max-width: 1344px; + } +} +.content li + li { + margin-top: 0.25em; +} +.content p:not(:last-child), +.content dl:not(:last-child), +.content ol:not(:last-child), +.content ul:not(:last-child), +.content blockquote:not(:last-child), +.content pre:not(:last-child), +.content table:not(:last-child) { + margin-bottom: 1em; +} +.content h1, +.content h2, +.content h3, +.content h4, +.content h5, +.content h6 { + color: #363636; + font-weight: 400; + line-height: 1.125; +} +.content h1 { + font-size: 2em; + margin-bottom: 0.5em; +} +.content h1:not(:first-child) { + margin-top: 1em; +} +.content h2 { + font-size: 1.75em; + margin-bottom: 0.5714em; +} +.content h2:not(:first-child) { + margin-top: 1.1428em; +} +.content h3 { + font-size: 1.5em; + margin-bottom: 0.6666em; +} +.content h3:not(:first-child) { + margin-top: 1.3333em; +} +.content h4 { + font-size: 1.25em; + margin-bottom: 0.8em; +} +.content h5 { + font-size: 1.125em; + margin-bottom: 0.8888em; +} +.content h6 { + font-size: 1em; + margin-bottom: 1em; +} +.content blockquote { + background-color: #f5f5f5; + border-left: 5px solid #dbdbdb; + padding: 1.25em 1.5em; +} +.content ol { + list-style-position: outside; + margin-left: 2em; + margin-top: 1em; +} +.content ol:not([type]) { + list-style-type: decimal; +} +.content ol:not([type]).is-lower-alpha { + list-style-type: lower-alpha; +} +.content ol:not([type]).is-lower-roman { + list-style-type: lower-roman; +} +.content ol:not([type]).is-upper-alpha { + list-style-type: upper-alpha; +} +.content ol:not([type]).is-upper-roman { + list-style-type: upper-roman; +} +.content ul { + list-style: disc outside; + margin-left: 2em; + margin-top: 1em; +} +.content ul ul { + list-style-type: circle; + margin-top: 0.5em; +} +.content ul ul ul { + list-style-type: square; +} +.content dd { + margin-left: 2em; +} +.content figure { + margin-left: 2em; + margin-right: 2em; + text-align: center; +} +.content figure:not(:first-child) { + margin-top: 2em; +} +.content figure:not(:last-child) { + margin-bottom: 2em; +} +.content figure img { + display: inline-block; +} +.content figure figcaption { + font-style: italic; +} +.content pre { + -webkit-overflow-scrolling: touch; + overflow-x: auto; + padding: 1.25em 1.5em; + white-space: pre; + word-wrap: normal; +} +.content sup, +.content sub { + font-size: 75%; +} +.content table { + width: 100%; +} +.content table td, +.content table th { + border: 1px solid #dbdbdb; + border-width: 0 0 1px; + padding: 0.5em 0.75em; + vertical-align: top; +} +.content table th { + color: #363636; +} +.content table th:not([align]) { + text-align: left; +} +.content table thead td, +.content table thead th { + border-width: 0 0 2px; + color: #363636; +} +.content table tfoot td, +.content table tfoot th { + border-width: 2px 0 0; + color: #363636; +} +.content table tbody tr:last-child td, +.content table tbody tr:last-child th { + border-bottom-width: 0; +} +.content .tabs li + li { + margin-top: 0; +} +.content.is-small { + font-size: 0.75rem; +} +.content.is-medium { + font-size: 1.25rem; +} +.content.is-large { + font-size: 1.5rem; +} +.icon { + align-items: center; + display: inline-flex; + justify-content: center; + height: 1.5rem; + width: 1.5rem; +} +.icon.is-small { + height: 1rem; + width: 1rem; +} +.icon.is-medium { + height: 2rem; + width: 2rem; +} +.icon.is-large { + height: 3rem; + width: 3rem; +} +.image { + display: block; + position: relative; +} +.image img { + display: block; + height: auto; + width: 100%; +} +.image img.is-rounded { + border-radius: 290486px; +} +.image.is-fullwidth { + width: 100%; +} +.image.is-square img, +.image.is-1by1 img, +.image.is-5by4 img, +.image.is-4by3 img, +.image.is-3by2 img, +.image.is-5by3 img, +.image.is-16by9 img, +.image.is-2by1 img, +.image.is-3by1 img, +.image.is-4by5 img, +.image.is-3by4 img, +.image.is-2by3 img, +.image.is-3by5 img, +.image.is-9by16 img, +.image.is-1by2 img, +.image.is-1by3 img, +.image.is-square .has-ratio, +.image.is-1by1 .has-ratio, +.image.is-5by4 .has-ratio, +.image.is-4by3 .has-ratio, +.image.is-3by2 .has-ratio, +.image.is-5by3 .has-ratio, +.image.is-16by9 .has-ratio, +.image.is-2by1 .has-ratio, +.image.is-3by1 .has-ratio, +.image.is-4by5 .has-ratio, +.image.is-3by4 .has-ratio, +.image.is-2by3 .has-ratio, +.image.is-3by5 .has-ratio, +.image.is-9by16 .has-ratio, +.image.is-1by2 .has-ratio, +.image.is-1by3 .has-ratio { + height: 100%; + width: 100%; +} +.image.is-square, +.image.is-1by1 { + padding-top: 100%; +} +.image.is-5by4 { + padding-top: 80%; +} +.image.is-4by3 { + padding-top: 75%; +} +.image.is-3by2 { + padding-top: 66.6666%; +} +.image.is-5by3 { + padding-top: 60%; +} +.image.is-16by9 { + padding-top: 56.25%; +} +.image.is-2by1 { + padding-top: 50%; +} +.image.is-3by1 { + padding-top: 33.3333%; +} +.image.is-4by5 { + padding-top: 125%; +} +.image.is-3by4 { + padding-top: 133.3333%; +} +.image.is-2by3 { + padding-top: 150%; +} +.image.is-3by5 { + padding-top: 166.6666%; +} +.image.is-9by16 { + padding-top: 177.7777%; +} +.image.is-1by2 { + padding-top: 200%; +} +.image.is-1by3 { + padding-top: 300%; +} +.image.is-16x16 { + height: 16px; + width: 16px; +} +.image.is-24x24 { + height: 24px; + width: 24px; +} +.image.is-32x32 { + height: 32px; + width: 32px; +} +.image.is-48x48 { + height: 48px; + width: 48px; +} +.image.is-64x64 { + height: 64px; + width: 64px; +} +.image.is-96x96 { + height: 96px; + width: 96px; +} +.image.is-128x128 { + height: 128px; + width: 128px; +} +.notification { + background-color: #f5f5f5; + border-radius: 4px; + padding: 1.25rem 2.5rem 1.25rem 1.5rem; + position: relative; +} +.notification a:not(.button):not(.dropdown-item) { + color: currentColor; + text-decoration: underline; +} +.notification strong { + color: currentColor; +} +.notification code, +.notification pre { + background: #fff; +} +.notification pre code { + background: transparent; +} +.notification > .delete { + position: absolute; + right: 0.5rem; + top: 0.5rem; +} +.notification .title, +.notification .subtitle, +.notification .content { + color: currentColor; +} +.notification.is-white { + background-color: #fff; + color: #0a0a0a; +} +.notification.is-black { + background-color: #0a0a0a; + color: #fff; +} +.notification.is-light { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.notification.is-dark { + background-color: #363636; + color: #fff; +} +.notification.is-primary { + background-color: #3273dc; + color: #fff; +} +.notification.is-link { + background-color: #3273dc; + color: #fff; +} +.notification.is-info { + background-color: #3298dc; + color: #fff; +} +.notification.is-success { + background-color: #48c774; + color: #fff; +} +.notification.is-warning { + background-color: #ffdd57; + color: rgba(0,0,0,0.7); +} +.notification.is-danger { + background-color: #f14668; + color: #fff; +} +.notification.is-grey-lightest { + background-color: #ededed; + color: #363636; +} +.progress { + -moz-appearance: none; + -webkit-appearance: none; + border: none; + border-radius: 290486px; + display: block; + height: 1rem; + overflow: hidden; + padding: 0; + width: 100%; +} +.progress::-webkit-progress-bar { + background-color: #ededed; +} +.progress::-webkit-progress-value { + background-color: #4a4a4a; +} +.progress::-moz-progress-bar { + background-color: #4a4a4a; +} +.progress::-ms-fill { + background-color: #4a4a4a; + border: none; +} +.progress.is-white::-webkit-progress-value { + background-color: #fff; +} +.progress.is-white::-moz-progress-bar { + background-color: #fff; +} +.progress.is-white::-ms-fill { + background-color: #fff; +} +.progress.is-white:indeterminate { + background-image: linear-gradient(to right, #fff 30%, #ededed 30%); +} +.progress.is-black::-webkit-progress-value { + background-color: #0a0a0a; +} +.progress.is-black::-moz-progress-bar { + background-color: #0a0a0a; +} +.progress.is-black::-ms-fill { + background-color: #0a0a0a; +} +.progress.is-black:indeterminate { + background-image: linear-gradient(to right, #0a0a0a 30%, #ededed 30%); +} +.progress.is-light::-webkit-progress-value { + background-color: #f5f5f5; +} +.progress.is-light::-moz-progress-bar { + background-color: #f5f5f5; +} +.progress.is-light::-ms-fill { + background-color: #f5f5f5; +} +.progress.is-light:indeterminate { + background-image: linear-gradient(to right, #f5f5f5 30%, #ededed 30%); +} +.progress.is-dark::-webkit-progress-value { + background-color: #363636; +} +.progress.is-dark::-moz-progress-bar { + background-color: #363636; +} +.progress.is-dark::-ms-fill { + background-color: #363636; +} +.progress.is-dark:indeterminate { + background-image: linear-gradient(to right, #363636 30%, #ededed 30%); +} +.progress.is-primary::-webkit-progress-value { + background-color: #3273dc; +} +.progress.is-primary::-moz-progress-bar { + background-color: #3273dc; +} +.progress.is-primary::-ms-fill { + background-color: #3273dc; +} +.progress.is-primary:indeterminate { + background-image: linear-gradient(to right, #3273dc 30%, #ededed 30%); +} +.progress.is-link::-webkit-progress-value { + background-color: #3273dc; +} +.progress.is-link::-moz-progress-bar { + background-color: #3273dc; +} +.progress.is-link::-ms-fill { + background-color: #3273dc; +} +.progress.is-link:indeterminate { + background-image: linear-gradient(to right, #3273dc 30%, #ededed 30%); +} +.progress.is-info::-webkit-progress-value { + background-color: #3298dc; +} +.progress.is-info::-moz-progress-bar { + background-color: #3298dc; +} +.progress.is-info::-ms-fill { + background-color: #3298dc; +} +.progress.is-info:indeterminate { + background-image: linear-gradient(to right, #3298dc 30%, #ededed 30%); +} +.progress.is-success::-webkit-progress-value { + background-color: #48c774; +} +.progress.is-success::-moz-progress-bar { + background-color: #48c774; +} +.progress.is-success::-ms-fill { + background-color: #48c774; +} +.progress.is-success:indeterminate { + background-image: linear-gradient(to right, #48c774 30%, #ededed 30%); +} +.progress.is-warning::-webkit-progress-value { + background-color: #ffdd57; +} +.progress.is-warning::-moz-progress-bar { + background-color: #ffdd57; +} +.progress.is-warning::-ms-fill { + background-color: #ffdd57; +} +.progress.is-warning:indeterminate { + background-image: linear-gradient(to right, #ffdd57 30%, #ededed 30%); +} +.progress.is-danger::-webkit-progress-value { + background-color: #f14668; +} +.progress.is-danger::-moz-progress-bar { + background-color: #f14668; +} +.progress.is-danger::-ms-fill { + background-color: #f14668; +} +.progress.is-danger:indeterminate { + background-image: linear-gradient(to right, #f14668 30%, #ededed 30%); +} +.progress.is-grey-lightest::-webkit-progress-value { + background-color: #ededed; +} +.progress.is-grey-lightest::-moz-progress-bar { + background-color: #ededed; +} +.progress.is-grey-lightest::-ms-fill { + background-color: #ededed; +} +.progress.is-grey-lightest:indeterminate { + background-image: linear-gradient(to right, #ededed 30%, #ededed 30%); +} +.progress:indeterminate { + animation-duration: 1.5s; + animation-iteration-count: infinite; + animation-name: moveIndeterminate; + animation-timing-function: linear; + background-color: #ededed; + background-image: linear-gradient(to right, #4a4a4a 30%, #ededed 30%); + background-position: top left; + background-repeat: no-repeat; + background-size: 150% 150%; +} +.progress:indeterminate::-webkit-progress-bar { + background-color: transparent; +} +.progress:indeterminate::-moz-progress-bar { + background-color: transparent; +} +.progress.is-small { + height: 0.75rem; +} +.progress.is-medium { + height: 1.25rem; +} +.progress.is-large { + height: 1.5rem; +} +@-moz-keyframes moveIndeterminate { + from { + background-position: 200% 0; + } + to { + background-position: -200% 0; + } +} +@-webkit-keyframes moveIndeterminate { + from { + background-position: 200% 0; + } + to { + background-position: -200% 0; + } +} +@-o-keyframes moveIndeterminate { + from { + background-position: 200% 0; + } + to { + background-position: -200% 0; + } +} +@keyframes moveIndeterminate { + from { + background-position: 200% 0; + } + to { + background-position: -200% 0; + } +} +.table { + background-color: #fff; + color: #363636; +} +.table td, +.table th { + border: 1px solid #dbdbdb; + border-width: 0 0 1px; + padding: 0.5em 0.75em; + vertical-align: top; +} +.table td.is-white, +.table th.is-white { + background-color: #fff; + border-color: #fff; + color: #0a0a0a; +} +.table td.is-black, +.table th.is-black { + background-color: #0a0a0a; + border-color: #0a0a0a; + color: #fff; +} +.table td.is-light, +.table th.is-light { + background-color: #f5f5f5; + border-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.table td.is-dark, +.table th.is-dark { + background-color: #363636; + border-color: #363636; + color: #fff; +} +.table td.is-primary, +.table th.is-primary { + background-color: #3273dc; + border-color: #3273dc; + color: #fff; +} +.table td.is-link, +.table th.is-link { + background-color: #3273dc; + border-color: #3273dc; + color: #fff; +} +.table td.is-info, +.table th.is-info { + background-color: #3298dc; + border-color: #3298dc; + color: #fff; +} +.table td.is-success, +.table th.is-success { + background-color: #48c774; + border-color: #48c774; + color: #fff; +} +.table td.is-warning, +.table th.is-warning { + background-color: #ffdd57; + border-color: #ffdd57; + color: rgba(0,0,0,0.7); +} +.table td.is-danger, +.table th.is-danger { + background-color: #f14668; + border-color: #f14668; + color: #fff; +} +.table td.is-grey-lightest, +.table th.is-grey-lightest { + background-color: #ededed; + border-color: #ededed; + color: #363636; +} +.table td.is-narrow, +.table th.is-narrow { + white-space: nowrap; + width: 1%; +} +.table td.is-selected, +.table th.is-selected { + background-color: #3273dc; + color: #fff; +} +.table td.is-selected a, +.table th.is-selected a, +.table td.is-selected strong, +.table th.is-selected strong { + color: currentColor; +} +.table th { + color: #363636; +} +.table th:not([align]) { + text-align: left; +} +.table tr.is-selected { + background-color: #3273dc; + color: #fff; +} +.table tr.is-selected a, +.table tr.is-selected strong { + color: currentColor; +} +.table tr.is-selected td, +.table tr.is-selected th { + border-color: #fff; + color: currentColor; +} +.table thead { + background-color: transparent; +} +.table thead td, +.table thead th { + border-width: 0 0 2px; + color: #363636; +} +.table tfoot { + background-color: transparent; +} +.table tfoot td, +.table tfoot th { + border-width: 2px 0 0; + color: #363636; +} +.table tbody { + background-color: transparent; +} +.table tbody tr:last-child td, +.table tbody tr:last-child th { + border-bottom-width: 0; +} +.table.is-bordered td, +.table.is-bordered th { + border-width: 1px; +} +.table.is-bordered tr:last-child td, +.table.is-bordered tr:last-child th { + border-bottom-width: 1px; +} +.table.is-fullwidth { + width: 100%; +} +.table.is-hoverable tbody tr:not(.is-selected):hover { + background-color: #fafafa; +} +.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover { + background-color: #fafafa; +} +.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover:nth-child(even) { + background-color: #f5f5f5; +} +.table.is-narrow td, +.table.is-narrow th { + padding: 0.25em 0.5em; +} +.table.is-striped tbody tr:not(.is-selected):nth-child(even) { + background-color: #fafafa; +} +.table-container { + -webkit-overflow-scrolling: touch; + overflow: auto; + overflow-y: hidden; + max-width: 100%; +} +.tags { + align-items: center; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; +} +.tags .tag { + margin-bottom: 0.5rem; +} +.tags .tag:not(:last-child) { + margin-right: 0.5rem; +} +.tags:last-child { + margin-bottom: -0.5rem; +} +.tags:not(:last-child) { + margin-bottom: 1rem; +} +.tags.are-medium .tag:not(.is-normal):not(.is-large) { + font-size: 1rem; +} +.tags.are-large .tag:not(.is-normal):not(.is-medium) { + font-size: 1.25rem; +} +.tags.is-centered { + justify-content: center; +} +.tags.is-centered .tag { + margin-right: 0.25rem; + margin-left: 0.25rem; +} +.tags.is-right { + justify-content: flex-end; +} +.tags.is-right .tag:not(:first-child) { + margin-left: 0.5rem; +} +.tags.is-right .tag:not(:last-child) { + margin-right: 0; +} +.tags.has-addons .tag { + margin-right: 0; +} +.tags.has-addons .tag:not(:first-child) { + margin-left: 0; + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.tags.has-addons .tag:not(:last-child) { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.tag:not(body) { + align-items: center; + background-color: #f5f5f5; + border-radius: 4px; + color: #4a4a4a; + display: inline-flex; + font-size: 0.75rem; + height: 2em; + justify-content: center; + line-height: 1.5; + padding-left: 0.75em; + padding-right: 0.75em; + white-space: nowrap; +} +.tag:not(body) .delete { + margin-left: 0.25rem; + margin-right: -0.375rem; +} +.tag:not(body).is-white { + background-color: #fff; + color: #0a0a0a; +} +.tag:not(body).is-black { + background-color: #0a0a0a; + color: #fff; +} +.tag:not(body).is-light { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.tag:not(body).is-dark { + background-color: #363636; + color: #fff; +} +.tag:not(body).is-primary { + background-color: #3273dc; + color: #fff; +} +.tag:not(body).is-primary.is-light { + background-color: #eef3fc; + color: #2160c4; +} +.tag:not(body).is-link { + background-color: #3273dc; + color: #fff; +} +.tag:not(body).is-link.is-light { + background-color: #eef3fc; + color: #2160c4; +} +.tag:not(body).is-info { + background-color: #3298dc; + color: #fff; +} +.tag:not(body).is-info.is-light { + background-color: #eef6fc; + color: #1d72aa; +} +.tag:not(body).is-success { + background-color: #48c774; + color: #fff; +} +.tag:not(body).is-success.is-light { + background-color: #effaf3; + color: #257942; +} +.tag:not(body).is-warning { + background-color: #ffdd57; + color: rgba(0,0,0,0.7); +} +.tag:not(body).is-warning.is-light { + background-color: #fffbeb; + color: #947600; +} +.tag:not(body).is-danger { + background-color: #f14668; + color: #fff; +} +.tag:not(body).is-danger.is-light { + background-color: #feecf0; + color: #cc0f35; +} +.tag:not(body).is-grey-lightest { + background-color: #ededed; + color: #363636; +} +.tag:not(body).is-normal { + font-size: 0.75rem; +} +.tag:not(body).is-medium { + font-size: 1rem; +} +.tag:not(body).is-large { + font-size: 1.25rem; +} +.tag:not(body) .icon:first-child:not(:last-child) { + margin-left: -0.375em; + margin-right: 0.1875em; +} +.tag:not(body) .icon:last-child:not(:first-child) { + margin-left: 0.1875em; + margin-right: -0.375em; +} +.tag:not(body) .icon:first-child:last-child { + margin-left: -0.375em; + margin-right: -0.375em; +} +.tag:not(body).is-delete { + margin-left: 1px; + padding: 0; + position: relative; + width: 2em; +} +.tag:not(body).is-delete::before, +.tag:not(body).is-delete::after { + background-color: currentColor; + content: ""; + display: block; + left: 50%; + position: absolute; + top: 50%; + transform: translateX(-50%) translateY(-50%) rotate(45deg); + transform-origin: center center; +} +.tag:not(body).is-delete::before { + height: 1px; + width: 50%; +} +.tag:not(body).is-delete::after { + height: 50%; + width: 1px; +} +.tag:not(body).is-delete:hover, +.tag:not(body).is-delete:focus { + background-color: #e8e8e8; +} +.tag:not(body).is-delete:active { + background-color: #dbdbdb; +} +.tag:not(body).is-rounded { + border-radius: 290486px; +} +a.tag:hover { + text-decoration: underline; +} +.title, +.subtitle { + word-break: break-word; +} +.title em, +.subtitle em, +.title span, +.subtitle span { + font-weight: inherit; +} +.title sub, +.subtitle sub { + font-size: 0.75em; +} +.title sup, +.subtitle sup { + font-size: 0.75em; +} +.title .tag, +.subtitle .tag { + vertical-align: middle; +} +.title { + color: #363636; + font-size: 2rem; + font-weight: 400; + line-height: 1.125; +} +.title strong { + color: inherit; + font-weight: inherit; +} +.title + .highlight { + margin-top: -0.75rem; +} +.title:not(.is-spaced) + .subtitle { + margin-top: -1.25rem; +} +.title.is-1 { + font-size: 3rem; +} +.title.is-2 { + font-size: 2.5rem; +} +.title.is-3 { + font-size: 2rem; +} +.title.is-4 { + font-size: 1.5rem; +} +.title.is-5 { + font-size: 1.25rem; +} +.title.is-6 { + font-size: 1rem; +} +.title.is-7 { + font-size: 0.85rem; +} +.subtitle { + color: #4a4a4a; + font-size: 1.25rem; + font-weight: 400; + line-height: 1.25; +} +.subtitle strong { + color: #363636; + font-weight: 600; +} +.subtitle:not(.is-spaced) + .title { + margin-top: -1.25rem; +} +.subtitle.is-1 { + font-size: 3rem; +} +.subtitle.is-2 { + font-size: 2.5rem; +} +.subtitle.is-3 { + font-size: 2rem; +} +.subtitle.is-4 { + font-size: 1.5rem; +} +.subtitle.is-5 { + font-size: 1.25rem; +} +.subtitle.is-6 { + font-size: 1rem; +} +.subtitle.is-7 { + font-size: 0.85rem; +} +.heading { + display: block; + font-size: 11px; + letter-spacing: 1px; + margin-bottom: 5px; + text-transform: uppercase; +} +.highlight { + font-weight: 400; + max-width: 100%; + overflow: hidden; + padding: 0; +} +.highlight pre { + overflow: auto; + max-width: 100%; +} +.number { + align-items: center; + background-color: #f5f5f5; + border-radius: 290486px; + display: inline-flex; + font-size: 1.25rem; + height: 2em; + justify-content: center; + margin-right: 1.5rem; + min-width: 2.5em; + padding: 0.25rem 0.5rem; + text-align: center; + vertical-align: top; +} +.input, +.textarea, +.select select { + background-color: #fff; + border-color: #dbdbdb; + border-radius: 4px; + color: #363636; +} +.input::-moz-placeholder, +.textarea::-moz-placeholder, +.select select::-moz-placeholder { + color: rgba(54,54,54,0.3); +} +.input::-webkit-input-placeholder, +.textarea::-webkit-input-placeholder, +.select select::-webkit-input-placeholder { + color: rgba(54,54,54,0.3); +} +.input:-moz-placeholder, +.textarea:-moz-placeholder, +.select select:-moz-placeholder { + color: rgba(54,54,54,0.3); +} +.input:-ms-input-placeholder, +.textarea:-ms-input-placeholder, +.select select:-ms-input-placeholder { + color: rgba(54,54,54,0.3); +} +.input:hover, +.textarea:hover, +.select select:hover, +.input.is-hovered, +.textarea.is-hovered, +.select select.is-hovered { + border-color: #b5b5b5; +} +.input:focus, +.textarea:focus, +.select select:focus, +.input.is-focused, +.textarea.is-focused, +.select select.is-focused, +.input:active, +.textarea:active, +.select select:active, +.input.is-active, +.textarea.is-active, +.select select.is-active { + border-color: #3273dc; + box-shadow: 0 0 0 0.125em rgba(50,115,220,0.25); +} +.input[disabled], +.textarea[disabled], +.select select[disabled], +fieldset[disabled] .input, +fieldset[disabled] .textarea, +fieldset[disabled] .select select { + background-color: #f5f5f5; + border-color: #f5f5f5; + box-shadow: none; + color: #7a7a7a; +} +.input[disabled]::-moz-placeholder, +.textarea[disabled]::-moz-placeholder, +.select select[disabled]::-moz-placeholder, +fieldset[disabled] .input::-moz-placeholder, +fieldset[disabled] .textarea::-moz-placeholder, +fieldset[disabled] .select select::-moz-placeholder { + color: rgba(122,122,122,0.3); +} +.input[disabled]::-webkit-input-placeholder, +.textarea[disabled]::-webkit-input-placeholder, +.select select[disabled]::-webkit-input-placeholder, +fieldset[disabled] .input::-webkit-input-placeholder, +fieldset[disabled] .textarea::-webkit-input-placeholder, +fieldset[disabled] .select select::-webkit-input-placeholder { + color: rgba(122,122,122,0.3); +} +.input[disabled]:-moz-placeholder, +.textarea[disabled]:-moz-placeholder, +.select select[disabled]:-moz-placeholder, +fieldset[disabled] .input:-moz-placeholder, +fieldset[disabled] .textarea:-moz-placeholder, +fieldset[disabled] .select select:-moz-placeholder { + color: rgba(122,122,122,0.3); +} +.input[disabled]:-ms-input-placeholder, +.textarea[disabled]:-ms-input-placeholder, +.select select[disabled]:-ms-input-placeholder, +fieldset[disabled] .input:-ms-input-placeholder, +fieldset[disabled] .textarea:-ms-input-placeholder, +fieldset[disabled] .select select:-ms-input-placeholder { + color: rgba(122,122,122,0.3); +} +.input, +.textarea { + box-shadow: inset 0 0.0625em 0.125em rgba(10,10,10,0.05); + max-width: 100%; + width: 100%; +} +.input[readonly], +.textarea[readonly] { + box-shadow: none; +} +.input.is-white, +.textarea.is-white { + border-color: #fff; +} +.input.is-white:focus, +.textarea.is-white:focus, +.input.is-white.is-focused, +.textarea.is-white.is-focused, +.input.is-white:active, +.textarea.is-white:active, +.input.is-white.is-active, +.textarea.is-white.is-active { + box-shadow: 0 0 0 0.125em rgba(255,255,255,0.25); +} +.input.is-black, +.textarea.is-black { + border-color: #0a0a0a; +} +.input.is-black:focus, +.textarea.is-black:focus, +.input.is-black.is-focused, +.textarea.is-black.is-focused, +.input.is-black:active, +.textarea.is-black:active, +.input.is-black.is-active, +.textarea.is-black.is-active { + box-shadow: 0 0 0 0.125em rgba(10,10,10,0.25); +} +.input.is-light, +.textarea.is-light { + border-color: #f5f5f5; +} +.input.is-light:focus, +.textarea.is-light:focus, +.input.is-light.is-focused, +.textarea.is-light.is-focused, +.input.is-light:active, +.textarea.is-light:active, +.input.is-light.is-active, +.textarea.is-light.is-active { + box-shadow: 0 0 0 0.125em rgba(245,245,245,0.25); +} +.input.is-dark, +.textarea.is-dark { + border-color: #363636; +} +.input.is-dark:focus, +.textarea.is-dark:focus, +.input.is-dark.is-focused, +.textarea.is-dark.is-focused, +.input.is-dark:active, +.textarea.is-dark:active, +.input.is-dark.is-active, +.textarea.is-dark.is-active { + box-shadow: 0 0 0 0.125em rgba(54,54,54,0.25); +} +.input.is-primary, +.textarea.is-primary { + border-color: #3273dc; +} +.input.is-primary:focus, +.textarea.is-primary:focus, +.input.is-primary.is-focused, +.textarea.is-primary.is-focused, +.input.is-primary:active, +.textarea.is-primary:active, +.input.is-primary.is-active, +.textarea.is-primary.is-active { + box-shadow: 0 0 0 0.125em rgba(50,115,220,0.25); +} +.input.is-link, +.textarea.is-link { + border-color: #3273dc; +} +.input.is-link:focus, +.textarea.is-link:focus, +.input.is-link.is-focused, +.textarea.is-link.is-focused, +.input.is-link:active, +.textarea.is-link:active, +.input.is-link.is-active, +.textarea.is-link.is-active { + box-shadow: 0 0 0 0.125em rgba(50,115,220,0.25); +} +.input.is-info, +.textarea.is-info { + border-color: #3298dc; +} +.input.is-info:focus, +.textarea.is-info:focus, +.input.is-info.is-focused, +.textarea.is-info.is-focused, +.input.is-info:active, +.textarea.is-info:active, +.input.is-info.is-active, +.textarea.is-info.is-active { + box-shadow: 0 0 0 0.125em rgba(50,152,220,0.25); +} +.input.is-success, +.textarea.is-success { + border-color: #48c774; +} +.input.is-success:focus, +.textarea.is-success:focus, +.input.is-success.is-focused, +.textarea.is-success.is-focused, +.input.is-success:active, +.textarea.is-success:active, +.input.is-success.is-active, +.textarea.is-success.is-active { + box-shadow: 0 0 0 0.125em rgba(72,199,116,0.25); +} +.input.is-warning, +.textarea.is-warning { + border-color: #ffdd57; +} +.input.is-warning:focus, +.textarea.is-warning:focus, +.input.is-warning.is-focused, +.textarea.is-warning.is-focused, +.input.is-warning:active, +.textarea.is-warning:active, +.input.is-warning.is-active, +.textarea.is-warning.is-active { + box-shadow: 0 0 0 0.125em rgba(255,221,87,0.25); +} +.input.is-danger, +.textarea.is-danger { + border-color: #f14668; +} +.input.is-danger:focus, +.textarea.is-danger:focus, +.input.is-danger.is-focused, +.textarea.is-danger.is-focused, +.input.is-danger:active, +.textarea.is-danger:active, +.input.is-danger.is-active, +.textarea.is-danger.is-active { + box-shadow: 0 0 0 0.125em rgba(241,70,104,0.25); +} +.input.is-grey-lightest, +.textarea.is-grey-lightest { + border-color: #ededed; +} +.input.is-grey-lightest:focus, +.textarea.is-grey-lightest:focus, +.input.is-grey-lightest.is-focused, +.textarea.is-grey-lightest.is-focused, +.input.is-grey-lightest:active, +.textarea.is-grey-lightest:active, +.input.is-grey-lightest.is-active, +.textarea.is-grey-lightest.is-active { + box-shadow: 0 0 0 0.125em rgba(237,237,237,0.25); +} +.input.is-small, +.textarea.is-small { + border-radius: 2px; + font-size: 0.75rem; +} +.input.is-medium, +.textarea.is-medium { + font-size: 1.25rem; +} +.input.is-large, +.textarea.is-large { + font-size: 1.5rem; +} +.input.is-fullwidth, +.textarea.is-fullwidth { + display: block; + width: 100%; +} +.input.is-inline, +.textarea.is-inline { + display: inline; + width: auto; +} +.input.is-rounded { + border-radius: 290486px; + padding-left: calc(calc(0.75em - 1px) + 0.375em); + padding-right: calc(calc(0.75em - 1px) + 0.375em); +} +.input.is-static { + background-color: transparent; + border-color: transparent; + box-shadow: none; + padding-left: 0; + padding-right: 0; +} +.textarea { + display: block; + max-width: 100%; + min-width: 100%; + padding: calc(0.75em - 1px); + resize: vertical; +} +.textarea:not([rows]) { + max-height: 40em; + min-height: 8em; +} +.textarea[rows] { + height: initial; +} +.textarea.has-fixed-size { + resize: none; +} +.checkbox, +.radio { + cursor: pointer; + display: inline-block; + line-height: 1.25; + position: relative; +} +.checkbox input, +.radio input { + cursor: pointer; +} +.checkbox:hover, +.radio:hover { + color: #363636; +} +.checkbox[disabled], +.radio[disabled], +fieldset[disabled] .checkbox, +fieldset[disabled] .radio { + color: #7a7a7a; + cursor: not-allowed; +} +.radio + .radio { + margin-left: 0.5em; +} +.select { + display: inline-block; + max-width: 100%; + position: relative; + vertical-align: top; +} +.select:not(.is-multiple) { + height: 2.25em; +} +.select:not(.is-multiple):not(.is-loading)::after { + border-color: #3273dc; + right: 1.125em; + z-index: 4; +} +.select.is-rounded select { + border-radius: 290486px; + padding-left: 1em; +} +.select select { + cursor: pointer; + display: block; + font-size: 1em; + max-width: 100%; + outline: none; +} +.select select::-ms-expand { + display: none; +} +.select select[disabled]:hover, +fieldset[disabled] .select select:hover { + border-color: #f5f5f5; +} +.select select:not([multiple]) { + padding-right: 2.5em; +} +.select select[multiple] { + height: auto; + padding: 0; +} +.select select[multiple] option { + padding: 0.5em 1em; +} +.select:not(.is-multiple):not(.is-loading):hover::after { + border-color: #363636; +} +.select.is-white:not(:hover)::after { + border-color: #fff; +} +.select.is-white select { + border-color: #fff; +} +.select.is-white select:hover, +.select.is-white select.is-hovered { + border-color: #f2f2f2; +} +.select.is-white select:focus, +.select.is-white select.is-focused, +.select.is-white select:active, +.select.is-white select.is-active { + box-shadow: 0 0 0 0.125em rgba(255,255,255,0.25); +} +.select.is-black:not(:hover)::after { + border-color: #0a0a0a; +} +.select.is-black select { + border-color: #0a0a0a; +} +.select.is-black select:hover, +.select.is-black select.is-hovered { + border-color: #000; +} +.select.is-black select:focus, +.select.is-black select.is-focused, +.select.is-black select:active, +.select.is-black select.is-active { + box-shadow: 0 0 0 0.125em rgba(10,10,10,0.25); +} +.select.is-light:not(:hover)::after { + border-color: #f5f5f5; +} +.select.is-light select { + border-color: #f5f5f5; +} +.select.is-light select:hover, +.select.is-light select.is-hovered { + border-color: #e8e8e8; +} +.select.is-light select:focus, +.select.is-light select.is-focused, +.select.is-light select:active, +.select.is-light select.is-active { + box-shadow: 0 0 0 0.125em rgba(245,245,245,0.25); +} +.select.is-dark:not(:hover)::after { + border-color: #363636; +} +.select.is-dark select { + border-color: #363636; +} +.select.is-dark select:hover, +.select.is-dark select.is-hovered { + border-color: #292929; +} +.select.is-dark select:focus, +.select.is-dark select.is-focused, +.select.is-dark select:active, +.select.is-dark select.is-active { + box-shadow: 0 0 0 0.125em rgba(54,54,54,0.25); +} +.select.is-primary:not(:hover)::after { + border-color: #3273dc; +} +.select.is-primary select { + border-color: #3273dc; +} +.select.is-primary select:hover, +.select.is-primary select.is-hovered { + border-color: #2366d1; +} +.select.is-primary select:focus, +.select.is-primary select.is-focused, +.select.is-primary select:active, +.select.is-primary select.is-active { + box-shadow: 0 0 0 0.125em rgba(50,115,220,0.25); +} +.select.is-link:not(:hover)::after { + border-color: #3273dc; +} +.select.is-link select { + border-color: #3273dc; +} +.select.is-link select:hover, +.select.is-link select.is-hovered { + border-color: #2366d1; +} +.select.is-link select:focus, +.select.is-link select.is-focused, +.select.is-link select:active, +.select.is-link select.is-active { + box-shadow: 0 0 0 0.125em rgba(50,115,220,0.25); +} +.select.is-info:not(:hover)::after { + border-color: #3298dc; +} +.select.is-info select { + border-color: #3298dc; +} +.select.is-info select:hover, +.select.is-info select.is-hovered { + border-color: #238cd1; +} +.select.is-info select:focus, +.select.is-info select.is-focused, +.select.is-info select:active, +.select.is-info select.is-active { + box-shadow: 0 0 0 0.125em rgba(50,152,220,0.25); +} +.select.is-success:not(:hover)::after { + border-color: #48c774; +} +.select.is-success select { + border-color: #48c774; +} +.select.is-success select:hover, +.select.is-success select.is-hovered { + border-color: #3abb67; +} +.select.is-success select:focus, +.select.is-success select.is-focused, +.select.is-success select:active, +.select.is-success select.is-active { + box-shadow: 0 0 0 0.125em rgba(72,199,116,0.25); +} +.select.is-warning:not(:hover)::after { + border-color: #ffdd57; +} +.select.is-warning select { + border-color: #ffdd57; +} +.select.is-warning select:hover, +.select.is-warning select.is-hovered { + border-color: #ffd83d; +} +.select.is-warning select:focus, +.select.is-warning select.is-focused, +.select.is-warning select:active, +.select.is-warning select.is-active { + box-shadow: 0 0 0 0.125em rgba(255,221,87,0.25); +} +.select.is-danger:not(:hover)::after { + border-color: #f14668; +} +.select.is-danger select { + border-color: #f14668; +} +.select.is-danger select:hover, +.select.is-danger select.is-hovered { + border-color: #ef2e55; +} +.select.is-danger select:focus, +.select.is-danger select.is-focused, +.select.is-danger select:active, +.select.is-danger select.is-active { + box-shadow: 0 0 0 0.125em rgba(241,70,104,0.25); +} +.select.is-grey-lightest:not(:hover)::after { + border-color: #ededed; +} +.select.is-grey-lightest select { + border-color: #ededed; +} +.select.is-grey-lightest select:hover, +.select.is-grey-lightest select.is-hovered { + border-color: #e0e0e0; +} +.select.is-grey-lightest select:focus, +.select.is-grey-lightest select.is-focused, +.select.is-grey-lightest select:active, +.select.is-grey-lightest select.is-active { + box-shadow: 0 0 0 0.125em rgba(237,237,237,0.25); +} +.select.is-small { + border-radius: 2px; + font-size: 0.75rem; +} +.select.is-medium { + font-size: 1.25rem; +} +.select.is-large { + font-size: 1.5rem; +} +.select.is-disabled::after { + border-color: #7a7a7a; +} +.select.is-fullwidth { + width: 100%; +} +.select.is-fullwidth select { + width: 100%; +} +.select.is-loading::after { + margin-top: 0; + position: absolute; + right: 0.625em; + top: 0.625em; + transform: none; +} +.select.is-loading.is-small:after { + font-size: 0.75rem; +} +.select.is-loading.is-medium:after { + font-size: 1.25rem; +} +.select.is-loading.is-large:after { + font-size: 1.5rem; +} +.file { + align-items: stretch; + display: flex; + justify-content: flex-start; + position: relative; +} +.file.is-white .file-cta { + background-color: #fff; + border-color: transparent; + color: #0a0a0a; +} +.file.is-white:hover .file-cta, +.file.is-white.is-hovered .file-cta { + background-color: #f9f9f9; + border-color: transparent; + color: #0a0a0a; +} +.file.is-white:focus .file-cta, +.file.is-white.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(255,255,255,0.25); + color: #0a0a0a; +} +.file.is-white:active .file-cta, +.file.is-white.is-active .file-cta { + background-color: #f2f2f2; + border-color: transparent; + color: #0a0a0a; +} +.file.is-black .file-cta { + background-color: #0a0a0a; + border-color: transparent; + color: #fff; +} +.file.is-black:hover .file-cta, +.file.is-black.is-hovered .file-cta { + background-color: #040404; + border-color: transparent; + color: #fff; +} +.file.is-black:focus .file-cta, +.file.is-black.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(10,10,10,0.25); + color: #fff; +} +.file.is-black:active .file-cta, +.file.is-black.is-active .file-cta { + background-color: #000; + border-color: transparent; + color: #fff; +} +.file.is-light .file-cta { + background-color: #f5f5f5; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.file.is-light:hover .file-cta, +.file.is-light.is-hovered .file-cta { + background-color: #eee; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.file.is-light:focus .file-cta, +.file.is-light.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(245,245,245,0.25); + color: rgba(0,0,0,0.7); +} +.file.is-light:active .file-cta, +.file.is-light.is-active .file-cta { + background-color: #e8e8e8; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.file.is-dark .file-cta { + background-color: #363636; + border-color: transparent; + color: #fff; +} +.file.is-dark:hover .file-cta, +.file.is-dark.is-hovered .file-cta { + background-color: #2f2f2f; + border-color: transparent; + color: #fff; +} +.file.is-dark:focus .file-cta, +.file.is-dark.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(54,54,54,0.25); + color: #fff; +} +.file.is-dark:active .file-cta, +.file.is-dark.is-active .file-cta { + background-color: #292929; + border-color: transparent; + color: #fff; +} +.file.is-primary .file-cta { + background-color: #3273dc; + border-color: transparent; + color: #fff; +} +.file.is-primary:hover .file-cta, +.file.is-primary.is-hovered .file-cta { + background-color: #276cda; + border-color: transparent; + color: #fff; +} +.file.is-primary:focus .file-cta, +.file.is-primary.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(50,115,220,0.25); + color: #fff; +} +.file.is-primary:active .file-cta, +.file.is-primary.is-active .file-cta { + background-color: #2366d1; + border-color: transparent; + color: #fff; +} +.file.is-link .file-cta { + background-color: #3273dc; + border-color: transparent; + color: #fff; +} +.file.is-link:hover .file-cta, +.file.is-link.is-hovered .file-cta { + background-color: #276cda; + border-color: transparent; + color: #fff; +} +.file.is-link:focus .file-cta, +.file.is-link.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(50,115,220,0.25); + color: #fff; +} +.file.is-link:active .file-cta, +.file.is-link.is-active .file-cta { + background-color: #2366d1; + border-color: transparent; + color: #fff; +} +.file.is-info .file-cta { + background-color: #3298dc; + border-color: transparent; + color: #fff; +} +.file.is-info:hover .file-cta, +.file.is-info.is-hovered .file-cta { + background-color: #2793da; + border-color: transparent; + color: #fff; +} +.file.is-info:focus .file-cta, +.file.is-info.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(50,152,220,0.25); + color: #fff; +} +.file.is-info:active .file-cta, +.file.is-info.is-active .file-cta { + background-color: #238cd1; + border-color: transparent; + color: #fff; +} +.file.is-success .file-cta { + background-color: #48c774; + border-color: transparent; + color: #fff; +} +.file.is-success:hover .file-cta, +.file.is-success.is-hovered .file-cta { + background-color: #3ec46d; + border-color: transparent; + color: #fff; +} +.file.is-success:focus .file-cta, +.file.is-success.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(72,199,116,0.25); + color: #fff; +} +.file.is-success:active .file-cta, +.file.is-success.is-active .file-cta { + background-color: #3abb67; + border-color: transparent; + color: #fff; +} +.file.is-warning .file-cta { + background-color: #ffdd57; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.file.is-warning:hover .file-cta, +.file.is-warning.is-hovered .file-cta { + background-color: #ffdb4a; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.file.is-warning:focus .file-cta, +.file.is-warning.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(255,221,87,0.25); + color: rgba(0,0,0,0.7); +} +.file.is-warning:active .file-cta, +.file.is-warning.is-active .file-cta { + background-color: #ffd83d; + border-color: transparent; + color: rgba(0,0,0,0.7); +} +.file.is-danger .file-cta { + background-color: #f14668; + border-color: transparent; + color: #fff; +} +.file.is-danger:hover .file-cta, +.file.is-danger.is-hovered .file-cta { + background-color: #f03a5f; + border-color: transparent; + color: #fff; +} +.file.is-danger:focus .file-cta, +.file.is-danger.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(241,70,104,0.25); + color: #fff; +} +.file.is-danger:active .file-cta, +.file.is-danger.is-active .file-cta { + background-color: #ef2e55; + border-color: transparent; + color: #fff; +} +.file.is-grey-lightest .file-cta { + background-color: #ededed; + border-color: transparent; + color: #363636; +} +.file.is-grey-lightest:hover .file-cta, +.file.is-grey-lightest.is-hovered .file-cta { + background-color: #e7e7e7; + border-color: transparent; + color: #363636; +} +.file.is-grey-lightest:focus .file-cta, +.file.is-grey-lightest.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(237,237,237,0.25); + color: #363636; +} +.file.is-grey-lightest:active .file-cta, +.file.is-grey-lightest.is-active .file-cta { + background-color: #e0e0e0; + border-color: transparent; + color: #363636; +} +.file.is-small { + font-size: 0.75rem; +} +.file.is-medium { + font-size: 1.25rem; +} +.file.is-medium .file-icon .fa { + font-size: 21px; +} +.file.is-large { + font-size: 1.5rem; +} +.file.is-large .file-icon .fa { + font-size: 28px; +} +.file.has-name .file-cta { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.file.has-name .file-name { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.file.has-name.is-empty .file-cta { + border-radius: 4px; +} +.file.has-name.is-empty .file-name { + display: none; +} +.file.is-boxed .file-label { + flex-direction: column; +} +.file.is-boxed .file-cta { + flex-direction: column; + height: auto; + padding: 1em 3em; +} +.file.is-boxed .file-name { + border-width: 0 1px 1px; +} +.file.is-boxed .file-icon { + height: 1.5em; + width: 1.5em; +} +.file.is-boxed .file-icon .fa { + font-size: 21px; +} +.file.is-boxed.is-small .file-icon .fa { + font-size: 14px; +} +.file.is-boxed.is-medium .file-icon .fa { + font-size: 28px; +} +.file.is-boxed.is-large .file-icon .fa { + font-size: 35px; +} +.file.is-boxed.has-name .file-cta { + border-radius: 4px 4px 0 0; +} +.file.is-boxed.has-name .file-name { + border-radius: 0 0 4px 4px; + border-width: 0 1px 1px; +} +.file.is-centered { + justify-content: center; +} +.file.is-fullwidth .file-label { + width: 100%; +} +.file.is-fullwidth .file-name { + flex-grow: 1; + max-width: none; +} +.file.is-right { + justify-content: flex-end; +} +.file.is-right .file-cta { + border-radius: 0 4px 4px 0; +} +.file.is-right .file-name { + border-radius: 4px 0 0 4px; + border-width: 1px 0 1px 1px; + order: -1; +} +.file-label { + align-items: stretch; + display: flex; + cursor: pointer; + justify-content: flex-start; + overflow: hidden; + position: relative; +} +.file-label:hover .file-cta { + background-color: #eee; + color: #363636; +} +.file-label:hover .file-name { + border-color: #d5d5d5; +} +.file-label:active .file-cta { + background-color: #e8e8e8; + color: #363636; +} +.file-label:active .file-name { + border-color: #cfcfcf; +} +.file-input { + height: 100%; + left: 0; + opacity: 0; + outline: none; + position: absolute; + top: 0; + width: 100%; +} +.file-cta, +.file-name { + border-color: #dbdbdb; + border-radius: 4px; + font-size: 1em; + padding-left: 1em; + padding-right: 1em; + white-space: nowrap; +} +.file-cta { + background-color: #f5f5f5; + color: #4a4a4a; +} +.file-name { + border-color: #dbdbdb; + border-style: solid; + border-width: 1px 1px 1px 0; + display: block; + max-width: 16em; + overflow: hidden; + text-align: left; + text-overflow: ellipsis; +} +.file-icon { + align-items: center; + display: flex; + height: 1em; + justify-content: center; + margin-right: 0.5em; + width: 1em; +} +.file-icon .fa { + font-size: 14px; +} +.label { + color: #363636; + display: block; + font-size: 1rem; + font-weight: 700; +} +.label:not(:last-child) { + margin-bottom: 0.5em; +} +.label.is-small { + font-size: 0.75rem; +} +.label.is-medium { + font-size: 1.25rem; +} +.label.is-large { + font-size: 1.5rem; +} +.help { + display: block; + font-size: 0.75rem; + margin-top: 0.25rem; +} +.help.is-white { + color: #fff; +} +.help.is-black { + color: #0a0a0a; +} +.help.is-light { + color: #f5f5f5; +} +.help.is-dark { + color: #363636; +} +.help.is-primary { + color: #3273dc; +} +.help.is-link { + color: #3273dc; +} +.help.is-info { + color: #3298dc; +} +.help.is-success { + color: #48c774; +} +.help.is-warning { + color: #ffdd57; +} +.help.is-danger { + color: #f14668; +} +.help.is-grey-lightest { + color: #ededed; +} +.field:not(:last-child) { + margin-bottom: 0.75rem; +} +.field.has-addons { + display: flex; + justify-content: flex-start; +} +.field.has-addons .control:not(:last-child) { + margin-right: -1px; +} +.field.has-addons .control:not(:first-child):not(:last-child) .button, +.field.has-addons .control:not(:first-child):not(:last-child) .input, +.field.has-addons .control:not(:first-child):not(:last-child) .select select { + border-radius: 0; +} +.field.has-addons .control:first-child:not(:only-child) .button, +.field.has-addons .control:first-child:not(:only-child) .input, +.field.has-addons .control:first-child:not(:only-child) .select select { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.field.has-addons .control:last-child:not(:only-child) .button, +.field.has-addons .control:last-child:not(:only-child) .input, +.field.has-addons .control:last-child:not(:only-child) .select select { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.field.has-addons .control .button:not([disabled]):hover, +.field.has-addons .control .input:not([disabled]):hover, +.field.has-addons .control .select select:not([disabled]):hover, +.field.has-addons .control .button:not([disabled]).is-hovered, +.field.has-addons .control .input:not([disabled]).is-hovered, +.field.has-addons .control .select select:not([disabled]).is-hovered { + z-index: 2; +} +.field.has-addons .control .button:not([disabled]):focus, +.field.has-addons .control .input:not([disabled]):focus, +.field.has-addons .control .select select:not([disabled]):focus, +.field.has-addons .control .button:not([disabled]).is-focused, +.field.has-addons .control .input:not([disabled]).is-focused, +.field.has-addons .control .select select:not([disabled]).is-focused, +.field.has-addons .control .button:not([disabled]):active, +.field.has-addons .control .input:not([disabled]):active, +.field.has-addons .control .select select:not([disabled]):active, +.field.has-addons .control .button:not([disabled]).is-active, +.field.has-addons .control .input:not([disabled]).is-active, +.field.has-addons .control .select select:not([disabled]).is-active { + z-index: 3; +} +.field.has-addons .control .button:not([disabled]):focus:hover, +.field.has-addons .control .input:not([disabled]):focus:hover, +.field.has-addons .control .select select:not([disabled]):focus:hover, +.field.has-addons .control .button:not([disabled]).is-focused:hover, +.field.has-addons .control .input:not([disabled]).is-focused:hover, +.field.has-addons .control .select select:not([disabled]).is-focused:hover, +.field.has-addons .control .button:not([disabled]):active:hover, +.field.has-addons .control .input:not([disabled]):active:hover, +.field.has-addons .control .select select:not([disabled]):active:hover, +.field.has-addons .control .button:not([disabled]).is-active:hover, +.field.has-addons .control .input:not([disabled]).is-active:hover, +.field.has-addons .control .select select:not([disabled]).is-active:hover { + z-index: 4; +} +.field.has-addons .control.is-expanded { + flex-grow: 1; + flex-shrink: 1; +} +.field.has-addons.has-addons-centered { + justify-content: center; +} +.field.has-addons.has-addons-right { + justify-content: flex-end; +} +.field.has-addons.has-addons-fullwidth .control { + flex-grow: 1; + flex-shrink: 0; +} +.field.is-grouped { + display: flex; + justify-content: flex-start; +} +.field.is-grouped > .control { + flex-shrink: 0; +} +.field.is-grouped > .control:not(:last-child) { + margin-bottom: 0; + margin-right: 0.75rem; +} +.field.is-grouped > .control.is-expanded { + flex-grow: 1; + flex-shrink: 1; +} +.field.is-grouped.is-grouped-centered { + justify-content: center; +} +.field.is-grouped.is-grouped-right { + justify-content: flex-end; +} +.field.is-grouped.is-grouped-multiline { + flex-wrap: wrap; +} +.field.is-grouped.is-grouped-multiline > .control:last-child, +.field.is-grouped.is-grouped-multiline > .control:not(:last-child) { + margin-bottom: 0.75rem; +} +.field.is-grouped.is-grouped-multiline:last-child { + margin-bottom: -0.75rem; +} +.field.is-grouped.is-grouped-multiline:not(:last-child) { + margin-bottom: 0; +} +@media screen and (min-width: 769px), print { + .field.is-horizontal { + display: flex; + } +} +.field-label .label { + font-size: inherit; +} +@media screen and (max-width: 768px) { + .field-label { + margin-bottom: 0.5rem; + } +} +@media screen and (min-width: 769px), print { + .field-label { + flex-basis: 0; + flex-grow: 1; + flex-shrink: 0; + margin-right: 1.5rem; + text-align: right; + } + .field-label.is-small { + font-size: 0.75rem; + padding-top: 0.375em; + } + .field-label.is-normal { + padding-top: 0.375em; + } + .field-label.is-medium { + font-size: 1.25rem; + padding-top: 0.375em; + } + .field-label.is-large { + font-size: 1.5rem; + padding-top: 0.375em; + } +} +.field-body .field .field { + margin-bottom: 0; +} +@media screen and (min-width: 769px), print { + .field-body { + display: flex; + flex-basis: 0; + flex-grow: 5; + flex-shrink: 1; + } + .field-body .field { + margin-bottom: 0; + } + .field-body > .field { + flex-shrink: 1; + } + .field-body > .field:not(.is-narrow) { + flex-grow: 1; + } + .field-body > .field:not(:last-child) { + margin-right: 0.75rem; + } +} +.control { + box-sizing: border-box; + clear: both; + font-size: 1rem; + position: relative; + text-align: left; +} +.control.has-icons-left .input:focus ~ .icon, +.control.has-icons-right .input:focus ~ .icon, +.control.has-icons-left .select:focus ~ .icon, +.control.has-icons-right .select:focus ~ .icon { + color: #4a4a4a; +} +.control.has-icons-left .input.is-small ~ .icon, +.control.has-icons-right .input.is-small ~ .icon, +.control.has-icons-left .select.is-small ~ .icon, +.control.has-icons-right .select.is-small ~ .icon { + font-size: 0.75rem; +} +.control.has-icons-left .input.is-medium ~ .icon, +.control.has-icons-right .input.is-medium ~ .icon, +.control.has-icons-left .select.is-medium ~ .icon, +.control.has-icons-right .select.is-medium ~ .icon { + font-size: 1.25rem; +} +.control.has-icons-left .input.is-large ~ .icon, +.control.has-icons-right .input.is-large ~ .icon, +.control.has-icons-left .select.is-large ~ .icon, +.control.has-icons-right .select.is-large ~ .icon { + font-size: 1.5rem; +} +.control.has-icons-left .icon, +.control.has-icons-right .icon { + color: #dbdbdb; + height: 2.25em; + pointer-events: none; + position: absolute; + top: 0; + width: 2.25em; + z-index: 4; +} +.control.has-icons-left .input, +.control.has-icons-left .select select { + padding-left: 2.25em; +} +.control.has-icons-left .icon.is-left { + left: 0; +} +.control.has-icons-right .input, +.control.has-icons-right .select select { + padding-right: 2.25em; +} +.control.has-icons-right .icon.is-right { + right: 0; +} +.control.is-loading::after { + position: absolute !important; + right: 0.625em; + top: 0.625em; + z-index: 4; +} +.control.is-loading.is-small:after { + font-size: 0.75rem; +} +.control.is-loading.is-medium:after { + font-size: 1.25rem; +} +.control.is-loading.is-large:after { + font-size: 1.5rem; +} +.column { + display: block; + flex-basis: 0; + flex-grow: 1; + flex-shrink: 1; + padding: 0.75rem; +} +.columns.is-mobile > .column.is-narrow { + flex: none; +} +.columns.is-mobile > .column.is-full { + flex: none; + width: 100%; +} +.columns.is-mobile > .column.is-three-quarters { + flex: none; + width: 75%; +} +.columns.is-mobile > .column.is-two-thirds { + flex: none; + width: 66.6666%; +} +.columns.is-mobile > .column.is-half { + flex: none; + width: 50%; +} +.columns.is-mobile > .column.is-one-third { + flex: none; + width: 33.3333%; +} +.columns.is-mobile > .column.is-one-quarter { + flex: none; + width: 25%; +} +.columns.is-mobile > .column.is-one-fifth { + flex: none; + width: 20%; +} +.columns.is-mobile > .column.is-two-fifths { + flex: none; + width: 40%; +} +.columns.is-mobile > .column.is-three-fifths { + flex: none; + width: 60%; +} +.columns.is-mobile > .column.is-four-fifths { + flex: none; + width: 80%; +} +.columns.is-mobile > .column.is-offset-three-quarters { + margin-left: 75%; +} +.columns.is-mobile > .column.is-offset-two-thirds { + margin-left: 66.6666%; +} +.columns.is-mobile > .column.is-offset-half { + margin-left: 50%; +} +.columns.is-mobile > .column.is-offset-one-third { + margin-left: 33.3333%; +} +.columns.is-mobile > .column.is-offset-one-quarter { + margin-left: 25%; +} +.columns.is-mobile > .column.is-offset-one-fifth { + margin-left: 20%; +} +.columns.is-mobile > .column.is-offset-two-fifths { + margin-left: 40%; +} +.columns.is-mobile > .column.is-offset-three-fifths { + margin-left: 60%; +} +.columns.is-mobile > .column.is-offset-four-fifths { + margin-left: 80%; +} +.columns.is-mobile > .column.is-0 { + flex: none; + width: 0%; +} +.columns.is-mobile > .column.is-offset-0 { + margin-left: 0%; +} +.columns.is-mobile > .column.is-1 { + flex: none; + width: 8.33333%; +} +.columns.is-mobile > .column.is-offset-1 { + margin-left: 8.33333%; +} +.columns.is-mobile > .column.is-2 { + flex: none; + width: 16.66667%; +} +.columns.is-mobile > .column.is-offset-2 { + margin-left: 16.66667%; +} +.columns.is-mobile > .column.is-3 { + flex: none; + width: 25%; +} +.columns.is-mobile > .column.is-offset-3 { + margin-left: 25%; +} +.columns.is-mobile > .column.is-4 { + flex: none; + width: 33.33333%; +} +.columns.is-mobile > .column.is-offset-4 { + margin-left: 33.33333%; +} +.columns.is-mobile > .column.is-5 { + flex: none; + width: 41.66667%; +} +.columns.is-mobile > .column.is-offset-5 { + margin-left: 41.66667%; +} +.columns.is-mobile > .column.is-6 { + flex: none; + width: 50%; +} +.columns.is-mobile > .column.is-offset-6 { + margin-left: 50%; +} +.columns.is-mobile > .column.is-7 { + flex: none; + width: 58.33333%; +} +.columns.is-mobile > .column.is-offset-7 { + margin-left: 58.33333%; +} +.columns.is-mobile > .column.is-8 { + flex: none; + width: 66.66667%; +} +.columns.is-mobile > .column.is-offset-8 { + margin-left: 66.66667%; +} +.columns.is-mobile > .column.is-9 { + flex: none; + width: 75%; +} +.columns.is-mobile > .column.is-offset-9 { + margin-left: 75%; +} +.columns.is-mobile > .column.is-10 { + flex: none; + width: 83.33333%; +} +.columns.is-mobile > .column.is-offset-10 { + margin-left: 83.33333%; +} +.columns.is-mobile > .column.is-11 { + flex: none; + width: 91.66667%; +} +.columns.is-mobile > .column.is-offset-11 { + margin-left: 91.66667%; +} +.columns.is-mobile > .column.is-12 { + flex: none; + width: 100%; +} +.columns.is-mobile > .column.is-offset-12 { + margin-left: 100%; +} +@media screen and (max-width: 768px) { + .column.is-narrow-mobile { + flex: none; + } + .column.is-full-mobile { + flex: none; + width: 100%; + } + .column.is-three-quarters-mobile { + flex: none; + width: 75%; + } + .column.is-two-thirds-mobile { + flex: none; + width: 66.6666%; + } + .column.is-half-mobile { + flex: none; + width: 50%; + } + .column.is-one-third-mobile { + flex: none; + width: 33.3333%; + } + .column.is-one-quarter-mobile { + flex: none; + width: 25%; + } + .column.is-one-fifth-mobile { + flex: none; + width: 20%; + } + .column.is-two-fifths-mobile { + flex: none; + width: 40%; + } + .column.is-three-fifths-mobile { + flex: none; + width: 60%; + } + .column.is-four-fifths-mobile { + flex: none; + width: 80%; + } + .column.is-offset-three-quarters-mobile { + margin-left: 75%; + } + .column.is-offset-two-thirds-mobile { + margin-left: 66.6666%; + } + .column.is-offset-half-mobile { + margin-left: 50%; + } + .column.is-offset-one-third-mobile { + margin-left: 33.3333%; + } + .column.is-offset-one-quarter-mobile { + margin-left: 25%; + } + .column.is-offset-one-fifth-mobile { + margin-left: 20%; + } + .column.is-offset-two-fifths-mobile { + margin-left: 40%; + } + .column.is-offset-three-fifths-mobile { + margin-left: 60%; + } + .column.is-offset-four-fifths-mobile { + margin-left: 80%; + } + .column.is-0-mobile { + flex: none; + width: 0%; + } + .column.is-offset-0-mobile { + margin-left: 0%; + } + .column.is-1-mobile { + flex: none; + width: 8.33333%; + } + .column.is-offset-1-mobile { + margin-left: 8.33333%; + } + .column.is-2-mobile { + flex: none; + width: 16.66667%; + } + .column.is-offset-2-mobile { + margin-left: 16.66667%; + } + .column.is-3-mobile { + flex: none; + width: 25%; + } + .column.is-offset-3-mobile { + margin-left: 25%; + } + .column.is-4-mobile { + flex: none; + width: 33.33333%; + } + .column.is-offset-4-mobile { + margin-left: 33.33333%; + } + .column.is-5-mobile { + flex: none; + width: 41.66667%; + } + .column.is-offset-5-mobile { + margin-left: 41.66667%; + } + .column.is-6-mobile { + flex: none; + width: 50%; + } + .column.is-offset-6-mobile { + margin-left: 50%; + } + .column.is-7-mobile { + flex: none; + width: 58.33333%; + } + .column.is-offset-7-mobile { + margin-left: 58.33333%; + } + .column.is-8-mobile { + flex: none; + width: 66.66667%; + } + .column.is-offset-8-mobile { + margin-left: 66.66667%; + } + .column.is-9-mobile { + flex: none; + width: 75%; + } + .column.is-offset-9-mobile { + margin-left: 75%; + } + .column.is-10-mobile { + flex: none; + width: 83.33333%; + } + .column.is-offset-10-mobile { + margin-left: 83.33333%; + } + .column.is-11-mobile { + flex: none; + width: 91.66667%; + } + .column.is-offset-11-mobile { + margin-left: 91.66667%; + } + .column.is-12-mobile { + flex: none; + width: 100%; + } + .column.is-offset-12-mobile { + margin-left: 100%; + } +} +@media screen and (min-width: 769px), print { + .column.is-narrow, + .column.is-narrow-tablet { + flex: none; + } + .column.is-full, + .column.is-full-tablet { + flex: none; + width: 100%; + } + .column.is-three-quarters, + .column.is-three-quarters-tablet { + flex: none; + width: 75%; + } + .column.is-two-thirds, + .column.is-two-thirds-tablet { + flex: none; + width: 66.6666%; + } + .column.is-half, + .column.is-half-tablet { + flex: none; + width: 50%; + } + .column.is-one-third, + .column.is-one-third-tablet { + flex: none; + width: 33.3333%; + } + .column.is-one-quarter, + .column.is-one-quarter-tablet { + flex: none; + width: 25%; + } + .column.is-one-fifth, + .column.is-one-fifth-tablet { + flex: none; + width: 20%; + } + .column.is-two-fifths, + .column.is-two-fifths-tablet { + flex: none; + width: 40%; + } + .column.is-three-fifths, + .column.is-three-fifths-tablet { + flex: none; + width: 60%; + } + .column.is-four-fifths, + .column.is-four-fifths-tablet { + flex: none; + width: 80%; + } + .column.is-offset-three-quarters, + .column.is-offset-three-quarters-tablet { + margin-left: 75%; + } + .column.is-offset-two-thirds, + .column.is-offset-two-thirds-tablet { + margin-left: 66.6666%; + } + .column.is-offset-half, + .column.is-offset-half-tablet { + margin-left: 50%; + } + .column.is-offset-one-third, + .column.is-offset-one-third-tablet { + margin-left: 33.3333%; + } + .column.is-offset-one-quarter, + .column.is-offset-one-quarter-tablet { + margin-left: 25%; + } + .column.is-offset-one-fifth, + .column.is-offset-one-fifth-tablet { + margin-left: 20%; + } + .column.is-offset-two-fifths, + .column.is-offset-two-fifths-tablet { + margin-left: 40%; + } + .column.is-offset-three-fifths, + .column.is-offset-three-fifths-tablet { + margin-left: 60%; + } + .column.is-offset-four-fifths, + .column.is-offset-four-fifths-tablet { + margin-left: 80%; + } + .column.is-0, + .column.is-0-tablet { + flex: none; + width: 0%; + } + .column.is-offset-0, + .column.is-offset-0-tablet { + margin-left: 0%; + } + .column.is-1, + .column.is-1-tablet { + flex: none; + width: 8.33333%; + } + .column.is-offset-1, + .column.is-offset-1-tablet { + margin-left: 8.33333%; + } + .column.is-2, + .column.is-2-tablet { + flex: none; + width: 16.66667%; + } + .column.is-offset-2, + .column.is-offset-2-tablet { + margin-left: 16.66667%; + } + .column.is-3, + .column.is-3-tablet { + flex: none; + width: 25%; + } + .column.is-offset-3, + .column.is-offset-3-tablet { + margin-left: 25%; + } + .column.is-4, + .column.is-4-tablet { + flex: none; + width: 33.33333%; + } + .column.is-offset-4, + .column.is-offset-4-tablet { + margin-left: 33.33333%; + } + .column.is-5, + .column.is-5-tablet { + flex: none; + width: 41.66667%; + } + .column.is-offset-5, + .column.is-offset-5-tablet { + margin-left: 41.66667%; + } + .column.is-6, + .column.is-6-tablet { + flex: none; + width: 50%; + } + .column.is-offset-6, + .column.is-offset-6-tablet { + margin-left: 50%; + } + .column.is-7, + .column.is-7-tablet { + flex: none; + width: 58.33333%; + } + .column.is-offset-7, + .column.is-offset-7-tablet { + margin-left: 58.33333%; + } + .column.is-8, + .column.is-8-tablet { + flex: none; + width: 66.66667%; + } + .column.is-offset-8, + .column.is-offset-8-tablet { + margin-left: 66.66667%; + } + .column.is-9, + .column.is-9-tablet { + flex: none; + width: 75%; + } + .column.is-offset-9, + .column.is-offset-9-tablet { + margin-left: 75%; + } + .column.is-10, + .column.is-10-tablet { + flex: none; + width: 83.33333%; + } + .column.is-offset-10, + .column.is-offset-10-tablet { + margin-left: 83.33333%; + } + .column.is-11, + .column.is-11-tablet { + flex: none; + width: 91.66667%; + } + .column.is-offset-11, + .column.is-offset-11-tablet { + margin-left: 91.66667%; + } + .column.is-12, + .column.is-12-tablet { + flex: none; + width: 100%; + } + .column.is-offset-12, + .column.is-offset-12-tablet { + margin-left: 100%; + } +} +@media screen and (max-width: 1087px) { + .column.is-narrow-touch { + flex: none; + } + .column.is-full-touch { + flex: none; + width: 100%; + } + .column.is-three-quarters-touch { + flex: none; + width: 75%; + } + .column.is-two-thirds-touch { + flex: none; + width: 66.6666%; + } + .column.is-half-touch { + flex: none; + width: 50%; + } + .column.is-one-third-touch { + flex: none; + width: 33.3333%; + } + .column.is-one-quarter-touch { + flex: none; + width: 25%; + } + .column.is-one-fifth-touch { + flex: none; + width: 20%; + } + .column.is-two-fifths-touch { + flex: none; + width: 40%; + } + .column.is-three-fifths-touch { + flex: none; + width: 60%; + } + .column.is-four-fifths-touch { + flex: none; + width: 80%; + } + .column.is-offset-three-quarters-touch { + margin-left: 75%; + } + .column.is-offset-two-thirds-touch { + margin-left: 66.6666%; + } + .column.is-offset-half-touch { + margin-left: 50%; + } + .column.is-offset-one-third-touch { + margin-left: 33.3333%; + } + .column.is-offset-one-quarter-touch { + margin-left: 25%; + } + .column.is-offset-one-fifth-touch { + margin-left: 20%; + } + .column.is-offset-two-fifths-touch { + margin-left: 40%; + } + .column.is-offset-three-fifths-touch { + margin-left: 60%; + } + .column.is-offset-four-fifths-touch { + margin-left: 80%; + } + .column.is-0-touch { + flex: none; + width: 0%; + } + .column.is-offset-0-touch { + margin-left: 0%; + } + .column.is-1-touch { + flex: none; + width: 8.33333%; + } + .column.is-offset-1-touch { + margin-left: 8.33333%; + } + .column.is-2-touch { + flex: none; + width: 16.66667%; + } + .column.is-offset-2-touch { + margin-left: 16.66667%; + } + .column.is-3-touch { + flex: none; + width: 25%; + } + .column.is-offset-3-touch { + margin-left: 25%; + } + .column.is-4-touch { + flex: none; + width: 33.33333%; + } + .column.is-offset-4-touch { + margin-left: 33.33333%; + } + .column.is-5-touch { + flex: none; + width: 41.66667%; + } + .column.is-offset-5-touch { + margin-left: 41.66667%; + } + .column.is-6-touch { + flex: none; + width: 50%; + } + .column.is-offset-6-touch { + margin-left: 50%; + } + .column.is-7-touch { + flex: none; + width: 58.33333%; + } + .column.is-offset-7-touch { + margin-left: 58.33333%; + } + .column.is-8-touch { + flex: none; + width: 66.66667%; + } + .column.is-offset-8-touch { + margin-left: 66.66667%; + } + .column.is-9-touch { + flex: none; + width: 75%; + } + .column.is-offset-9-touch { + margin-left: 75%; + } + .column.is-10-touch { + flex: none; + width: 83.33333%; + } + .column.is-offset-10-touch { + margin-left: 83.33333%; + } + .column.is-11-touch { + flex: none; + width: 91.66667%; + } + .column.is-offset-11-touch { + margin-left: 91.66667%; + } + .column.is-12-touch { + flex: none; + width: 100%; + } + .column.is-offset-12-touch { + margin-left: 100%; + } +} +@media screen and (min-width: 1088px) { + .column.is-narrow-desktop { + flex: none; + } + .column.is-full-desktop { + flex: none; + width: 100%; + } + .column.is-three-quarters-desktop { + flex: none; + width: 75%; + } + .column.is-two-thirds-desktop { + flex: none; + width: 66.6666%; + } + .column.is-half-desktop { + flex: none; + width: 50%; + } + .column.is-one-third-desktop { + flex: none; + width: 33.3333%; + } + .column.is-one-quarter-desktop { + flex: none; + width: 25%; + } + .column.is-one-fifth-desktop { + flex: none; + width: 20%; + } + .column.is-two-fifths-desktop { + flex: none; + width: 40%; + } + .column.is-three-fifths-desktop { + flex: none; + width: 60%; + } + .column.is-four-fifths-desktop { + flex: none; + width: 80%; + } + .column.is-offset-three-quarters-desktop { + margin-left: 75%; + } + .column.is-offset-two-thirds-desktop { + margin-left: 66.6666%; + } + .column.is-offset-half-desktop { + margin-left: 50%; + } + .column.is-offset-one-third-desktop { + margin-left: 33.3333%; + } + .column.is-offset-one-quarter-desktop { + margin-left: 25%; + } + .column.is-offset-one-fifth-desktop { + margin-left: 20%; + } + .column.is-offset-two-fifths-desktop { + margin-left: 40%; + } + .column.is-offset-three-fifths-desktop { + margin-left: 60%; + } + .column.is-offset-four-fifths-desktop { + margin-left: 80%; + } + .column.is-0-desktop { + flex: none; + width: 0%; + } + .column.is-offset-0-desktop { + margin-left: 0%; + } + .column.is-1-desktop { + flex: none; + width: 8.33333%; + } + .column.is-offset-1-desktop { + margin-left: 8.33333%; + } + .column.is-2-desktop { + flex: none; + width: 16.66667%; + } + .column.is-offset-2-desktop { + margin-left: 16.66667%; + } + .column.is-3-desktop { + flex: none; + width: 25%; + } + .column.is-offset-3-desktop { + margin-left: 25%; + } + .column.is-4-desktop { + flex: none; + width: 33.33333%; + } + .column.is-offset-4-desktop { + margin-left: 33.33333%; + } + .column.is-5-desktop { + flex: none; + width: 41.66667%; + } + .column.is-offset-5-desktop { + margin-left: 41.66667%; + } + .column.is-6-desktop { + flex: none; + width: 50%; + } + .column.is-offset-6-desktop { + margin-left: 50%; + } + .column.is-7-desktop { + flex: none; + width: 58.33333%; + } + .column.is-offset-7-desktop { + margin-left: 58.33333%; + } + .column.is-8-desktop { + flex: none; + width: 66.66667%; + } + .column.is-offset-8-desktop { + margin-left: 66.66667%; + } + .column.is-9-desktop { + flex: none; + width: 75%; + } + .column.is-offset-9-desktop { + margin-left: 75%; + } + .column.is-10-desktop { + flex: none; + width: 83.33333%; + } + .column.is-offset-10-desktop { + margin-left: 83.33333%; + } + .column.is-11-desktop { + flex: none; + width: 91.66667%; + } + .column.is-offset-11-desktop { + margin-left: 91.66667%; + } + .column.is-12-desktop { + flex: none; + width: 100%; + } + .column.is-offset-12-desktop { + margin-left: 100%; + } +} +@media screen and (min-width: 1280px) { + .column.is-narrow-widescreen { + flex: none; + } + .column.is-full-widescreen { + flex: none; + width: 100%; + } + .column.is-three-quarters-widescreen { + flex: none; + width: 75%; + } + .column.is-two-thirds-widescreen { + flex: none; + width: 66.6666%; + } + .column.is-half-widescreen { + flex: none; + width: 50%; + } + .column.is-one-third-widescreen { + flex: none; + width: 33.3333%; + } + .column.is-one-quarter-widescreen { + flex: none; + width: 25%; + } + .column.is-one-fifth-widescreen { + flex: none; + width: 20%; + } + .column.is-two-fifths-widescreen { + flex: none; + width: 40%; + } + .column.is-three-fifths-widescreen { + flex: none; + width: 60%; + } + .column.is-four-fifths-widescreen { + flex: none; + width: 80%; + } + .column.is-offset-three-quarters-widescreen { + margin-left: 75%; + } + .column.is-offset-two-thirds-widescreen { + margin-left: 66.6666%; + } + .column.is-offset-half-widescreen { + margin-left: 50%; + } + .column.is-offset-one-third-widescreen { + margin-left: 33.3333%; + } + .column.is-offset-one-quarter-widescreen { + margin-left: 25%; + } + .column.is-offset-one-fifth-widescreen { + margin-left: 20%; + } + .column.is-offset-two-fifths-widescreen { + margin-left: 40%; + } + .column.is-offset-three-fifths-widescreen { + margin-left: 60%; + } + .column.is-offset-four-fifths-widescreen { + margin-left: 80%; + } + .column.is-0-widescreen { + flex: none; + width: 0%; + } + .column.is-offset-0-widescreen { + margin-left: 0%; + } + .column.is-1-widescreen { + flex: none; + width: 8.33333%; + } + .column.is-offset-1-widescreen { + margin-left: 8.33333%; + } + .column.is-2-widescreen { + flex: none; + width: 16.66667%; + } + .column.is-offset-2-widescreen { + margin-left: 16.66667%; + } + .column.is-3-widescreen { + flex: none; + width: 25%; + } + .column.is-offset-3-widescreen { + margin-left: 25%; + } + .column.is-4-widescreen { + flex: none; + width: 33.33333%; + } + .column.is-offset-4-widescreen { + margin-left: 33.33333%; + } + .column.is-5-widescreen { + flex: none; + width: 41.66667%; + } + .column.is-offset-5-widescreen { + margin-left: 41.66667%; + } + .column.is-6-widescreen { + flex: none; + width: 50%; + } + .column.is-offset-6-widescreen { + margin-left: 50%; + } + .column.is-7-widescreen { + flex: none; + width: 58.33333%; + } + .column.is-offset-7-widescreen { + margin-left: 58.33333%; + } + .column.is-8-widescreen { + flex: none; + width: 66.66667%; + } + .column.is-offset-8-widescreen { + margin-left: 66.66667%; + } + .column.is-9-widescreen { + flex: none; + width: 75%; + } + .column.is-offset-9-widescreen { + margin-left: 75%; + } + .column.is-10-widescreen { + flex: none; + width: 83.33333%; + } + .column.is-offset-10-widescreen { + margin-left: 83.33333%; + } + .column.is-11-widescreen { + flex: none; + width: 91.66667%; + } + .column.is-offset-11-widescreen { + margin-left: 91.66667%; + } + .column.is-12-widescreen { + flex: none; + width: 100%; + } + .column.is-offset-12-widescreen { + margin-left: 100%; + } +} +@media screen and (min-width: 1472px) { + .column.is-narrow-fullhd { + flex: none; + } + .column.is-full-fullhd { + flex: none; + width: 100%; + } + .column.is-three-quarters-fullhd { + flex: none; + width: 75%; + } + .column.is-two-thirds-fullhd { + flex: none; + width: 66.6666%; + } + .column.is-half-fullhd { + flex: none; + width: 50%; + } + .column.is-one-third-fullhd { + flex: none; + width: 33.3333%; + } + .column.is-one-quarter-fullhd { + flex: none; + width: 25%; + } + .column.is-one-fifth-fullhd { + flex: none; + width: 20%; + } + .column.is-two-fifths-fullhd { + flex: none; + width: 40%; + } + .column.is-three-fifths-fullhd { + flex: none; + width: 60%; + } + .column.is-four-fifths-fullhd { + flex: none; + width: 80%; + } + .column.is-offset-three-quarters-fullhd { + margin-left: 75%; + } + .column.is-offset-two-thirds-fullhd { + margin-left: 66.6666%; + } + .column.is-offset-half-fullhd { + margin-left: 50%; + } + .column.is-offset-one-third-fullhd { + margin-left: 33.3333%; + } + .column.is-offset-one-quarter-fullhd { + margin-left: 25%; + } + .column.is-offset-one-fifth-fullhd { + margin-left: 20%; + } + .column.is-offset-two-fifths-fullhd { + margin-left: 40%; + } + .column.is-offset-three-fifths-fullhd { + margin-left: 60%; + } + .column.is-offset-four-fifths-fullhd { + margin-left: 80%; + } + .column.is-0-fullhd { + flex: none; + width: 0%; + } + .column.is-offset-0-fullhd { + margin-left: 0%; + } + .column.is-1-fullhd { + flex: none; + width: 8.33333%; + } + .column.is-offset-1-fullhd { + margin-left: 8.33333%; + } + .column.is-2-fullhd { + flex: none; + width: 16.66667%; + } + .column.is-offset-2-fullhd { + margin-left: 16.66667%; + } + .column.is-3-fullhd { + flex: none; + width: 25%; + } + .column.is-offset-3-fullhd { + margin-left: 25%; + } + .column.is-4-fullhd { + flex: none; + width: 33.33333%; + } + .column.is-offset-4-fullhd { + margin-left: 33.33333%; + } + .column.is-5-fullhd { + flex: none; + width: 41.66667%; + } + .column.is-offset-5-fullhd { + margin-left: 41.66667%; + } + .column.is-6-fullhd { + flex: none; + width: 50%; + } + .column.is-offset-6-fullhd { + margin-left: 50%; + } + .column.is-7-fullhd { + flex: none; + width: 58.33333%; + } + .column.is-offset-7-fullhd { + margin-left: 58.33333%; + } + .column.is-8-fullhd { + flex: none; + width: 66.66667%; + } + .column.is-offset-8-fullhd { + margin-left: 66.66667%; + } + .column.is-9-fullhd { + flex: none; + width: 75%; + } + .column.is-offset-9-fullhd { + margin-left: 75%; + } + .column.is-10-fullhd { + flex: none; + width: 83.33333%; + } + .column.is-offset-10-fullhd { + margin-left: 83.33333%; + } + .column.is-11-fullhd { + flex: none; + width: 91.66667%; + } + .column.is-offset-11-fullhd { + margin-left: 91.66667%; + } + .column.is-12-fullhd { + flex: none; + width: 100%; + } + .column.is-offset-12-fullhd { + margin-left: 100%; + } +} +.columns { + margin-left: -0.75rem; + margin-right: -0.75rem; + margin-top: -0.75rem; +} +.columns:last-child { + margin-bottom: -0.75rem; +} +.columns:not(:last-child) { + margin-bottom: calc(1.5rem - 0.75rem); +} +.columns.is-centered { + justify-content: center; +} +.columns.is-gapless { + margin-left: 0; + margin-right: 0; + margin-top: 0; +} +.columns.is-gapless > .column { + margin: 0; + padding: 0 !important; +} +.columns.is-gapless:not(:last-child) { + margin-bottom: 1.5rem; +} +.columns.is-gapless:last-child { + margin-bottom: 0; +} +.columns.is-mobile { + display: flex; +} +.columns.is-multiline { + flex-wrap: wrap; +} +.columns.is-vcentered { + align-items: center; +} +@media screen and (min-width: 769px), print { + .columns:not(.is-desktop) { + display: flex; + } +} +@media screen and (min-width: 1088px) { + .columns.is-desktop { + display: flex; + } +} +.columns.is-variable { + --columnGap: 0.75rem; + margin-left: calc(-1 * var(--columnGap)); + margin-right: calc(-1 * var(--columnGap)); +} +.columns.is-variable .column { + padding-left: var(--columnGap); + padding-right: var(--columnGap); +} +.columns.is-variable.is-0 { + --columnGap: 0rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-0-mobile { + --columnGap: 0rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-0-tablet { + --columnGap: 0rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-0-tablet-only { + --columnGap: 0rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-0-touch { + --columnGap: 0rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-0-desktop { + --columnGap: 0rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-0-desktop-only { + --columnGap: 0rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-0-widescreen { + --columnGap: 0rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-0-widescreen-only { + --columnGap: 0rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-0-fullhd { + --columnGap: 0rem; + } +} +.columns.is-variable.is-1 { + --columnGap: 0.25rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-1-mobile { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-1-tablet { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-1-tablet-only { + --columnGap: 0.25rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-1-touch { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-1-desktop { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-1-desktop-only { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-1-widescreen { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-1-widescreen-only { + --columnGap: 0.25rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-1-fullhd { + --columnGap: 0.25rem; + } +} +.columns.is-variable.is-2 { + --columnGap: 0.5rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-2-mobile { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-2-tablet { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-2-tablet-only { + --columnGap: 0.5rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-2-touch { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-2-desktop { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-2-desktop-only { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-2-widescreen { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-2-widescreen-only { + --columnGap: 0.5rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-2-fullhd { + --columnGap: 0.5rem; + } +} +.columns.is-variable.is-3 { + --columnGap: 0.75rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-3-mobile { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-3-tablet { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-3-tablet-only { + --columnGap: 0.75rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-3-touch { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-3-desktop { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-3-desktop-only { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-3-widescreen { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-3-widescreen-only { + --columnGap: 0.75rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-3-fullhd { + --columnGap: 0.75rem; + } +} +.columns.is-variable.is-4 { + --columnGap: 1rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-4-mobile { + --columnGap: 1rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-4-tablet { + --columnGap: 1rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-4-tablet-only { + --columnGap: 1rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-4-touch { + --columnGap: 1rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-4-desktop { + --columnGap: 1rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-4-desktop-only { + --columnGap: 1rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-4-widescreen { + --columnGap: 1rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-4-widescreen-only { + --columnGap: 1rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-4-fullhd { + --columnGap: 1rem; + } +} +.columns.is-variable.is-5 { + --columnGap: 1.25rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-5-mobile { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-5-tablet { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-5-tablet-only { + --columnGap: 1.25rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-5-touch { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-5-desktop { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-5-desktop-only { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-5-widescreen { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-5-widescreen-only { + --columnGap: 1.25rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-5-fullhd { + --columnGap: 1.25rem; + } +} +.columns.is-variable.is-6 { + --columnGap: 1.5rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-6-mobile { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-6-tablet { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-6-tablet-only { + --columnGap: 1.5rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-6-touch { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-6-desktop { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-6-desktop-only { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-6-widescreen { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-6-widescreen-only { + --columnGap: 1.5rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-6-fullhd { + --columnGap: 1.5rem; + } +} +.columns.is-variable.is-7 { + --columnGap: 1.75rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-7-mobile { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-7-tablet { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-7-tablet-only { + --columnGap: 1.75rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-7-touch { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-7-desktop { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-7-desktop-only { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-7-widescreen { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-7-widescreen-only { + --columnGap: 1.75rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-7-fullhd { + --columnGap: 1.75rem; + } +} +.columns.is-variable.is-8 { + --columnGap: 2rem; +} +@media screen and (max-width: 768px) { + .columns.is-variable.is-8-mobile { + --columnGap: 2rem; + } +} +@media screen and (min-width: 769px), print { + .columns.is-variable.is-8-tablet { + --columnGap: 2rem; + } +} +@media screen and (min-width: 769px) and (max-width: 1087px) { + .columns.is-variable.is-8-tablet-only { + --columnGap: 2rem; + } +} +@media screen and (max-width: 1087px) { + .columns.is-variable.is-8-touch { + --columnGap: 2rem; + } +} +@media screen and (min-width: 1088px) { + .columns.is-variable.is-8-desktop { + --columnGap: 2rem; + } +} +@media screen and (min-width: 1088px) and (max-width: 1279px) { + .columns.is-variable.is-8-desktop-only { + --columnGap: 2rem; + } +} +@media screen and (min-width: 1280px) { + .columns.is-variable.is-8-widescreen { + --columnGap: 2rem; + } +} +@media screen and (min-width: 1280px) and (max-width: 1471px) { + .columns.is-variable.is-8-widescreen-only { + --columnGap: 2rem; + } +} +@media screen and (min-width: 1472px) { + .columns.is-variable.is-8-fullhd { + --columnGap: 2rem; + } +} +.tile { + align-items: stretch; + display: block; + flex-basis: 0; + flex-grow: 1; + flex-shrink: 1; + min-height: min-content; +} +.tile.is-ancestor { + margin-left: -0.75rem; + margin-right: -0.75rem; + margin-top: -0.75rem; +} +.tile.is-ancestor:last-child { + margin-bottom: -0.75rem; +} +.tile.is-ancestor:not(:last-child) { + margin-bottom: 0.75rem; +} +.tile.is-child { + margin: 0 !important; +} +.tile.is-parent { + padding: 0.75rem; +} +.tile.is-vertical { + flex-direction: column; +} +.tile.is-vertical > .tile.is-child:not(:last-child) { + margin-bottom: 1.5rem !important; +} +@media screen and (min-width: 769px), print { + .tile:not(.is-child) { + display: flex; + } + .tile.is-1 { + flex: none; + width: 8.33333%; + } + .tile.is-2 { + flex: none; + width: 16.66667%; + } + .tile.is-3 { + flex: none; + width: 25%; + } + .tile.is-4 { + flex: none; + width: 33.33333%; + } + .tile.is-5 { + flex: none; + width: 41.66667%; + } + .tile.is-6 { + flex: none; + width: 50%; + } + .tile.is-7 { + flex: none; + width: 58.33333%; + } + .tile.is-8 { + flex: none; + width: 66.66667%; + } + .tile.is-9 { + flex: none; + width: 75%; + } + .tile.is-10 { + flex: none; + width: 83.33333%; + } + .tile.is-11 { + flex: none; + width: 91.66667%; + } + .tile.is-12 { + flex: none; + width: 100%; + } +} +.hero { + align-items: stretch; + display: flex; + flex-direction: column; + justify-content: space-between; +} +.hero .navbar { + background: none; +} +.hero .tabs ul { + border-bottom: none; +} +.hero.is-white { + background-color: #fff; + color: #0a0a0a; +} +.hero.is-white a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-white strong { + color: inherit; +} +.hero.is-white .title { + color: #0a0a0a; +} +.hero.is-white .subtitle { + color: rgba(10,10,10,0.9); +} +.hero.is-white .subtitle a:not(.button), +.hero.is-white .subtitle strong { + color: #0a0a0a; +} +@media screen and (max-width: 1087px) { + .hero.is-white .navbar-menu { + background-color: #fff; + } +} +.hero.is-white .navbar-item, +.hero.is-white .navbar-link { + color: rgba(10,10,10,0.7); +} +.hero.is-white a.navbar-item:hover, +.hero.is-white .navbar-link:hover, +.hero.is-white a.navbar-item.is-active, +.hero.is-white .navbar-link.is-active { + background-color: #f2f2f2; + color: #0a0a0a; +} +.hero.is-white .tabs a { + color: #0a0a0a; + opacity: 0.9; +} +.hero.is-white .tabs a:hover { + opacity: 1; +} +.hero.is-white .tabs li.is-active a { + opacity: 1; +} +.hero.is-white .tabs.is-boxed a, +.hero.is-white .tabs.is-toggle a { + color: #0a0a0a; +} +.hero.is-white .tabs.is-boxed a:hover, +.hero.is-white .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-white .tabs.is-boxed li.is-active a, +.hero.is-white .tabs.is-toggle li.is-active a, +.hero.is-white .tabs.is-boxed li.is-active a:hover, +.hero.is-white .tabs.is-toggle li.is-active a:hover { + background-color: #0a0a0a; + border-color: #0a0a0a; + color: #fff; +} +.hero.is-white.is-bold { + background-image: linear-gradient(141deg, #e8e3e3 0%, #fff 71%, #fff 100%); +} +@media screen and (max-width: 768px) { + .hero.is-white.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #e8e3e3 0%, #fff 71%, #fff 100%); + } +} +.hero.is-black { + background-color: #0a0a0a; + color: #fff; +} +.hero.is-black a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-black strong { + color: inherit; +} +.hero.is-black .title { + color: #fff; +} +.hero.is-black .subtitle { + color: rgba(255,255,255,0.9); +} +.hero.is-black .subtitle a:not(.button), +.hero.is-black .subtitle strong { + color: #fff; +} +@media screen and (max-width: 1087px) { + .hero.is-black .navbar-menu { + background-color: #0a0a0a; + } +} +.hero.is-black .navbar-item, +.hero.is-black .navbar-link { + color: rgba(255,255,255,0.7); +} +.hero.is-black a.navbar-item:hover, +.hero.is-black .navbar-link:hover, +.hero.is-black a.navbar-item.is-active, +.hero.is-black .navbar-link.is-active { + background-color: #000; + color: #fff; +} +.hero.is-black .tabs a { + color: #fff; + opacity: 0.9; +} +.hero.is-black .tabs a:hover { + opacity: 1; +} +.hero.is-black .tabs li.is-active a { + opacity: 1; +} +.hero.is-black .tabs.is-boxed a, +.hero.is-black .tabs.is-toggle a { + color: #fff; +} +.hero.is-black .tabs.is-boxed a:hover, +.hero.is-black .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-black .tabs.is-boxed li.is-active a, +.hero.is-black .tabs.is-toggle li.is-active a, +.hero.is-black .tabs.is-boxed li.is-active a:hover, +.hero.is-black .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #0a0a0a; +} +.hero.is-black.is-bold { + background-image: linear-gradient(141deg, #000 0%, #0a0a0a 71%, #181616 100%); +} +@media screen and (max-width: 768px) { + .hero.is-black.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #000 0%, #0a0a0a 71%, #181616 100%); + } +} +.hero.is-light { + background-color: #f5f5f5; + color: rgba(0,0,0,0.7); +} +.hero.is-light a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-light strong { + color: inherit; +} +.hero.is-light .title { + color: rgba(0,0,0,0.7); +} +.hero.is-light .subtitle { + color: rgba(0,0,0,0.9); +} +.hero.is-light .subtitle a:not(.button), +.hero.is-light .subtitle strong { + color: rgba(0,0,0,0.7); +} +@media screen and (max-width: 1087px) { + .hero.is-light .navbar-menu { + background-color: #f5f5f5; + } +} +.hero.is-light .navbar-item, +.hero.is-light .navbar-link { + color: rgba(0,0,0,0.7); +} +.hero.is-light a.navbar-item:hover, +.hero.is-light .navbar-link:hover, +.hero.is-light a.navbar-item.is-active, +.hero.is-light .navbar-link.is-active { + background-color: #e8e8e8; + color: rgba(0,0,0,0.7); +} +.hero.is-light .tabs a { + color: rgba(0,0,0,0.7); + opacity: 0.9; +} +.hero.is-light .tabs a:hover { + opacity: 1; +} +.hero.is-light .tabs li.is-active a { + opacity: 1; +} +.hero.is-light .tabs.is-boxed a, +.hero.is-light .tabs.is-toggle a { + color: rgba(0,0,0,0.7); +} +.hero.is-light .tabs.is-boxed a:hover, +.hero.is-light .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-light .tabs.is-boxed li.is-active a, +.hero.is-light .tabs.is-toggle li.is-active a, +.hero.is-light .tabs.is-boxed li.is-active a:hover, +.hero.is-light .tabs.is-toggle li.is-active a:hover { + background-color: rgba(0,0,0,0.7); + border-color: rgba(0,0,0,0.7); + color: #f5f5f5; +} +.hero.is-light.is-bold { + background-image: linear-gradient(141deg, #dfd8d8 0%, #f5f5f5 71%, #fff 100%); +} +@media screen and (max-width: 768px) { + .hero.is-light.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #dfd8d8 0%, #f5f5f5 71%, #fff 100%); + } +} +.hero.is-dark { + background-color: #363636; + color: #fff; +} +.hero.is-dark a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-dark strong { + color: inherit; +} +.hero.is-dark .title { + color: #fff; +} +.hero.is-dark .subtitle { + color: rgba(255,255,255,0.9); +} +.hero.is-dark .subtitle a:not(.button), +.hero.is-dark .subtitle strong { + color: #fff; +} +@media screen and (max-width: 1087px) { + .hero.is-dark .navbar-menu { + background-color: #363636; + } +} +.hero.is-dark .navbar-item, +.hero.is-dark .navbar-link { + color: rgba(255,255,255,0.7); +} +.hero.is-dark a.navbar-item:hover, +.hero.is-dark .navbar-link:hover, +.hero.is-dark a.navbar-item.is-active, +.hero.is-dark .navbar-link.is-active { + background-color: #292929; + color: #fff; +} +.hero.is-dark .tabs a { + color: #fff; + opacity: 0.9; +} +.hero.is-dark .tabs a:hover { + opacity: 1; +} +.hero.is-dark .tabs li.is-active a { + opacity: 1; +} +.hero.is-dark .tabs.is-boxed a, +.hero.is-dark .tabs.is-toggle a { + color: #fff; +} +.hero.is-dark .tabs.is-boxed a:hover, +.hero.is-dark .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-dark .tabs.is-boxed li.is-active a, +.hero.is-dark .tabs.is-toggle li.is-active a, +.hero.is-dark .tabs.is-boxed li.is-active a:hover, +.hero.is-dark .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #363636; +} +.hero.is-dark.is-bold { + background-image: linear-gradient(141deg, #1f1a1a 0%, #363636 71%, #463f3f 100%); +} +@media screen and (max-width: 768px) { + .hero.is-dark.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #1f1a1a 0%, #363636 71%, #463f3f 100%); + } +} +.hero.is-primary { + background-color: #3273dc; + color: #fff; +} +.hero.is-primary a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-primary strong { + color: inherit; +} +.hero.is-primary .title { + color: #fff; +} +.hero.is-primary .subtitle { + color: rgba(255,255,255,0.9); +} +.hero.is-primary .subtitle a:not(.button), +.hero.is-primary .subtitle strong { + color: #fff; +} +@media screen and (max-width: 1087px) { + .hero.is-primary .navbar-menu { + background-color: #3273dc; + } +} +.hero.is-primary .navbar-item, +.hero.is-primary .navbar-link { + color: rgba(255,255,255,0.7); +} +.hero.is-primary a.navbar-item:hover, +.hero.is-primary .navbar-link:hover, +.hero.is-primary a.navbar-item.is-active, +.hero.is-primary .navbar-link.is-active { + background-color: #2366d1; + color: #fff; +} +.hero.is-primary .tabs a { + color: #fff; + opacity: 0.9; +} +.hero.is-primary .tabs a:hover { + opacity: 1; +} +.hero.is-primary .tabs li.is-active a { + opacity: 1; +} +.hero.is-primary .tabs.is-boxed a, +.hero.is-primary .tabs.is-toggle a { + color: #fff; +} +.hero.is-primary .tabs.is-boxed a:hover, +.hero.is-primary .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-primary .tabs.is-boxed li.is-active a, +.hero.is-primary .tabs.is-toggle li.is-active a, +.hero.is-primary .tabs.is-boxed li.is-active a:hover, +.hero.is-primary .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #3273dc; +} +.hero.is-primary.is-bold { + background-image: linear-gradient(141deg, #1576c6 0%, #3273dc 71%, #4266e5 100%); +} +@media screen and (max-width: 768px) { + .hero.is-primary.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #1576c6 0%, #3273dc 71%, #4266e5 100%); + } +} +.hero.is-link { + background-color: #3273dc; + color: #fff; +} +.hero.is-link a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-link strong { + color: inherit; +} +.hero.is-link .title { + color: #fff; +} +.hero.is-link .subtitle { + color: rgba(255,255,255,0.9); +} +.hero.is-link .subtitle a:not(.button), +.hero.is-link .subtitle strong { + color: #fff; +} +@media screen and (max-width: 1087px) { + .hero.is-link .navbar-menu { + background-color: #3273dc; + } +} +.hero.is-link .navbar-item, +.hero.is-link .navbar-link { + color: rgba(255,255,255,0.7); +} +.hero.is-link a.navbar-item:hover, +.hero.is-link .navbar-link:hover, +.hero.is-link a.navbar-item.is-active, +.hero.is-link .navbar-link.is-active { + background-color: #2366d1; + color: #fff; +} +.hero.is-link .tabs a { + color: #fff; + opacity: 0.9; +} +.hero.is-link .tabs a:hover { + opacity: 1; +} +.hero.is-link .tabs li.is-active a { + opacity: 1; +} +.hero.is-link .tabs.is-boxed a, +.hero.is-link .tabs.is-toggle a { + color: #fff; +} +.hero.is-link .tabs.is-boxed a:hover, +.hero.is-link .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-link .tabs.is-boxed li.is-active a, +.hero.is-link .tabs.is-toggle li.is-active a, +.hero.is-link .tabs.is-boxed li.is-active a:hover, +.hero.is-link .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #3273dc; +} +.hero.is-link.is-bold { + background-image: linear-gradient(141deg, #1576c6 0%, #3273dc 71%, #4266e5 100%); +} +@media screen and (max-width: 768px) { + .hero.is-link.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #1576c6 0%, #3273dc 71%, #4266e5 100%); + } +} +.hero.is-info { + background-color: #3298dc; + color: #fff; +} +.hero.is-info a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-info strong { + color: inherit; +} +.hero.is-info .title { + color: #fff; +} +.hero.is-info .subtitle { + color: rgba(255,255,255,0.9); +} +.hero.is-info .subtitle a:not(.button), +.hero.is-info .subtitle strong { + color: #fff; +} +@media screen and (max-width: 1087px) { + .hero.is-info .navbar-menu { + background-color: #3298dc; + } +} +.hero.is-info .navbar-item, +.hero.is-info .navbar-link { + color: rgba(255,255,255,0.7); +} +.hero.is-info a.navbar-item:hover, +.hero.is-info .navbar-link:hover, +.hero.is-info a.navbar-item.is-active, +.hero.is-info .navbar-link.is-active { + background-color: #238cd1; + color: #fff; +} +.hero.is-info .tabs a { + color: #fff; + opacity: 0.9; +} +.hero.is-info .tabs a:hover { + opacity: 1; +} +.hero.is-info .tabs li.is-active a { + opacity: 1; +} +.hero.is-info .tabs.is-boxed a, +.hero.is-info .tabs.is-toggle a { + color: #fff; +} +.hero.is-info .tabs.is-boxed a:hover, +.hero.is-info .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-info .tabs.is-boxed li.is-active a, +.hero.is-info .tabs.is-toggle li.is-active a, +.hero.is-info .tabs.is-boxed li.is-active a:hover, +.hero.is-info .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #3298dc; +} +.hero.is-info.is-bold { + background-image: linear-gradient(141deg, #159cc6 0%, #3298dc 71%, #4289e5 100%); +} +@media screen and (max-width: 768px) { + .hero.is-info.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #159cc6 0%, #3298dc 71%, #4289e5 100%); + } +} +.hero.is-success { + background-color: #48c774; + color: #fff; +} +.hero.is-success a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-success strong { + color: inherit; +} +.hero.is-success .title { + color: #fff; +} +.hero.is-success .subtitle { + color: rgba(255,255,255,0.9); +} +.hero.is-success .subtitle a:not(.button), +.hero.is-success .subtitle strong { + color: #fff; +} +@media screen and (max-width: 1087px) { + .hero.is-success .navbar-menu { + background-color: #48c774; + } +} +.hero.is-success .navbar-item, +.hero.is-success .navbar-link { + color: rgba(255,255,255,0.7); +} +.hero.is-success a.navbar-item:hover, +.hero.is-success .navbar-link:hover, +.hero.is-success a.navbar-item.is-active, +.hero.is-success .navbar-link.is-active { + background-color: #3abb67; + color: #fff; +} +.hero.is-success .tabs a { + color: #fff; + opacity: 0.9; +} +.hero.is-success .tabs a:hover { + opacity: 1; +} +.hero.is-success .tabs li.is-active a { + opacity: 1; +} +.hero.is-success .tabs.is-boxed a, +.hero.is-success .tabs.is-toggle a { + color: #fff; +} +.hero.is-success .tabs.is-boxed a:hover, +.hero.is-success .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-success .tabs.is-boxed li.is-active a, +.hero.is-success .tabs.is-toggle li.is-active a, +.hero.is-success .tabs.is-boxed li.is-active a:hover, +.hero.is-success .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #48c774; +} +.hero.is-success.is-bold { + background-image: linear-gradient(141deg, #29b342 0%, #48c774 71%, #56d296 100%); +} +@media screen and (max-width: 768px) { + .hero.is-success.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #29b342 0%, #48c774 71%, #56d296 100%); + } +} +.hero.is-warning { + background-color: #ffdd57; + color: rgba(0,0,0,0.7); +} +.hero.is-warning a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-warning strong { + color: inherit; +} +.hero.is-warning .title { + color: rgba(0,0,0,0.7); +} +.hero.is-warning .subtitle { + color: rgba(0,0,0,0.9); +} +.hero.is-warning .subtitle a:not(.button), +.hero.is-warning .subtitle strong { + color: rgba(0,0,0,0.7); +} +@media screen and (max-width: 1087px) { + .hero.is-warning .navbar-menu { + background-color: #ffdd57; + } +} +.hero.is-warning .navbar-item, +.hero.is-warning .navbar-link { + color: rgba(0,0,0,0.7); +} +.hero.is-warning a.navbar-item:hover, +.hero.is-warning .navbar-link:hover, +.hero.is-warning a.navbar-item.is-active, +.hero.is-warning .navbar-link.is-active { + background-color: #ffd83d; + color: rgba(0,0,0,0.7); +} +.hero.is-warning .tabs a { + color: rgba(0,0,0,0.7); + opacity: 0.9; +} +.hero.is-warning .tabs a:hover { + opacity: 1; +} +.hero.is-warning .tabs li.is-active a { + opacity: 1; +} +.hero.is-warning .tabs.is-boxed a, +.hero.is-warning .tabs.is-toggle a { + color: rgba(0,0,0,0.7); +} +.hero.is-warning .tabs.is-boxed a:hover, +.hero.is-warning .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-warning .tabs.is-boxed li.is-active a, +.hero.is-warning .tabs.is-toggle li.is-active a, +.hero.is-warning .tabs.is-boxed li.is-active a:hover, +.hero.is-warning .tabs.is-toggle li.is-active a:hover { + background-color: rgba(0,0,0,0.7); + border-color: rgba(0,0,0,0.7); + color: #ffdd57; +} +.hero.is-warning.is-bold { + background-image: linear-gradient(141deg, #ffae24 0%, #ffdd57 71%, #fffa71 100%); +} +@media screen and (max-width: 768px) { + .hero.is-warning.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #ffae24 0%, #ffdd57 71%, #fffa71 100%); + } +} +.hero.is-danger { + background-color: #f14668; + color: #fff; +} +.hero.is-danger a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-danger strong { + color: inherit; +} +.hero.is-danger .title { + color: #fff; +} +.hero.is-danger .subtitle { + color: rgba(255,255,255,0.9); +} +.hero.is-danger .subtitle a:not(.button), +.hero.is-danger .subtitle strong { + color: #fff; +} +@media screen and (max-width: 1087px) { + .hero.is-danger .navbar-menu { + background-color: #f14668; + } +} +.hero.is-danger .navbar-item, +.hero.is-danger .navbar-link { + color: rgba(255,255,255,0.7); +} +.hero.is-danger a.navbar-item:hover, +.hero.is-danger .navbar-link:hover, +.hero.is-danger a.navbar-item.is-active, +.hero.is-danger .navbar-link.is-active { + background-color: #ef2e55; + color: #fff; +} +.hero.is-danger .tabs a { + color: #fff; + opacity: 0.9; +} +.hero.is-danger .tabs a:hover { + opacity: 1; +} +.hero.is-danger .tabs li.is-active a { + opacity: 1; +} +.hero.is-danger .tabs.is-boxed a, +.hero.is-danger .tabs.is-toggle a { + color: #fff; +} +.hero.is-danger .tabs.is-boxed a:hover, +.hero.is-danger .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-danger .tabs.is-boxed li.is-active a, +.hero.is-danger .tabs.is-toggle li.is-active a, +.hero.is-danger .tabs.is-boxed li.is-active a:hover, +.hero.is-danger .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #f14668; +} +.hero.is-danger.is-bold { + background-image: linear-gradient(141deg, #fa0a61 0%, #f14668 71%, #f7595f 100%); +} +@media screen and (max-width: 768px) { + .hero.is-danger.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #fa0a61 0%, #f14668 71%, #f7595f 100%); + } +} +.hero.is-grey-lightest { + background-color: #ededed; + color: #363636; +} +.hero.is-grey-lightest a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), +.hero.is-grey-lightest strong { + color: inherit; +} +.hero.is-grey-lightest .title { + color: #363636; +} +.hero.is-grey-lightest .subtitle { + color: rgba(54,54,54,0.9); +} +.hero.is-grey-lightest .subtitle a:not(.button), +.hero.is-grey-lightest .subtitle strong { + color: #363636; +} +@media screen and (max-width: 1087px) { + .hero.is-grey-lightest .navbar-menu { + background-color: #ededed; + } +} +.hero.is-grey-lightest .navbar-item, +.hero.is-grey-lightest .navbar-link { + color: rgba(54,54,54,0.7); +} +.hero.is-grey-lightest a.navbar-item:hover, +.hero.is-grey-lightest .navbar-link:hover, +.hero.is-grey-lightest a.navbar-item.is-active, +.hero.is-grey-lightest .navbar-link.is-active { + background-color: #e0e0e0; + color: #363636; +} +.hero.is-grey-lightest .tabs a { + color: #363636; + opacity: 0.9; +} +.hero.is-grey-lightest .tabs a:hover { + opacity: 1; +} +.hero.is-grey-lightest .tabs li.is-active a { + opacity: 1; +} +.hero.is-grey-lightest .tabs.is-boxed a, +.hero.is-grey-lightest .tabs.is-toggle a { + color: #363636; +} +.hero.is-grey-lightest .tabs.is-boxed a:hover, +.hero.is-grey-lightest .tabs.is-toggle a:hover { + background-color: rgba(10,10,10,0.1); +} +.hero.is-grey-lightest .tabs.is-boxed li.is-active a, +.hero.is-grey-lightest .tabs.is-toggle li.is-active a, +.hero.is-grey-lightest .tabs.is-boxed li.is-active a:hover, +.hero.is-grey-lightest .tabs.is-toggle li.is-active a:hover { + background-color: #363636; + border-color: #363636; + color: #ededed; +} +.hero.is-grey-lightest.is-bold { + background-image: linear-gradient(141deg, #d8cfcf 0%, #ededed 71%, #faf9f9 100%); +} +@media screen and (max-width: 768px) { + .hero.is-grey-lightest.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #d8cfcf 0%, #ededed 71%, #faf9f9 100%); + } +} +.hero.is-small .hero-body { + padding-bottom: 1.5rem; + padding-top: 1.5rem; +} +@media screen and (min-width: 769px), print { + .hero.is-medium .hero-body { + padding-bottom: 9rem; + padding-top: 9rem; + } +} +@media screen and (min-width: 769px), print { + .hero.is-large .hero-body { + padding-bottom: 18rem; + padding-top: 18rem; + } +} +.hero.is-halfheight .hero-body, +.hero.is-fullheight .hero-body, +.hero.is-fullheight-with-navbar .hero-body { + align-items: center; + display: flex; +} +.hero.is-halfheight .hero-body > .container, +.hero.is-fullheight .hero-body > .container, +.hero.is-fullheight-with-navbar .hero-body > .container { + flex-grow: 1; + flex-shrink: 1; +} +.hero.is-halfheight { + min-height: 50vh; +} +.hero.is-fullheight { + min-height: 100vh; +} +.hero-video { + overflow: hidden; +} +.hero-video video { + left: 50%; + min-height: 100%; + min-width: 100%; + position: absolute; + top: 50%; + transform: translate3d(-50%, -50%, 0); +} +.hero-video.is-transparent { + opacity: 0.3; +} +@media screen and (max-width: 768px) { + .hero-video { + display: none; + } +} +.hero-buttons { + margin-top: 1.5rem; +} +@media screen and (max-width: 768px) { + .hero-buttons .button { + display: flex; + } + .hero-buttons .button:not(:last-child) { + margin-bottom: 0.75rem; + } +} +@media screen and (min-width: 769px), print { + .hero-buttons { + display: flex; + justify-content: center; + } + .hero-buttons .button:not(:last-child) { + margin-right: 1.5rem; + } +} +.hero-head, +.hero-foot { + flex-grow: 0; + flex-shrink: 0; +} +.hero-body { + flex-grow: 1; + flex-shrink: 0; + padding: 3rem 1.5rem; +} +.section { + padding: 3rem 1.5rem; +} +@media screen and (min-width: 1088px) { + .section.is-medium { + padding: 9rem 1.5rem; + } + .section.is-large { + padding: 18rem 1.5rem; + } +} +.footer { + background-color: #fff; + padding: 3rem 1.5rem 6rem; +} +html { + height: 100%; + -webkit-text-size-adjust: 100%; + -moz-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + text-size-adjust: 100%; +} +body { + min-height: 100%; + display: flex; + flex-direction: column; +} +body > .section { + flex-grow: 1; +} +@media screen and (min-width: 1088px) { + ::-webkit-scrollbar { + width: 8px; + height: 8px; + } + ::-webkit-scrollbar-track { + border-radius: 3px; + background: rgba(0,0,0,0.06); + box-shadow: inset 0 0 5px rgba(0,0,0,0.1); + } + ::-webkit-scrollbar-thumb { + border-radius: 3px; + background: rgba(0,0,0,0.12); + box-shadow: inset 0 0 10px rgba(0,0,0,0.2); + } + ::-webkit-scrollbar-thumb:hover { + background: rgba(0,0,0,0.24); + } +} +.ml-0, +.mx-0 { + margin-left: 0 !important; +} +.mr-0, +.mx-0 { + margin-right: 0 !important; +} +.ml-n0, +.mx-n0 { + margin-left: 0 !important; +} +.mr-n0, +.mx-n0 { + margin-right: 0 !important; +} +.mt-0, +.my-0 { + margin-top: 0 !important; +} +.mb-0, +.my-0 { + margin-bottom: 0 !important; +} +.mt-n0, +.my-n0 { + margin-top: 0 !important; +} +.mb-n0, +.my-n0 { + margin-bottom: 0 !important; +} +.pl-0, +.px-0 { + padding-left: 0 !important; +} +.pr-0, +.px-0 { + padding-right: 0 !important; +} +.pl-n0, +.px-n0 { + padding-left: 0 !important; +} +.pr-n0, +.px-n0 { + padding-right: 0 !important; +} +.pt-0, +.py-0 { + padding-top: 0 !important; +} +.pb-0, +.py-0 { + padding-bottom: 0 !important; +} +.pt-n0, +.py-n0 { + padding-top: 0 !important; +} +.pb-n0, +.py-n0 { + padding-bottom: 0 !important; +} +.ml-1, +.mx-1 { + margin-left: 0.25rem !important; +} +.mr-1, +.mx-1, +.article-licensing .licensing-meta .icon { + margin-right: 0.25rem !important; +} +.ml-n1, +.mx-n1 { + margin-left: -0.25rem !important; +} +.mr-n1, +.mx-n1 { + margin-right: -0.25rem !important; +} +.mt-1, +.my-1 { + margin-top: 0.25rem !important; +} +.mb-1, +.my-1, +.article-licensing .licensing-title p:not(:last-child) { + margin-bottom: 0.25rem !important; +} +.mt-n1, +.my-n1 { + margin-top: -0.25rem !important; +} +.mb-n1, +.my-n1 { + margin-bottom: -0.25rem !important; +} +.pl-1, +.px-1 { + padding-left: 0.25rem !important; +} +.pr-1, +.px-1 { + padding-right: 0.25rem !important; +} +.pl-n1, +.px-n1 { + padding-left: -0.25rem !important; +} +.pr-n1, +.px-n1 { + padding-right: -0.25rem !important; +} +.pt-1, +.py-1 { + padding-top: 0.25rem !important; +} +.pb-1, +.py-1 { + padding-bottom: 0.25rem !important; +} +.pt-n1, +.py-n1 { + padding-top: -0.25rem !important; +} +.pb-n1, +.py-n1 { + padding-bottom: -0.25rem !important; +} +.ml-2, +.mx-2 { + margin-left: 0.5rem !important; +} +.mr-2, +.mx-2 { + margin-right: 0.5rem !important; +} +.ml-n2, +.mx-n2 { + margin-left: -0.5rem !important; +} +.mr-n2, +.mx-n2 { + margin-right: -0.5rem !important; +} +.mt-2, +.my-2 { + margin-top: 0.5rem !important; +} +.mb-2, +.my-2 { + margin-bottom: 0.5rem !important; +} +.mt-n2, +.my-n2 { + margin-top: -0.5rem !important; +} +.mb-n2, +.my-n2 { + margin-bottom: -0.5rem !important; +} +.pl-2, +.px-2 { + padding-left: 0.5rem !important; +} +.pr-2, +.px-2 { + padding-right: 0.5rem !important; +} +.pl-n2, +.px-n2 { + padding-left: -0.5rem !important; +} +.pr-n2, +.px-n2 { + padding-right: -0.5rem !important; +} +.pt-2, +.py-2 { + padding-top: 0.5rem !important; +} +.pb-2, +.py-2 { + padding-bottom: 0.5rem !important; +} +.pt-n2, +.py-n2 { + padding-top: -0.5rem !important; +} +.pb-n2, +.py-n2 { + padding-bottom: -0.5rem !important; +} +.ml-3, +.mx-3 { + margin-left: 1rem !important; +} +.mr-3, +.mx-3 { + margin-right: 1rem !important; +} +.ml-n3, +.mx-n3 { + margin-left: -1rem !important; +} +.mr-n3, +.mx-n3 { + margin-right: -1rem !important; +} +.mt-3, +.my-3 { + margin-top: 1rem !important; +} +.mb-3, +.my-3, +.article-licensing .licensing-title { + margin-bottom: 1rem !important; +} +.mt-n3, +.my-n3 { + margin-top: -1rem !important; +} +.mb-n3, +.my-n3 { + margin-bottom: -1rem !important; +} +.pl-3, +.px-3 { + padding-left: 1rem !important; +} +.pr-3, +.px-3 { + padding-right: 1rem !important; +} +.pl-n3, +.px-n3 { + padding-left: -1rem !important; +} +.pr-n3, +.px-n3 { + padding-right: -1rem !important; +} +.pt-3, +.py-3 { + padding-top: 1rem !important; +} +.pb-3, +.py-3 { + padding-bottom: 1rem !important; +} +.pt-n3, +.py-n3 { + padding-top: -1rem !important; +} +.pb-n3, +.py-n3 { + padding-bottom: -1rem !important; +} +.ml-4, +.mx-4 { + margin-left: 1.5rem !important; +} +.mr-4, +.mx-4, +.article-licensing .licensing-meta .level-item { + margin-right: 1.5rem !important; +} +.ml-n4, +.mx-n4 { + margin-left: -1.5rem !important; +} +.mr-n4, +.mx-n4 { + margin-right: -1.5rem !important; +} +.mt-4, +.my-4 { + margin-top: 1.5rem !important; +} +.mb-4, +.my-4 { + margin-bottom: 1.5rem !important; +} +.mt-n4, +.my-n4 { + margin-top: -1.5rem !important; +} +.mb-n4, +.my-n4 { + margin-bottom: -1.5rem !important; +} +.pl-4, +.px-4 { + padding-left: 1.5rem !important; +} +.pr-4, +.px-4 { + padding-right: 1.5rem !important; +} +.pl-n4, +.px-n4 { + padding-left: -1.5rem !important; +} +.pr-n4, +.px-n4 { + padding-right: -1.5rem !important; +} +.pt-4, +.py-4 { + padding-top: 1.5rem !important; +} +.pb-4, +.py-4 { + padding-bottom: 1.5rem !important; +} +.pt-n4, +.py-n4 { + padding-top: -1.5rem !important; +} +.pb-n4, +.py-n4 { + padding-bottom: -1.5rem !important; +} +.ml-5, +.mx-5 { + margin-left: 3rem !important; +} +.mr-5, +.mx-5 { + margin-right: 3rem !important; +} +.ml-n5, +.mx-n5 { + margin-left: -3rem !important; +} +.mr-n5, +.mx-n5 { + margin-right: -3rem !important; +} +.mt-5, +.my-5 { + margin-top: 3rem !important; +} +.mb-5, +.my-5 { + margin-bottom: 3rem !important; +} +.mt-n5, +.my-n5 { + margin-top: -3rem !important; +} +.mb-n5, +.my-n5 { + margin-bottom: -3rem !important; +} +.pl-5, +.px-5 { + padding-left: 3rem !important; +} +.pr-5, +.px-5 { + padding-right: 3rem !important; +} +.pl-n5, +.px-n5 { + padding-left: -3rem !important; +} +.pr-n5, +.px-n5 { + padding-right: -3rem !important; +} +.pt-5, +.py-5 { + padding-top: 3rem !important; +} +.pb-5, +.py-5 { + padding-bottom: 3rem !important; +} +.pt-n5, +.py-n5 { + padding-top: -3rem !important; +} +.pb-n5, +.py-n5 { + padding-bottom: -3rem !important; +} +.ml-auto, +.mx-auto { + margin-left: auto !important; +} +.mr-auto, +.mx-auto { + margin-right: auto !important; +} +.mt-auto, +.my-auto { + margin-top: auto !important; +} +.mb-auto, +.my-auto { + margin-bottom: auto !important; +} +.pl-auto, +.px-auto { + margin-left: auto !important; +} +.pr-auto, +.px-auto { + margin-right: auto !important; +} +.pt-auto, +.py-auto { + margin-top: auto !important; +} +.pb-auto, +.py-auto { + margin-bottom: auto !important; +} +.order-0 { + order: 0 !important; +} +.order-1 { + order: 1 !important; +} +.order-2 { + order: 2 !important; +} +.order-3 { + order: 3 !important; +} +.order-4 { + order: 4 !important; +} +.order-5 { + order: 5 !important; +} +.justify-content-start { + justify-content: start !important; +} +.justify-content-center { + justify-content: center !important; +} +.flex-shrink-1 { + flex-shrink: 1 !important; +} +.link-muted { + color: inherit; +} +.link-muted:hover { + color: #3273dc !important; +} +.image.is-7by3 { + padding-top: 42.8%; +} +.image.is-7by3 img { + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; +} +.image .avatar { + height: 100%; + object-fit: cover; +} +.image .fill { + object-fit: cover; + width: 100% !important; + height: 100% !important; +} +.button.is-transparent { + color: inherit; + background: transparent; + border-color: transparent; +} +.card { + overflow: visible; + border-radius: 4px; +} +.card + .card, +.card + .column-right-shadow { + margin-top: 1.5rem; +} +.card .card-image { + overflow: hidden; + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} +.card .media + .media { + border: none; + margin-top: 0; +} +article.media { + color: #7a7a7a; +} +article.media a { + color: inherit; +} +article.media a:hover { + color: #3273dc; +} +article.media .image { + width: 64px; + height: 64px; +} +article.media .image img { + object-fit: cover; + width: 100%; + height: 100%; +} +article.media .title { + margin-bottom: 0.25em; +} +article.media .media-content { + color: #7a7a7a; +} +article.media .media-content .title { + margin: 0; + line-height: inherit; +} +article.article .article-meta, +article.article .article-tags { + color: #7a7a7a; +} +article.article .article-meta { + overflow-x: auto; + margin-bottom: 0.5rem; +} +article.article .content { + word-wrap: break-word; + font-size: 1.1rem; +} +article.article .content h1 { + font-size: 1.75em; +} +article.article .content h2 { + font-size: 1.5em; +} +article.article .content h3 { + font-size: 1.25em; +} +article.article .content h4 { + font-size: 1.125em; +} +article.article .content h5 { + font-size: 1em; +} +article.article .content pre { + font-size: 0.85em; +} +article.article .content code { + padding: 0; + background: transparent; + overflow-wrap: break-word; +} +article.article .content blockquote.pullquote { + float: right; + max-width: 50%; + font-size: 1.15rem; + position: relative; +} +article.article .content blockquote footer strong + cite { + margin-left: 0.5em; +} +article.article .content .message.message-immersive { + border-radius: 0; + margin: 0 -1.5rem 1.5rem -1.5rem; +} +article.article .content .message.message-immersive .message-body { + border: none; +} +.rtl { + direction: rtl; +} +.rtl .level .level-item:not(:last-child), +.rtl .level.is-mobile .level-item:not(:last-child) { + margin-left: 0.75rem; + margin-right: 0; +} +.table-overflow { + overflow-x: auto; +} +.table-overflow table { + width: auto !important; +} +.table-overflow table th { + word-break: keep-all; +} +.video-container { + position: relative; + padding-bottom: 56.25%; + padding-top: 25px; + height: 0; +} +.video-container iframe { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.article-licensing { + position: relative; + z-index: 1; + box-shadow: none; + background: #f5f5f5; + border-radius: 4px; + overflow: hidden; +} +.article-licensing:after { + position: absolute; + z-index: -1; + right: -50px; + top: -87.87px; + content: '\f25e'; + font-size: 200px; + font-family: 'Font Awesome 5 Brands'; + opacity: 0.1; +} +.article-licensing .level-left { + flex-wrap: wrap; + max-width: 100%; +} +.article-licensing .licensing-title { + line-height: 1.2; +} +.article-licensing .licensing-meta .icon { + width: 1.2em; + height: 1.2em; + font-size: 1.2em; + vertical-align: bottom; +} +.article-licensing .licensing-meta a { + color: inherit; +} +a.article-nav-prev span { + text-align: left; + flex-shrink: 1; + word-wrap: break-word; + white-space: normal; +} +a.article-nav-next span { + text-align: right; + flex-shrink: 1; + word-wrap: break-word; + white-space: normal; +} +.navbar-main { + box-shadow: 0 4px 10px rgba(0,0,0,0.05); +} +.navbar-main .navbar-menu, +.navbar-main .navbar-start, +.navbar-main .navbar-end { + align-items: stretch; + display: flex; + padding: 0; + flex-shrink: 0; +} +.navbar-main .navbar-menu { + flex-grow: 1; + flex-shrink: 0; + overflow-x: auto; +} +.navbar-main .navbar-start { + justify-content: flex-start; + margin-right: auto; +} +.navbar-main .navbar-end { + justify-content: flex-end; + margin-left: auto; +} +.navbar-main .navbar-item { + display: flex; + align-items: center; + padding: 1.25rem 0.75rem; + margin: 0 0; +} +.navbar-main .navbar-item.is-active { + background-color: transparent; +} +@media screen and (max-width: 1087px) { + .navbar-main .navbar-menu { + justify-content: center; + box-shadow: none; + } + .navbar-main .navbar-start { + margin-right: 0; + } + .navbar-main .navbar-end { + margin-left: 0; + } +} +.navbar-logo img { + max-height: 1.75rem; +} +@media screen and (max-width: 768px) { + footer.footer .level-start { + text-align: center; + } +} +footer.footer .level-end .field { + flex-wrap: wrap; + align-items: center; +} +@media screen and (max-width: 768px) { + footer.footer .level-end .field { + justify-content: center; + margin-top: 1rem; + } +} +.footer-logo img { + max-height: 1.75rem; +} +.pagination { + margin-top: 1.5rem; +} +.pagination .pagination-link a, +.pagination .pagination-ellipsis a, +.pagination .pagination-previous a, +.pagination .pagination-next a { + color: #363636; +} +.pagination .pagination-link, +.pagination .pagination-previous, +.pagination .pagination-next { + border: none; + background: #fff; + box-shadow: 0 4px 10px rgba(0,0,0,0.05), 0 0 1px rgba(0,0,0,0.1); +} +.pagination .pagination-link.is-current { + background: #3273dc; +} +.post-navigation { + color: #7a7a7a; + flex-wrap: wrap; + justify-content: space-around; +} +.post-navigation .level-item { + margin-bottom: 0; +} +.timeline { + margin-left: 1rem; + padding: 1rem 0 0 1.5rem; + border-left: 1px solid #dbdbdb; +} +.timeline .media { + position: relative; +} +.timeline .media:before, +.timeline .media:last-child:after { + content: ''; + display: block; + position: absolute; + left: calc(-0.375rem - 1.5rem - 0.25px); +} +.timeline .media:before { + width: 0.75rem; + height: 0.75rem; + top: calc(1rem + 1.5 * 0.85rem / 2 - 0.75rem / 2); + background: #dbdbdb; + border-radius: 50%; +} +.timeline .media:first-child:before { + top: calc(1.5 * 0.85rem / 2 - 0.75rem / 2); +} +.timeline .media:last-child:after { + width: 0.75rem; + top: calc(1rem + 1.5 * 0.85rem / 2 + 0.75rem / 2); + bottom: 0; + background: #fff; +} +.timeline .media:first-child:last-child:after { + top: calc(1.5 * 0.85rem / 2 + 0.75rem / 2); +} +.searchbox { + display: none; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 100; + font-size: 1rem; + line-height: 0; + background: rgba(10,10,10,0.86); +} +.searchbox.show { + display: flex; +} +.searchbox a, +.searchbox a:hover { + color: inherit; + text-decoration: none; +} +.searchbox input { + font-size: 1rem; + border: none; + outline: none; + box-shadow: none; + border-radius: 0; +} +.searchbox, +.searchbox .searchbox-container { + position: fixed; + align-items: center; + flex-direction: column; + line-height: 1.25em; +} +.searchbox .searchbox-container { + z-index: 101; + display: flex; + overflow: hidden; + box-shadow: 0 4px 10px rgba(0,0,0,0.05), 0 0 1px rgba(0,0,0,0.1); + border-radius: 4px; + background-color: #f5f5f5; + width: 540px; + top: 100px; + bottom: 100px; +} +.searchbox .searchbox-header, +.searchbox .searchbox-body, +.searchbox .searchbox-footer { + width: 100%; +} +.searchbox .searchbox-header { + display: flex; + flex-direction: row; + line-height: 1.5em; + font-weight: normal; + background-color: #fff; + min-height: 3rem; +} +.searchbox .searchbox-input-container { + display: flex; + flex-grow: 1; +} +.searchbox .searchbox-input { + flex-grow: 1; + color: inherit; + box-sizing: border-box; + padding: 0.75em 0 0.75em 1.25em; + background: #fff; +} +.searchbox .searchbox-close { + display: inline-block; + font-size: 1.5em; + padding: 0.5em 0.75em; + cursor: pointer; +} +.searchbox .searchbox-close:hover { + background: #f5f5f5; +} +.searchbox .searchbox-close:active { + background: #dbdbdb; +} +.searchbox .searchbox-body { + flex-grow: 1; + overflow-y: auto; + border-top: 1px solid #dbdbdb; +} +.searchbox .searchbox-result-section header, +.searchbox .searchbox-result-item { + padding: 0.75em 1em; +} +.searchbox .searchbox-result-section { + border-bottom: 1px solid #dbdbdb; +} +.searchbox .searchbox-result-section header { + color: #b5b5b5; +} +.searchbox .searchbox-result-item { + display: flex; + flex-direction: row; +} +.searchbox .searchbox-result-item:not(.disabled):not(.active):not(:active):hover { + background-color: #fff; +} +.searchbox .searchbox-result-item:active, +.searchbox .searchbox-result-item.active { + color: #fff; + background-color: #3273dc; +} +.searchbox .searchbox-result-item em { + font-style: normal; + background: #ffdd57; +} +.searchbox .searchbox-result-icon { + margin-right: 1em; +} +.searchbox .searchbox-result-content { + overflow: hidden; +} +.searchbox .searchbox-result-title, +.searchbox .searchbox-result-preview { + display: block; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.searchbox .searchbox-result-title-secondary { + color: #b5b5b5; +} +.searchbox .searchbox-result-preview { + margin-top: 0.25em; +} +.searchbox .searchbox-result-item:not(:active):not(.active) .searchbox-result-preview { + color: #b5b5b5; +} +.searchbox .searchbox-footer { + padding: 0.5em 1em; +} +.searchbox .searchbox-pagination { + margin: 0; + padding: 0; + list-style: none; + text-align: center; +} +.searchbox .searchbox-pagination .searchbox-pagination-item { + margin: 0 0.25rem; +} +.searchbox .searchbox-pagination .searchbox-pagination-item, +.searchbox .searchbox-pagination .searchbox-pagination-link { + display: inline-block; +} +.searchbox .searchbox-pagination .searchbox-pagination-link { + overflow: hidden; + padding: 0.5em 0.8em; + box-shadow: 0 4px 10px rgba(0,0,0,0.05), 0 0 1px rgba(0,0,0,0.1); + border-radius: 4px; + background-color: #fff; +} +.searchbox .searchbox-pagination .searchbox-pagination-item.active .searchbox-pagination-link { + color: #fff; + background-color: #3273dc; +} +.searchbox .searchbox-pagination .searchbox-pagination-item.disabled .searchbox-pagination-link { + cursor: not-allowed; + background-color: #f5f5f5; +} +.searchbox .searchbox-pagination .searchbox-pagination-item:not(.active):not(.disabled) .searchbox-pagination-link:hover { + background-color: #f5f5f5; +} +@media screen and (max-width: 559px), screen and (max-height: 479px) { + .searchbox .searchbox-container { + top: 0; + left: 0; + width: 100%; + height: 100%; + border-radius: 0; + } +} +figure.highlight { + padding: 0; + width: 100%; + position: relative; + margin: 1em 0 1em !important; + border-radius: 4px; +} +figure.highlight.folded .highlight-body { + height: 0; +} +figure.highlight .copy { + opacity: 0.7; +} +figure.highlight pre, +figure.highlight table tr:hover { + color: inherit; + background: transparent; +} +figure.highlight table { + width: auto; +} +figure.highlight table tr td { + border: none; +} +figure.highlight table tr:not(:first-child) td { + padding-top: 0; +} +figure.highlight table tr:not(:last-child) td { + padding-bottom: 0; +} +figure.highlight pre { + padding: 0; + overflow: visible; +} +figure.highlight pre .line, +figure.highlight pre code .hljs { + line-height: 1.5rem; +} +figure.highlight figcaption, +figure.highlight .gutter { + background: rgba(200,200,200,0.15); +} +figure.highlight figcaption { + margin: 0 !important; + padding: 0.3em 0em 0.3em 0.75em; + font-style: normal; + font-size: 0.8em; +} +figure.highlight figcaption * { + color: inherit; +} +figure.highlight figcaption span { + font-weight: 500; + font-family: 'Source Code Pro', monospace, 'Microsoft YaHei'; +} +figure.highlight figcaption .level-left *:not(:last-child) { + margin-right: 0.5em; +} +figure.highlight figcaption .level-right *:not(:first-child) { + margin-left: 0.5em; +} +figure.highlight figcaption .fold { + cursor: pointer; +} +figure.highlight figcaption.level { + overflow: auto; +} +figure.highlight figcaption.level .level-right a { + padding: 0em 0.75em; +} +figure.highlight .highlight-body { + overflow: auto; +} +figure.highlight .gutter { + text-align: right; +} +figure.highlight .tag, +figure.highlight .title, +figure.highlight .number, +figure.highlight .section { + display: inherit; + font: inherit; + margin: inherit; + padding: inherit; + background: inherit; + height: inherit; + text-align: inherit; + vertical-align: inherit; + min-width: inherit; + border-radius: inherit; +} +.gist table tr:hover { + background: transparent; +} +.gist table td { + border: none; +} +.gist .file { + all: initial; +} +.widget .menu-list li ul { + margin-right: 0; +} +.widget .menu-list .level { + margin-bottom: 0; +} +.widget .menu-list .level .level-left, +.widget .menu-list .level .level-right, +.widget .menu-list .level .level-item { + flex-shrink: 1; +} +.widget .menu-list .level .level-left, +.widget .menu-list .level .level-right { + align-items: flex-start; +} +.widget .menu-list .tag { + background: $light-grey; + color: $white-invert; +} +.widget .tags .tag:first-child { + background: #3273dc; + color: #fff; +} +.widget .tags .tag:last-child { + background: $light-grey; + color: $white-invert; +} +.level.is-multiline { + flex-wrap: wrap; +} +.donate { + position: relative; +} +.donate .qrcode { + display: none; + position: absolute; + z-index: 99; + bottom: 2.5em; + line-height: 0; + overflow: hidden; + box-shadow: 0 4px 10px rgba(0,0,0,0.05), 0 0 1px rgba(0,0,0,0.1); + border-radius: 4px; +} +.donate .qrcode img { + max-width: 280px; +} +.donate:hover .qrcode { + display: block; +} +.donate:first-child:not(:last-child) .qrcode { + left: -0.75rem; +} +.donate:last-child:not(:first-child) .qrcode { + right: -0.75rem; +} +.donate[data-type="afdian"] { + color: #fff; + background-color: #885fd9; + border-color: transparent; +} +.donate[data-type="afdian"]:active { + background-color: #794ad4; +} +.donate[data-type="afdian"]:hover { + background-color: #8055d7; +} +.donate[data-type="afdian"]:focus:not(:active) { + box-shadow: 0 0 0 0.125em rgba(136,95,217,0.25); +} +.donate[data-type="alipay"] { + color: #fff; + background-color: #00a0e8; + border-color: transparent; +} +.donate[data-type="alipay"]:active { + background-color: #008ecf; +} +.donate[data-type="alipay"]:hover { + background-color: #0097db; +} +.donate[data-type="alipay"]:focus:not(:active) { + box-shadow: 0 0 0 0.125em rgba(0,160,232,0.25); +} +.donate[data-type="buymeacoffee"] { + color: rgba(0,0,0,0.7); + background-color: #fd0; + border-color: transparent; +} +.donate[data-type="buymeacoffee"]:active { + background-color: #e6c700; +} +.donate[data-type="buymeacoffee"]:hover { + background-color: #f2d200; +} +.donate[data-type="buymeacoffee"]:focus:not(:active) { + box-shadow: 0 0 0 0.125em rgba(255,221,0,0.25); +} +.donate[data-type="paypal"] { + color: rgba(0,0,0,0.7); + background-color: #feb700; + border-color: transparent; +} +.donate[data-type="paypal"]:active { + background-color: #e5a500; +} +.donate[data-type="paypal"]:hover { + background-color: #f1ae00; +} +.donate[data-type="paypal"]:focus:not(:active) { + box-shadow: 0 0 0 0.125em rgba(254,183,0,0.25); +} +.donate[data-type="patreon"] { + color: #fff; + background-color: #ff424d; + border-color: transparent; +} +.donate[data-type="patreon"]:active { + background-color: #ff2835; +} +.donate[data-type="patreon"]:hover { + background-color: #ff3541; +} +.donate[data-type="patreon"]:focus:not(:active) { + box-shadow: 0 0 0 0.125em rgba(255,66,77,0.25); +} +.donate[data-type="wechat"] { + color: #fff; + background-color: #1aad19; + border-color: transparent; +} +.donate[data-type="wechat"]:active { + background-color: #179716; +} +.donate[data-type="wechat"]:hover { + background-color: #18a217; +} +.donate[data-type="wechat"]:focus:not(:active) { + box-shadow: 0 0 0 0.125em rgba(26,173,25,0.25); +} +#back-to-top { + position: fixed; + opacity: 0; + outline: none; + padding: 8px 0; + line-height: 24px; + border-radius: 4px; + transform: translateY(120px); + transition: 0.4s ease opacity, 0.4s ease width, 0.4s ease transform, 0.4s ease border-radius; +} +#back-to-top.is-rounded { + border-radius: 50%; +} +#back-to-top.fade-in { + opacity: 1; +} +#back-to-top.rise-up { + transform: translateY(0); +} +.gallery-item .caption { + color: #7a7a7a; +} +@media screen and (max-width: 768px) { + #toc { + display: none; + position: fixed; + margin: 1rem; + left: 0; + right: 0; + bottom: 0; + z-index: 100; + max-height: calc(100vh - 2rem); + overflow-y: auto; + } + #toc-mask { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 99; + background: rgba(0,0,0,0.7); + } + #toc.is-active, + #toc-mask.is-active { + display: block; + } +} +.pace { + user-select: none; + pointer-events: none; +} +.pace .pace-progress { + top: 0; + right: 100%; + width: 100%; + height: 2px; + z-index: 2000; + position: fixed; + background: #3273dc; +} +.pace-inactive { + display: none; +} +.fa, +.fab, +.fal, +.far, +.fas { + line-height: inherit; +} +.MathJax, +.katex-display { + overflow-x: auto; + overflow-y: hidden; +} +.katex { + white-space: nowrap; +} +.katex-display { + margin-top: -1em !important; +} +.katex-html { + padding-top: 1em; +} +.katex-html .tag { + align-items: unset; + background-color: unset; + border-radius: unset; + color: unset; + display: unset; + font-size: unset; + height: unset; + justify-content: unset; + line-height: unset; + padding-left: unset; + padding-right: unset; + white-space: unset; +} +.cc-window, +.cc-revoke { + font-size: 1.1rem !important; + font-family: Ubuntu, Roboto, 'Open Sans', 'Microsoft YaHei', sans-serif !important; +} +.cc-window { + color: #4a4a4a !important; + background-color: #fff !important; +} +.cc-window.cc-floating { + border-radius: 4px; + box-shadow: 0 4px 10px rgba(0,0,0,0.05), 0 0 1px rgba(0,0,0,0.1); +} +.cc-window.cc-banner { + background-color: #f9f9f9 !important; +} +.cc-window.cc-theme-block .cc-compliance > .cc-btn, +.cc-window.cc-theme-classic .cc-compliance > .cc-btn { + border-radius: 290486px; +} +.cc-window .cc-compliance > .cc-btn { + font-weight: 400; + border: none; + color: #fff; + background-color: #3273dc; +} +.cc-window .cc-compliance > .cc-btn:hover, +.cc-window .cc-compliance > .cc-btn:focus { + background-color: #276cda; +} +.cc-window .cc-compliance > .cc-btn.cc-deny:hover { + color: #3273dc; + text-decoration: none; +} +.cc-revoke { + padding: 0.5rem 1rem !important; + color: #fff !important; + background-color: #3273dc !important; +} +.cc-revoke:hover { + text-decoration: none !important; + background-color: #276cda; +} +@media screen and (min-width: 1280px) { + .is-1-column .container, + .is-2-column .container { + max-width: 960px; + width: 960px; + } +} +@media screen and (min-width: 1472px) { + .is-2-column .container { + max-width: 1152px; + width: 1152px; + } + .is-1-column .container { + max-width: 960px; + width: 960px; + } +} +@media screen and (min-width: 769px), print { + .is-sticky { + position: -webkit-sticky; + position: sticky; + top: 1.5rem; + z-index: 99; + } + .column-main.is-sticky, + .column-left.is-sticky, + .column-right.is-sticky, + .column-right-shadow.is-sticky { + top: 0.75rem; + align-self: flex-start; + } +} +@media screen and (max-width: 768px) { + .section { + padding: 1.5rem 1rem; + } +} diff --git a/db.json b/db.json deleted file mode 100644 index 5c528db..0000000 --- a/db.json +++ /dev/null @@ -1 +0,0 @@ -{"meta":{"version":1,"warehouse":"4.0.0"},"models":{"Asset":[{"_id":"source/images/avatar.jpeg","path":"images/avatar.jpeg","modified":0,"renderable":0},{"_id":"node_modules/hexo-theme-icarus/source/css/cyberpunk.styl","path":"css/cyberpunk.styl","modified":0,"renderable":1},{"_id":"node_modules/hexo-theme-icarus/source/css/default.styl","path":"css/default.styl","modified":0,"renderable":1},{"_id":"node_modules/hexo-theme-icarus/source/css/style.styl","path":"css/style.styl","modified":0,"renderable":1},{"_id":"node_modules/hexo-theme-icarus/source/img/avatar.png","path":"img/avatar.png","modified":0,"renderable":1},{"_id":"node_modules/hexo-theme-icarus/source/img/favicon.svg","path":"img/favicon.svg","modified":0,"renderable":1},{"_id":"node_modules/hexo-theme-icarus/source/img/logo.svg","path":"img/logo.svg","modified":0,"renderable":1},{"_id":"node_modules/hexo-theme-icarus/source/img/razor-bottom-black.svg","path":"img/razor-bottom-black.svg","modified":0,"renderable":1},{"_id":"node_modules/hexo-theme-icarus/source/img/og_image.png","path":"img/og_image.png","modified":0,"renderable":1},{"_id":"node_modules/hexo-theme-icarus/source/img/razor-top-black.svg","path":"img/razor-top-black.svg","modified":0,"renderable":1},{"_id":"node_modules/hexo-theme-icarus/source/js/animation.js","path":"js/animation.js","modified":0,"renderable":1},{"_id":"node_modules/hexo-theme-icarus/source/js/back_to_top.js","path":"js/back_to_top.js","modified":0,"renderable":1},{"_id":"node_modules/hexo-theme-icarus/source/js/column.js","path":"js/column.js","modified":0,"renderable":1},{"_id":"node_modules/hexo-theme-icarus/source/js/main.js","path":"js/main.js","modified":0,"renderable":1}],"Cache":[{"_id":"source/_posts/2020年阅读总结.md","hash":"e3d090b133f8fd42c62aa15a97678860e6fc5ba8","modified":1688473570184},{"_id":"source/_posts/AKS中重写规则踩坑小记录.md","hash":"1146c4ee448d30991c41c5b2c89f1548fc02ed43","modified":1688473570184},{"_id":"source/_posts/lagrange项目回顾.md","hash":"83b2e41ee2cb3834a3d5b20b034213e1e9418b40","modified":1688473570184},{"_id":"source/_posts/Lagrange项目回顾2.md","hash":"16e7a0542731087dfada583e5dceb1b46af826a2","modified":1688473570184},{"_id":"source/_posts/hot-to-install-nodejs-nginx-mongodb-on-ubuntu.md","hash":"cca9fcdfbb51a60cef2a99bd06fd95668a9cacd1","modified":1688473570184},{"_id":"source/_posts/Spring-Cloud-Kubernetes环境下使用Jasypt.md","hash":"52f29fdff63a43375c40c45e0bfa556d1b1f3499","modified":1688473570184},{"_id":"source/_posts/重学Java-一-泛型.md","hash":"5c97652e0b36fe6a0dd346d343e6275d18d3bd22","modified":1688473570184},{"_id":"source/_posts/阿里云Kubernetes上线踩坑记.md","hash":"ced83ac649849463488e9eb627908587ac319231","modified":1688473570184},{"_id":"source/images/avatar.jpeg","hash":"7f4209a3e844b9184a97df2dd127c6e872e7d4f0","modified":1688473570185},{"_id":"node_modules/hexo-theme-icarus/layout/comment/.gitkeep","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1688474003831},{"_id":"node_modules/hexo-theme-icarus/layout/donate/.gitkeep","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1688474003837},{"_id":"node_modules/hexo-theme-icarus/layout/misc/.gitkeep","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1688474003842},{"_id":"node_modules/hexo-theme-icarus/layout/search/.gitkeep","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1688474003846},{"_id":"node_modules/hexo-theme-icarus/layout/share/.gitkeep","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1688474003854},{"_id":"node_modules/hexo-theme-icarus/include/schema/comment/.gitkeep","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1688474003716},{"_id":"node_modules/hexo-theme-icarus/include/schema/donate/.gitkeep","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1688474003760},{"_id":"node_modules/hexo-theme-icarus/include/schema/misc/.gitkeep","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1688474003788},{"_id":"node_modules/hexo-theme-icarus/include/schema/search/.gitkeep","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1688474003814},{"_id":"node_modules/hexo-theme-icarus/include/schema/share/.gitkeep","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1688474003827},{"_id":"node_modules/hexo-theme-icarus/package.json","hash":"635402ea312dfe670bafc736328b9788b993a500","modified":1688474003906},{"_id":"node_modules/hexo-theme-icarus/include/config.js","hash":"1ff0f174e9670074ad2bee890d5b6da486800c9a","modified":1688474003866},{"_id":"node_modules/hexo-theme-icarus/CONTRIBUTING.md","hash":"70254c6778c1e41bb2ff222bbf3a70b2239b9bc1","modified":1688474003934},{"_id":"node_modules/hexo-theme-icarus/include/dependency.js","hash":"d30dbcefd58619f6705d6369b644bc7ba44d2421","modified":1688474003883},{"_id":"node_modules/hexo-theme-icarus/LICENSE","hash":"86037e5335a49321fa73b7815cab542057fac944","modified":1688474003857},{"_id":"node_modules/hexo-theme-icarus/README.md","hash":"247ec8047ee3105d31099dd8e6ca498b6bff1336","modified":1688474003935},{"_id":"node_modules/hexo-theme-icarus/include/register.js","hash":"a974b56a1fbb254f1ae048cc2221363faaccec25","modified":1688474003890},{"_id":"node_modules/hexo-theme-icarus/languages/de.yml","hash":"01d9c27c3c9224d8c58b1cf7099ef008b9411a45","modified":1688474003958},{"_id":"node_modules/hexo-theme-icarus/languages/en.yml","hash":"1bdb74288808c4d306b46630860e586a1fcd88a7","modified":1688474003958},{"_id":"node_modules/hexo-theme-icarus/languages/es.yml","hash":"48fe3d7d304239b5e5e93f63600093700d6f0fed","modified":1688474003959},{"_id":"node_modules/hexo-theme-icarus/languages/fr.yml","hash":"14765cc6216b4af5a05341315c9f6ee54d636a78","modified":1688474003959},{"_id":"node_modules/hexo-theme-icarus/languages/ja.yml","hash":"7568f246a21813527ccea8a1da72a9526aa2d233","modified":1688474003960},{"_id":"node_modules/hexo-theme-icarus/languages/id.yml","hash":"c04fca89e536d539a8bf95980bff7dff79125ba3","modified":1688474003960},{"_id":"node_modules/hexo-theme-icarus/languages/ko.yml","hash":"f2211a8ca1d73f05a1c931aa11accb4e34c483c4","modified":1688474003960},{"_id":"node_modules/hexo-theme-icarus/languages/tr.yml","hash":"96d94f7a5d56b2682ae2792f0808139d4b950704","modified":1688474003962},{"_id":"node_modules/hexo-theme-icarus/languages/pl.yml","hash":"612639b0e0a15185c12eecfe3e8913b629ecdfdf","modified":1688474003961},{"_id":"node_modules/hexo-theme-icarus/languages/ru.yml","hash":"bf784c6f31e8fb7ed78509468bddecd447c3b73b","modified":1688474003961},{"_id":"node_modules/hexo-theme-icarus/languages/zh-CN.yml","hash":"82ff607b1671b88d259d10b6ce8ca1bb05b2cff4","modified":1688474003963},{"_id":"node_modules/hexo-theme-icarus/languages/zh-TW.yml","hash":"92f8cf599ad06bb14f79f4806ac4c695f60044d7","modified":1688474003964},{"_id":"node_modules/hexo-theme-icarus/languages/pt-BR.yml","hash":"531b22c71fab8aae60ddc7599aaa5f46140cf2c1","modified":1688474003961},{"_id":"node_modules/hexo-theme-icarus/languages/vn.yml","hash":"64307bfa16d851334e2f37a29a84d644624c2060","modified":1688474003962},{"_id":"node_modules/hexo-theme-icarus/layout/archive.jsx","hash":"05677e93d4a43f417dbbf0d63ca37a99e6349e3b","modified":1688474003917},{"_id":"node_modules/hexo-theme-icarus/layout/categories.jsx","hash":"b8ad43e28a4990d222bfbb95b032f88555492347","modified":1688474003920},{"_id":"node_modules/hexo-theme-icarus/layout/category.jsx","hash":"fd15e4eac32de9ac8687aeb3dbe179ab61375700","modified":1688474003921},{"_id":"node_modules/hexo-theme-icarus/layout/index.jsx","hash":"0a84a2348394fa9fc5080dd396bd28d357594f47","modified":1688474003925},{"_id":"node_modules/hexo-theme-icarus/layout/layout.jsx","hash":"a5829907b219e95266f7ed5ee6203e60e2273f93","modified":1688474003926},{"_id":"node_modules/hexo-theme-icarus/layout/page.jsx","hash":"d26c2db57e5a88d6483a03aeb51cda9d191d8cea","modified":1688474003927},{"_id":"node_modules/hexo-theme-icarus/include/migration/head.js","hash":"269ba172013cbd2f10b9bc51af0496628081329b","modified":1688474003885},{"_id":"node_modules/hexo-theme-icarus/scripts/index.js","hash":"0c666db6fcb4ffc4d300f4e108c00ee42b1cbbe6","modified":1688474003887},{"_id":"node_modules/hexo-theme-icarus/include/migration/v3_v4.js","hash":"9faf2184d7fe87debfbe007f3fc9079dcbcafcfe","modified":1688474003892},{"_id":"node_modules/hexo-theme-icarus/include/schema/config.json","hash":"ac633f9d349bca4f089d59d2c3738b57376f1b31","modified":1688474003903},{"_id":"node_modules/hexo-theme-icarus/layout/post.jsx","hash":"d26c2db57e5a88d6483a03aeb51cda9d191d8cea","modified":1688474003928},{"_id":"node_modules/hexo-theme-icarus/layout/tag.jsx","hash":"d2f18cac32ca2725d34ccff3f2051c623be6c892","modified":1688474003932},{"_id":"node_modules/hexo-theme-icarus/include/util/console.js","hash":"59cf9d277d3ac85a496689bd811b1c316001641d","modified":1688474003868},{"_id":"node_modules/hexo-theme-icarus/layout/tags.jsx","hash":"2c42cb64778235dd220c563a27a92108ddc50cc4","modified":1688474003933},{"_id":"node_modules/hexo-theme-icarus/include/migration/v2_v3.js","hash":"3ccb2d2ce11018bebd7172da66faecc3983bff00","modified":1688474003891},{"_id":"node_modules/hexo-theme-icarus/include/style/base.styl","hash":"2bca6ad099949d52236c87db8db1002ffb99774c","modified":1688474003944},{"_id":"node_modules/hexo-theme-icarus/include/style/article.styl","hash":"be25e890113e926bbac3bf1461d7ce5427914774","modified":1688474003943},{"_id":"node_modules/hexo-theme-icarus/include/style/button.styl","hash":"0fb35b4786be1b387c751fa2849bc71523fcedd4","modified":1688474003945},{"_id":"node_modules/hexo-theme-icarus/include/style/codeblock.styl","hash":"30bee4cf6792e9665eb648cc20b352d9eaff1207","modified":1688474003945},{"_id":"node_modules/hexo-theme-icarus/include/style/card.styl","hash":"f78674422eb408cd17c17bbdc3ee1ebe4a453e05","modified":1688474003945},{"_id":"node_modules/hexo-theme-icarus/include/style/footer.styl","hash":"a4ad715dee38b249538ac6cce94efc9b355a904b","modified":1688474003950},{"_id":"node_modules/hexo-theme-icarus/include/style/donate.styl","hash":"8d0af00628c13134b5f30a558608e7bebf18c2ec","modified":1688474003949},{"_id":"node_modules/hexo-theme-icarus/include/style/navbar.styl","hash":"ecc73c8ad504c0fa4bb910eb51500c14e0a8d662","modified":1688474003951},{"_id":"node_modules/hexo-theme-icarus/include/style/plugin.styl","hash":"dc98160142c95ef81ba4789351002a5fcf29c04c","modified":1688474003952},{"_id":"node_modules/hexo-theme-icarus/include/style/helper.styl","hash":"9f3393e6122cc9f351091bfab960674e962da343","modified":1688474003950},{"_id":"node_modules/hexo-theme-icarus/include/style/responsive.styl","hash":"207083fe287612cddee6608b541861b14ac8de81","modified":1688474003953},{"_id":"node_modules/hexo-theme-icarus/include/style/timeline.styl","hash":"ea61798a09bffdda07efb93c2ff800b63bddc4c4","modified":1688474003954},{"_id":"node_modules/hexo-theme-icarus/include/style/widget.styl","hash":"0720a70448ffd78800f7556a7d67300eac19aa87","modified":1688474003955},{"_id":"node_modules/hexo-theme-icarus/include/style/search.styl","hash":"416737e1da4e7e907bd03609b0fee9e2aacfe56c","modified":1688474003954},{"_id":"node_modules/hexo-theme-icarus/include/style/pagination.styl","hash":"b81bcd7ff915b4e9299533addc01bc4575ec35e3","modified":1688474003951},{"_id":"node_modules/hexo-theme-icarus/layout/common/article.jsx","hash":"85d13095ed49e3bb362fbac830df63fc416e79bf","modified":1688474003918},{"_id":"node_modules/hexo-theme-icarus/layout/common/donates.jsx","hash":"889fb0a7ccc502f0a43b4a18eb330e351e50493c","modified":1688474003922},{"_id":"node_modules/hexo-theme-icarus/layout/common/comment.jsx","hash":"427089c33002707b76e2f38709459a6824fd0f9b","modified":1688474003921},{"_id":"node_modules/hexo-theme-icarus/layout/common/footer.jsx","hash":"baa8e40e036a3ec0114d96893b708435104b4ba9","modified":1688474003923},{"_id":"node_modules/hexo-theme-icarus/layout/common/head.jsx","hash":"bcee2e258d13af6ac439ee6adaeefc06ea384e7e","modified":1688474003925},{"_id":"node_modules/hexo-theme-icarus/layout/common/navbar.jsx","hash":"fcd9fd4624dee49207ef09ea2a1c63f524f3710c","modified":1688474003926},{"_id":"node_modules/hexo-theme-icarus/layout/common/plugins.jsx","hash":"f6826c1a5f5f59f4a0aa00c63bdb0ad4ff4eab69","modified":1688474003928},{"_id":"node_modules/hexo-theme-icarus/layout/common/scripts.jsx","hash":"0fe1fddab431fb9f63906d8c480d5cd6b33abc32","modified":1688474003930},{"_id":"node_modules/hexo-theme-icarus/layout/common/widgets.jsx","hash":"689cf4a6b79337b11d1d56afa9dda09223a809a1","modified":1688474003933},{"_id":"node_modules/hexo-theme-icarus/layout/common/search.jsx","hash":"6f244a37293031670a2964fe424ecd062e591d7b","modified":1688474003931},{"_id":"node_modules/hexo-theme-icarus/layout/plugin/animejs.jsx","hash":"e2aa27c3501a58ef1e91e511557b77395c2c02aa","modified":1688474003915},{"_id":"node_modules/hexo-theme-icarus/layout/widget/profile.jsx","hash":"0d3a7fd922c12cc45d2c8d26a8f4d3a9a6ed0ae0","modified":1688474003929},{"_id":"node_modules/hexo-theme-icarus/layout/common/share.jsx","hash":"c9fb0319ad5e5a10ad3636b26a6c2afed14c590f","modified":1688474003932},{"_id":"node_modules/hexo-theme-icarus/layout/plugin/back_to_top.jsx","hash":"7fc0c5aaabd7d0eaff04cb68ec139442dc3414e8","modified":1688474003920},{"_id":"node_modules/hexo-theme-icarus/source/css/cyberpunk.styl","hash":"ae17d3528df0c3f089df14a06b7bd82f1bc5fed9","modified":1688474003948},{"_id":"node_modules/hexo-theme-icarus/source/css/default.styl","hash":"b01da3028e5a1267a40aaae5c86a11187a2259e3","modified":1688474003949},{"_id":"node_modules/hexo-theme-icarus/source/css/style.styl","hash":"5b9815586e993a6ccbe8cdcfc0c65ea38fc315ac","modified":1688474003954},{"_id":"node_modules/hexo-theme-icarus/source/img/avatar.png","hash":"0d8236dcca871735500e9d06bbdbe0853ed6775b","modified":1688474003938},{"_id":"node_modules/hexo-theme-icarus/source/img/logo.svg","hash":"e9b5c1438ddb576693a15d0713b2a1d9ceda4be9","modified":1688474003956},{"_id":"node_modules/hexo-theme-icarus/source/img/favicon.svg","hash":"16fd847265845063a16596761cddb32926073dd2","modified":1688474003955},{"_id":"node_modules/hexo-theme-icarus/source/img/razor-bottom-black.svg","hash":"a3eda07b1c605b456da9cdf335a1075db5e5d72c","modified":1688474003956},{"_id":"node_modules/hexo-theme-icarus/source/img/og_image.png","hash":"b03f163096ca9c350ec962feee9836277b5c2509","modified":1688474003940},{"_id":"node_modules/hexo-theme-icarus/source/img/razor-top-black.svg","hash":"201f1171a43ce667a39091fe47c0f278857f18f0","modified":1688474003957},{"_id":"node_modules/hexo-theme-icarus/source/js/animation.js","hash":"12cedd5caaf9109eed97e50eeab8f883f6e49be3","modified":1688474003860},{"_id":"node_modules/hexo-theme-icarus/include/schema/common/article.json","hash":"8d78149f44629d0848921c6fb9c008b03cef3116","modified":1688474003898},{"_id":"node_modules/hexo-theme-icarus/include/schema/common/donates.json","hash":"ae86e6f177bedf4afbe638502c12635027539305","modified":1688474003903},{"_id":"node_modules/hexo-theme-icarus/include/schema/common/comment.json","hash":"bd30bd9ffc84e88e704384acdfcaab09019a744f","modified":1688474003901},{"_id":"node_modules/hexo-theme-icarus/include/schema/common/footer.json","hash":"09d706cbb94d6da9a0d15c719ce7139325cae1c7","modified":1688474003904},{"_id":"node_modules/hexo-theme-icarus/include/schema/common/head.json","hash":"98889f059c635e6bdbd51effd04cf1cf44968a66","modified":1688474003905},{"_id":"node_modules/hexo-theme-icarus/include/schema/common/plugins.json","hash":"6036a805749816416850d944f7d64aaae62e5e75","modified":1688474003908},{"_id":"node_modules/hexo-theme-icarus/include/schema/common/navbar.json","hash":"6691e587284c4cf450e0288680d5ff0f3565f090","modified":1688474003905},{"_id":"node_modules/hexo-theme-icarus/include/schema/common/search.json","hash":"985fbcbf47054af714ead1a124869d54f2a8b607","modified":1688474003911},{"_id":"node_modules/hexo-theme-icarus/source/js/column.js","hash":"0baee024ab67474c073a4c41b495f3e7f0df4505","modified":1688474003862},{"_id":"node_modules/hexo-theme-icarus/source/js/back_to_top.js","hash":"d91f10c08c726135a13dfa1f422c49d8764ef03f","modified":1688474003861},{"_id":"node_modules/hexo-theme-icarus/include/schema/common/providers.json","hash":"97ec953d497fb53594227ae98acaef8a8baa91da","modified":1688474003911},{"_id":"node_modules/hexo-theme-icarus/include/schema/common/sidebar.json","hash":"eb241beaec4c73e3085dfb3139ce72e827e20549","modified":1688474003912},{"_id":"node_modules/hexo-theme-icarus/include/schema/plugin/animejs.json","hash":"e62ab6e20bd8862efa1ed32e7c0db0f8acbcfdec","modified":1688474003895},{"_id":"node_modules/hexo-theme-icarus/include/schema/plugin/back_to_top.json","hash":"dc0febab7e7b67075d0ad3f80f5ec8b798b68dea","modified":1688474003899},{"_id":"node_modules/hexo-theme-icarus/include/schema/common/widgets.json","hash":"d000b4d1d09bdd64756265aa4cd2ea980ab7ddc7","modified":1688474003913},{"_id":"node_modules/hexo-theme-icarus/include/schema/common/share.json","hash":"cf4f9ff4fb27c3541b35f57db355c228fa6873e4","modified":1688474003912},{"_id":"node_modules/hexo-theme-icarus/include/schema/widget/profile.json","hash":"690ee1b0791cab47ea03cf42b5b4932ed2aa5675","modified":1688474003909},{"_id":"node_modules/hexo-theme-icarus/source/js/main.js","hash":"13e4b1c4fa287f3db61aae329ad093a81992f23d","modified":1688474003888},{"_id":"source/_drafts/H项目心路历程记-前言.md","hash":"eb016ea7cb134b6c23b7fef2b44ec76e9e85f2c4","modified":1688624864700},{"_id":"source/_drafts/H项目心路历程记(一).md","hash":"4164dde5b5224970a8c48a4aac4b31bb5f1dc43a","modified":1688973275769},{"_id":"source/_posts/H项目心路历程记-1.md","hash":"bd2c4b2c23ee2f91ce0551306bc9e40e5fe26663","modified":1688980621579},{"_id":"source/_drafts/H项目心路历程记-3.md","hash":"6da187d23711536f2e35a3e8cb313b57d188eeca","modified":1695193320917},{"_id":"source/_posts/H项目心路历程记-2.md","hash":"ef71e6470807d19a2c4e2bd986c2a4fb334c56e2","modified":1689154176609}],"Category":[{"name":"杂记","_id":"cljo9zkjo00024goi1kpe1pou"},{"name":"后端","_id":"cljo9zkjq00074goifz3v6gea"},{"name":"设计","_id":"cljo9zkjs000d4goi3nzx2545"},{"name":"Cloud","parent":"cljo9zkjq00074goifz3v6gea","_id":"cljo9zkjt000p4goi5v02detb"}],"Data":[],"Page":[],"Post":[{"title":"2020年阅读总结","date":"2020-12-29T02:45:41.000Z","_content":"\n\n多灾多难的2020年已经没有几天了,整个2020年要说还真是像一记刹车,去年很多想法,计划都有因为疫情原因搁置了。平时的生活更是从宅家变成了重度宅家的状态,自然就多出了许多大段大段的连续时间,可以用来好好玩玩大作,看看小说。仔细盘点一下今年总共看了10本小说外加2本科普书籍( [2020年阅读书单](https://github.com/hashmaparraylist/ReadList/blob/master/ReadList2020.md)\n),算是自2018年以来读书最多的一个年份了。其中不乏拖了近3年的那本《西方文化中的数学》。接着就来絮叨絮叨今年看的这12本书吧。\n\n---\n\n> 《莱博维茨的赞歌》\n\n第一次被安利《莱博维茨的赞歌》的机核的电台节目[辐射-视觉、音乐以及文学](https://www.gcores.com/radios/16773), 当时听完节目就相当好奇,可以被一部载入史册的CRPG游戏誉为精神文本的小说到底是个什么样子的。但是当时新星版的译本几乎就是绝本了,某鱼的二手价也要已经飙到了3位数。直到今年年初在核市节目上听到了中信版译本的消息,才有机会买来拜读。\n\n全书通过3个故事讲述了核战灭世后莱博维茨修道院从保卫文明,到重建,然后看着文明再度毁灭的故事。各种无不透露着辐射那股 `War, War never changed`的味道。\n\n> 《占星术杀人事件》《屋顶上的小丑》《亿男》\n\n这3本书都是2019年上海书展上买的。前两本都是岛田庄司的推理名篇,其中《占星术杀人事件》在本格派推理史上有着非凡的地位,各种被抄袭从未被超越过。《屋顶上的小丑》要是最吸引人的话就是多线叙事,每条线都掐在恰如其分的地方。《亿男》则是挂着悬疑羊头卖着人生哲理。\n\n> 《神经漫游者》\n\n威廉吉布森的蔓生三部曲之一,开创了赛博朋克的科幻流派,说实话这本书我看的很痛苦,很多地方实在读不懂,到现在我还是不明白那些无法读懂的地方到底是原著如此,还是翻译的不行,亦或者是Kindle版排版太烂了。\n\n> 《神们自己》,《神的九十亿个名字》\n\n阿瑟克拉克的中篇小说和短篇小说集,这两本书阅读体验奇佳,尤其是在读完《神经漫游者》后,虽然三本书被Amazon打在同一个包里来卖的,但是光从排版上就比《神经漫游者》高出不少,所以才会有我对后者排版问题的质疑。\n\n《神们自己》三线叙事,讲述了2个平行宇宙中的2个物种在能源,种族存续,家庭伦理上的各种讨论和故事。 《神的九十亿个名字》作为短篇小说集收录了好几篇风格题材迥异的小说,花上十几分钟读上一个故事是相当惬意的。\n\n> 《佐伊的战争》 《人类决裂》 《万物的终结》\n\n约翰斯卡尔齐的《老人的战争》系列的后3部,前三部大概是3,4年前读的吧,在这个宇宙观里最吸引人的设定就绿皮的人类防卫军战士,以及跨种族间的政治斗争。系列的六本书相当于在这个大设定下讲的六个故事。每个故事虽说都是独立的故事,但是前后皆有关联,而且最重要的就是每本的长度都算不上很长,相当适合于一口气读完。\n\n> 《西方文化中的数学》\n\n这本书大概是从2017年开始读的,当时意气奋发觉得对于理科生出身的我,书中内容应该很好理解,没想到最后不光是文化还是数学读的都是一知半解。只能说自大了。\n\n> 《一想到还有95%的问题留给人类,我就放心了》\n\n大概是18年还是19年前后对量子物理和宇宙物理开始有了兴趣,虽然各中理论并不是很了解,但是仍然很好奇的买了这本书,全书插画相当有趣,同时使用浅显易懂的语言来解释各种复杂的物理现象,以及当今人类物理知识的边界。强烈推荐给想假装成学霸的朋友。\n\n---\n\n> 2021年读什么\n\n具体读啥还没想好基本上还是以科幻和悬疑为主,但是肯定会读的应该有海明威的《丧钟为谁而鸣》和小林泰三的《醉步男》吧。\n\n","source":"_posts/2020年阅读总结.md","raw":"---\ntitle: 2020年阅读总结\ntags:\n - 阅读\ncategories:\n - 杂记\ndate: 2020-12-29 10:45:41\n---\n\n\n多灾多难的2020年已经没有几天了,整个2020年要说还真是像一记刹车,去年很多想法,计划都有因为疫情原因搁置了。平时的生活更是从宅家变成了重度宅家的状态,自然就多出了许多大段大段的连续时间,可以用来好好玩玩大作,看看小说。仔细盘点一下今年总共看了10本小说外加2本科普书籍( [2020年阅读书单](https://github.com/hashmaparraylist/ReadList/blob/master/ReadList2020.md)\n),算是自2018年以来读书最多的一个年份了。其中不乏拖了近3年的那本《西方文化中的数学》。接着就来絮叨絮叨今年看的这12本书吧。\n\n---\n\n> 《莱博维茨的赞歌》\n\n第一次被安利《莱博维茨的赞歌》的机核的电台节目[辐射-视觉、音乐以及文学](https://www.gcores.com/radios/16773), 当时听完节目就相当好奇,可以被一部载入史册的CRPG游戏誉为精神文本的小说到底是个什么样子的。但是当时新星版的译本几乎就是绝本了,某鱼的二手价也要已经飙到了3位数。直到今年年初在核市节目上听到了中信版译本的消息,才有机会买来拜读。\n\n全书通过3个故事讲述了核战灭世后莱博维茨修道院从保卫文明,到重建,然后看着文明再度毁灭的故事。各种无不透露着辐射那股 `War, War never changed`的味道。\n\n> 《占星术杀人事件》《屋顶上的小丑》《亿男》\n\n这3本书都是2019年上海书展上买的。前两本都是岛田庄司的推理名篇,其中《占星术杀人事件》在本格派推理史上有着非凡的地位,各种被抄袭从未被超越过。《屋顶上的小丑》要是最吸引人的话就是多线叙事,每条线都掐在恰如其分的地方。《亿男》则是挂着悬疑羊头卖着人生哲理。\n\n> 《神经漫游者》\n\n威廉吉布森的蔓生三部曲之一,开创了赛博朋克的科幻流派,说实话这本书我看的很痛苦,很多地方实在读不懂,到现在我还是不明白那些无法读懂的地方到底是原著如此,还是翻译的不行,亦或者是Kindle版排版太烂了。\n\n> 《神们自己》,《神的九十亿个名字》\n\n阿瑟克拉克的中篇小说和短篇小说集,这两本书阅读体验奇佳,尤其是在读完《神经漫游者》后,虽然三本书被Amazon打在同一个包里来卖的,但是光从排版上就比《神经漫游者》高出不少,所以才会有我对后者排版问题的质疑。\n\n《神们自己》三线叙事,讲述了2个平行宇宙中的2个物种在能源,种族存续,家庭伦理上的各种讨论和故事。 《神的九十亿个名字》作为短篇小说集收录了好几篇风格题材迥异的小说,花上十几分钟读上一个故事是相当惬意的。\n\n> 《佐伊的战争》 《人类决裂》 《万物的终结》\n\n约翰斯卡尔齐的《老人的战争》系列的后3部,前三部大概是3,4年前读的吧,在这个宇宙观里最吸引人的设定就绿皮的人类防卫军战士,以及跨种族间的政治斗争。系列的六本书相当于在这个大设定下讲的六个故事。每个故事虽说都是独立的故事,但是前后皆有关联,而且最重要的就是每本的长度都算不上很长,相当适合于一口气读完。\n\n> 《西方文化中的数学》\n\n这本书大概是从2017年开始读的,当时意气奋发觉得对于理科生出身的我,书中内容应该很好理解,没想到最后不光是文化还是数学读的都是一知半解。只能说自大了。\n\n> 《一想到还有95%的问题留给人类,我就放心了》\n\n大概是18年还是19年前后对量子物理和宇宙物理开始有了兴趣,虽然各中理论并不是很了解,但是仍然很好奇的买了这本书,全书插画相当有趣,同时使用浅显易懂的语言来解释各种复杂的物理现象,以及当今人类物理知识的边界。强烈推荐给想假装成学霸的朋友。\n\n---\n\n> 2021年读什么\n\n具体读啥还没想好基本上还是以科幻和悬疑为主,但是肯定会读的应该有海明威的《丧钟为谁而鸣》和小林泰三的《醉步男》吧。\n\n","slug":"2020年阅读总结","published":1,"updated":"2023-07-04T12:26:10.184Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cljo9zkjl00004goifkkd2ktv","content":"

多灾多难的2020年已经没有几天了,整个2020年要说还真是像一记刹车,去年很多想法,计划都有因为疫情原因搁置了。平时的生活更是从宅家变成了重度宅家的状态,自然就多出了许多大段大段的连续时间,可以用来好好玩玩大作,看看小说。仔细盘点一下今年总共看了10本小说外加2本科普书籍( 2020年阅读书单
),算是自2018年以来读书最多的一个年份了。其中不乏拖了近3年的那本《西方文化中的数学》。接着就来絮叨絮叨今年看的这12本书吧。

\n
\n
\n

《莱博维茨的赞歌》

\n
\n

第一次被安利《莱博维茨的赞歌》的机核的电台节目辐射-视觉、音乐以及文学, 当时听完节目就相当好奇,可以被一部载入史册的CRPG游戏誉为精神文本的小说到底是个什么样子的。但是当时新星版的译本几乎就是绝本了,某鱼的二手价也要已经飙到了3位数。直到今年年初在核市节目上听到了中信版译本的消息,才有机会买来拜读。

\n

全书通过3个故事讲述了核战灭世后莱博维茨修道院从保卫文明,到重建,然后看着文明再度毁灭的故事。各种无不透露着辐射那股 War, War never changed的味道。

\n
\n

《占星术杀人事件》《屋顶上的小丑》《亿男》

\n
\n

这3本书都是2019年上海书展上买的。前两本都是岛田庄司的推理名篇,其中《占星术杀人事件》在本格派推理史上有着非凡的地位,各种被抄袭从未被超越过。《屋顶上的小丑》要是最吸引人的话就是多线叙事,每条线都掐在恰如其分的地方。《亿男》则是挂着悬疑羊头卖着人生哲理。

\n
\n

《神经漫游者》

\n
\n

威廉吉布森的蔓生三部曲之一,开创了赛博朋克的科幻流派,说实话这本书我看的很痛苦,很多地方实在读不懂,到现在我还是不明白那些无法读懂的地方到底是原著如此,还是翻译的不行,亦或者是Kindle版排版太烂了。

\n
\n

《神们自己》,《神的九十亿个名字》

\n
\n

阿瑟克拉克的中篇小说和短篇小说集,这两本书阅读体验奇佳,尤其是在读完《神经漫游者》后,虽然三本书被Amazon打在同一个包里来卖的,但是光从排版上就比《神经漫游者》高出不少,所以才会有我对后者排版问题的质疑。

\n

《神们自己》三线叙事,讲述了2个平行宇宙中的2个物种在能源,种族存续,家庭伦理上的各种讨论和故事。 《神的九十亿个名字》作为短篇小说集收录了好几篇风格题材迥异的小说,花上十几分钟读上一个故事是相当惬意的。

\n
\n

《佐伊的战争》 《人类决裂》 《万物的终结》

\n
\n

约翰斯卡尔齐的《老人的战争》系列的后3部,前三部大概是3,4年前读的吧,在这个宇宙观里最吸引人的设定就绿皮的人类防卫军战士,以及跨种族间的政治斗争。系列的六本书相当于在这个大设定下讲的六个故事。每个故事虽说都是独立的故事,但是前后皆有关联,而且最重要的就是每本的长度都算不上很长,相当适合于一口气读完。

\n
\n

《西方文化中的数学》

\n
\n

这本书大概是从2017年开始读的,当时意气奋发觉得对于理科生出身的我,书中内容应该很好理解,没想到最后不光是文化还是数学读的都是一知半解。只能说自大了。

\n
\n

《一想到还有95%的问题留给人类,我就放心了》

\n
\n

大概是18年还是19年前后对量子物理和宇宙物理开始有了兴趣,虽然各中理论并不是很了解,但是仍然很好奇的买了这本书,全书插画相当有趣,同时使用浅显易懂的语言来解释各种复杂的物理现象,以及当今人类物理知识的边界。强烈推荐给想假装成学霸的朋友。

\n
\n
\n

2021年读什么

\n
\n

具体读啥还没想好基本上还是以科幻和悬疑为主,但是肯定会读的应该有海明威的《丧钟为谁而鸣》和小林泰三的《醉步男》吧。

\n","site":{"data":{}},"excerpt":"","more":"

多灾多难的2020年已经没有几天了,整个2020年要说还真是像一记刹车,去年很多想法,计划都有因为疫情原因搁置了。平时的生活更是从宅家变成了重度宅家的状态,自然就多出了许多大段大段的连续时间,可以用来好好玩玩大作,看看小说。仔细盘点一下今年总共看了10本小说外加2本科普书籍( 2020年阅读书单
),算是自2018年以来读书最多的一个年份了。其中不乏拖了近3年的那本《西方文化中的数学》。接着就来絮叨絮叨今年看的这12本书吧。

\n
\n
\n

《莱博维茨的赞歌》

\n
\n

第一次被安利《莱博维茨的赞歌》的机核的电台节目辐射-视觉、音乐以及文学, 当时听完节目就相当好奇,可以被一部载入史册的CRPG游戏誉为精神文本的小说到底是个什么样子的。但是当时新星版的译本几乎就是绝本了,某鱼的二手价也要已经飙到了3位数。直到今年年初在核市节目上听到了中信版译本的消息,才有机会买来拜读。

\n

全书通过3个故事讲述了核战灭世后莱博维茨修道院从保卫文明,到重建,然后看着文明再度毁灭的故事。各种无不透露着辐射那股 War, War never changed的味道。

\n
\n

《占星术杀人事件》《屋顶上的小丑》《亿男》

\n
\n

这3本书都是2019年上海书展上买的。前两本都是岛田庄司的推理名篇,其中《占星术杀人事件》在本格派推理史上有着非凡的地位,各种被抄袭从未被超越过。《屋顶上的小丑》要是最吸引人的话就是多线叙事,每条线都掐在恰如其分的地方。《亿男》则是挂着悬疑羊头卖着人生哲理。

\n
\n

《神经漫游者》

\n
\n

威廉吉布森的蔓生三部曲之一,开创了赛博朋克的科幻流派,说实话这本书我看的很痛苦,很多地方实在读不懂,到现在我还是不明白那些无法读懂的地方到底是原著如此,还是翻译的不行,亦或者是Kindle版排版太烂了。

\n
\n

《神们自己》,《神的九十亿个名字》

\n
\n

阿瑟克拉克的中篇小说和短篇小说集,这两本书阅读体验奇佳,尤其是在读完《神经漫游者》后,虽然三本书被Amazon打在同一个包里来卖的,但是光从排版上就比《神经漫游者》高出不少,所以才会有我对后者排版问题的质疑。

\n

《神们自己》三线叙事,讲述了2个平行宇宙中的2个物种在能源,种族存续,家庭伦理上的各种讨论和故事。 《神的九十亿个名字》作为短篇小说集收录了好几篇风格题材迥异的小说,花上十几分钟读上一个故事是相当惬意的。

\n
\n

《佐伊的战争》 《人类决裂》 《万物的终结》

\n
\n

约翰斯卡尔齐的《老人的战争》系列的后3部,前三部大概是3,4年前读的吧,在这个宇宙观里最吸引人的设定就绿皮的人类防卫军战士,以及跨种族间的政治斗争。系列的六本书相当于在这个大设定下讲的六个故事。每个故事虽说都是独立的故事,但是前后皆有关联,而且最重要的就是每本的长度都算不上很长,相当适合于一口气读完。

\n
\n

《西方文化中的数学》

\n
\n

这本书大概是从2017年开始读的,当时意气奋发觉得对于理科生出身的我,书中内容应该很好理解,没想到最后不光是文化还是数学读的都是一知半解。只能说自大了。

\n
\n

《一想到还有95%的问题留给人类,我就放心了》

\n
\n

大概是18年还是19年前后对量子物理和宇宙物理开始有了兴趣,虽然各中理论并不是很了解,但是仍然很好奇的买了这本书,全书插画相当有趣,同时使用浅显易懂的语言来解释各种复杂的物理现象,以及当今人类物理知识的边界。强烈推荐给想假装成学霸的朋友。

\n
\n
\n

2021年读什么

\n
\n

具体读啥还没想好基本上还是以科幻和悬疑为主,但是肯定会读的应该有海明威的《丧钟为谁而鸣》和小林泰三的《醉步男》吧。

\n"},{"title":"AKS中重写规则踩坑小记录","date":"2022-09-23T05:51:49.000Z","_content":"\n# 前言\n\n最近在做标准产品在不同云平台中的部署验证,有幸体验了一下微软的Azure。负责采购的运维部门这次采用了`Application Gateway`来搭配`AKS`(`Azure Kubernetes Service`)对外暴露服务,正好借着这个机会来体验一下`Application Gateway`。\n\n# 应用场景\n\n1. 域名`api.demo.com`指向`Application Gateway`的IP地址\n2. 在`AKS`内部2个Service, `gateway-service`和`backend-service`分别需要通过`Application Gateway`对外暴露。\n3. `/gateway/`指向`gateway-service`, 然后`/backend/`指向`backend-service`。而且两个Service都没有context-path,所以需要做一个Rewrite重写URI到Service的根目录上。\n\n# 定义重写集\n\n打开`AKS`对应的应用程序网关`设置` > `重写`。选择`添加重写集`。在`1. 名称和关联`这个Tab上只需要填写名称这项即可(名称后面在做ingress时需要使用), `关联的传递规则`不需要选择。`2. 重写规则配置`里添加一个重写规则,然后填上重写规则的名称,并添加条件(默认新建重写规则时,只会生成操作,不会生成条件)\n\n## `条件`做如下设置\n\n- **要检查的变量类型** : `服务器变量`\n- **服务器变量**: `request_uri`\n- **区分大小写**: `否` \n- **运算符**: `等号(=)`\n- **要匹配的模式**: `/(gateway|backend)/?(.*)`\n\n## `操作`做如下设置\n\n- **重写类型**: `URL`\n- **操作类型**: `设置`\n- **组件**: `URL路径和URL查询字符串`\n- **URL路径值**: `/{var_request_uri_2}`\n- **重新计算路径映射**: `不选中`\n- **URL查询字符串值**: `留空不设值`\n\n## 特殊说明\n\n`操作`里的`URL路径值`不能使用正则表达式GROUP替换组,例如`$1`和`$2`之类的。Azure自己定义了一套对应的替换组命名规则。具体可以参考这个网页[使用应用程序网关重写 HTTP 标头和 URL](https://docs.azure.cn/zh-cn/application-gateway/rewrite-http-headers-url)。\n\n另外一个需要注意一点,如果在`条件`里选择了`服务器变量`的`request_uri`的时候,注意这个`request_uri`是完整的原始请求URI(携带了查询参数)。例如: 在请求`http://api.demo.com/gateway/search?foo=bar&hello=world`中,`request_uri`的值将为`/gateway/search?foo=bar&hello=world`。由于`request_uri`里包含了查询参数,所以在`操作`的`组件`中建议勾选`URL路径和URL查询字符串`。如果只选择`URL路径`的情况下可能出现无法预期的错误。以我们上述的配置来说明。\n\n对象URL: `http://api.demo.com/gateway/search?foo=bar&hello=world`\n\n**组件** | `URL路径和URL查询字符串` | `URL路径`\n--- | --- | ---\n**结果** | `/search?foo=bar&hello=world` | `/search?foo=bar&hello=world?foo=bar&hello=world`\n\n# `ACK`的Ingress设置\n\n当选择了`Application Gateway`作为对外暴露Service的方式时,Kubernetes集群里(`kube-system`命名空间里)多一个`Application Gateway Ingress Controller`(Azure工单时通常会简称为`agic`)的Deployment,所以对外暴露服务时可以像传统`nginx ingress controller`一样添加一个`Ingress`对象即可(甚至配置也和ngic大致相同,只是多了2个annotations)\n\n```yaml\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n annotations:\n # 这里指定重写规则集(不是重写规则的名字)\n appgw.ingress.kubernetes.io/rewrite-rule-set: rule-backend\n # 指定说明你这里ingress的类型是agic\n kubernetes.io/ingress.class: azure/application-gateway\n name: backend-ingress\n namespace: default\nspec:\n rules:\n - host: api.demo.com\n http:\n paths:\n - backend:\n service:\n name: gateway-service\n port:\n number: 8080\n path: /gateway/\n pathType: Prefix\n - backend:\n service:\n name: backend-service\n port:\n number: 8080\n path: /backend/\n pathType: Prefix\n```\n\n# 总结\n\n由于微软云这块文档有部分缺失,导致在配置这块花了一点时间去排查,甚至开了工单。总结下来Ingress的配置主要是根据请求路径路由到对应的Service,重写规则集才是实际负责根据正则来进行匹配重写。","source":"_posts/AKS中重写规则踩坑小记录.md","raw":"---\ntitle: AKS中重写规则踩坑小记录\ndate: 2022-09-23 13:51:49\ntags:\n - Aliyun\n - Kubernetes\ncategories:\n - 后端\n - Cloud\n---\n\n# 前言\n\n最近在做标准产品在不同云平台中的部署验证,有幸体验了一下微软的Azure。负责采购的运维部门这次采用了`Application Gateway`来搭配`AKS`(`Azure Kubernetes Service`)对外暴露服务,正好借着这个机会来体验一下`Application Gateway`。\n\n# 应用场景\n\n1. 域名`api.demo.com`指向`Application Gateway`的IP地址\n2. 在`AKS`内部2个Service, `gateway-service`和`backend-service`分别需要通过`Application Gateway`对外暴露。\n3. `/gateway/`指向`gateway-service`, 然后`/backend/`指向`backend-service`。而且两个Service都没有context-path,所以需要做一个Rewrite重写URI到Service的根目录上。\n\n# 定义重写集\n\n打开`AKS`对应的应用程序网关`设置` > `重写`。选择`添加重写集`。在`1. 名称和关联`这个Tab上只需要填写名称这项即可(名称后面在做ingress时需要使用), `关联的传递规则`不需要选择。`2. 重写规则配置`里添加一个重写规则,然后填上重写规则的名称,并添加条件(默认新建重写规则时,只会生成操作,不会生成条件)\n\n## `条件`做如下设置\n\n- **要检查的变量类型** : `服务器变量`\n- **服务器变量**: `request_uri`\n- **区分大小写**: `否` \n- **运算符**: `等号(=)`\n- **要匹配的模式**: `/(gateway|backend)/?(.*)`\n\n## `操作`做如下设置\n\n- **重写类型**: `URL`\n- **操作类型**: `设置`\n- **组件**: `URL路径和URL查询字符串`\n- **URL路径值**: `/{var_request_uri_2}`\n- **重新计算路径映射**: `不选中`\n- **URL查询字符串值**: `留空不设值`\n\n## 特殊说明\n\n`操作`里的`URL路径值`不能使用正则表达式GROUP替换组,例如`$1`和`$2`之类的。Azure自己定义了一套对应的替换组命名规则。具体可以参考这个网页[使用应用程序网关重写 HTTP 标头和 URL](https://docs.azure.cn/zh-cn/application-gateway/rewrite-http-headers-url)。\n\n另外一个需要注意一点,如果在`条件`里选择了`服务器变量`的`request_uri`的时候,注意这个`request_uri`是完整的原始请求URI(携带了查询参数)。例如: 在请求`http://api.demo.com/gateway/search?foo=bar&hello=world`中,`request_uri`的值将为`/gateway/search?foo=bar&hello=world`。由于`request_uri`里包含了查询参数,所以在`操作`的`组件`中建议勾选`URL路径和URL查询字符串`。如果只选择`URL路径`的情况下可能出现无法预期的错误。以我们上述的配置来说明。\n\n对象URL: `http://api.demo.com/gateway/search?foo=bar&hello=world`\n\n**组件** | `URL路径和URL查询字符串` | `URL路径`\n--- | --- | ---\n**结果** | `/search?foo=bar&hello=world` | `/search?foo=bar&hello=world?foo=bar&hello=world`\n\n# `ACK`的Ingress设置\n\n当选择了`Application Gateway`作为对外暴露Service的方式时,Kubernetes集群里(`kube-system`命名空间里)多一个`Application Gateway Ingress Controller`(Azure工单时通常会简称为`agic`)的Deployment,所以对外暴露服务时可以像传统`nginx ingress controller`一样添加一个`Ingress`对象即可(甚至配置也和ngic大致相同,只是多了2个annotations)\n\n```yaml\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n annotations:\n # 这里指定重写规则集(不是重写规则的名字)\n appgw.ingress.kubernetes.io/rewrite-rule-set: rule-backend\n # 指定说明你这里ingress的类型是agic\n kubernetes.io/ingress.class: azure/application-gateway\n name: backend-ingress\n namespace: default\nspec:\n rules:\n - host: api.demo.com\n http:\n paths:\n - backend:\n service:\n name: gateway-service\n port:\n number: 8080\n path: /gateway/\n pathType: Prefix\n - backend:\n service:\n name: backend-service\n port:\n number: 8080\n path: /backend/\n pathType: Prefix\n```\n\n# 总结\n\n由于微软云这块文档有部分缺失,导致在配置这块花了一点时间去排查,甚至开了工单。总结下来Ingress的配置主要是根据请求路径路由到对应的Service,重写规则集才是实际负责根据正则来进行匹配重写。","slug":"AKS中重写规则踩坑小记录","published":1,"updated":"2023-07-04T12:26:10.184Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cljo9zkjn00014goi59hyfxy3","content":"

前言

最近在做标准产品在不同云平台中的部署验证,有幸体验了一下微软的Azure。负责采购的运维部门这次采用了Application Gateway来搭配AKS(Azure Kubernetes Service)对外暴露服务,正好借着这个机会来体验一下Application Gateway

\n

应用场景

    \n
  1. 域名api.demo.com指向Application Gateway的IP地址
  2. \n
  3. AKS内部2个Service, gateway-servicebackend-service分别需要通过Application Gateway对外暴露。
  4. \n
  5. /gateway/指向gateway-service, 然后/backend/指向backend-service。而且两个Service都没有context-path,所以需要做一个Rewrite重写URI到Service的根目录上。
  6. \n
\n

定义重写集

打开AKS对应的应用程序网关设置 > 重写。选择添加重写集。在1. 名称和关联这个Tab上只需要填写名称这项即可(名称后面在做ingress时需要使用), 关联的传递规则不需要选择。2. 重写规则配置里添加一个重写规则,然后填上重写规则的名称,并添加条件(默认新建重写规则时,只会生成操作,不会生成条件)

\n

条件做如下设置

\n

操作做如下设置

\n

特殊说明

操作里的URL路径值不能使用正则表达式GROUP替换组,例如$1$2之类的。Azure自己定义了一套对应的替换组命名规则。具体可以参考这个网页使用应用程序网关重写 HTTP 标头和 URL

\n

另外一个需要注意一点,如果在条件里选择了服务器变量request_uri的时候,注意这个request_uri是完整的原始请求URI(携带了查询参数)。例如: 在请求http://api.demo.com/gateway/search?foo=bar&hello=world中,request_uri的值将为/gateway/search?foo=bar&hello=world。由于request_uri里包含了查询参数,所以在操作组件中建议勾选URL路径和URL查询字符串。如果只选择URL路径的情况下可能出现无法预期的错误。以我们上述的配置来说明。

\n

对象URL: http://api.demo.com/gateway/search?foo=bar&hello=world

\n\n\n\n\n\n\n\n\n\n\n\n\n\n
组件URL路径和URL查询字符串URL路径
结果/search?foo=bar&hello=world/search?foo=bar&hello=world?foo=bar&hello=world
\n

ACK的Ingress设置

当选择了Application Gateway作为对外暴露Service的方式时,Kubernetes集群里(kube-system命名空间里)多一个Application Gateway Ingress Controller(Azure工单时通常会简称为agic)的Deployment,所以对外暴露服务时可以像传统nginx ingress controller一样添加一个Ingress对象即可(甚至配置也和ngic大致相同,只是多了2个annotations)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
# 这里指定重写规则集(不是重写规则的名字)
appgw.ingress.kubernetes.io/rewrite-rule-set: rule-backend
# 指定说明你这里ingress的类型是agic
kubernetes.io/ingress.class: azure/application-gateway
name: backend-ingress
namespace: default
spec:
rules:
- host: api.demo.com
http:
paths:
- backend:
service:
name: gateway-service
port:
number: 8080
path: /gateway/
pathType: Prefix
- backend:
service:
name: backend-service
port:
number: 8080
path: /backend/
pathType: Prefix
\n\n

总结

由于微软云这块文档有部分缺失,导致在配置这块花了一点时间去排查,甚至开了工单。总结下来Ingress的配置主要是根据请求路径路由到对应的Service,重写规则集才是实际负责根据正则来进行匹配重写。

\n","site":{"data":{}},"excerpt":"","more":"

前言

最近在做标准产品在不同云平台中的部署验证,有幸体验了一下微软的Azure。负责采购的运维部门这次采用了Application Gateway来搭配AKS(Azure Kubernetes Service)对外暴露服务,正好借着这个机会来体验一下Application Gateway

\n

应用场景

    \n
  1. 域名api.demo.com指向Application Gateway的IP地址
  2. \n
  3. AKS内部2个Service, gateway-servicebackend-service分别需要通过Application Gateway对外暴露。
  4. \n
  5. /gateway/指向gateway-service, 然后/backend/指向backend-service。而且两个Service都没有context-path,所以需要做一个Rewrite重写URI到Service的根目录上。
  6. \n
\n

定义重写集

打开AKS对应的应用程序网关设置 > 重写。选择添加重写集。在1. 名称和关联这个Tab上只需要填写名称这项即可(名称后面在做ingress时需要使用), 关联的传递规则不需要选择。2. 重写规则配置里添加一个重写规则,然后填上重写规则的名称,并添加条件(默认新建重写规则时,只会生成操作,不会生成条件)

\n

条件做如下设置

\n

操作做如下设置

\n

特殊说明

操作里的URL路径值不能使用正则表达式GROUP替换组,例如$1$2之类的。Azure自己定义了一套对应的替换组命名规则。具体可以参考这个网页使用应用程序网关重写 HTTP 标头和 URL

\n

另外一个需要注意一点,如果在条件里选择了服务器变量request_uri的时候,注意这个request_uri是完整的原始请求URI(携带了查询参数)。例如: 在请求http://api.demo.com/gateway/search?foo=bar&hello=world中,request_uri的值将为/gateway/search?foo=bar&hello=world。由于request_uri里包含了查询参数,所以在操作组件中建议勾选URL路径和URL查询字符串。如果只选择URL路径的情况下可能出现无法预期的错误。以我们上述的配置来说明。

\n

对象URL: http://api.demo.com/gateway/search?foo=bar&hello=world

\n\n\n\n\n\n\n\n\n\n\n\n\n\n
组件URL路径和URL查询字符串URL路径
结果/search?foo=bar&hello=world/search?foo=bar&hello=world?foo=bar&hello=world
\n

ACK的Ingress设置

当选择了Application Gateway作为对外暴露Service的方式时,Kubernetes集群里(kube-system命名空间里)多一个Application Gateway Ingress Controller(Azure工单时通常会简称为agic)的Deployment,所以对外暴露服务时可以像传统nginx ingress controller一样添加一个Ingress对象即可(甚至配置也和ngic大致相同,只是多了2个annotations)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
# 这里指定重写规则集(不是重写规则的名字)
appgw.ingress.kubernetes.io/rewrite-rule-set: rule-backend
# 指定说明你这里ingress的类型是agic
kubernetes.io/ingress.class: azure/application-gateway
name: backend-ingress
namespace: default
spec:
rules:
- host: api.demo.com
http:
paths:
- backend:
service:
name: gateway-service
port:
number: 8080
path: /gateway/
pathType: Prefix
- backend:
service:
name: backend-service
port:
number: 8080
path: /backend/
pathType: Prefix
\n\n

总结

由于微软云这块文档有部分缺失,导致在配置这块花了一点时间去排查,甚至开了工单。总结下来Ingress的配置主要是根据请求路径路由到对应的Service,重写规则集才是实际负责根据正则来进行匹配重写。

\n"},{"title":"Largrange项目架构与设计回顾 (二)","date":"2020-05-21T02:49:41.000Z","_content":"\n\n在[Largrange项目架构与设计回顾 (一)](/2020/05/09/lagrange项目回顾/) 里面我讲了一下项目开始架构和技术选型的一些内容。这一章来聊聊业务设计上的这点事。\n\n总体来说项目设计的时候,我们对业务模块的划分和拆解总体上来说都是遵循着高内聚,低耦合的原则来进行划分的。大部分和业务相关的服务,都还是能很好的进行功能划分的。但是有一些和具体业务关联性并不是很高的服务在界定与实现时出现了一些问题。\n\n## 任务调度服务\n\n由于平台方面会有一些控制命令下发给Android设备,而且下发的命令并不一定都是实时的,大部分都是指定一个时间来下发,所以在设计的时候就考虑到需要一个任务调度服务(以下略称`cron`),来处理这些下发指令以及未来可能会有的定时批处理任务的业务需求。技术选型的时候考虑到整个服务是构建在Kubernetes上的一个分布式的微服务架构,所以就没有选择Spring Scheduler,而是使用了Quarter来支撑整个`cron`。 \n\n从技术选型上来说`cron`没有什么问题,但是在设计如何使用`cron`上还是有点问题的, 我们先来看看已推送服务为例,整个平台中`cron`的处理流程是怎样的。\n\n1. `Platform` 调用 `cron`的创建定时任务Job的接口(接口的参数为推送时使用的相关参数)\n2. `cron` 创建Quartz的Job和Trigger,并将Job和Trigger的Name(Quartz中Job和Trigger的唯一标识符)返回个`Platform`\n3. `cron` 在指定时间触发推送的Job,即调用`Push`服务的推送接口\n4. `Push`的推送接口调用第三方服务商的推送服务,并将第三方推送服务的调用结果返回给`cron`\n5. `cron`通过调用`Platform`预留的推送服务回调地址将第三方推送服务调用接口返还给`Platform`\n6. `Platform` 将第三方推送服务的调用结果留档保存,并继续业务处理\n\n设计之初考虑到不想在`cron`中牵扯到具体的业务, 所以设计了一个`Platform`的回调接口来处理推送后的具体业务处理。虽然保证了`cron`尽量减少了和业务逻辑接触与数据库的访问。但是前前后后要访问集群内部的其他微服务2次,额外增加了网络开销,以及因为网络通信造成额外的通信失败风险。而且针对每种不同的定时任务,都需要额外开发一个QuartzJobBean,很难形成统一的QuartzJobBean来进行处理。今后如果还有相似项目还是需要重新考量一下如何设计一个更加完善的定时任务。\n\n## 总结\n\n暂时就想到了这些东西,今后想到啥还会继续在这里补存。\n","source":"_posts/Lagrange项目回顾2.md","raw":"---\ntitle: Largrange项目架构与设计回顾 (二)\ntags:\n - 架构\n - Kubernetes\n - Aliyun\n - Java\ncategories:\n - 设计\ndate: 2020-05-21 10:49:41\n---\n\n\n在[Largrange项目架构与设计回顾 (一)](/2020/05/09/lagrange项目回顾/) 里面我讲了一下项目开始架构和技术选型的一些内容。这一章来聊聊业务设计上的这点事。\n\n总体来说项目设计的时候,我们对业务模块的划分和拆解总体上来说都是遵循着高内聚,低耦合的原则来进行划分的。大部分和业务相关的服务,都还是能很好的进行功能划分的。但是有一些和具体业务关联性并不是很高的服务在界定与实现时出现了一些问题。\n\n## 任务调度服务\n\n由于平台方面会有一些控制命令下发给Android设备,而且下发的命令并不一定都是实时的,大部分都是指定一个时间来下发,所以在设计的时候就考虑到需要一个任务调度服务(以下略称`cron`),来处理这些下发指令以及未来可能会有的定时批处理任务的业务需求。技术选型的时候考虑到整个服务是构建在Kubernetes上的一个分布式的微服务架构,所以就没有选择Spring Scheduler,而是使用了Quarter来支撑整个`cron`。 \n\n从技术选型上来说`cron`没有什么问题,但是在设计如何使用`cron`上还是有点问题的, 我们先来看看已推送服务为例,整个平台中`cron`的处理流程是怎样的。\n\n1. `Platform` 调用 `cron`的创建定时任务Job的接口(接口的参数为推送时使用的相关参数)\n2. `cron` 创建Quartz的Job和Trigger,并将Job和Trigger的Name(Quartz中Job和Trigger的唯一标识符)返回个`Platform`\n3. `cron` 在指定时间触发推送的Job,即调用`Push`服务的推送接口\n4. `Push`的推送接口调用第三方服务商的推送服务,并将第三方推送服务的调用结果返回给`cron`\n5. `cron`通过调用`Platform`预留的推送服务回调地址将第三方推送服务调用接口返还给`Platform`\n6. `Platform` 将第三方推送服务的调用结果留档保存,并继续业务处理\n\n设计之初考虑到不想在`cron`中牵扯到具体的业务, 所以设计了一个`Platform`的回调接口来处理推送后的具体业务处理。虽然保证了`cron`尽量减少了和业务逻辑接触与数据库的访问。但是前前后后要访问集群内部的其他微服务2次,额外增加了网络开销,以及因为网络通信造成额外的通信失败风险。而且针对每种不同的定时任务,都需要额外开发一个QuartzJobBean,很难形成统一的QuartzJobBean来进行处理。今后如果还有相似项目还是需要重新考量一下如何设计一个更加完善的定时任务。\n\n## 总结\n\n暂时就想到了这些东西,今后想到啥还会继续在这里补存。\n","slug":"Lagrange项目回顾2","published":1,"updated":"2023-07-04T12:26:10.184Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cljo9zkjp00044goibj6kewct","content":"

Largrange项目架构与设计回顾 (一) 里面我讲了一下项目开始架构和技术选型的一些内容。这一章来聊聊业务设计上的这点事。

\n

总体来说项目设计的时候,我们对业务模块的划分和拆解总体上来说都是遵循着高内聚,低耦合的原则来进行划分的。大部分和业务相关的服务,都还是能很好的进行功能划分的。但是有一些和具体业务关联性并不是很高的服务在界定与实现时出现了一些问题。

\n

任务调度服务

由于平台方面会有一些控制命令下发给Android设备,而且下发的命令并不一定都是实时的,大部分都是指定一个时间来下发,所以在设计的时候就考虑到需要一个任务调度服务(以下略称cron),来处理这些下发指令以及未来可能会有的定时批处理任务的业务需求。技术选型的时候考虑到整个服务是构建在Kubernetes上的一个分布式的微服务架构,所以就没有选择Spring Scheduler,而是使用了Quarter来支撑整个cron

\n

从技术选型上来说cron没有什么问题,但是在设计如何使用cron上还是有点问题的, 我们先来看看已推送服务为例,整个平台中cron的处理流程是怎样的。

\n
    \n
  1. Platform 调用 cron的创建定时任务Job的接口(接口的参数为推送时使用的相关参数)
  2. \n
  3. cron 创建Quartz的Job和Trigger,并将Job和Trigger的Name(Quartz中Job和Trigger的唯一标识符)返回个Platform
  4. \n
  5. cron 在指定时间触发推送的Job,即调用Push服务的推送接口
  6. \n
  7. Push的推送接口调用第三方服务商的推送服务,并将第三方推送服务的调用结果返回给cron
  8. \n
  9. cron通过调用Platform预留的推送服务回调地址将第三方推送服务调用接口返还给Platform
  10. \n
  11. Platform 将第三方推送服务的调用结果留档保存,并继续业务处理
  12. \n
\n

设计之初考虑到不想在cron中牵扯到具体的业务, 所以设计了一个Platform的回调接口来处理推送后的具体业务处理。虽然保证了cron尽量减少了和业务逻辑接触与数据库的访问。但是前前后后要访问集群内部的其他微服务2次,额外增加了网络开销,以及因为网络通信造成额外的通信失败风险。而且针对每种不同的定时任务,都需要额外开发一个QuartzJobBean,很难形成统一的QuartzJobBean来进行处理。今后如果还有相似项目还是需要重新考量一下如何设计一个更加完善的定时任务。

\n

总结

暂时就想到了这些东西,今后想到啥还会继续在这里补存。

\n","site":{"data":{}},"excerpt":"","more":"

Largrange项目架构与设计回顾 (一) 里面我讲了一下项目开始架构和技术选型的一些内容。这一章来聊聊业务设计上的这点事。

\n

总体来说项目设计的时候,我们对业务模块的划分和拆解总体上来说都是遵循着高内聚,低耦合的原则来进行划分的。大部分和业务相关的服务,都还是能很好的进行功能划分的。但是有一些和具体业务关联性并不是很高的服务在界定与实现时出现了一些问题。

\n

任务调度服务

由于平台方面会有一些控制命令下发给Android设备,而且下发的命令并不一定都是实时的,大部分都是指定一个时间来下发,所以在设计的时候就考虑到需要一个任务调度服务(以下略称cron),来处理这些下发指令以及未来可能会有的定时批处理任务的业务需求。技术选型的时候考虑到整个服务是构建在Kubernetes上的一个分布式的微服务架构,所以就没有选择Spring Scheduler,而是使用了Quarter来支撑整个cron

\n

从技术选型上来说cron没有什么问题,但是在设计如何使用cron上还是有点问题的, 我们先来看看已推送服务为例,整个平台中cron的处理流程是怎样的。

\n
    \n
  1. Platform 调用 cron的创建定时任务Job的接口(接口的参数为推送时使用的相关参数)
  2. \n
  3. cron 创建Quartz的Job和Trigger,并将Job和Trigger的Name(Quartz中Job和Trigger的唯一标识符)返回个Platform
  4. \n
  5. cron 在指定时间触发推送的Job,即调用Push服务的推送接口
  6. \n
  7. Push的推送接口调用第三方服务商的推送服务,并将第三方推送服务的调用结果返回给cron
  8. \n
  9. cron通过调用Platform预留的推送服务回调地址将第三方推送服务调用接口返还给Platform
  10. \n
  11. Platform 将第三方推送服务的调用结果留档保存,并继续业务处理
  12. \n
\n

设计之初考虑到不想在cron中牵扯到具体的业务, 所以设计了一个Platform的回调接口来处理推送后的具体业务处理。虽然保证了cron尽量减少了和业务逻辑接触与数据库的访问。但是前前后后要访问集群内部的其他微服务2次,额外增加了网络开销,以及因为网络通信造成额外的通信失败风险。而且针对每种不同的定时任务,都需要额外开发一个QuartzJobBean,很难形成统一的QuartzJobBean来进行处理。今后如果还有相似项目还是需要重新考量一下如何设计一个更加完善的定时任务。

\n

总结

暂时就想到了这些东西,今后想到啥还会继续在这里补存。

\n"},{"title":"Spring Cloud Kubernetes环境下使用Jasypt","date":"2021-09-29T03:28:11.000Z","_content":"\n\n# 前言\n\n最近半年着手开始做了基于微服务的中台项目,整个项目的技术栈采用的是`Java` + `Spring Cloud` + `Kubernetes` + `Istio`。\n\n业务开放上还是相当顺利的。但是在安全审核上,运维组提出了一个简易。现在项目一些敏感配置,例如MySQL用户的密码,Redis的密码等现在都是明文保存在Kubernetes的ConfigMap中的(是的,我们并没有Nacos作为微服务的配置中心)。这样可能存在安全隐患。\n\n# 首次尝试\n\n既然有问题,那就解决问题。要给配置文件中的属性项目加密很简单,稍微Google一下,就有现成的方案了。\n\n现在比较常用的解决方案就是集成`Jasypt`,然后通过`jasypt-spring-boot-starter`来融合进Spring。\n\n### POM包加入`jasypt-spring-boot-starter`\n\n```xml\n\n com.github.ulisesbocchio\n jasypt-spring-boot-starter\n 3.0.4\n\n```\n\n### Dockerfile中增加java参数\n\n```\n...\nENTRYPOINT [\"sh\",\"-c\",\"java $JAVA_OPTS -jar app.jar --jasypt.encryptor.password=helloworld $PARAMS\"]\n```\n\n### 在ConfigMap中添加加密属性\n\n```yaml\napiVersion: v1\nkind: ConfigMap\nmetadata:\n name: demo\ndata:\n application.yaml: |-\n test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)\n```\n\n### 利用`actuator`接口测试\n\n在`management.endpoints.web.exposure.include`属性中增加`env`,这样我们就可以通过调用`/actuator/env`来查看一下`env`接口返回的整个Spring 容器中所有的PropertySource。\n\n```json\n{\n ...\n \"propertySources\": [\n {\n \"name\": \"bootstrapProperties-configmap.demo.default\",\n \"properties\": {\n \"test2\": {\n \"value\": \"Hello,world\"\n }\n }\n }\n ...\n ]\n}\n```\n\nOK, 这下配置项已经加密了。问题解决了。\n\n但是...\n\n## 新的问题\n\n自从项目集成了`Jayspt`以后,出现了一个奇怪的问题。每次项目试图通过修改ConfigMap的配置文件,然后试图通过`spring-cloud-starter-kubernetes-fabric8-config`来做自动Reload,都失败了。然而查阅应用日志,并没有出现任何异常。无奈只能打开`spring-cloud`和`jasypt-spring-boot`的`DEBUG`日志。\n\n进过几天对日志和两边源代码的分析。终于找到了原因\n\n### 原因\n\n在Spring Boot启动时`jasypt-spring-boot`会将下面6种配置(并不仅限与这6种配置文件)\n\n- `Classpath`下的`application.yaml`\n- `Classpath`下的`bootstrap.yaml`\n- 集群里名称为`${spring.cloud.kubernetes.config.name}`的ConfigMap\n- 集群里名称为`${spring.cloud.kubernetes.config.name}-kubernetes`的ConfigMap\n- Java启动参数\n- 环境变量\n\n转换成`jasypt-spring-boot`自己的PropertySource实现类`EncryptableMapPropertySourceWrapper`。\n\n但是如果使用Kubernetes的ConfigMap来作微服务配置中心的时候,Spring Cloud会在`ConfigurationChangeDetector`中查找配置类`org.springframework.cloud.bootstrap.config.BootstrapPropertySource`, 并依据`BootstrapPropertySource`的类型来判断容器内的配置与集群中ConfigMap里的配置是否有差异,来触发配置reload。\n\n由于`jasypt-spring-boot`已经将所有的配置文件转型成了`EncryptableMapPropertySourceWrapper`, 所以`ConfigurationChangeDetector`无法找到`BootstrapPropertySource`所以会一直任务ConfigMap的里的配置没有变化,导致整个Reload失效(无论是使用polling还是event方式)\n\n### 解决问题\n\n为了保证ConfigMap变化后自动Reload的功能,所以`jasypt-spring-boot`不能把`BootstrapPropertySource`转换成`EncryptableMapPropertySourceWrapper`\n\n所以我们需要设置`jasypt.encryptor.skip-property-sources`配置项, Classpath中的application.yaml需要增加配置\n\n```yaml\njasypt:\n encryptor:\n skip-property-sources: org.springframework.cloud.bootstrap.config.BootstrapPropertySource\n```\n\n`skip-property-sources`配置项配置后,加密项目就不能配置在ConfigMap里了,毕竟已经被我们忽略了。那么我们只能另外找一个PropertySource来存放加密项目了。\n\n`Classpath`中的两个Yaml由于编译时会被Maven打包进Jar文件,会牵涉多个CI/CD多个流程显然不合适,启动参数配置项的也要影响到Docker镜像制作这个流程。所以判断下来最适合的PropertySource就是环境变量了。\n\n### 环境变量增加加密项\n\n在Kubernetes的部署Yaml中,添加加密数据项`application.test.str`\n\n```yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n labels:\n app: demo\n name: demo\nspec:\n replicas: 1\n selector:\n matchLabels:\n app: demo\n template:\n metadata:\n labels:\n app: demo\n spec:\n containers:\n - env:\n - name: TZ\n value: Asia/Shanghai\n - name: application.test.str\n value: >-\n ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)\n ....\n```\n\n如果需要更加严密的加密方针的话,我们可以把环境变量的内容放进Kubernetes的Secrets中。\n\n### 在ConfigMap中引用`application.test.str`\n\n```yaml\napiVersion: v1\nkind: ConfigMap\nmetadata:\n name: demo\ndata:\n application.yaml: |-\n test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)\n test3: ${application.test.str}\n```\n\n### 通过`actuator`接口来测试\n\n通过`actuator\\env`接口来测试一下\n\n```json\n{\n ...\n \"propertySources\": [\n {\n \"name\": \"bootstrapProperties-configmap.demo.default\",\n \"properties\": {\n \"test2\": {\n \"value\": \"ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)\"\n },\n \"test3\": {\n \"value\": \"Hello,world\"\n }\n }\n }\n ...\n ]\n}\n```\n\n这样ConfigMap中的配置项`test3`就可以通过环境变量引用并使用加密配置项了。同时修改ConfigMap依然可以触发auto reload了。这下终于算是解决了。","source":"_posts/Spring-Cloud-Kubernetes环境下使用Jasypt.md","raw":"---\ntitle: Spring Cloud Kubernetes环境下使用Jasypt\ntags:\n - Java\n - Spring\ncategories:\n - 后端\ndate: 2021-09-29 11:28:11\n---\n\n\n# 前言\n\n最近半年着手开始做了基于微服务的中台项目,整个项目的技术栈采用的是`Java` + `Spring Cloud` + `Kubernetes` + `Istio`。\n\n业务开放上还是相当顺利的。但是在安全审核上,运维组提出了一个简易。现在项目一些敏感配置,例如MySQL用户的密码,Redis的密码等现在都是明文保存在Kubernetes的ConfigMap中的(是的,我们并没有Nacos作为微服务的配置中心)。这样可能存在安全隐患。\n\n# 首次尝试\n\n既然有问题,那就解决问题。要给配置文件中的属性项目加密很简单,稍微Google一下,就有现成的方案了。\n\n现在比较常用的解决方案就是集成`Jasypt`,然后通过`jasypt-spring-boot-starter`来融合进Spring。\n\n### POM包加入`jasypt-spring-boot-starter`\n\n```xml\n\n com.github.ulisesbocchio\n jasypt-spring-boot-starter\n 3.0.4\n\n```\n\n### Dockerfile中增加java参数\n\n```\n...\nENTRYPOINT [\"sh\",\"-c\",\"java $JAVA_OPTS -jar app.jar --jasypt.encryptor.password=helloworld $PARAMS\"]\n```\n\n### 在ConfigMap中添加加密属性\n\n```yaml\napiVersion: v1\nkind: ConfigMap\nmetadata:\n name: demo\ndata:\n application.yaml: |-\n test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)\n```\n\n### 利用`actuator`接口测试\n\n在`management.endpoints.web.exposure.include`属性中增加`env`,这样我们就可以通过调用`/actuator/env`来查看一下`env`接口返回的整个Spring 容器中所有的PropertySource。\n\n```json\n{\n ...\n \"propertySources\": [\n {\n \"name\": \"bootstrapProperties-configmap.demo.default\",\n \"properties\": {\n \"test2\": {\n \"value\": \"Hello,world\"\n }\n }\n }\n ...\n ]\n}\n```\n\nOK, 这下配置项已经加密了。问题解决了。\n\n但是...\n\n## 新的问题\n\n自从项目集成了`Jayspt`以后,出现了一个奇怪的问题。每次项目试图通过修改ConfigMap的配置文件,然后试图通过`spring-cloud-starter-kubernetes-fabric8-config`来做自动Reload,都失败了。然而查阅应用日志,并没有出现任何异常。无奈只能打开`spring-cloud`和`jasypt-spring-boot`的`DEBUG`日志。\n\n进过几天对日志和两边源代码的分析。终于找到了原因\n\n### 原因\n\n在Spring Boot启动时`jasypt-spring-boot`会将下面6种配置(并不仅限与这6种配置文件)\n\n- `Classpath`下的`application.yaml`\n- `Classpath`下的`bootstrap.yaml`\n- 集群里名称为`${spring.cloud.kubernetes.config.name}`的ConfigMap\n- 集群里名称为`${spring.cloud.kubernetes.config.name}-kubernetes`的ConfigMap\n- Java启动参数\n- 环境变量\n\n转换成`jasypt-spring-boot`自己的PropertySource实现类`EncryptableMapPropertySourceWrapper`。\n\n但是如果使用Kubernetes的ConfigMap来作微服务配置中心的时候,Spring Cloud会在`ConfigurationChangeDetector`中查找配置类`org.springframework.cloud.bootstrap.config.BootstrapPropertySource`, 并依据`BootstrapPropertySource`的类型来判断容器内的配置与集群中ConfigMap里的配置是否有差异,来触发配置reload。\n\n由于`jasypt-spring-boot`已经将所有的配置文件转型成了`EncryptableMapPropertySourceWrapper`, 所以`ConfigurationChangeDetector`无法找到`BootstrapPropertySource`所以会一直任务ConfigMap的里的配置没有变化,导致整个Reload失效(无论是使用polling还是event方式)\n\n### 解决问题\n\n为了保证ConfigMap变化后自动Reload的功能,所以`jasypt-spring-boot`不能把`BootstrapPropertySource`转换成`EncryptableMapPropertySourceWrapper`\n\n所以我们需要设置`jasypt.encryptor.skip-property-sources`配置项, Classpath中的application.yaml需要增加配置\n\n```yaml\njasypt:\n encryptor:\n skip-property-sources: org.springframework.cloud.bootstrap.config.BootstrapPropertySource\n```\n\n`skip-property-sources`配置项配置后,加密项目就不能配置在ConfigMap里了,毕竟已经被我们忽略了。那么我们只能另外找一个PropertySource来存放加密项目了。\n\n`Classpath`中的两个Yaml由于编译时会被Maven打包进Jar文件,会牵涉多个CI/CD多个流程显然不合适,启动参数配置项的也要影响到Docker镜像制作这个流程。所以判断下来最适合的PropertySource就是环境变量了。\n\n### 环境变量增加加密项\n\n在Kubernetes的部署Yaml中,添加加密数据项`application.test.str`\n\n```yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n labels:\n app: demo\n name: demo\nspec:\n replicas: 1\n selector:\n matchLabels:\n app: demo\n template:\n metadata:\n labels:\n app: demo\n spec:\n containers:\n - env:\n - name: TZ\n value: Asia/Shanghai\n - name: application.test.str\n value: >-\n ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)\n ....\n```\n\n如果需要更加严密的加密方针的话,我们可以把环境变量的内容放进Kubernetes的Secrets中。\n\n### 在ConfigMap中引用`application.test.str`\n\n```yaml\napiVersion: v1\nkind: ConfigMap\nmetadata:\n name: demo\ndata:\n application.yaml: |-\n test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)\n test3: ${application.test.str}\n```\n\n### 通过`actuator`接口来测试\n\n通过`actuator\\env`接口来测试一下\n\n```json\n{\n ...\n \"propertySources\": [\n {\n \"name\": \"bootstrapProperties-configmap.demo.default\",\n \"properties\": {\n \"test2\": {\n \"value\": \"ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)\"\n },\n \"test3\": {\n \"value\": \"Hello,world\"\n }\n }\n }\n ...\n ]\n}\n```\n\n这样ConfigMap中的配置项`test3`就可以通过环境变量引用并使用加密配置项了。同时修改ConfigMap依然可以触发auto reload了。这下终于算是解决了。","slug":"Spring-Cloud-Kubernetes环境下使用Jasypt","published":1,"updated":"2023-07-04T12:26:10.184Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cljo9zkjp00054goi3n9a45hn","content":"

前言

最近半年着手开始做了基于微服务的中台项目,整个项目的技术栈采用的是Java + Spring Cloud + Kubernetes + Istio

\n

业务开放上还是相当顺利的。但是在安全审核上,运维组提出了一个简易。现在项目一些敏感配置,例如MySQL用户的密码,Redis的密码等现在都是明文保存在Kubernetes的ConfigMap中的(是的,我们并没有Nacos作为微服务的配置中心)。这样可能存在安全隐患。

\n

首次尝试

既然有问题,那就解决问题。要给配置文件中的属性项目加密很简单,稍微Google一下,就有现成的方案了。

\n

现在比较常用的解决方案就是集成Jasypt,然后通过jasypt-spring-boot-starter来融合进Spring。

\n

POM包加入jasypt-spring-boot-starter

1
2
3
4
5
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
\n\n

Dockerfile中增加java参数

1
2
...
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar app.jar --jasypt.encryptor.password=helloworld $PARAMS"]
\n\n

在ConfigMap中添加加密属性

1
2
3
4
5
6
7
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
data:
application.yaml: |-
test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
\n\n

利用actuator接口测试

management.endpoints.web.exposure.include属性中增加env,这样我们就可以通过调用/actuator/env来查看一下env接口返回的整个Spring 容器中所有的PropertySource。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
...
"propertySources": [
{
"name": "bootstrapProperties-configmap.demo.default",
"properties": {
"test2": {
"value": "Hello,world"
}
}
}
...
]
}
\n\n

OK, 这下配置项已经加密了。问题解决了。

\n

但是…

\n

新的问题

自从项目集成了Jayspt以后,出现了一个奇怪的问题。每次项目试图通过修改ConfigMap的配置文件,然后试图通过spring-cloud-starter-kubernetes-fabric8-config来做自动Reload,都失败了。然而查阅应用日志,并没有出现任何异常。无奈只能打开spring-cloudjasypt-spring-bootDEBUG日志。

\n

进过几天对日志和两边源代码的分析。终于找到了原因

\n

原因

在Spring Boot启动时jasypt-spring-boot会将下面6种配置(并不仅限与这6种配置文件)

\n\n

转换成jasypt-spring-boot自己的PropertySource实现类EncryptableMapPropertySourceWrapper

\n

但是如果使用Kubernetes的ConfigMap来作微服务配置中心的时候,Spring Cloud会在ConfigurationChangeDetector中查找配置类org.springframework.cloud.bootstrap.config.BootstrapPropertySource, 并依据BootstrapPropertySource的类型来判断容器内的配置与集群中ConfigMap里的配置是否有差异,来触发配置reload。

\n

由于jasypt-spring-boot已经将所有的配置文件转型成了EncryptableMapPropertySourceWrapper, 所以ConfigurationChangeDetector无法找到BootstrapPropertySource所以会一直任务ConfigMap的里的配置没有变化,导致整个Reload失效(无论是使用polling还是event方式)

\n

解决问题

为了保证ConfigMap变化后自动Reload的功能,所以jasypt-spring-boot不能把BootstrapPropertySource转换成EncryptableMapPropertySourceWrapper

\n

所以我们需要设置jasypt.encryptor.skip-property-sources配置项, Classpath中的application.yaml需要增加配置

\n
1
2
3
jasypt:
encryptor:
skip-property-sources: org.springframework.cloud.bootstrap.config.BootstrapPropertySource
\n\n

skip-property-sources配置项配置后,加密项目就不能配置在ConfigMap里了,毕竟已经被我们忽略了。那么我们只能另外找一个PropertySource来存放加密项目了。

\n

Classpath中的两个Yaml由于编译时会被Maven打包进Jar文件,会牵涉多个CI/CD多个流程显然不合适,启动参数配置项的也要影响到Docker镜像制作这个流程。所以判断下来最适合的PropertySource就是环境变量了。

\n

环境变量增加加密项

在Kubernetes的部署Yaml中,添加加密数据项application.test.str

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: demo
name: demo
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- env:
- name: TZ
value: Asia/Shanghai
- name: application.test.str
value: >-
ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
....
\n\n

如果需要更加严密的加密方针的话,我们可以把环境变量的内容放进Kubernetes的Secrets中。

\n

在ConfigMap中引用application.test.str

1
2
3
4
5
6
7
8
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
data:
application.yaml: |-
test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
test3: ${application.test.str}
\n\n

通过actuator接口来测试

通过actuator\\env接口来测试一下

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
...
"propertySources": [
{
"name": "bootstrapProperties-configmap.demo.default",
"properties": {
"test2": {
"value": "ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)"
},
"test3": {
"value": "Hello,world"
}
}
}
...
]
}
\n\n

这样ConfigMap中的配置项test3就可以通过环境变量引用并使用加密配置项了。同时修改ConfigMap依然可以触发auto reload了。这下终于算是解决了。

\n","site":{"data":{}},"excerpt":"","more":"

前言

最近半年着手开始做了基于微服务的中台项目,整个项目的技术栈采用的是Java + Spring Cloud + Kubernetes + Istio

\n

业务开放上还是相当顺利的。但是在安全审核上,运维组提出了一个简易。现在项目一些敏感配置,例如MySQL用户的密码,Redis的密码等现在都是明文保存在Kubernetes的ConfigMap中的(是的,我们并没有Nacos作为微服务的配置中心)。这样可能存在安全隐患。

\n

首次尝试

既然有问题,那就解决问题。要给配置文件中的属性项目加密很简单,稍微Google一下,就有现成的方案了。

\n

现在比较常用的解决方案就是集成Jasypt,然后通过jasypt-spring-boot-starter来融合进Spring。

\n

POM包加入jasypt-spring-boot-starter

1
2
3
4
5
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
\n\n

Dockerfile中增加java参数

1
2
...
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar app.jar --jasypt.encryptor.password=helloworld $PARAMS"]
\n\n

在ConfigMap中添加加密属性

1
2
3
4
5
6
7
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
data:
application.yaml: |-
test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
\n\n

利用actuator接口测试

management.endpoints.web.exposure.include属性中增加env,这样我们就可以通过调用/actuator/env来查看一下env接口返回的整个Spring 容器中所有的PropertySource。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
...
"propertySources": [
{
"name": "bootstrapProperties-configmap.demo.default",
"properties": {
"test2": {
"value": "Hello,world"
}
}
}
...
]
}
\n\n

OK, 这下配置项已经加密了。问题解决了。

\n

但是…

\n

新的问题

自从项目集成了Jayspt以后,出现了一个奇怪的问题。每次项目试图通过修改ConfigMap的配置文件,然后试图通过spring-cloud-starter-kubernetes-fabric8-config来做自动Reload,都失败了。然而查阅应用日志,并没有出现任何异常。无奈只能打开spring-cloudjasypt-spring-bootDEBUG日志。

\n

进过几天对日志和两边源代码的分析。终于找到了原因

\n

原因

在Spring Boot启动时jasypt-spring-boot会将下面6种配置(并不仅限与这6种配置文件)

\n\n

转换成jasypt-spring-boot自己的PropertySource实现类EncryptableMapPropertySourceWrapper

\n

但是如果使用Kubernetes的ConfigMap来作微服务配置中心的时候,Spring Cloud会在ConfigurationChangeDetector中查找配置类org.springframework.cloud.bootstrap.config.BootstrapPropertySource, 并依据BootstrapPropertySource的类型来判断容器内的配置与集群中ConfigMap里的配置是否有差异,来触发配置reload。

\n

由于jasypt-spring-boot已经将所有的配置文件转型成了EncryptableMapPropertySourceWrapper, 所以ConfigurationChangeDetector无法找到BootstrapPropertySource所以会一直任务ConfigMap的里的配置没有变化,导致整个Reload失效(无论是使用polling还是event方式)

\n

解决问题

为了保证ConfigMap变化后自动Reload的功能,所以jasypt-spring-boot不能把BootstrapPropertySource转换成EncryptableMapPropertySourceWrapper

\n

所以我们需要设置jasypt.encryptor.skip-property-sources配置项, Classpath中的application.yaml需要增加配置

\n
1
2
3
jasypt:
encryptor:
skip-property-sources: org.springframework.cloud.bootstrap.config.BootstrapPropertySource
\n\n

skip-property-sources配置项配置后,加密项目就不能配置在ConfigMap里了,毕竟已经被我们忽略了。那么我们只能另外找一个PropertySource来存放加密项目了。

\n

Classpath中的两个Yaml由于编译时会被Maven打包进Jar文件,会牵涉多个CI/CD多个流程显然不合适,启动参数配置项的也要影响到Docker镜像制作这个流程。所以判断下来最适合的PropertySource就是环境变量了。

\n

环境变量增加加密项

在Kubernetes的部署Yaml中,添加加密数据项application.test.str

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: demo
name: demo
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- env:
- name: TZ
value: Asia/Shanghai
- name: application.test.str
value: >-
ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
....
\n\n

如果需要更加严密的加密方针的话,我们可以把环境变量的内容放进Kubernetes的Secrets中。

\n

在ConfigMap中引用application.test.str

1
2
3
4
5
6
7
8
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
data:
application.yaml: |-
test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
test3: ${application.test.str}
\n\n

通过actuator接口来测试

通过actuator\\env接口来测试一下

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
...
"propertySources": [
{
"name": "bootstrapProperties-configmap.demo.default",
"properties": {
"test2": {
"value": "ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)"
},
"test3": {
"value": "Hello,world"
}
}
}
...
]
}
\n\n

这样ConfigMap中的配置项test3就可以通过环境变量引用并使用加密配置项了。同时修改ConfigMap依然可以触发auto reload了。这下终于算是解决了。

\n"},{"title":"Largrange项目架构与设计回顾 (一)","date":"2020-05-09T02:34:47.000Z","_content":"\n\n> 项目背景\n\n从去年年底开始一个老客户希望在他们的一个传统的机械设备(后面略称 `E机关` )上外装一个Android设备。 Android设备和 `E机关` 之间通过串口或是RJ45接口进行数据交互,主要是Android设备获取 `E机关` 内部的数据,并不会通过接口来控制 `E机关` 。 Android设备则通过4G 来和平台交互,上报Android设备的状态数据,同时接受平台的控制。以此来实现让传统机械设备也能拥抱物联网的概念。\n\n最后围绕着客户的需求分成了3个项目来并行推进\n\n1. Android设备的硬件设备的设计、选材、样机制作与量产规划\n2. 在Android设备上,进行与`E机关`以及平台进行交互的APP开发\n3. 用于Android设备交互的平台的架构、设计与开发\n\n我们平台Team就负责 `3.用于Android设备交互的平台` 并命名Lagrange (拉格朗日) 。\n\n# 设计&架构\n\n整个平台这块不仅需要向Android设备的APP提供数据交互接口,还需要有一个供相关运营人员使用的前端Web应用,以及与云平台(主要是Aliyun) 交互的功能。所以考虑到多方面使用微服务的架构来实现整个平台端。\n由于`E机关`的工作工况不能保证长期较的稳定的连接到4G网络,所以我们并不考虑使用Socket长连接的方式来做APP和平台之间的数据交互,要实现平台对Android设备进行反控的话,只能实现推送方式来把控制命令下发给Android设备,所以还需要有一个第三方推送服务商交互的服务。同时控制推送也有实时和非实时以及定期推送的需求,所以可能还需要一个任务调度的服务。\n\n根据对上述业务进行梳理,我们将项目分成几个服务\n\n- 提供Android设备的交互接口的 `App Service`\n- 提供运营人员使用前端Web应用 `Platform Web Service`\n- 前端Web应用使用到的一些接口 `Platform Service`\n- 负责第三方推送服务商交互的 `Push Service`\n- 提供云平台鉴权用的 `Auth Service`\n- 用来管理任务调度的 `Cron Service`\n\n由于前一个项目实施的时候没有使用容器部署,每当访问量峰值的时候,我们这些码农兼运维就各种加班,所以这次项目决定直接将服务容器化,同时选择了`Kubernetes`来管理容器。由于客观原因线上的`Kubernetes`直接购买了Aliyun的托管版`Kubernetes`服务。 内部的开发测试环境则使用了 `Rancher 2.0` 来构建`Kubernetes`集群。\n\n\n# 技术选型\n\n## a. 后端服务选型\n\n由于不考虑长连接的原因,所以在后端服务在技术选型上基本就不考虑Netty了,直接上Spring大礼包。\n\n- Spring Boot 开发接口\n- Spring Data 配合 JPA 来进行数据的持久化\n- Spring Cloud Kubenetes 来做数据的Config的autoreload\n- Quartz 负责处理任务调度\n\n服务之间调用都使用HTTP服务, 服务发现也有`Kubernetes`的DNS机制支持。服务网格则选择了比较成熟的`Istio`,主要还是Aliyun的`Kubernetes`可以集成`Istio`,部署和使用都相当方便。\n\n## b. 前端Web应用选型\n\n因为前端Web应用主要是给运营人员使用,所以我们考虑使用Single Page Application来做个前后端分离的Web应用。框架这块由于团队成员基本上没有什么前端开发经验,基本都是后台写Java的码农,所以框架选择有点随性,直接就点名了vue.js。前端控件库则用的是阿里系的Antd。\n\n## c. 数据持久层\n\n主数据库选择了mysql,缓存用的redis。这些都是团队比较熟悉的。由于一些特殊的业务需求和使用场景,我们还加了一个mongodb来做为一个副数据库,主要存放一些特殊业务使用的数据。\n\n## d. DevOps\n\n用了相当传统的GitLab CE 加上Jenkins的组合,实现前后端代码的自动编译,推送到私有的镜像仓库(使用Aliyun的镜像仓库服务)\n\n# 最后\n\n以上基本是项目最早做设计时候的各种考量。暂时先写这么多,过两天再回顾一下当初设计上有哪些觉得不足的地方。\n\n","source":"_posts/lagrange项目回顾.md","raw":"---\ntitle: Largrange项目架构与设计回顾 (一)\ntags:\n - 架构\n - Kubernetes\n - Aliyun\n - Java\ncategories:\n - 设计\ndate: 2020-05-09 10:34:47\n---\n\n\n> 项目背景\n\n从去年年底开始一个老客户希望在他们的一个传统的机械设备(后面略称 `E机关` )上外装一个Android设备。 Android设备和 `E机关` 之间通过串口或是RJ45接口进行数据交互,主要是Android设备获取 `E机关` 内部的数据,并不会通过接口来控制 `E机关` 。 Android设备则通过4G 来和平台交互,上报Android设备的状态数据,同时接受平台的控制。以此来实现让传统机械设备也能拥抱物联网的概念。\n\n最后围绕着客户的需求分成了3个项目来并行推进\n\n1. Android设备的硬件设备的设计、选材、样机制作与量产规划\n2. 在Android设备上,进行与`E机关`以及平台进行交互的APP开发\n3. 用于Android设备交互的平台的架构、设计与开发\n\n我们平台Team就负责 `3.用于Android设备交互的平台` 并命名Lagrange (拉格朗日) 。\n\n# 设计&架构\n\n整个平台这块不仅需要向Android设备的APP提供数据交互接口,还需要有一个供相关运营人员使用的前端Web应用,以及与云平台(主要是Aliyun) 交互的功能。所以考虑到多方面使用微服务的架构来实现整个平台端。\n由于`E机关`的工作工况不能保证长期较的稳定的连接到4G网络,所以我们并不考虑使用Socket长连接的方式来做APP和平台之间的数据交互,要实现平台对Android设备进行反控的话,只能实现推送方式来把控制命令下发给Android设备,所以还需要有一个第三方推送服务商交互的服务。同时控制推送也有实时和非实时以及定期推送的需求,所以可能还需要一个任务调度的服务。\n\n根据对上述业务进行梳理,我们将项目分成几个服务\n\n- 提供Android设备的交互接口的 `App Service`\n- 提供运营人员使用前端Web应用 `Platform Web Service`\n- 前端Web应用使用到的一些接口 `Platform Service`\n- 负责第三方推送服务商交互的 `Push Service`\n- 提供云平台鉴权用的 `Auth Service`\n- 用来管理任务调度的 `Cron Service`\n\n由于前一个项目实施的时候没有使用容器部署,每当访问量峰值的时候,我们这些码农兼运维就各种加班,所以这次项目决定直接将服务容器化,同时选择了`Kubernetes`来管理容器。由于客观原因线上的`Kubernetes`直接购买了Aliyun的托管版`Kubernetes`服务。 内部的开发测试环境则使用了 `Rancher 2.0` 来构建`Kubernetes`集群。\n\n\n# 技术选型\n\n## a. 后端服务选型\n\n由于不考虑长连接的原因,所以在后端服务在技术选型上基本就不考虑Netty了,直接上Spring大礼包。\n\n- Spring Boot 开发接口\n- Spring Data 配合 JPA 来进行数据的持久化\n- Spring Cloud Kubenetes 来做数据的Config的autoreload\n- Quartz 负责处理任务调度\n\n服务之间调用都使用HTTP服务, 服务发现也有`Kubernetes`的DNS机制支持。服务网格则选择了比较成熟的`Istio`,主要还是Aliyun的`Kubernetes`可以集成`Istio`,部署和使用都相当方便。\n\n## b. 前端Web应用选型\n\n因为前端Web应用主要是给运营人员使用,所以我们考虑使用Single Page Application来做个前后端分离的Web应用。框架这块由于团队成员基本上没有什么前端开发经验,基本都是后台写Java的码农,所以框架选择有点随性,直接就点名了vue.js。前端控件库则用的是阿里系的Antd。\n\n## c. 数据持久层\n\n主数据库选择了mysql,缓存用的redis。这些都是团队比较熟悉的。由于一些特殊的业务需求和使用场景,我们还加了一个mongodb来做为一个副数据库,主要存放一些特殊业务使用的数据。\n\n## d. DevOps\n\n用了相当传统的GitLab CE 加上Jenkins的组合,实现前后端代码的自动编译,推送到私有的镜像仓库(使用Aliyun的镜像仓库服务)\n\n# 最后\n\n以上基本是项目最早做设计时候的各种考量。暂时先写这么多,过两天再回顾一下当初设计上有哪些觉得不足的地方。\n\n","slug":"lagrange项目回顾","published":1,"updated":"2023-07-04T12:26:10.184Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cljo9zkjq00064goihyn69kzz","content":"
\n

项目背景

\n
\n

从去年年底开始一个老客户希望在他们的一个传统的机械设备(后面略称 E机关 )上外装一个Android设备。 Android设备和 E机关 之间通过串口或是RJ45接口进行数据交互,主要是Android设备获取 E机关 内部的数据,并不会通过接口来控制 E机关 。 Android设备则通过4G 来和平台交互,上报Android设备的状态数据,同时接受平台的控制。以此来实现让传统机械设备也能拥抱物联网的概念。

\n

最后围绕着客户的需求分成了3个项目来并行推进

\n
    \n
  1. Android设备的硬件设备的设计、选材、样机制作与量产规划
  2. \n
  3. 在Android设备上,进行与E机关以及平台进行交互的APP开发
  4. \n
  5. 用于Android设备交互的平台的架构、设计与开发
  6. \n
\n

我们平台Team就负责 3.用于Android设备交互的平台 并命名Lagrange (拉格朗日) 。

\n

设计&架构

整个平台这块不仅需要向Android设备的APP提供数据交互接口,还需要有一个供相关运营人员使用的前端Web应用,以及与云平台(主要是Aliyun) 交互的功能。所以考虑到多方面使用微服务的架构来实现整个平台端。
由于E机关的工作工况不能保证长期较的稳定的连接到4G网络,所以我们并不考虑使用Socket长连接的方式来做APP和平台之间的数据交互,要实现平台对Android设备进行反控的话,只能实现推送方式来把控制命令下发给Android设备,所以还需要有一个第三方推送服务商交互的服务。同时控制推送也有实时和非实时以及定期推送的需求,所以可能还需要一个任务调度的服务。

\n

根据对上述业务进行梳理,我们将项目分成几个服务

\n\n

由于前一个项目实施的时候没有使用容器部署,每当访问量峰值的时候,我们这些码农兼运维就各种加班,所以这次项目决定直接将服务容器化,同时选择了Kubernetes来管理容器。由于客观原因线上的Kubernetes直接购买了Aliyun的托管版Kubernetes服务。 内部的开发测试环境则使用了 Rancher 2.0 来构建Kubernetes集群。

\n

技术选型

a. 后端服务选型

由于不考虑长连接的原因,所以在后端服务在技术选型上基本就不考虑Netty了,直接上Spring大礼包。

\n\n

服务之间调用都使用HTTP服务, 服务发现也有Kubernetes的DNS机制支持。服务网格则选择了比较成熟的Istio,主要还是Aliyun的Kubernetes可以集成Istio,部署和使用都相当方便。

\n

b. 前端Web应用选型

因为前端Web应用主要是给运营人员使用,所以我们考虑使用Single Page Application来做个前后端分离的Web应用。框架这块由于团队成员基本上没有什么前端开发经验,基本都是后台写Java的码农,所以框架选择有点随性,直接就点名了vue.js。前端控件库则用的是阿里系的Antd。

\n

c. 数据持久层

主数据库选择了mysql,缓存用的redis。这些都是团队比较熟悉的。由于一些特殊的业务需求和使用场景,我们还加了一个mongodb来做为一个副数据库,主要存放一些特殊业务使用的数据。

\n

d. DevOps

用了相当传统的GitLab CE 加上Jenkins的组合,实现前后端代码的自动编译,推送到私有的镜像仓库(使用Aliyun的镜像仓库服务)

\n

最后

以上基本是项目最早做设计时候的各种考量。暂时先写这么多,过两天再回顾一下当初设计上有哪些觉得不足的地方。

\n","site":{"data":{}},"excerpt":"","more":"
\n

项目背景

\n
\n

从去年年底开始一个老客户希望在他们的一个传统的机械设备(后面略称 E机关 )上外装一个Android设备。 Android设备和 E机关 之间通过串口或是RJ45接口进行数据交互,主要是Android设备获取 E机关 内部的数据,并不会通过接口来控制 E机关 。 Android设备则通过4G 来和平台交互,上报Android设备的状态数据,同时接受平台的控制。以此来实现让传统机械设备也能拥抱物联网的概念。

\n

最后围绕着客户的需求分成了3个项目来并行推进

\n
    \n
  1. Android设备的硬件设备的设计、选材、样机制作与量产规划
  2. \n
  3. 在Android设备上,进行与E机关以及平台进行交互的APP开发
  4. \n
  5. 用于Android设备交互的平台的架构、设计与开发
  6. \n
\n

我们平台Team就负责 3.用于Android设备交互的平台 并命名Lagrange (拉格朗日) 。

\n

设计&架构

整个平台这块不仅需要向Android设备的APP提供数据交互接口,还需要有一个供相关运营人员使用的前端Web应用,以及与云平台(主要是Aliyun) 交互的功能。所以考虑到多方面使用微服务的架构来实现整个平台端。
由于E机关的工作工况不能保证长期较的稳定的连接到4G网络,所以我们并不考虑使用Socket长连接的方式来做APP和平台之间的数据交互,要实现平台对Android设备进行反控的话,只能实现推送方式来把控制命令下发给Android设备,所以还需要有一个第三方推送服务商交互的服务。同时控制推送也有实时和非实时以及定期推送的需求,所以可能还需要一个任务调度的服务。

\n

根据对上述业务进行梳理,我们将项目分成几个服务

\n\n

由于前一个项目实施的时候没有使用容器部署,每当访问量峰值的时候,我们这些码农兼运维就各种加班,所以这次项目决定直接将服务容器化,同时选择了Kubernetes来管理容器。由于客观原因线上的Kubernetes直接购买了Aliyun的托管版Kubernetes服务。 内部的开发测试环境则使用了 Rancher 2.0 来构建Kubernetes集群。

\n

技术选型

a. 后端服务选型

由于不考虑长连接的原因,所以在后端服务在技术选型上基本就不考虑Netty了,直接上Spring大礼包。

\n\n

服务之间调用都使用HTTP服务, 服务发现也有Kubernetes的DNS机制支持。服务网格则选择了比较成熟的Istio,主要还是Aliyun的Kubernetes可以集成Istio,部署和使用都相当方便。

\n

b. 前端Web应用选型

因为前端Web应用主要是给运营人员使用,所以我们考虑使用Single Page Application来做个前后端分离的Web应用。框架这块由于团队成员基本上没有什么前端开发经验,基本都是后台写Java的码农,所以框架选择有点随性,直接就点名了vue.js。前端控件库则用的是阿里系的Antd。

\n

c. 数据持久层

主数据库选择了mysql,缓存用的redis。这些都是团队比较熟悉的。由于一些特殊的业务需求和使用场景,我们还加了一个mongodb来做为一个副数据库,主要存放一些特殊业务使用的数据。

\n

d. DevOps

用了相当传统的GitLab CE 加上Jenkins的组合,实现前后端代码的自动编译,推送到私有的镜像仓库(使用Aliyun的镜像仓库服务)

\n

最后

以上基本是项目最早做设计时候的各种考量。暂时先写这么多,过两天再回顾一下当初设计上有哪些觉得不足的地方。

\n"},{"title":"在ubuntu上从零搭建node.js + nginx + mongodb环境","date":"2019-12-09T02:40:05.000Z","_content":"\n\n说到后端开发环境,最有名的莫过于LAMP和LNMP,最近由于node.js的强势崛起,越来越多的后端开发也开始试水node.js了。我最近也因为各种原因,前前后后总够构建了好几台node.js + nginx + mongodb的Linux服务器。\n\n首先关于Linux服务器,比起CentOS来说,我更加喜欢ubuntu一点。所以无论是阿里云还是一些海外的vps服务器上,我也倾向选用ubuntu服务器,本贴也是基于ubuntu服务器里说明的。\n\n## 1.开始前的一些准备\n\n首先还是需要刷新一下ubuntu的包索引并安装build-essential和libssl-dev这2个包以及curl这个工具。\n\n```sh\nsudo apt-get update\nsudo apt-get install build-essential libssl-dev\nsudo apt-get isntall curl\n```\n\n## 2.安装node.js\n\n关于安装node.js这一点,我不是很推荐使用apt-get 来安装node.js的环境。主要是因为node.js和io.js合并以后,版本迭代速度相当频繁(主要还是因为更多ES6的特性得到了支持)。今后很有可能会有在一台服务器上使用不同版本的node.js的需求。\n\n这里推荐一个管理不同版本node.js的工具:nvm,官网: [https://github.com/creationix/nvm](https://github.com/creationix/nvm)  。安装nvm,如果前面你安装了curl的话可以\n\n```sh\ncurl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash\n```\n如果没有按照curl的话,也可以使用wget来进行安装\n\n```sh\nwget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash\n```\n\n然后nvm就会自动安装到home目录下面的.nvm目录里,并会在.bashrc里自动添加nvm的环境变量。为了让环境变量生效,最简单的方法就是通过ssh或是telnet重新连接你的服务器。\n\n安装完nvm后,就可以通过nvm来安装指定版本的node.js了。\n\n```sh\n# 列出可以安装的node版本号\nnvm ls-remote\n\n# 安装指定版本的node (当前最新版本为v5.7.1, LTS版是v4.3.2)\nnvm install v4.3.2\n```\n\n## 3.安装nginx\n\n由于ubuntu源(尤其是阿里云的源)上的nginx经常不是最新的,如果需要安装最新版本nginx的时候需要手动添加nginx的源。\n\n```sh\n# 添加nginx的mainline仓库\ncd /tmp/ && wget http://nginx.org/keys/nginx_signing.key\nsudo apt-key add nginx_signing.key\n\n# 编辑/etc/apt/sources.list.d/nginx.list 添加下面2行内容,井号不需要\n# deb http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx\n# deb-src http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx\nsudo vi /etc/apt/sources.list.d/nginx.list\n\n# 更新源,并安装nginx\nsudo apt-get update && sudo apt-get install nginx\n```\n\n在编辑/etc/apt/sources.list.d/nginx.list的时候需要注意,“ubuntu代号”需要根据ubuntu服务器的版本不同手动调整的,比如14.04是trusty。通过下面的命令可以获取ubuntu的代号。\n```sh\nlsb_release -cs\n```\n\n## 4.安装mongodb\n\n同样和nginx有同样的问题,要安装最新3.2版本的mongodb也需要手动添加ubuntu的源。\n\n```sh\n# 导入mongodb的public key\nsudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927\n\n# 生成mongodb的源list\necho \"deb http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.2 multiverse\" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list\n\n# 更新源\nsudo apt-get update\n\n# 安装最新版本的mongodb\nsudo apt-get install -y mongodb-org\n```\n\n以上一台node.js + nginx + mongodb的ubuntu服务器就完成了。\n\n","source":"_posts/hot-to-install-nodejs-nginx-mongodb-on-ubuntu.md","raw":"---\ntitle: 在ubuntu上从零搭建node.js + nginx + mongodb环境\ntags:\n - ubuntu\n - node.js\n - nginx\n - mongodb\ncategories:\n - 后端\ndate: 2019-12-09 10:40:05\n---\n\n\n说到后端开发环境,最有名的莫过于LAMP和LNMP,最近由于node.js的强势崛起,越来越多的后端开发也开始试水node.js了。我最近也因为各种原因,前前后后总够构建了好几台node.js + nginx + mongodb的Linux服务器。\n\n首先关于Linux服务器,比起CentOS来说,我更加喜欢ubuntu一点。所以无论是阿里云还是一些海外的vps服务器上,我也倾向选用ubuntu服务器,本贴也是基于ubuntu服务器里说明的。\n\n## 1.开始前的一些准备\n\n首先还是需要刷新一下ubuntu的包索引并安装build-essential和libssl-dev这2个包以及curl这个工具。\n\n```sh\nsudo apt-get update\nsudo apt-get install build-essential libssl-dev\nsudo apt-get isntall curl\n```\n\n## 2.安装node.js\n\n关于安装node.js这一点,我不是很推荐使用apt-get 来安装node.js的环境。主要是因为node.js和io.js合并以后,版本迭代速度相当频繁(主要还是因为更多ES6的特性得到了支持)。今后很有可能会有在一台服务器上使用不同版本的node.js的需求。\n\n这里推荐一个管理不同版本node.js的工具:nvm,官网: [https://github.com/creationix/nvm](https://github.com/creationix/nvm)  。安装nvm,如果前面你安装了curl的话可以\n\n```sh\ncurl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash\n```\n如果没有按照curl的话,也可以使用wget来进行安装\n\n```sh\nwget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash\n```\n\n然后nvm就会自动安装到home目录下面的.nvm目录里,并会在.bashrc里自动添加nvm的环境变量。为了让环境变量生效,最简单的方法就是通过ssh或是telnet重新连接你的服务器。\n\n安装完nvm后,就可以通过nvm来安装指定版本的node.js了。\n\n```sh\n# 列出可以安装的node版本号\nnvm ls-remote\n\n# 安装指定版本的node (当前最新版本为v5.7.1, LTS版是v4.3.2)\nnvm install v4.3.2\n```\n\n## 3.安装nginx\n\n由于ubuntu源(尤其是阿里云的源)上的nginx经常不是最新的,如果需要安装最新版本nginx的时候需要手动添加nginx的源。\n\n```sh\n# 添加nginx的mainline仓库\ncd /tmp/ && wget http://nginx.org/keys/nginx_signing.key\nsudo apt-key add nginx_signing.key\n\n# 编辑/etc/apt/sources.list.d/nginx.list 添加下面2行内容,井号不需要\n# deb http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx\n# deb-src http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx\nsudo vi /etc/apt/sources.list.d/nginx.list\n\n# 更新源,并安装nginx\nsudo apt-get update && sudo apt-get install nginx\n```\n\n在编辑/etc/apt/sources.list.d/nginx.list的时候需要注意,“ubuntu代号”需要根据ubuntu服务器的版本不同手动调整的,比如14.04是trusty。通过下面的命令可以获取ubuntu的代号。\n```sh\nlsb_release -cs\n```\n\n## 4.安装mongodb\n\n同样和nginx有同样的问题,要安装最新3.2版本的mongodb也需要手动添加ubuntu的源。\n\n```sh\n# 导入mongodb的public key\nsudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927\n\n# 生成mongodb的源list\necho \"deb http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.2 multiverse\" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list\n\n# 更新源\nsudo apt-get update\n\n# 安装最新版本的mongodb\nsudo apt-get install -y mongodb-org\n```\n\n以上一台node.js + nginx + mongodb的ubuntu服务器就完成了。\n\n","slug":"hot-to-install-nodejs-nginx-mongodb-on-ubuntu","published":1,"updated":"2023-07-04T12:26:10.184Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cljo9zkjr000a4goihf3c0c7k","content":"

说到后端开发环境,最有名的莫过于LAMP和LNMP,最近由于node.js的强势崛起,越来越多的后端开发也开始试水node.js了。我最近也因为各种原因,前前后后总够构建了好几台node.js + nginx + mongodb的Linux服务器。

\n

首先关于Linux服务器,比起CentOS来说,我更加喜欢ubuntu一点。所以无论是阿里云还是一些海外的vps服务器上,我也倾向选用ubuntu服务器,本贴也是基于ubuntu服务器里说明的。

\n

1.开始前的一些准备

首先还是需要刷新一下ubuntu的包索引并安装build-essential和libssl-dev这2个包以及curl这个工具。

\n
1
2
3
sudo apt-get update
sudo apt-get install build-essential libssl-dev
sudo apt-get isntall curl
\n\n

2.安装node.js

关于安装node.js这一点,我不是很推荐使用apt-get 来安装node.js的环境。主要是因为node.js和io.js合并以后,版本迭代速度相当频繁(主要还是因为更多ES6的特性得到了支持)。今后很有可能会有在一台服务器上使用不同版本的node.js的需求。

\n

这里推荐一个管理不同版本node.js的工具:nvm,官网: https://github.com/creationix/nvm  。安装nvm,如果前面你安装了curl的话可以

\n
1
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
\n

如果没有按照curl的话,也可以使用wget来进行安装

\n
1
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
\n\n

然后nvm就会自动安装到home目录下面的.nvm目录里,并会在.bashrc里自动添加nvm的环境变量。为了让环境变量生效,最简单的方法就是通过ssh或是telnet重新连接你的服务器。

\n

安装完nvm后,就可以通过nvm来安装指定版本的node.js了。

\n
1
2
3
4
5
# 列出可以安装的node版本号
nvm ls-remote

# 安装指定版本的node (当前最新版本为v5.7.1, LTS版是v4.3.2)
nvm install v4.3.2
\n\n

3.安装nginx

由于ubuntu源(尤其是阿里云的源)上的nginx经常不是最新的,如果需要安装最新版本nginx的时候需要手动添加nginx的源。

\n
1
2
3
4
5
6
7
8
9
10
11
# 添加nginx的mainline仓库
cd /tmp/ && wget http://nginx.org/keys/nginx_signing.key
sudo apt-key add nginx_signing.key

# 编辑/etc/apt/sources.list.d/nginx.list 添加下面2行内容,井号不需要
# deb http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
# deb-src http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
sudo vi /etc/apt/sources.list.d/nginx.list

# 更新源,并安装nginx
sudo apt-get update && sudo apt-get install nginx
\n\n

在编辑/etc/apt/sources.list.d/nginx.list的时候需要注意,“ubuntu代号”需要根据ubuntu服务器的版本不同手动调整的,比如14.04是trusty。通过下面的命令可以获取ubuntu的代号。

\n
1
lsb_release -cs
\n\n

4.安装mongodb

同样和nginx有同样的问题,要安装最新3.2版本的mongodb也需要手动添加ubuntu的源。

\n
1
2
3
4
5
6
7
8
9
10
11
# 导入mongodb的public key
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927

# 生成mongodb的源list
echo "deb http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list

# 更新源
sudo apt-get update

# 安装最新版本的mongodb
sudo apt-get install -y mongodb-org
\n\n

以上一台node.js + nginx + mongodb的ubuntu服务器就完成了。

\n","site":{"data":{}},"excerpt":"","more":"

说到后端开发环境,最有名的莫过于LAMP和LNMP,最近由于node.js的强势崛起,越来越多的后端开发也开始试水node.js了。我最近也因为各种原因,前前后后总够构建了好几台node.js + nginx + mongodb的Linux服务器。

\n

首先关于Linux服务器,比起CentOS来说,我更加喜欢ubuntu一点。所以无论是阿里云还是一些海外的vps服务器上,我也倾向选用ubuntu服务器,本贴也是基于ubuntu服务器里说明的。

\n

1.开始前的一些准备

首先还是需要刷新一下ubuntu的包索引并安装build-essential和libssl-dev这2个包以及curl这个工具。

\n
1
2
3
sudo apt-get update
sudo apt-get install build-essential libssl-dev
sudo apt-get isntall curl
\n\n

2.安装node.js

关于安装node.js这一点,我不是很推荐使用apt-get 来安装node.js的环境。主要是因为node.js和io.js合并以后,版本迭代速度相当频繁(主要还是因为更多ES6的特性得到了支持)。今后很有可能会有在一台服务器上使用不同版本的node.js的需求。

\n

这里推荐一个管理不同版本node.js的工具:nvm,官网: https://github.com/creationix/nvm  。安装nvm,如果前面你安装了curl的话可以

\n
1
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
\n

如果没有按照curl的话,也可以使用wget来进行安装

\n
1
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
\n\n

然后nvm就会自动安装到home目录下面的.nvm目录里,并会在.bashrc里自动添加nvm的环境变量。为了让环境变量生效,最简单的方法就是通过ssh或是telnet重新连接你的服务器。

\n

安装完nvm后,就可以通过nvm来安装指定版本的node.js了。

\n
1
2
3
4
5
# 列出可以安装的node版本号
nvm ls-remote

# 安装指定版本的node (当前最新版本为v5.7.1, LTS版是v4.3.2)
nvm install v4.3.2
\n\n

3.安装nginx

由于ubuntu源(尤其是阿里云的源)上的nginx经常不是最新的,如果需要安装最新版本nginx的时候需要手动添加nginx的源。

\n
1
2
3
4
5
6
7
8
9
10
11
# 添加nginx的mainline仓库
cd /tmp/ && wget http://nginx.org/keys/nginx_signing.key
sudo apt-key add nginx_signing.key

# 编辑/etc/apt/sources.list.d/nginx.list 添加下面2行内容,井号不需要
# deb http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
# deb-src http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
sudo vi /etc/apt/sources.list.d/nginx.list

# 更新源,并安装nginx
sudo apt-get update && sudo apt-get install nginx
\n\n

在编辑/etc/apt/sources.list.d/nginx.list的时候需要注意,“ubuntu代号”需要根据ubuntu服务器的版本不同手动调整的,比如14.04是trusty。通过下面的命令可以获取ubuntu的代号。

\n
1
lsb_release -cs
\n\n

4.安装mongodb

同样和nginx有同样的问题,要安装最新3.2版本的mongodb也需要手动添加ubuntu的源。

\n
1
2
3
4
5
6
7
8
9
10
11
# 导入mongodb的public key
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927

# 生成mongodb的源list
echo "deb http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list

# 更新源
sudo apt-get update

# 安装最新版本的mongodb
sudo apt-get install -y mongodb-org
\n\n

以上一台node.js + nginx + mongodb的ubuntu服务器就完成了。

\n"},{"title":"阿里云Kubernetes上线踩坑记","date":"2020-04-01T01:47:38.000Z","_content":"\n```\nUpdate:\n2020-04-08 增加istio-ingressgateway高可用的设置\n```\n\n最近公司因为项目需要,在阿里云上部署了一个Kubernetes集群。虽然阿里云的文档说的还算细致,但是还是有些没有明确说明的细节。\n\n# 1. 购买篇\n\n申请项目预算的时候,只考虑到Worker节点,1个SLB节点以及域名和证书的预算。但是实际购买的时候发现还有许多额外的开销。\n\n## 1.1 SNAT\n\n这个和EIP一并购买,可以方便通过公网使用kubectl访问集群。关于SNAT网关至今不是很明白需要购买这个服务的意义何在,只是为了一个EIP来访问集群吗?\n\n## 1.2 Ingress\n\n这个选上了后,阿里云会给你买个SLB而且还是带公网访问的,如果你后期考虑使用Istio的话,建议你集群创建后,直接停止这个SLB,以免产生额外的费用。\n\n## 1.3 日志服务\n\n通过阿里云的日志服务来收集应用的的日志,挺好用的。但是另外收费,如果有能力的自建日志服务的可不购买。\n\n# 2. Istio\n\n阿里云的Kubernetes集群完美集成了Istio,根据向导就能很简单的部署成功。\n\n## 2.1 额外的SLB\n\nIstio的Gateway 需要绑定一个新的SLB,和Ingress的SLB不能是同一个,又是一笔额外的开销\n\n## 2.2 集群外访问\n\n这个在阿里云的Istio FAQ中有提到,按照指导很容易解决\n\n## 2.2 SLB的443监听\n\n为了方便443端口的证书绑定,我们直接删除了SLB上原有的443监听(TCP协议), 重新建了一个443监听(HTTPS协议),指向和80端口同样的虚拟服务器组。但是设置健康检查时一直出错,经过排查发现SLB健康检查发送的请求协议是HTTP 1.0的,Istio的envoy直接反悔了`426(Upgrade Required)`这个状态码,所以我们无奈只能把健康检查的检查返回状态改为http_4xx,这样就能通过SLB的健康检查了。\n\n## 2.3 istio-ingressgateway的高可用\n\n`istio-ingressgateway`要达成高可用,只需要增加通过伸缩POD就可以实现,于`istio-ingressgateway`对应的SLB中的虚拟服务器组也会自动增加,完全不需要进行额外的手动设定。\n\n由于`istio-ingressgateway`中挂载了HPA`HorizontalPodAutoscaler`(简称HPA),通常三节点的集群中最小POD数只有1台,在3节点的集群中,要实现高可用,需要手动修改HPA,增加最小POD数。\n\n---\n\n基本上现在遇到了这些坑,再有在总结吧。\n","source":"_posts/阿里云Kubernetes上线踩坑记.md","raw":"---\ntitle: 阿里云Kubernetes上线踩坑记\ndate: 2020-04-01 09:47:38\ntags:\n - Aliyun\n - Kubernetes\ncategories:\n - 后端\n - Cloud\n---\n\n```\nUpdate:\n2020-04-08 增加istio-ingressgateway高可用的设置\n```\n\n最近公司因为项目需要,在阿里云上部署了一个Kubernetes集群。虽然阿里云的文档说的还算细致,但是还是有些没有明确说明的细节。\n\n# 1. 购买篇\n\n申请项目预算的时候,只考虑到Worker节点,1个SLB节点以及域名和证书的预算。但是实际购买的时候发现还有许多额外的开销。\n\n## 1.1 SNAT\n\n这个和EIP一并购买,可以方便通过公网使用kubectl访问集群。关于SNAT网关至今不是很明白需要购买这个服务的意义何在,只是为了一个EIP来访问集群吗?\n\n## 1.2 Ingress\n\n这个选上了后,阿里云会给你买个SLB而且还是带公网访问的,如果你后期考虑使用Istio的话,建议你集群创建后,直接停止这个SLB,以免产生额外的费用。\n\n## 1.3 日志服务\n\n通过阿里云的日志服务来收集应用的的日志,挺好用的。但是另外收费,如果有能力的自建日志服务的可不购买。\n\n# 2. Istio\n\n阿里云的Kubernetes集群完美集成了Istio,根据向导就能很简单的部署成功。\n\n## 2.1 额外的SLB\n\nIstio的Gateway 需要绑定一个新的SLB,和Ingress的SLB不能是同一个,又是一笔额外的开销\n\n## 2.2 集群外访问\n\n这个在阿里云的Istio FAQ中有提到,按照指导很容易解决\n\n## 2.2 SLB的443监听\n\n为了方便443端口的证书绑定,我们直接删除了SLB上原有的443监听(TCP协议), 重新建了一个443监听(HTTPS协议),指向和80端口同样的虚拟服务器组。但是设置健康检查时一直出错,经过排查发现SLB健康检查发送的请求协议是HTTP 1.0的,Istio的envoy直接反悔了`426(Upgrade Required)`这个状态码,所以我们无奈只能把健康检查的检查返回状态改为http_4xx,这样就能通过SLB的健康检查了。\n\n## 2.3 istio-ingressgateway的高可用\n\n`istio-ingressgateway`要达成高可用,只需要增加通过伸缩POD就可以实现,于`istio-ingressgateway`对应的SLB中的虚拟服务器组也会自动增加,完全不需要进行额外的手动设定。\n\n由于`istio-ingressgateway`中挂载了HPA`HorizontalPodAutoscaler`(简称HPA),通常三节点的集群中最小POD数只有1台,在3节点的集群中,要实现高可用,需要手动修改HPA,增加最小POD数。\n\n---\n\n基本上现在遇到了这些坑,再有在总结吧。\n","slug":"阿里云Kubernetes上线踩坑记","published":1,"updated":"2023-07-04T12:26:10.184Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cljo9zkjr000b4goi8fzg64kj","content":"
1
2
Update:
2020-04-08 增加istio-ingressgateway高可用的设置
\n\n

最近公司因为项目需要,在阿里云上部署了一个Kubernetes集群。虽然阿里云的文档说的还算细致,但是还是有些没有明确说明的细节。

\n

1. 购买篇

申请项目预算的时候,只考虑到Worker节点,1个SLB节点以及域名和证书的预算。但是实际购买的时候发现还有许多额外的开销。

\n

1.1 SNAT

这个和EIP一并购买,可以方便通过公网使用kubectl访问集群。关于SNAT网关至今不是很明白需要购买这个服务的意义何在,只是为了一个EIP来访问集群吗?

\n

1.2 Ingress

这个选上了后,阿里云会给你买个SLB而且还是带公网访问的,如果你后期考虑使用Istio的话,建议你集群创建后,直接停止这个SLB,以免产生额外的费用。

\n

1.3 日志服务

通过阿里云的日志服务来收集应用的的日志,挺好用的。但是另外收费,如果有能力的自建日志服务的可不购买。

\n

2. Istio

阿里云的Kubernetes集群完美集成了Istio,根据向导就能很简单的部署成功。

\n

2.1 额外的SLB

Istio的Gateway 需要绑定一个新的SLB,和Ingress的SLB不能是同一个,又是一笔额外的开销

\n

2.2 集群外访问

这个在阿里云的Istio FAQ中有提到,按照指导很容易解决

\n

2.2 SLB的443监听

为了方便443端口的证书绑定,我们直接删除了SLB上原有的443监听(TCP协议), 重新建了一个443监听(HTTPS协议),指向和80端口同样的虚拟服务器组。但是设置健康检查时一直出错,经过排查发现SLB健康检查发送的请求协议是HTTP 1.0的,Istio的envoy直接反悔了426(Upgrade Required)这个状态码,所以我们无奈只能把健康检查的检查返回状态改为http_4xx,这样就能通过SLB的健康检查了。

\n

2.3 istio-ingressgateway的高可用

istio-ingressgateway要达成高可用,只需要增加通过伸缩POD就可以实现,于istio-ingressgateway对应的SLB中的虚拟服务器组也会自动增加,完全不需要进行额外的手动设定。

\n

由于istio-ingressgateway中挂载了HPAHorizontalPodAutoscaler(简称HPA),通常三节点的集群中最小POD数只有1台,在3节点的集群中,要实现高可用,需要手动修改HPA,增加最小POD数。

\n
\n

基本上现在遇到了这些坑,再有在总结吧。

\n","site":{"data":{}},"excerpt":"","more":"
1
2
Update:
2020-04-08 增加istio-ingressgateway高可用的设置
\n\n

最近公司因为项目需要,在阿里云上部署了一个Kubernetes集群。虽然阿里云的文档说的还算细致,但是还是有些没有明确说明的细节。

\n

1. 购买篇

申请项目预算的时候,只考虑到Worker节点,1个SLB节点以及域名和证书的预算。但是实际购买的时候发现还有许多额外的开销。

\n

1.1 SNAT

这个和EIP一并购买,可以方便通过公网使用kubectl访问集群。关于SNAT网关至今不是很明白需要购买这个服务的意义何在,只是为了一个EIP来访问集群吗?

\n

1.2 Ingress

这个选上了后,阿里云会给你买个SLB而且还是带公网访问的,如果你后期考虑使用Istio的话,建议你集群创建后,直接停止这个SLB,以免产生额外的费用。

\n

1.3 日志服务

通过阿里云的日志服务来收集应用的的日志,挺好用的。但是另外收费,如果有能力的自建日志服务的可不购买。

\n

2. Istio

阿里云的Kubernetes集群完美集成了Istio,根据向导就能很简单的部署成功。

\n

2.1 额外的SLB

Istio的Gateway 需要绑定一个新的SLB,和Ingress的SLB不能是同一个,又是一笔额外的开销

\n

2.2 集群外访问

这个在阿里云的Istio FAQ中有提到,按照指导很容易解决

\n

2.2 SLB的443监听

为了方便443端口的证书绑定,我们直接删除了SLB上原有的443监听(TCP协议), 重新建了一个443监听(HTTPS协议),指向和80端口同样的虚拟服务器组。但是设置健康检查时一直出错,经过排查发现SLB健康检查发送的请求协议是HTTP 1.0的,Istio的envoy直接反悔了426(Upgrade Required)这个状态码,所以我们无奈只能把健康检查的检查返回状态改为http_4xx,这样就能通过SLB的健康检查了。

\n

2.3 istio-ingressgateway的高可用

istio-ingressgateway要达成高可用,只需要增加通过伸缩POD就可以实现,于istio-ingressgateway对应的SLB中的虚拟服务器组也会自动增加,完全不需要进行额外的手动设定。

\n

由于istio-ingressgateway中挂载了HPAHorizontalPodAutoscaler(简称HPA),通常三节点的集群中最小POD数只有1台,在3节点的集群中,要实现高可用,需要手动修改HPA,增加最小POD数。

\n
\n

基本上现在遇到了这些坑,再有在总结吧。

\n"},{"title":"重学Java (一) 泛型","date":"2021-03-15T08:01:46.000Z","_content":"\n## 1. 前言\n\n泛型编程自从 Java 5.0 中引入后已经超过15个年头了。对于现在的 Java 码农来说熟练使用泛型编程已经是家常便饭的事情了。所以本文就在不对泛型的基础使用在做说明了。 如果你还不会使用泛型的话,可以参考下面两个链接\n\n- [Java 泛型详解](https://blog.csdn.net/qq_24084925/article/details/68491132)\n- [The Java™ Tutorials (Lesson: Generics)](https://docs.oracle.com/javase/tutorial/java/generics/index.html)\n\n这篇文章就简答聊一下,我实际在开发工作中很少用的到泛型方法这个知识点,以及在实际项目中有哪些东西会使用到泛型。\n\n## 2. 泛型方法\n\n在阅读代码的时候我们经常会看到下面这样的方法 (这段代码摘自 `java.util.AbstractCollection`)\n\n```java\n public T[] toArray(T[] a) {\n // Estimate size of array; be prepared to see more or fewer elements\n int size = size();\n T[] r = a.length >= size ? a :\n (T[])java.lang.reflect.Array\n .newInstance(a.getClass().getComponentType(), size);\n Iterator it = iterator();\n\n for (int i = 0; i < r.length; i++) {\n if (! it.hasNext()) { // fewer elements than expected\n if (a == r) {\n r[i] = null; // null-terminate\n } else if (a.length < i) {\n return Arrays.copyOf(r, i);\n } else {\n System.arraycopy(r, 0, a, 0, i);\n if (a.length > i) {\n a[i] = null;\n }\n }\n return a;\n }\n r[i] = (T)it.next();\n }\n // more elements than expected\n return it.hasNext() ? finishToArray(r, it) : r;\n}\n```\n\n那么 `pulic` 关键字后面的那个 `` 就是用来标记这个方法是一个泛型方法。 那什么是泛型方法呢。\n\n官方的解释是这样的\n\n```\nGeneric methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.\n```\n\n通俗点来将就是将一个方法泛型化,让一个普通的类的某一个方法具有泛型功能。 如果在一个泛型类中增加一个泛型方法,那这个泛型方法就可以有一套独立于这个类的泛型类型。\n\n通过一个简单的例子, 我们来看看\n\n```java\n/**\n * GenericClass 这个泛型类是一个简单的套皮的 HashMap\n */\npublic class GenericClass {\n\n private Map map = new HashMap<>();\n\n public V put(K key, V value) {\n return map.put(key, value);\n }\n\n public V get(K key) {\n return map.get(key);\n }\n\n // 泛型方法 genericMethod 可以接受一个全新的、作用域只限本函数的泛型类型T\n public T genericMethod(T t) {\n return t;\n }\n\n}\n```\n\n实际使用起来\n\n```java\nGenericClass map = new GenericClass<>();\n// put 和 get 方法的参数必须使用定义时指定的 String 和 Integer\nSystem.out.println(map.put(\"One\", 1));\nSystem.out.println(map.get(\"One\"));\n// 泛型方法 genericMethod 就可以接受一个 String 和 Integer 以外的类型\nSystem.out.println(map.genericMethod(new Double(1.0)).getClass());\n```\n\n我们再来看看 JDK 中使用到泛型方法的例子。我们最常使用的泛型容器 `ArrayList` 中有个 `toArray` 方法。JDK 在它的实现中就提供了两个版本,其中一个就是泛型方法的版本\n\n```java\npublic class ArrayList extends AbstractList\n implements List, RandomAccess, Cloneable, java.io.Serializable \n{\n // 这是一个普通版本,返回一个Object的数组\n public Object[] toArray() {\n return Arrays.copyOf(elementData, size);\n }\n \n // 这是一个泛型方法的版本,将容器里存储的元素输出到 T[] 数组中。 其中 T 必须是 E 的父类,否则 System.arraycopy 会抛出 ArrayStoreException 异常 \n public T[] toArray(T[] a) {\n if (a.length < size)\n // Make a new array of a's runtime type, but my contents:\n return (T[]) Arrays.copyOf(elementData, size, a.getClass());\n System.arraycopy(elementData, 0, a, 0, size);\n if (a.length > size)\n a[size] = null;\n return a;\n }\n}\n```\n\n泛型方法总体上来说就是可以给与现有的方法实现上,增加一个更加灵活的实现可能。\n\n## 3. 实战应用\n\n在实际的项目中,对于泛型的使用,除了像倾倒垃圾一样往泛型容易里塞各种 java bean 和其他泛型对象。还能怎么使用泛型呢?\n\n我们在实际的一些项目中,会对数据库中的一些表(多数时候是全部)先实现 CRUD (Create, Read, Update, Delete)的操作,并从这些操作中延伸出一些简单的 REST 风格的 WebAPI 接口,然后才会根据实际业务需要实现一些更复杂的业务接口。 \n\n大体上会是下面这个样子。 \n\n```java\n\n// 这是一个简单的 Entity 对象\n// 通常现在的 Java 应用都会使用到 Lombok 和 Spring Boot\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\n@ToString\n@Entity\n@Table(name = \"user\")\npublic class User {\n @Id\n private Long id;\n private String username;\n private String password;\n}\n\n// 然后这个是 DAO 接口继承自 spring-data 的 JpaRepository\npublic interface UserDao extends JpaRepository {\n}\n\n// 在来是一个访问 User 资源的 Service 和他的实现\npublic interface UserService {\n List findAll();\n Optional findById(Long id);\n User save (User user)\n void deleteById(Long id);\n}\n\n@Service\npublic class UserSerivceImpl implements UserService {\n private UserDao userDao;\n public UserServiceImpl(UserDao userDao) {\n this.userDao = userDao;\n }\n\n @Override\n public List findAll() {\n return this.dao.findAll();\n }\n\n @Override\n public Optional findById(Long id) {\n return this.dao.findById(id);\n }\n\n @Override\n public User save(User user) {\n return this.dao.save(user);\n }\n\n @Override\n public void deleteById(Long id) {\n this.dao.deleteById(id);\n }\n}\n\n// 最后就是 WebAPI 的接口了\n@RestController\n@RequestMapping(\"/user/\")\npublic class UserController{\n private UserService userService;\n public UserController(userService userService) {\n this.userService = userService;\n }\n\n @GetMapping\n @ResponseBody\n public List fetch() {\n return this.userService.findAll();\n }\n\n @GetMapping(\"{id}\")\n @ResponseBody\n public User get(@PathVariable(\"id\") Long id) {\n // 由于是示例这里就不考虑没有数据的情况了\n return this.userService.findById(id).get();\n }\n\n @PostMapping\n @ResponseBody\n public User create(@RequestBody User user) {\n return this.userService.save(user);\n }\n\n @PutMapping(\"{id}\")\n @ResponseBody\n public User update(@RequestBody User user) {\n return this.userService.save(user);\n }\n\n @DeleteMapping(\"{id}\")\n @ResponseBody\n public User delete(@PathVariable(\"id\") Long id) {\n User user = this.userService.findById(id);\n this.userService.deleteById(id);\n return user;\n }\n}\n```\n\n大致一个表的一套相关接口就是这个样子的。如果你的数据库中有大量表的话,而且每个表都需要提供 REST 风格的 WebAPI 接口的话,那么这将是一个相当枯燥的而又及其容易出错的工作。\n\n为了不让这项枯燥而又容易犯错的工作占去我们宝贵的私人时间,我们可以通过泛型和继承的技巧来重构从 Service 层到 Controller 的这段代码(感谢 spring-data 提供了 `JpaRepository`, 让我们不至于从 DAO 层重构)\n\n## 3.1 Service 层的重构\n\n首先是 Service 接口的重构,我们 Service 层接口就是定义了一组 CRUD 的操作,我们可以将这组 CRUD 操作抽象到一个父接口,然后所有 Service 层的接口都将继承自这个父接口。而接口中出现的 Entity 和主键的类型(上例中 User 的主键 id 的类型是 Long)就可以用泛型来展现。\n\n```java\n// 这里泛型表示 E 来指代 Entity, ID 用来指代 Entity 主键的类型\npublic interface ICrudService {\n List findAll();\n Optional findById(ID id);\n E save(E e);\n void deleteById(ID id);\n}\n\n// 然后 Service 层的接口,就可以简化成这样\npublic interface UserService extends ICrudService {\n}\n```\n\n同样 Service 层的实现也可以使用相似的方法具体实现可以抽象到一个基类中。\n\n```java\n// 相比 ICrudService 这里有多了一个泛型 T 来代表 Entity 对应的 DAO, 我们的每一个 DAO 都继承自\n// spring-data 的 JpaRepository 所以,这里可以使用到泛型的边界\npublic abstract class AbstractCrudService, E, ID> {\n private T dao;\n public AbstractCrudService(T dao) {\n this.dao = dao;\n }\n\n public List findAll() {\n return this.dao.findAll();\n }\n\n public Optional findById(ID id) {\n return this.dao.findById(id);\n }\n\n public E save(E e) {\n return this.dao.save(e);\n }\n\n public void deleteById(ID id) {\n this.dao.deleteById(id);\n }\n}\n\n// 那 Service 的实现类可以简化成这样\n@Service\npublic class UserServiceImpl extends AbstractCrudService implements UserService {\n public UserServiceImpl(UserDao dao) {\n supper(dao);\n }\n}\n```\n\n同样我们可以通过相同的方法来对 Controller 层进行重构\n\n```java\n// Controller 层的基类\npublic abstract class AbstractCrudController, E, ID> {\n private T service;\n public AbstractCrudController(T service) {\n this.service = service;\n }\n\n @GetMapping\n @ResponseBody\n public List fetch() {\n return this.service.findAll();\n }\n\n @GetMapping(\"{id}\")\n @ResponseBody\n public E get(@PathVariable(\"id\") ID id) {\n // 由于是示例这里就不考虑没有数据的情况了\n return this.service.findById(id).get();\n }\n\n @PostMapping\n @ResponseBody\n public E create(@RequestBody E e) {\n return this.service.save(e);\n }\n\n @PutMapping(\"{id}\")\n @ResponseBody\n public E update(@RequestBody E e) {\n return this.service.save(e);\n }\n\n @DeleteMapping(\"{id}\")\n @ResponseBody\n public E delete(@PathVariable(\"id\") ID id) {\n E e = this.service.findById(id).get();\n this.service.deleteById(id);\n return e;\n }\n}\n\n// 具体的 WebAPI\n@RestController\n@RequestMapping(\"/user/\")\npublic class UserController extends AbstractCrudController {\n public UserController(UserService service) {\n super(service);\n }\n}\n```\n\n经过重构可以消减掉 Servcie 和 Controller 中的大量重复代码,使代码更容易维护了。\n\n## 4. 结尾\n\n关于泛型就简单的说这些了,泛型作为 Java 日常开发中一个常用的知识点,其实还有很多知识点可以供我们挖掘,奈何本人才疏学浅,这么多年工作下来,只积累出来这么点内容。\n\n文末放上示例代码的代码库: \n\n- [GitHub入口](https://github.com/hashmaparraylist/re-study-java)\n- [gitee入口](https://gitee.com/hashmaparraylist/re-study-java)\n\n","source":"_posts/重学Java-一-泛型.md","raw":"---\ntitle: 重学Java (一) 泛型\ndate: 2021-03-15 16:01:46\ntags: \n - java\n - generic\ncategories:\n - 后端\n---\n\n## 1. 前言\n\n泛型编程自从 Java 5.0 中引入后已经超过15个年头了。对于现在的 Java 码农来说熟练使用泛型编程已经是家常便饭的事情了。所以本文就在不对泛型的基础使用在做说明了。 如果你还不会使用泛型的话,可以参考下面两个链接\n\n- [Java 泛型详解](https://blog.csdn.net/qq_24084925/article/details/68491132)\n- [The Java™ Tutorials (Lesson: Generics)](https://docs.oracle.com/javase/tutorial/java/generics/index.html)\n\n这篇文章就简答聊一下,我实际在开发工作中很少用的到泛型方法这个知识点,以及在实际项目中有哪些东西会使用到泛型。\n\n## 2. 泛型方法\n\n在阅读代码的时候我们经常会看到下面这样的方法 (这段代码摘自 `java.util.AbstractCollection`)\n\n```java\n public T[] toArray(T[] a) {\n // Estimate size of array; be prepared to see more or fewer elements\n int size = size();\n T[] r = a.length >= size ? a :\n (T[])java.lang.reflect.Array\n .newInstance(a.getClass().getComponentType(), size);\n Iterator it = iterator();\n\n for (int i = 0; i < r.length; i++) {\n if (! it.hasNext()) { // fewer elements than expected\n if (a == r) {\n r[i] = null; // null-terminate\n } else if (a.length < i) {\n return Arrays.copyOf(r, i);\n } else {\n System.arraycopy(r, 0, a, 0, i);\n if (a.length > i) {\n a[i] = null;\n }\n }\n return a;\n }\n r[i] = (T)it.next();\n }\n // more elements than expected\n return it.hasNext() ? finishToArray(r, it) : r;\n}\n```\n\n那么 `pulic` 关键字后面的那个 `` 就是用来标记这个方法是一个泛型方法。 那什么是泛型方法呢。\n\n官方的解释是这样的\n\n```\nGeneric methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.\n```\n\n通俗点来将就是将一个方法泛型化,让一个普通的类的某一个方法具有泛型功能。 如果在一个泛型类中增加一个泛型方法,那这个泛型方法就可以有一套独立于这个类的泛型类型。\n\n通过一个简单的例子, 我们来看看\n\n```java\n/**\n * GenericClass 这个泛型类是一个简单的套皮的 HashMap\n */\npublic class GenericClass {\n\n private Map map = new HashMap<>();\n\n public V put(K key, V value) {\n return map.put(key, value);\n }\n\n public V get(K key) {\n return map.get(key);\n }\n\n // 泛型方法 genericMethod 可以接受一个全新的、作用域只限本函数的泛型类型T\n public T genericMethod(T t) {\n return t;\n }\n\n}\n```\n\n实际使用起来\n\n```java\nGenericClass map = new GenericClass<>();\n// put 和 get 方法的参数必须使用定义时指定的 String 和 Integer\nSystem.out.println(map.put(\"One\", 1));\nSystem.out.println(map.get(\"One\"));\n// 泛型方法 genericMethod 就可以接受一个 String 和 Integer 以外的类型\nSystem.out.println(map.genericMethod(new Double(1.0)).getClass());\n```\n\n我们再来看看 JDK 中使用到泛型方法的例子。我们最常使用的泛型容器 `ArrayList` 中有个 `toArray` 方法。JDK 在它的实现中就提供了两个版本,其中一个就是泛型方法的版本\n\n```java\npublic class ArrayList extends AbstractList\n implements List, RandomAccess, Cloneable, java.io.Serializable \n{\n // 这是一个普通版本,返回一个Object的数组\n public Object[] toArray() {\n return Arrays.copyOf(elementData, size);\n }\n \n // 这是一个泛型方法的版本,将容器里存储的元素输出到 T[] 数组中。 其中 T 必须是 E 的父类,否则 System.arraycopy 会抛出 ArrayStoreException 异常 \n public T[] toArray(T[] a) {\n if (a.length < size)\n // Make a new array of a's runtime type, but my contents:\n return (T[]) Arrays.copyOf(elementData, size, a.getClass());\n System.arraycopy(elementData, 0, a, 0, size);\n if (a.length > size)\n a[size] = null;\n return a;\n }\n}\n```\n\n泛型方法总体上来说就是可以给与现有的方法实现上,增加一个更加灵活的实现可能。\n\n## 3. 实战应用\n\n在实际的项目中,对于泛型的使用,除了像倾倒垃圾一样往泛型容易里塞各种 java bean 和其他泛型对象。还能怎么使用泛型呢?\n\n我们在实际的一些项目中,会对数据库中的一些表(多数时候是全部)先实现 CRUD (Create, Read, Update, Delete)的操作,并从这些操作中延伸出一些简单的 REST 风格的 WebAPI 接口,然后才会根据实际业务需要实现一些更复杂的业务接口。 \n\n大体上会是下面这个样子。 \n\n```java\n\n// 这是一个简单的 Entity 对象\n// 通常现在的 Java 应用都会使用到 Lombok 和 Spring Boot\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\n@ToString\n@Entity\n@Table(name = \"user\")\npublic class User {\n @Id\n private Long id;\n private String username;\n private String password;\n}\n\n// 然后这个是 DAO 接口继承自 spring-data 的 JpaRepository\npublic interface UserDao extends JpaRepository {\n}\n\n// 在来是一个访问 User 资源的 Service 和他的实现\npublic interface UserService {\n List findAll();\n Optional findById(Long id);\n User save (User user)\n void deleteById(Long id);\n}\n\n@Service\npublic class UserSerivceImpl implements UserService {\n private UserDao userDao;\n public UserServiceImpl(UserDao userDao) {\n this.userDao = userDao;\n }\n\n @Override\n public List findAll() {\n return this.dao.findAll();\n }\n\n @Override\n public Optional findById(Long id) {\n return this.dao.findById(id);\n }\n\n @Override\n public User save(User user) {\n return this.dao.save(user);\n }\n\n @Override\n public void deleteById(Long id) {\n this.dao.deleteById(id);\n }\n}\n\n// 最后就是 WebAPI 的接口了\n@RestController\n@RequestMapping(\"/user/\")\npublic class UserController{\n private UserService userService;\n public UserController(userService userService) {\n this.userService = userService;\n }\n\n @GetMapping\n @ResponseBody\n public List fetch() {\n return this.userService.findAll();\n }\n\n @GetMapping(\"{id}\")\n @ResponseBody\n public User get(@PathVariable(\"id\") Long id) {\n // 由于是示例这里就不考虑没有数据的情况了\n return this.userService.findById(id).get();\n }\n\n @PostMapping\n @ResponseBody\n public User create(@RequestBody User user) {\n return this.userService.save(user);\n }\n\n @PutMapping(\"{id}\")\n @ResponseBody\n public User update(@RequestBody User user) {\n return this.userService.save(user);\n }\n\n @DeleteMapping(\"{id}\")\n @ResponseBody\n public User delete(@PathVariable(\"id\") Long id) {\n User user = this.userService.findById(id);\n this.userService.deleteById(id);\n return user;\n }\n}\n```\n\n大致一个表的一套相关接口就是这个样子的。如果你的数据库中有大量表的话,而且每个表都需要提供 REST 风格的 WebAPI 接口的话,那么这将是一个相当枯燥的而又及其容易出错的工作。\n\n为了不让这项枯燥而又容易犯错的工作占去我们宝贵的私人时间,我们可以通过泛型和继承的技巧来重构从 Service 层到 Controller 的这段代码(感谢 spring-data 提供了 `JpaRepository`, 让我们不至于从 DAO 层重构)\n\n## 3.1 Service 层的重构\n\n首先是 Service 接口的重构,我们 Service 层接口就是定义了一组 CRUD 的操作,我们可以将这组 CRUD 操作抽象到一个父接口,然后所有 Service 层的接口都将继承自这个父接口。而接口中出现的 Entity 和主键的类型(上例中 User 的主键 id 的类型是 Long)就可以用泛型来展现。\n\n```java\n// 这里泛型表示 E 来指代 Entity, ID 用来指代 Entity 主键的类型\npublic interface ICrudService {\n List findAll();\n Optional findById(ID id);\n E save(E e);\n void deleteById(ID id);\n}\n\n// 然后 Service 层的接口,就可以简化成这样\npublic interface UserService extends ICrudService {\n}\n```\n\n同样 Service 层的实现也可以使用相似的方法具体实现可以抽象到一个基类中。\n\n```java\n// 相比 ICrudService 这里有多了一个泛型 T 来代表 Entity 对应的 DAO, 我们的每一个 DAO 都继承自\n// spring-data 的 JpaRepository 所以,这里可以使用到泛型的边界\npublic abstract class AbstractCrudService, E, ID> {\n private T dao;\n public AbstractCrudService(T dao) {\n this.dao = dao;\n }\n\n public List findAll() {\n return this.dao.findAll();\n }\n\n public Optional findById(ID id) {\n return this.dao.findById(id);\n }\n\n public E save(E e) {\n return this.dao.save(e);\n }\n\n public void deleteById(ID id) {\n this.dao.deleteById(id);\n }\n}\n\n// 那 Service 的实现类可以简化成这样\n@Service\npublic class UserServiceImpl extends AbstractCrudService implements UserService {\n public UserServiceImpl(UserDao dao) {\n supper(dao);\n }\n}\n```\n\n同样我们可以通过相同的方法来对 Controller 层进行重构\n\n```java\n// Controller 层的基类\npublic abstract class AbstractCrudController, E, ID> {\n private T service;\n public AbstractCrudController(T service) {\n this.service = service;\n }\n\n @GetMapping\n @ResponseBody\n public List fetch() {\n return this.service.findAll();\n }\n\n @GetMapping(\"{id}\")\n @ResponseBody\n public E get(@PathVariable(\"id\") ID id) {\n // 由于是示例这里就不考虑没有数据的情况了\n return this.service.findById(id).get();\n }\n\n @PostMapping\n @ResponseBody\n public E create(@RequestBody E e) {\n return this.service.save(e);\n }\n\n @PutMapping(\"{id}\")\n @ResponseBody\n public E update(@RequestBody E e) {\n return this.service.save(e);\n }\n\n @DeleteMapping(\"{id}\")\n @ResponseBody\n public E delete(@PathVariable(\"id\") ID id) {\n E e = this.service.findById(id).get();\n this.service.deleteById(id);\n return e;\n }\n}\n\n// 具体的 WebAPI\n@RestController\n@RequestMapping(\"/user/\")\npublic class UserController extends AbstractCrudController {\n public UserController(UserService service) {\n super(service);\n }\n}\n```\n\n经过重构可以消减掉 Servcie 和 Controller 中的大量重复代码,使代码更容易维护了。\n\n## 4. 结尾\n\n关于泛型就简单的说这些了,泛型作为 Java 日常开发中一个常用的知识点,其实还有很多知识点可以供我们挖掘,奈何本人才疏学浅,这么多年工作下来,只积累出来这么点内容。\n\n文末放上示例代码的代码库: \n\n- [GitHub入口](https://github.com/hashmaparraylist/re-study-java)\n- [gitee入口](https://gitee.com/hashmaparraylist/re-study-java)\n\n","slug":"重学Java-一-泛型","published":1,"updated":"2023-07-04T12:26:10.184Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cljo9zkjs000f4goihc0y4xef","content":"

1. 前言

泛型编程自从 Java 5.0 中引入后已经超过15个年头了。对于现在的 Java 码农来说熟练使用泛型编程已经是家常便饭的事情了。所以本文就在不对泛型的基础使用在做说明了。 如果你还不会使用泛型的话,可以参考下面两个链接

\n\n

这篇文章就简答聊一下,我实际在开发工作中很少用的到泛型方法这个知识点,以及在实际项目中有哪些东西会使用到泛型。

\n

2. 泛型方法

在阅读代码的时候我们经常会看到下面这样的方法 (这段代码摘自 java.util.AbstractCollection)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 public <T> T[] toArray(T[] a) {
// Estimate size of array; be prepared to see more or fewer elements
int size = size();
T[] r = a.length >= size ? a :
(T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
Iterator<E> it = iterator();

for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) { // fewer elements than expected
if (a == r) {
r[i] = null; // null-terminate
} else if (a.length < i) {
return Arrays.copyOf(r, i);
} else {
System.arraycopy(r, 0, a, 0, i);
if (a.length > i) {
a[i] = null;
}
}
return a;
}
r[i] = (T)it.next();
}
// more elements than expected
return it.hasNext() ? finishToArray(r, it) : r;
}
\n\n

那么 pulic 关键字后面的那个 <T> 就是用来标记这个方法是一个泛型方法。 那什么是泛型方法呢。

\n

官方的解释是这样的

\n
1
Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.
\n\n

通俗点来将就是将一个方法泛型化,让一个普通的类的某一个方法具有泛型功能。 如果在一个泛型类中增加一个泛型方法,那这个泛型方法就可以有一套独立于这个类的泛型类型。

\n

通过一个简单的例子, 我们来看看

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* GenericClass 这个泛型类是一个简单的套皮的 HashMap
*/
public class GenericClass<K, V> {

private Map<K, V> map = new HashMap<>();

public V put(K key, V value) {
return map.put(key, value);
}

public V get(K key) {
return map.get(key);
}

// 泛型方法 genericMethod 可以接受一个全新的、作用域只限本函数的泛型类型T
public <T> T genericMethod(T t) {
return t;
}

}
\n\n

实际使用起来

\n
1
2
3
4
5
6
GenericClass<String, Integer> map = new GenericClass<>();
// put 和 get 方法的参数必须使用定义时指定的 String 和 Integer
System.out.println(map.put("One", 1));
System.out.println(map.get("One"));
// 泛型方法 genericMethod 就可以接受一个 String 和 Integer 以外的类型
System.out.println(map.genericMethod(new Double(1.0)).getClass());
\n\n

我们再来看看 JDK 中使用到泛型方法的例子。我们最常使用的泛型容器 ArrayList 中有个 toArray 方法。JDK 在它的实现中就提供了两个版本,其中一个就是泛型方法的版本

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
// 这是一个普通版本,返回一个Object的数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}

// 这是一个泛型方法的版本,将容器里存储的元素输出到 T[] 数组中。 其中 T 必须是 E 的父类,否则 System.arraycopy 会抛出 ArrayStoreException 异常
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
}
\n\n

泛型方法总体上来说就是可以给与现有的方法实现上,增加一个更加灵活的实现可能。

\n

3. 实战应用

在实际的项目中,对于泛型的使用,除了像倾倒垃圾一样往泛型容易里塞各种 java bean 和其他泛型对象。还能怎么使用泛型呢?

\n

我们在实际的一些项目中,会对数据库中的一些表(多数时候是全部)先实现 CRUD (Create, Read, Update, Delete)的操作,并从这些操作中延伸出一些简单的 REST 风格的 WebAPI 接口,然后才会根据实际业务需要实现一些更复杂的业务接口。

\n

大体上会是下面这个样子。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

// 这是一个简单的 Entity 对象
// 通常现在的 Java 应用都会使用到 Lombok 和 Spring Boot
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Entity
@Table(name = "user")
public class User {
@Id
private Long id;
private String username;
private String password;
}

// 然后这个是 DAO 接口继承自 spring-data 的 JpaRepository
public interface UserDao extends JpaRepository<User, Long> {
}

// 在来是一个访问 User 资源的 Service 和他的实现
public interface UserService {
List<User> findAll();
Optional<User> findById(Long id);
User save (User user)
void deleteById(Long id);
}

@Service
public class UserSerivceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}

@Override
public List<User> findAll() {
return this.dao.findAll();
}

@Override
public Optional<User> findById(Long id) {
return this.dao.findById(id);
}

@Override
public User save(User user) {
return this.dao.save(user);
}

@Override
public void deleteById(Long id) {
this.dao.deleteById(id);
}
}

// 最后就是 WebAPI 的接口了
@RestController
@RequestMapping("/user/")
public class UserController{
private UserService userService;
public UserController(userService userService) {
this.userService = userService;
}

@GetMapping
@ResponseBody
public List<User> fetch() {
return this.userService.findAll();
}

@GetMapping("{id}")
@ResponseBody
public User get(@PathVariable("id") Long id) {
// 由于是示例这里就不考虑没有数据的情况了
return this.userService.findById(id).get();
}

@PostMapping
@ResponseBody
public User create(@RequestBody User user) {
return this.userService.save(user);
}

@PutMapping("{id}")
@ResponseBody
public User update(@RequestBody User user) {
return this.userService.save(user);
}

@DeleteMapping("{id}")
@ResponseBody
public User delete(@PathVariable("id") Long id) {
User user = this.userService.findById(id);
this.userService.deleteById(id);
return user;
}
}
\n\n

大致一个表的一套相关接口就是这个样子的。如果你的数据库中有大量表的话,而且每个表都需要提供 REST 风格的 WebAPI 接口的话,那么这将是一个相当枯燥的而又及其容易出错的工作。

\n

为了不让这项枯燥而又容易犯错的工作占去我们宝贵的私人时间,我们可以通过泛型和继承的技巧来重构从 Service 层到 Controller 的这段代码(感谢 spring-data 提供了 JpaRepository, 让我们不至于从 DAO 层重构)

\n

3.1 Service 层的重构

首先是 Service 接口的重构,我们 Service 层接口就是定义了一组 CRUD 的操作,我们可以将这组 CRUD 操作抽象到一个父接口,然后所有 Service 层的接口都将继承自这个父接口。而接口中出现的 Entity 和主键的类型(上例中 User 的主键 id 的类型是 Long)就可以用泛型来展现。

\n
1
2
3
4
5
6
7
8
9
10
11
// 这里泛型表示 E 来指代 Entity, ID 用来指代 Entity 主键的类型
public interface ICrudService<E, ID> {
List<E> findAll();
Optional<E> findById(ID id);
E save(E e);
void deleteById(ID id);
}

// 然后 Service 层的接口,就可以简化成这样
public interface UserService extends ICrudService<User, Long> {
}
\n\n

同样 Service 层的实现也可以使用相似的方法具体实现可以抽象到一个基类中。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 相比 ICrudService 这里有多了一个泛型 T 来代表 Entity 对应的 DAO, 我们的每一个 DAO 都继承自
// spring-data 的 JpaRepository 所以,这里可以使用到泛型的边界
public abstract class AbstractCrudService<T extends JpaRepository<E, ID>, E, ID> {
private T dao;
public AbstractCrudService(T dao) {
this.dao = dao;
}

public List<E> findAll() {
return this.dao.findAll();
}

public Optional<E> findById(ID id) {
return this.dao.findById(id);
}

public E save(E e) {
return this.dao.save(e);
}

public void deleteById(ID id) {
this.dao.deleteById(id);
}
}

// 那 Service 的实现类可以简化成这样
@Service
public class UserServiceImpl extends AbstractCrudService<UserDao, User, Long> implements UserService {
public UserServiceImpl(UserDao dao) {
supper(dao);
}
}
\n\n

同样我们可以通过相同的方法来对 Controller 层进行重构

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Controller 层的基类
public abstract class AbstractCrudController<T extends ICrudService<E, ID>, E, ID> {
private T service;
public AbstractCrudController(T service) {
this.service = service;
}

@GetMapping
@ResponseBody
public List<E> fetch() {
return this.service.findAll();
}

@GetMapping("{id}")
@ResponseBody
public E get(@PathVariable("id") ID id) {
// 由于是示例这里就不考虑没有数据的情况了
return this.service.findById(id).get();
}

@PostMapping
@ResponseBody
public E create(@RequestBody E e) {
return this.service.save(e);
}

@PutMapping("{id}")
@ResponseBody
public E update(@RequestBody E e) {
return this.service.save(e);
}

@DeleteMapping("{id}")
@ResponseBody
public E delete(@PathVariable("id") ID id) {
E e = this.service.findById(id).get();
this.service.deleteById(id);
return e;
}
}

// 具体的 WebAPI
@RestController
@RequestMapping("/user/")
public class UserController extends AbstractCrudController<UserService, User, Long> {
public UserController(UserService service) {
super(service);
}
}
\n\n

经过重构可以消减掉 Servcie 和 Controller 中的大量重复代码,使代码更容易维护了。

\n

4. 结尾

关于泛型就简单的说这些了,泛型作为 Java 日常开发中一个常用的知识点,其实还有很多知识点可以供我们挖掘,奈何本人才疏学浅,这么多年工作下来,只积累出来这么点内容。

\n

文末放上示例代码的代码库:

\n\n","site":{"data":{}},"excerpt":"","more":"

1. 前言

泛型编程自从 Java 5.0 中引入后已经超过15个年头了。对于现在的 Java 码农来说熟练使用泛型编程已经是家常便饭的事情了。所以本文就在不对泛型的基础使用在做说明了。 如果你还不会使用泛型的话,可以参考下面两个链接

\n\n

这篇文章就简答聊一下,我实际在开发工作中很少用的到泛型方法这个知识点,以及在实际项目中有哪些东西会使用到泛型。

\n

2. 泛型方法

在阅读代码的时候我们经常会看到下面这样的方法 (这段代码摘自 java.util.AbstractCollection)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 public <T> T[] toArray(T[] a) {
// Estimate size of array; be prepared to see more or fewer elements
int size = size();
T[] r = a.length >= size ? a :
(T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
Iterator<E> it = iterator();

for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) { // fewer elements than expected
if (a == r) {
r[i] = null; // null-terminate
} else if (a.length < i) {
return Arrays.copyOf(r, i);
} else {
System.arraycopy(r, 0, a, 0, i);
if (a.length > i) {
a[i] = null;
}
}
return a;
}
r[i] = (T)it.next();
}
// more elements than expected
return it.hasNext() ? finishToArray(r, it) : r;
}
\n\n

那么 pulic 关键字后面的那个 <T> 就是用来标记这个方法是一个泛型方法。 那什么是泛型方法呢。

\n

官方的解释是这样的

\n
1
Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.
\n\n

通俗点来将就是将一个方法泛型化,让一个普通的类的某一个方法具有泛型功能。 如果在一个泛型类中增加一个泛型方法,那这个泛型方法就可以有一套独立于这个类的泛型类型。

\n

通过一个简单的例子, 我们来看看

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* GenericClass 这个泛型类是一个简单的套皮的 HashMap
*/
public class GenericClass<K, V> {

private Map<K, V> map = new HashMap<>();

public V put(K key, V value) {
return map.put(key, value);
}

public V get(K key) {
return map.get(key);
}

// 泛型方法 genericMethod 可以接受一个全新的、作用域只限本函数的泛型类型T
public <T> T genericMethod(T t) {
return t;
}

}
\n\n

实际使用起来

\n
1
2
3
4
5
6
GenericClass<String, Integer> map = new GenericClass<>();
// put 和 get 方法的参数必须使用定义时指定的 String 和 Integer
System.out.println(map.put("One", 1));
System.out.println(map.get("One"));
// 泛型方法 genericMethod 就可以接受一个 String 和 Integer 以外的类型
System.out.println(map.genericMethod(new Double(1.0)).getClass());
\n\n

我们再来看看 JDK 中使用到泛型方法的例子。我们最常使用的泛型容器 ArrayList 中有个 toArray 方法。JDK 在它的实现中就提供了两个版本,其中一个就是泛型方法的版本

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
// 这是一个普通版本,返回一个Object的数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}

// 这是一个泛型方法的版本,将容器里存储的元素输出到 T[] 数组中。 其中 T 必须是 E 的父类,否则 System.arraycopy 会抛出 ArrayStoreException 异常
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
}
\n\n

泛型方法总体上来说就是可以给与现有的方法实现上,增加一个更加灵活的实现可能。

\n

3. 实战应用

在实际的项目中,对于泛型的使用,除了像倾倒垃圾一样往泛型容易里塞各种 java bean 和其他泛型对象。还能怎么使用泛型呢?

\n

我们在实际的一些项目中,会对数据库中的一些表(多数时候是全部)先实现 CRUD (Create, Read, Update, Delete)的操作,并从这些操作中延伸出一些简单的 REST 风格的 WebAPI 接口,然后才会根据实际业务需要实现一些更复杂的业务接口。

\n

大体上会是下面这个样子。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

// 这是一个简单的 Entity 对象
// 通常现在的 Java 应用都会使用到 Lombok 和 Spring Boot
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Entity
@Table(name = "user")
public class User {
@Id
private Long id;
private String username;
private String password;
}

// 然后这个是 DAO 接口继承自 spring-data 的 JpaRepository
public interface UserDao extends JpaRepository<User, Long> {
}

// 在来是一个访问 User 资源的 Service 和他的实现
public interface UserService {
List<User> findAll();
Optional<User> findById(Long id);
User save (User user)
void deleteById(Long id);
}

@Service
public class UserSerivceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}

@Override
public List<User> findAll() {
return this.dao.findAll();
}

@Override
public Optional<User> findById(Long id) {
return this.dao.findById(id);
}

@Override
public User save(User user) {
return this.dao.save(user);
}

@Override
public void deleteById(Long id) {
this.dao.deleteById(id);
}
}

// 最后就是 WebAPI 的接口了
@RestController
@RequestMapping("/user/")
public class UserController{
private UserService userService;
public UserController(userService userService) {
this.userService = userService;
}

@GetMapping
@ResponseBody
public List<User> fetch() {
return this.userService.findAll();
}

@GetMapping("{id}")
@ResponseBody
public User get(@PathVariable("id") Long id) {
// 由于是示例这里就不考虑没有数据的情况了
return this.userService.findById(id).get();
}

@PostMapping
@ResponseBody
public User create(@RequestBody User user) {
return this.userService.save(user);
}

@PutMapping("{id}")
@ResponseBody
public User update(@RequestBody User user) {
return this.userService.save(user);
}

@DeleteMapping("{id}")
@ResponseBody
public User delete(@PathVariable("id") Long id) {
User user = this.userService.findById(id);
this.userService.deleteById(id);
return user;
}
}
\n\n

大致一个表的一套相关接口就是这个样子的。如果你的数据库中有大量表的话,而且每个表都需要提供 REST 风格的 WebAPI 接口的话,那么这将是一个相当枯燥的而又及其容易出错的工作。

\n

为了不让这项枯燥而又容易犯错的工作占去我们宝贵的私人时间,我们可以通过泛型和继承的技巧来重构从 Service 层到 Controller 的这段代码(感谢 spring-data 提供了 JpaRepository, 让我们不至于从 DAO 层重构)

\n

3.1 Service 层的重构

首先是 Service 接口的重构,我们 Service 层接口就是定义了一组 CRUD 的操作,我们可以将这组 CRUD 操作抽象到一个父接口,然后所有 Service 层的接口都将继承自这个父接口。而接口中出现的 Entity 和主键的类型(上例中 User 的主键 id 的类型是 Long)就可以用泛型来展现。

\n
1
2
3
4
5
6
7
8
9
10
11
// 这里泛型表示 E 来指代 Entity, ID 用来指代 Entity 主键的类型
public interface ICrudService<E, ID> {
List<E> findAll();
Optional<E> findById(ID id);
E save(E e);
void deleteById(ID id);
}

// 然后 Service 层的接口,就可以简化成这样
public interface UserService extends ICrudService<User, Long> {
}
\n\n

同样 Service 层的实现也可以使用相似的方法具体实现可以抽象到一个基类中。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 相比 ICrudService 这里有多了一个泛型 T 来代表 Entity 对应的 DAO, 我们的每一个 DAO 都继承自
// spring-data 的 JpaRepository 所以,这里可以使用到泛型的边界
public abstract class AbstractCrudService<T extends JpaRepository<E, ID>, E, ID> {
private T dao;
public AbstractCrudService(T dao) {
this.dao = dao;
}

public List<E> findAll() {
return this.dao.findAll();
}

public Optional<E> findById(ID id) {
return this.dao.findById(id);
}

public E save(E e) {
return this.dao.save(e);
}

public void deleteById(ID id) {
this.dao.deleteById(id);
}
}

// 那 Service 的实现类可以简化成这样
@Service
public class UserServiceImpl extends AbstractCrudService<UserDao, User, Long> implements UserService {
public UserServiceImpl(UserDao dao) {
supper(dao);
}
}
\n\n

同样我们可以通过相同的方法来对 Controller 层进行重构

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Controller 层的基类
public abstract class AbstractCrudController<T extends ICrudService<E, ID>, E, ID> {
private T service;
public AbstractCrudController(T service) {
this.service = service;
}

@GetMapping
@ResponseBody
public List<E> fetch() {
return this.service.findAll();
}

@GetMapping("{id}")
@ResponseBody
public E get(@PathVariable("id") ID id) {
// 由于是示例这里就不考虑没有数据的情况了
return this.service.findById(id).get();
}

@PostMapping
@ResponseBody
public E create(@RequestBody E e) {
return this.service.save(e);
}

@PutMapping("{id}")
@ResponseBody
public E update(@RequestBody E e) {
return this.service.save(e);
}

@DeleteMapping("{id}")
@ResponseBody
public E delete(@PathVariable("id") ID id) {
E e = this.service.findById(id).get();
this.service.deleteById(id);
return e;
}
}

// 具体的 WebAPI
@RestController
@RequestMapping("/user/")
public class UserController extends AbstractCrudController<UserService, User, Long> {
public UserController(UserService service) {
super(service);
}
}
\n\n

经过重构可以消减掉 Servcie 和 Controller 中的大量重复代码,使代码更容易维护了。

\n

4. 结尾

关于泛型就简单的说这些了,泛型作为 Java 日常开发中一个常用的知识点,其实还有很多知识点可以供我们挖掘,奈何本人才疏学浅,这么多年工作下来,只积累出来这么点内容。

\n

文末放上示例代码的代码库:

\n\n"},{"title":"H项目心路历程记 (一)","date":"2023-07-10T03:19:00.000Z","_content":"\n\n# 前言\n\n2021年年中从前一家公司离职,经过朋友介绍到了现在公司参与了`Halfling`项目(以下简称`H项目`),并从零开始的设计开发到第一个客户交付的全部流程。\n\n`H项目`是一个面向零售行业在电商时代的智能一体化中台产品。项目最早是从2021年中开始立项投入,期间经过数个版本迭代,以及2022年初的魔都封城等诸多困难。终于在2023年Q2季度完成了针对首个客户的上线。\n\n作为从头开始全程参与项目的技术人员,经过2年多的产品研发期间有不少想吐槽和想说的。\n\n# 业务设计\n\n最初`H项目`是以打造一个面向零售行业业务中台为目的进行项目立项的,其中一个很重要的点就是希望除核心组件以外都可以独立售卖。所以在架构设计的使用当下最流行的微服务架构(基于Spring Boot + Kubernetes集群的解决方案), 再做业务拆分到服务的时候,进行了更细颗粒度的拆分。所以前前后后一共拆分了近20个微服务。\n\n实际上到了2022年Q2前后,两个核心服务订单管理和库存服务实现以后发现,各个业务服务中间的耦合程度比想象中的还要精密,单个服务拆开单独售卖几乎不可能(应该说是完全不可能)。 然而由于服务拆分的过细,导致一个服务需要频繁访问其他服务获取大量数据。尤其类似订单关联数据比如商品的主档数据,会穿越好几个服务。而每个服务都会重复去获取主档数据,造成了大量的额外请求。 虽然最后在所有服务底部兜底使用同一个Redis作为缓存来减少额外的请求调用。\n\n随着时间来到2023年产品组负责人离职后,整个`H项目`的方向有从行业的业务中台降级成中小企业的订单管理系统(`Order Management System`, OMS)。前期做的很多设计比如用户权限体系(当初需求是要精确到每个页面的每个按钮,如果下拉列表框有外调请求也需要做权限控制)等就显的很重,导致用户在使用以及配置都很不方便。而有些设计就显得稍显欠缺。 所以整个2022年Q4到2023年都是在对业务层上反复横跳,比如库存模型和商品模型的调整。这点是相当影响团队士气,同时也加剧了研发组和产品组之间的矛盾。\n\n然后还有一个很重要的问题是多租户的态度上。早起项目是以服务中大体量客户为目的,所以设计之初并没有考虑多租户的问题。但实际项的进展过程中发现部分中大体量的客户也需要类似多租户的功能。这类客户需求与其说是需求多租户,不如说是希望有一个多租户级别的数据隔离,每个业务部门只能操作自己的业务部门的数据(数据包含商品,品牌,属性,平台店铺,订单,售后单据等),而总部依据可以查看一些汇总信息(例如销售对账单据等)。而随着2022年疫情状况严峻,很多中大客户对于内部平台升级和替换都变的更加谨慎和保守。所以当项目方向变成中小企业OMS时,多租户SaaS化的需求就更加迫切了。所以后期如何在原有系统上在扩展一套多租户SaaS化功能也是个课题。\n\n# 中间件\n\n`H项目`使用的中间件中比较特别的有分布式任务管理系统`xxl-job`,消息队列`RocketMQ`,这两个中间件都是第一次使用。关于这两个中间件也有不是可以谈谈的。\n\n## 分布式任务管理系统 `XXL-JOB`\n\n作为一个开源的分布式任务管理系统,`xxl-job`足够简单且相当容易上手使用,无论是基于Spring,还是原生Java做开发都是相当简单的。\n\n而不足之处也很明显,最近1年版本迭代速度明显变慢,有些功能和BUG都有待处理的。比如想实动态创建定时任务这类功能就必须自己动手去对`xxl-job-admin`(调度中心)进行修改。\n\n对于任务执行日志这块,现在`xxl-job`也是文件格式在执行器所在服务器上进行输出的,当执行器容器化或是运行在Kubernetes集群中的时候,当容器或是Pod重启后,就会丢失日志。这点在后期运维以及调试上还是有点麻烦的。\n\n## 消息队列 `RocketMQ`\n\n阿里出品的消息队列如果基于云产品来使用的话问题不大(阿里云卖的价钱也是有点黑的),但是在运维上还是有点问题的比如Topic,Group这些东西都只能人工去添加,阿里云并没有提供导入服务。全系统如果使用消息队列的场景多的话,维护这些Topic和Group还是很酸爽的。还有一点`RocketMQ`重复消息这一点是无法保证的。用阿里云的原话\n\n> 绝大多数情况下,消息是不重复的。作为一款分布式消息中间件,在网络抖动、应用处理超时等异常情况下,无法保证消息不重复,但是能保证消息不丢失。\n\n实际运用上,在大数据量并发的情况下,我们实际上是遇到了消息重复消费的问题。这点还是需要通过代码干预(例如增加一个唯一MsgId,对MsgId做分布式锁来解决)。\n\n还有一点就是在旧版本SDK的默认配置下消费者的负载是有点小问题,总是有一个节点会比其他节点更容易消费到大量消息。据阿里云自己说明在新版本SDK的默认负载配置也已经修复这个问题了。\n\n\n**(未完待续)**\n\n","source":"_posts/H项目心路历程记-1.md","raw":"---\ntitle: H项目心路历程记 (一)\ntags:\n - 架构\n - Kubernetes\n - Aliyun\n - Java\ncategories:\n - 设计\ndate: 2023-07-10 11:19:00\n---\n\n\n# 前言\n\n2021年年中从前一家公司离职,经过朋友介绍到了现在公司参与了`Halfling`项目(以下简称`H项目`),并从零开始的设计开发到第一个客户交付的全部流程。\n\n`H项目`是一个面向零售行业在电商时代的智能一体化中台产品。项目最早是从2021年中开始立项投入,期间经过数个版本迭代,以及2022年初的魔都封城等诸多困难。终于在2023年Q2季度完成了针对首个客户的上线。\n\n作为从头开始全程参与项目的技术人员,经过2年多的产品研发期间有不少想吐槽和想说的。\n\n# 业务设计\n\n最初`H项目`是以打造一个面向零售行业业务中台为目的进行项目立项的,其中一个很重要的点就是希望除核心组件以外都可以独立售卖。所以在架构设计的使用当下最流行的微服务架构(基于Spring Boot + Kubernetes集群的解决方案), 再做业务拆分到服务的时候,进行了更细颗粒度的拆分。所以前前后后一共拆分了近20个微服务。\n\n实际上到了2022年Q2前后,两个核心服务订单管理和库存服务实现以后发现,各个业务服务中间的耦合程度比想象中的还要精密,单个服务拆开单独售卖几乎不可能(应该说是完全不可能)。 然而由于服务拆分的过细,导致一个服务需要频繁访问其他服务获取大量数据。尤其类似订单关联数据比如商品的主档数据,会穿越好几个服务。而每个服务都会重复去获取主档数据,造成了大量的额外请求。 虽然最后在所有服务底部兜底使用同一个Redis作为缓存来减少额外的请求调用。\n\n随着时间来到2023年产品组负责人离职后,整个`H项目`的方向有从行业的业务中台降级成中小企业的订单管理系统(`Order Management System`, OMS)。前期做的很多设计比如用户权限体系(当初需求是要精确到每个页面的每个按钮,如果下拉列表框有外调请求也需要做权限控制)等就显的很重,导致用户在使用以及配置都很不方便。而有些设计就显得稍显欠缺。 所以整个2022年Q4到2023年都是在对业务层上反复横跳,比如库存模型和商品模型的调整。这点是相当影响团队士气,同时也加剧了研发组和产品组之间的矛盾。\n\n然后还有一个很重要的问题是多租户的态度上。早起项目是以服务中大体量客户为目的,所以设计之初并没有考虑多租户的问题。但实际项的进展过程中发现部分中大体量的客户也需要类似多租户的功能。这类客户需求与其说是需求多租户,不如说是希望有一个多租户级别的数据隔离,每个业务部门只能操作自己的业务部门的数据(数据包含商品,品牌,属性,平台店铺,订单,售后单据等),而总部依据可以查看一些汇总信息(例如销售对账单据等)。而随着2022年疫情状况严峻,很多中大客户对于内部平台升级和替换都变的更加谨慎和保守。所以当项目方向变成中小企业OMS时,多租户SaaS化的需求就更加迫切了。所以后期如何在原有系统上在扩展一套多租户SaaS化功能也是个课题。\n\n# 中间件\n\n`H项目`使用的中间件中比较特别的有分布式任务管理系统`xxl-job`,消息队列`RocketMQ`,这两个中间件都是第一次使用。关于这两个中间件也有不是可以谈谈的。\n\n## 分布式任务管理系统 `XXL-JOB`\n\n作为一个开源的分布式任务管理系统,`xxl-job`足够简单且相当容易上手使用,无论是基于Spring,还是原生Java做开发都是相当简单的。\n\n而不足之处也很明显,最近1年版本迭代速度明显变慢,有些功能和BUG都有待处理的。比如想实动态创建定时任务这类功能就必须自己动手去对`xxl-job-admin`(调度中心)进行修改。\n\n对于任务执行日志这块,现在`xxl-job`也是文件格式在执行器所在服务器上进行输出的,当执行器容器化或是运行在Kubernetes集群中的时候,当容器或是Pod重启后,就会丢失日志。这点在后期运维以及调试上还是有点麻烦的。\n\n## 消息队列 `RocketMQ`\n\n阿里出品的消息队列如果基于云产品来使用的话问题不大(阿里云卖的价钱也是有点黑的),但是在运维上还是有点问题的比如Topic,Group这些东西都只能人工去添加,阿里云并没有提供导入服务。全系统如果使用消息队列的场景多的话,维护这些Topic和Group还是很酸爽的。还有一点`RocketMQ`重复消息这一点是无法保证的。用阿里云的原话\n\n> 绝大多数情况下,消息是不重复的。作为一款分布式消息中间件,在网络抖动、应用处理超时等异常情况下,无法保证消息不重复,但是能保证消息不丢失。\n\n实际运用上,在大数据量并发的情况下,我们实际上是遇到了消息重复消费的问题。这点还是需要通过代码干预(例如增加一个唯一MsgId,对MsgId做分布式锁来解决)。\n\n还有一点就是在旧版本SDK的默认配置下消费者的负载是有点小问题,总是有一个节点会比其他节点更容易消费到大量消息。据阿里云自己说明在新版本SDK的默认负载配置也已经修复这个问题了。\n\n\n**(未完待续)**\n\n","slug":"H项目心路历程记-1","published":1,"updated":"2023-07-10T09:17:01.579Z","_id":"cljwneykb0006m3oi131phei7","comments":1,"layout":"post","photos":[],"link":"","content":"

前言

2021年年中从前一家公司离职,经过朋友介绍到了现在公司参与了Halfling项目(以下简称H项目),并从零开始的设计开发到第一个客户交付的全部流程。

\n

H项目是一个面向零售行业在电商时代的智能一体化中台产品。项目最早是从2021年中开始立项投入,期间经过数个版本迭代,以及2022年初的魔都封城等诸多困难。终于在2023年Q2季度完成了针对首个客户的上线。

\n

作为从头开始全程参与项目的技术人员,经过2年多的产品研发期间有不少想吐槽和想说的。

\n

业务设计

最初H项目是以打造一个面向零售行业业务中台为目的进行项目立项的,其中一个很重要的点就是希望除核心组件以外都可以独立售卖。所以在架构设计的使用当下最流行的微服务架构(基于Spring Boot + Kubernetes集群的解决方案), 再做业务拆分到服务的时候,进行了更细颗粒度的拆分。所以前前后后一共拆分了近20个微服务。

\n

实际上到了2022年Q2前后,两个核心服务订单管理和库存服务实现以后发现,各个业务服务中间的耦合程度比想象中的还要精密,单个服务拆开单独售卖几乎不可能(应该说是完全不可能)。 然而由于服务拆分的过细,导致一个服务需要频繁访问其他服务获取大量数据。尤其类似订单关联数据比如商品的主档数据,会穿越好几个服务。而每个服务都会重复去获取主档数据,造成了大量的额外请求。 虽然最后在所有服务底部兜底使用同一个Redis作为缓存来减少额外的请求调用。

\n

随着时间来到2023年产品组负责人离职后,整个H项目的方向有从行业的业务中台降级成中小企业的订单管理系统(Order Management System, OMS)。前期做的很多设计比如用户权限体系(当初需求是要精确到每个页面的每个按钮,如果下拉列表框有外调请求也需要做权限控制)等就显的很重,导致用户在使用以及配置都很不方便。而有些设计就显得稍显欠缺。 所以整个2022年Q4到2023年都是在对业务层上反复横跳,比如库存模型和商品模型的调整。这点是相当影响团队士气,同时也加剧了研发组和产品组之间的矛盾。

\n

然后还有一个很重要的问题是多租户的态度上。早起项目是以服务中大体量客户为目的,所以设计之初并没有考虑多租户的问题。但实际项的进展过程中发现部分中大体量的客户也需要类似多租户的功能。这类客户需求与其说是需求多租户,不如说是希望有一个多租户级别的数据隔离,每个业务部门只能操作自己的业务部门的数据(数据包含商品,品牌,属性,平台店铺,订单,售后单据等),而总部依据可以查看一些汇总信息(例如销售对账单据等)。而随着2022年疫情状况严峻,很多中大客户对于内部平台升级和替换都变的更加谨慎和保守。所以当项目方向变成中小企业OMS时,多租户SaaS化的需求就更加迫切了。所以后期如何在原有系统上在扩展一套多租户SaaS化功能也是个课题。

\n

中间件

H项目使用的中间件中比较特别的有分布式任务管理系统xxl-job,消息队列RocketMQ,这两个中间件都是第一次使用。关于这两个中间件也有不是可以谈谈的。

\n

分布式任务管理系统 XXL-JOB

作为一个开源的分布式任务管理系统,xxl-job足够简单且相当容易上手使用,无论是基于Spring,还是原生Java做开发都是相当简单的。

\n

而不足之处也很明显,最近1年版本迭代速度明显变慢,有些功能和BUG都有待处理的。比如想实动态创建定时任务这类功能就必须自己动手去对xxl-job-admin(调度中心)进行修改。

\n

对于任务执行日志这块,现在xxl-job也是文件格式在执行器所在服务器上进行输出的,当执行器容器化或是运行在Kubernetes集群中的时候,当容器或是Pod重启后,就会丢失日志。这点在后期运维以及调试上还是有点麻烦的。

\n

消息队列 RocketMQ

阿里出品的消息队列如果基于云产品来使用的话问题不大(阿里云卖的价钱也是有点黑的),但是在运维上还是有点问题的比如Topic,Group这些东西都只能人工去添加,阿里云并没有提供导入服务。全系统如果使用消息队列的场景多的话,维护这些Topic和Group还是很酸爽的。还有一点RocketMQ重复消息这一点是无法保证的。用阿里云的原话

\n
\n

绝大多数情况下,消息是不重复的。作为一款分布式消息中间件,在网络抖动、应用处理超时等异常情况下,无法保证消息不重复,但是能保证消息不丢失。

\n
\n

实际运用上,在大数据量并发的情况下,我们实际上是遇到了消息重复消费的问题。这点还是需要通过代码干预(例如增加一个唯一MsgId,对MsgId做分布式锁来解决)。

\n

还有一点就是在旧版本SDK的默认配置下消费者的负载是有点小问题,总是有一个节点会比其他节点更容易消费到大量消息。据阿里云自己说明在新版本SDK的默认负载配置也已经修复这个问题了。

\n

(未完待续)

\n","site":{"data":{}},"excerpt":"","more":"

前言

2021年年中从前一家公司离职,经过朋友介绍到了现在公司参与了Halfling项目(以下简称H项目),并从零开始的设计开发到第一个客户交付的全部流程。

\n

H项目是一个面向零售行业在电商时代的智能一体化中台产品。项目最早是从2021年中开始立项投入,期间经过数个版本迭代,以及2022年初的魔都封城等诸多困难。终于在2023年Q2季度完成了针对首个客户的上线。

\n

作为从头开始全程参与项目的技术人员,经过2年多的产品研发期间有不少想吐槽和想说的。

\n

业务设计

最初H项目是以打造一个面向零售行业业务中台为目的进行项目立项的,其中一个很重要的点就是希望除核心组件以外都可以独立售卖。所以在架构设计的使用当下最流行的微服务架构(基于Spring Boot + Kubernetes集群的解决方案), 再做业务拆分到服务的时候,进行了更细颗粒度的拆分。所以前前后后一共拆分了近20个微服务。

\n

实际上到了2022年Q2前后,两个核心服务订单管理和库存服务实现以后发现,各个业务服务中间的耦合程度比想象中的还要精密,单个服务拆开单独售卖几乎不可能(应该说是完全不可能)。 然而由于服务拆分的过细,导致一个服务需要频繁访问其他服务获取大量数据。尤其类似订单关联数据比如商品的主档数据,会穿越好几个服务。而每个服务都会重复去获取主档数据,造成了大量的额外请求。 虽然最后在所有服务底部兜底使用同一个Redis作为缓存来减少额外的请求调用。

\n

随着时间来到2023年产品组负责人离职后,整个H项目的方向有从行业的业务中台降级成中小企业的订单管理系统(Order Management System, OMS)。前期做的很多设计比如用户权限体系(当初需求是要精确到每个页面的每个按钮,如果下拉列表框有外调请求也需要做权限控制)等就显的很重,导致用户在使用以及配置都很不方便。而有些设计就显得稍显欠缺。 所以整个2022年Q4到2023年都是在对业务层上反复横跳,比如库存模型和商品模型的调整。这点是相当影响团队士气,同时也加剧了研发组和产品组之间的矛盾。

\n

然后还有一个很重要的问题是多租户的态度上。早起项目是以服务中大体量客户为目的,所以设计之初并没有考虑多租户的问题。但实际项的进展过程中发现部分中大体量的客户也需要类似多租户的功能。这类客户需求与其说是需求多租户,不如说是希望有一个多租户级别的数据隔离,每个业务部门只能操作自己的业务部门的数据(数据包含商品,品牌,属性,平台店铺,订单,售后单据等),而总部依据可以查看一些汇总信息(例如销售对账单据等)。而随着2022年疫情状况严峻,很多中大客户对于内部平台升级和替换都变的更加谨慎和保守。所以当项目方向变成中小企业OMS时,多租户SaaS化的需求就更加迫切了。所以后期如何在原有系统上在扩展一套多租户SaaS化功能也是个课题。

\n

中间件

H项目使用的中间件中比较特别的有分布式任务管理系统xxl-job,消息队列RocketMQ,这两个中间件都是第一次使用。关于这两个中间件也有不是可以谈谈的。

\n

分布式任务管理系统 XXL-JOB

作为一个开源的分布式任务管理系统,xxl-job足够简单且相当容易上手使用,无论是基于Spring,还是原生Java做开发都是相当简单的。

\n

而不足之处也很明显,最近1年版本迭代速度明显变慢,有些功能和BUG都有待处理的。比如想实动态创建定时任务这类功能就必须自己动手去对xxl-job-admin(调度中心)进行修改。

\n

对于任务执行日志这块,现在xxl-job也是文件格式在执行器所在服务器上进行输出的,当执行器容器化或是运行在Kubernetes集群中的时候,当容器或是Pod重启后,就会丢失日志。这点在后期运维以及调试上还是有点麻烦的。

\n

消息队列 RocketMQ

阿里出品的消息队列如果基于云产品来使用的话问题不大(阿里云卖的价钱也是有点黑的),但是在运维上还是有点问题的比如Topic,Group这些东西都只能人工去添加,阿里云并没有提供导入服务。全系统如果使用消息队列的场景多的话,维护这些Topic和Group还是很酸爽的。还有一点RocketMQ重复消息这一点是无法保证的。用阿里云的原话

\n
\n

绝大多数情况下,消息是不重复的。作为一款分布式消息中间件,在网络抖动、应用处理超时等异常情况下,无法保证消息不重复,但是能保证消息不丢失。

\n
\n

实际运用上,在大数据量并发的情况下,我们实际上是遇到了消息重复消费的问题。这点还是需要通过代码干预(例如增加一个唯一MsgId,对MsgId做分布式锁来解决)。

\n

还有一点就是在旧版本SDK的默认配置下消费者的负载是有点小问题,总是有一个节点会比其他节点更容易消费到大量消息。据阿里云自己说明在新版本SDK的默认负载配置也已经修复这个问题了。

\n

(未完待续)

\n"},{"title":"H项目心路历程记(三)","_content":"\n# 现状\n\n对于一个基于微服务的中台系统来说,需要以什么样的颗粒度来拆分系统。这一直是基于微服务架构的系统中绕不过去的问题。\n\n已H项目来说,当初考量是这么划分微服务的,其中\n\n- `基础服务`: 作用主要是负责用户,角色权限,登陆鉴权,以及一些基础主档数据的日常维护功能 \n- `商品服务`: 物料,销售商品,款式,平台商品等和实际商品相关的主档数据\n- `库存服务`:负责管理各个库存层级库存(总库存,冻结库存,在途库存等),以及展示各种出入库单据,并向其他微服务提供出入库服务接口。\n- `订单服务`: 负责接收各个公域平台的ToC订单,售后单(含发货前退款,退货,换货单等),并根据配置策略进行单据路由(分仓,分物流,Hold单,加送赠品,通知仓库发货,通知平台发货等)\n- `接口服务`: 主要承揽中台服务和第三方平台间的交互处理(主要是第三方平台发起的主调通知以及回调)\n\n上述五个服务为核心服务,是保证整个中台系统基础业务功能运行的重要组成部分。除此之外还有,\n\n- `采购服务`:负责ToB业务线的处理\n- `财务服务`:满足一些企业的财务对账以及开具电子发票的功能\n- `报表服务`: 每天,每周定期从其他服务(主要是订单)\n- `会员服务`:管理各类会员\n\n等7,8个非核心服务,组成非核心业务。来构成一个完整的中台系统。\n\n# 拆分策略以及缺点\n\n当时主要考虑到整个中台系统是可以拆分出售,客户可以只选择购买自身需要的服务,而不需要的服务则无需购买服务,所以当初在拆分服务上在业务领域层面上需要更加低颗粒度;同时考虑到再业务高峰期(下午到晚上,就是各位疯狂刷淘宝,京东的时候)各个服务的负载是各不相同的,如果在高峰期对于特定服务需要进行精准扩容的话,拆分的颗粒度也需要更加细致,所以结果上导致整个系统满血运行起来要至少要十几个服务,外加一些没有云平台的中间件。整个Kubernetes环境中,至少要有十五六个Deployment,考虑到系统稳定性,一个Deployment也至少要有2到3个Pod,那整个系统至少就是50个Pod。\n\n对于一个中大型的电商企业再服务器成本上可以负担这样一个能支持50个Pod的Kubernentes集群(考虑到需要一些中间件的云产品,当时估算最小的服务器成本都要到70K/年,一个中大型企业能面对双十一流量的服务器成本都要到300K/年以上)。所以整个系统对于中小型企业来说前期的投入负担相当大(尤其是在后疫情时代的2022年,2023年),以至于早起的好几个客户都是需要我们自己贴补服务器费用的。\n\n# 复盘\n\n现在重新复盘当初服务的拆分策略,个人觉得对于核心业务来说,相似领域的一些服务应该可以适当的聚合。比如上述的五大核心服务上中的`商品服务`,`订单服务`,`库存服务`三个核心服务应该可以合并在一起。首先这3个核心服务是`H项目`的最重要的竞争力,几乎不可能出现把这3个服务拆分出售的可能性。\n\n而且在业务高峰期有`订单服务`有新订单处理时,势必会产生库存的变化,因此会调用到`库存服务`。三个服务之间的调用是相当频繁的,尤其是负责处理商品主档数据的`商品服务`,无论是通过HTTP或是其他方式的远程调用的话,都会有许多浪费的带宽。如果把三个服务整合,那么之间的调用基本上就是线程间的交互,无论是效率和可靠性上都会有更加显著的提升。\n\n# 结尾\n\n怎么拆分服务一直是个难题。虽然事后复盘的时候我的很多想法也不一定是正确,但是什么样的规模的客户需要使用什么样的拆分颗粒度,都是需要仔细考虑的。绝对不是一尘不变的。","source":"_drafts/H项目心路历程记-3.md","raw":"---\ntitle: H项目心路历程记(三)\ntags:\n - 架构\n - Kubernetes\n - Aliyun\n - Java\ncategories:\n - 设计\n---\n\n# 现状\n\n对于一个基于微服务的中台系统来说,需要以什么样的颗粒度来拆分系统。这一直是基于微服务架构的系统中绕不过去的问题。\n\n已H项目来说,当初考量是这么划分微服务的,其中\n\n- `基础服务`: 作用主要是负责用户,角色权限,登陆鉴权,以及一些基础主档数据的日常维护功能 \n- `商品服务`: 物料,销售商品,款式,平台商品等和实际商品相关的主档数据\n- `库存服务`:负责管理各个库存层级库存(总库存,冻结库存,在途库存等),以及展示各种出入库单据,并向其他微服务提供出入库服务接口。\n- `订单服务`: 负责接收各个公域平台的ToC订单,售后单(含发货前退款,退货,换货单等),并根据配置策略进行单据路由(分仓,分物流,Hold单,加送赠品,通知仓库发货,通知平台发货等)\n- `接口服务`: 主要承揽中台服务和第三方平台间的交互处理(主要是第三方平台发起的主调通知以及回调)\n\n上述五个服务为核心服务,是保证整个中台系统基础业务功能运行的重要组成部分。除此之外还有,\n\n- `采购服务`:负责ToB业务线的处理\n- `财务服务`:满足一些企业的财务对账以及开具电子发票的功能\n- `报表服务`: 每天,每周定期从其他服务(主要是订单)\n- `会员服务`:管理各类会员\n\n等7,8个非核心服务,组成非核心业务。来构成一个完整的中台系统。\n\n# 拆分策略以及缺点\n\n当时主要考虑到整个中台系统是可以拆分出售,客户可以只选择购买自身需要的服务,而不需要的服务则无需购买服务,所以当初在拆分服务上在业务领域层面上需要更加低颗粒度;同时考虑到再业务高峰期(下午到晚上,就是各位疯狂刷淘宝,京东的时候)各个服务的负载是各不相同的,如果在高峰期对于特定服务需要进行精准扩容的话,拆分的颗粒度也需要更加细致,所以结果上导致整个系统满血运行起来要至少要十几个服务,外加一些没有云平台的中间件。整个Kubernetes环境中,至少要有十五六个Deployment,考虑到系统稳定性,一个Deployment也至少要有2到3个Pod,那整个系统至少就是50个Pod。\n\n对于一个中大型的电商企业再服务器成本上可以负担这样一个能支持50个Pod的Kubernentes集群(考虑到需要一些中间件的云产品,当时估算最小的服务器成本都要到70K/年,一个中大型企业能面对双十一流量的服务器成本都要到300K/年以上)。所以整个系统对于中小型企业来说前期的投入负担相当大(尤其是在后疫情时代的2022年,2023年),以至于早起的好几个客户都是需要我们自己贴补服务器费用的。\n\n# 复盘\n\n现在重新复盘当初服务的拆分策略,个人觉得对于核心业务来说,相似领域的一些服务应该可以适当的聚合。比如上述的五大核心服务上中的`商品服务`,`订单服务`,`库存服务`三个核心服务应该可以合并在一起。首先这3个核心服务是`H项目`的最重要的竞争力,几乎不可能出现把这3个服务拆分出售的可能性。\n\n而且在业务高峰期有`订单服务`有新订单处理时,势必会产生库存的变化,因此会调用到`库存服务`。三个服务之间的调用是相当频繁的,尤其是负责处理商品主档数据的`商品服务`,无论是通过HTTP或是其他方式的远程调用的话,都会有许多浪费的带宽。如果把三个服务整合,那么之间的调用基本上就是线程间的交互,无论是效率和可靠性上都会有更加显著的提升。\n\n# 结尾\n\n怎么拆分服务一直是个难题。虽然事后复盘的时候我的很多想法也不一定是正确,但是什么样的规模的客户需要使用什么样的拆分颗粒度,都是需要仔细考虑的。绝对不是一尘不变的。","slug":"H项目心路历程记-3","published":0,"date":"2023-09-18T08:54:30.245Z","updated":"2023-09-21T09:54:28.525Z","_id":"clmrawzlm00006soigydjh9x7","comments":1,"layout":"post","photos":[],"link":"","content":"

现状

对于一个基于微服务的中台系统来说,需要以什么样的颗粒度来拆分系统。这一直是基于微服务架构的系统中绕不过去的问题。

\n

已H项目来说,当初考量是这么划分微服务的,其中

\n
    \n
  • 基础服务: 作用主要是负责用户,角色权限,登陆鉴权,以及一些基础主档数据的日常维护功能
  • \n
  • 商品服务: 物料,销售商品,款式,平台商品等和实际商品相关的主档数据
  • \n
  • 库存服务:负责管理各个库存层级库存(总库存,冻结库存,在途库存等),以及展示各种出入库单据,并向其他微服务提供出入库服务接口。
  • \n
  • 订单服务: 负责接收各个公域平台的ToC订单,售后单(含发货前退款,退货,换货单等),并根据配置策略进行单据路由(分仓,分物流,Hold单,加送赠品,通知仓库发货,通知平台发货等)
  • \n
  • 接口服务: 主要承揽中台服务和第三方平台间的交互处理(主要是第三方平台发起的主调通知以及回调)
  • \n
\n

上述五个服务为核心服务,是保证整个中台系统基础业务功能运行的重要组成部分。除此之外还有,

\n
    \n
  • 采购服务:负责ToB业务线的处理
  • \n
  • 财务服务:满足一些企业的财务对账以及开具电子发票的功能
  • \n
  • 报表服务: 每天,每周定期从其他服务(主要是订单)
  • \n
  • 会员服务:管理各类会员
  • \n
\n

等7,8个非核心服务,组成非核心业务。来构成一个完整的中台系统。

\n

拆分策略以及缺点

当时主要考虑到整个中台系统是可以拆分出售,客户可以只选择购买自身需要的服务,而不需要的服务则无需购买服务,所以当初在拆分服务上在业务领域层面上需要更加低颗粒度;同时考虑到再业务高峰期(下午到晚上,就是各位疯狂刷淘宝,京东的时候)各个服务的负载是各不相同的,如果在高峰期对于特定服务需要进行精准扩容的话,拆分的颗粒度也需要更加细致,所以结果上导致整个系统满血运行起来要至少要十几个服务,外加一些没有云平台的中间件。整个Kubernetes环境中,至少要有十五六个Deployment,考虑到系统稳定性,一个Deployment也至少要有2到3个Pod,那整个系统至少就是50个Pod。

\n

对于一个中大型的电商企业再服务器成本上可以负担这样一个能支持50个Pod的Kubernentes集群(考虑到需要一些中间件的云产品,当时估算最小的服务器成本都要到70K/年,一个中大型企业能面对双十一流量的服务器成本都要到300K/年以上)。所以整个系统对于中小型企业来说前期的投入负担相当大(尤其是在后疫情时代的2022年,2023年),以至于早起的好几个客户都是需要我们自己贴补服务器费用的。

\n

复盘

现在重新复盘当初服务的拆分策略,个人觉得对于核心业务来说,相似领域的一些服务应该可以适当的聚合。比如上述的五大核心服务上中的商品服务,订单服务,库存服务三个核心服务应该可以合并在一起。首先这3个核心服务是H项目的最重要的竞争力,几乎不可能出现把这3个服务拆分出售的可能性。

\n

而且在业务高峰期有订单服务有新订单处理时,势必会产生库存的变化,因此会调用到库存服务。三个服务之间的调用是相当频繁的,尤其是负责处理商品主档数据的商品服务,无论是通过HTTP或是其他方式的远程调用的话,都会有许多浪费的带宽。如果把三个服务整合,那么之间的调用基本上就是线程间的交互,无论是效率和可靠性上都会有更加显著的提升。

\n

结尾

怎么拆分服务一直是个难题。虽然事后复盘的时候我的很多想法也不一定是正确,但是什么样的规模的客户需要使用什么样的拆分颗粒度,都是需要仔细考虑的。绝对不是一尘不变的。

\n","site":{"data":{}},"excerpt":"","more":"

现状

对于一个基于微服务的中台系统来说,需要以什么样的颗粒度来拆分系统。这一直是基于微服务架构的系统中绕不过去的问题。

\n

已H项目来说,当初考量是这么划分微服务的,其中

\n
    \n
  • 基础服务: 作用主要是负责用户,角色权限,登陆鉴权,以及一些基础主档数据的日常维护功能
  • \n
  • 商品服务: 物料,销售商品,款式,平台商品等和实际商品相关的主档数据
  • \n
  • 库存服务:负责管理各个库存层级库存(总库存,冻结库存,在途库存等),以及展示各种出入库单据,并向其他微服务提供出入库服务接口。
  • \n
  • 订单服务: 负责接收各个公域平台的ToC订单,售后单(含发货前退款,退货,换货单等),并根据配置策略进行单据路由(分仓,分物流,Hold单,加送赠品,通知仓库发货,通知平台发货等)
  • \n
  • 接口服务: 主要承揽中台服务和第三方平台间的交互处理(主要是第三方平台发起的主调通知以及回调)
  • \n
\n

上述五个服务为核心服务,是保证整个中台系统基础业务功能运行的重要组成部分。除此之外还有,

\n
    \n
  • 采购服务:负责ToB业务线的处理
  • \n
  • 财务服务:满足一些企业的财务对账以及开具电子发票的功能
  • \n
  • 报表服务: 每天,每周定期从其他服务(主要是订单)
  • \n
  • 会员服务:管理各类会员
  • \n
\n

等7,8个非核心服务,组成非核心业务。来构成一个完整的中台系统。

\n

拆分策略以及缺点

当时主要考虑到整个中台系统是可以拆分出售,客户可以只选择购买自身需要的服务,而不需要的服务则无需购买服务,所以当初在拆分服务上在业务领域层面上需要更加低颗粒度;同时考虑到再业务高峰期(下午到晚上,就是各位疯狂刷淘宝,京东的时候)各个服务的负载是各不相同的,如果在高峰期对于特定服务需要进行精准扩容的话,拆分的颗粒度也需要更加细致,所以结果上导致整个系统满血运行起来要至少要十几个服务,外加一些没有云平台的中间件。整个Kubernetes环境中,至少要有十五六个Deployment,考虑到系统稳定性,一个Deployment也至少要有2到3个Pod,那整个系统至少就是50个Pod。

\n

对于一个中大型的电商企业再服务器成本上可以负担这样一个能支持50个Pod的Kubernentes集群(考虑到需要一些中间件的云产品,当时估算最小的服务器成本都要到70K/年,一个中大型企业能面对双十一流量的服务器成本都要到300K/年以上)。所以整个系统对于中小型企业来说前期的投入负担相当大(尤其是在后疫情时代的2022年,2023年),以至于早起的好几个客户都是需要我们自己贴补服务器费用的。

\n

复盘

现在重新复盘当初服务的拆分策略,个人觉得对于核心业务来说,相似领域的一些服务应该可以适当的聚合。比如上述的五大核心服务上中的商品服务,订单服务,库存服务三个核心服务应该可以合并在一起。首先这3个核心服务是H项目的最重要的竞争力,几乎不可能出现把这3个服务拆分出售的可能性。

\n

而且在业务高峰期有订单服务有新订单处理时,势必会产生库存的变化,因此会调用到库存服务。三个服务之间的调用是相当频繁的,尤其是负责处理商品主档数据的商品服务,无论是通过HTTP或是其他方式的远程调用的话,都会有许多浪费的带宽。如果把三个服务整合,那么之间的调用基本上就是线程间的交互,无论是效率和可靠性上都会有更加显著的提升。

\n

结尾

怎么拆分服务一直是个难题。虽然事后复盘的时候我的很多想法也不一定是正确,但是什么样的规模的客户需要使用什么样的拆分颗粒度,都是需要仔细考虑的。绝对不是一尘不变的。

\n"},{"title":"H项目心路历程记 (二)","date":"2023-07-12T09:29:36.000Z","_content":"\n\n**(书接上文)**\n\n前文已经吐槽了产品设计以及中间件上的相关问题。接下来讨论一下技术上的一些问题。\n\n# 关于配置\n\n对于一个Spring的应用来说,应用配置怎么存取其实并不能说是一个技术问题。更应该算是设计或是架构问题。哪些配置应该进入配置文件,通过微服务配置中心(例如Nacos, Apollo,Kubernetes ConfigMap等)来进行在线配置,哪些配置应该通过其他方式来配置,亦或者哪些配置项目看似是配置项其实本质上就是常量。实际上截止现在`H项目`的确在有些微服务上是完全没有厘清三者之间的区别,以及界限。以至于现在堆积在配置文件中的内容实在太多。多个运行环境需要统一维护的难度实在太大。\n\n对于配置来说,理想的状态应该是下面这样的。\n\n1. 通过微服务配置中心来影响Spring的配置文件来实现动态配置的方案,通常会伴随Spring容器的重启(有些情况下甚至需要整个Docker容器),所以存在这里的配置项应该是随着运行环境改变才会需要改动的配置。例如:\n\n - 外部系统的链接信息(比如MySQL, Redis的连接信息)\n - 外部系统的认证凭证(数据库用户密码,第三方系统的密钥等)\n - 因为运行环境不同导致的不同变量 (例如消息队列的Topic,Group需要根据运行环境使用不同的名称)\n\n2. 通过数据库或是Redis存储的配置信息,这类配置主要是应该以业务级别的配置,可以通过页面进行修改,瞬间或是几分钟内生效,且不会造成微服务重启(无论是Spring或是Docker容器的重启)。对于这类配置有几个问题还是需要注意的。\n - 配置是否需要集中管理,每个微服务,都有自己独特的业务配置,这些业务配置是否需要集中到一个微服务统一管理,还是分散到各自的微服务进行单独管理。\n - 全局性统一管理的业务配置对外保留接口应该利用缓存有效的减少数据库访问的频次。甚至可以只使用内存而不是Redis来缓存这部分内容。如果使用内存的话则需要考虑失效时效和重新装载的机制。\n\n3. 无效配置,实际上很多配置说是可以修改,其实自从开始使用的那天开始就没有修改过。比如用来做分布式锁的键值。这部分需要梳理出来直接以常量的形式做在代码里。\n\n# 数据清理以及归档\n\n对于电商行业的零售业务来说每天产生的单据比如订单少做200,300条,多的是上千条。加上明细数据。几个月以后这些数据就会给MySQL的库表增加检索压力。整个`H项目`在这块是相当薄弱的。只依靠云平台的能力,对一些不需要留档的数据进行清理。远远没有能定期归档。\n\n对于数据清理这块大致可以分为这几类\n\n- 定期可以无脑物理删除的数据,比如一些日志类数据。\n- 定期直接归档的数据,归档后即可清理的数据。\n- 业务必须处理完成后才能归档并清理的数据。例如订单数据等。\n\n对于数据归档的时候,还需要考虑一些相关联单据数据一并归档。比如订单归档时,同时需要归档相关联的退换货单,发货单,以及操作日志等等。 归档格式基本上就是考虑单条数据以JSON格式存储,然后一个时间段的数据做成压缩包存储在OSS上,然后提供给客户下载。\n\n而对于归档数据的检索功能,客户选择需要归档的数据,我们可以将归档的数据(每个压缩包内的所有数据)导入到一个主数据库以外的,临时文档数据库(例如MongoDB),然后针对这个临时的文档数据库进行检索。当然提供的检索项目也会远少于热数据的检索项目。这个临时文档数据库基本上保持一两天数据,定期就全部回收。\n\n**未完待续**\n\n","source":"_posts/H项目心路历程记-2.md","raw":"---\ntitle: H项目心路历程记 (二)\ntags:\n - 架构\n - Kubernetes\n - Aliyun\n - Java\ncategories:\n - 设计\ndate: 2023-07-12 17:29:36\n---\n\n\n**(书接上文)**\n\n前文已经吐槽了产品设计以及中间件上的相关问题。接下来讨论一下技术上的一些问题。\n\n# 关于配置\n\n对于一个Spring的应用来说,应用配置怎么存取其实并不能说是一个技术问题。更应该算是设计或是架构问题。哪些配置应该进入配置文件,通过微服务配置中心(例如Nacos, Apollo,Kubernetes ConfigMap等)来进行在线配置,哪些配置应该通过其他方式来配置,亦或者哪些配置项目看似是配置项其实本质上就是常量。实际上截止现在`H项目`的确在有些微服务上是完全没有厘清三者之间的区别,以及界限。以至于现在堆积在配置文件中的内容实在太多。多个运行环境需要统一维护的难度实在太大。\n\n对于配置来说,理想的状态应该是下面这样的。\n\n1. 通过微服务配置中心来影响Spring的配置文件来实现动态配置的方案,通常会伴随Spring容器的重启(有些情况下甚至需要整个Docker容器),所以存在这里的配置项应该是随着运行环境改变才会需要改动的配置。例如:\n\n - 外部系统的链接信息(比如MySQL, Redis的连接信息)\n - 外部系统的认证凭证(数据库用户密码,第三方系统的密钥等)\n - 因为运行环境不同导致的不同变量 (例如消息队列的Topic,Group需要根据运行环境使用不同的名称)\n\n2. 通过数据库或是Redis存储的配置信息,这类配置主要是应该以业务级别的配置,可以通过页面进行修改,瞬间或是几分钟内生效,且不会造成微服务重启(无论是Spring或是Docker容器的重启)。对于这类配置有几个问题还是需要注意的。\n - 配置是否需要集中管理,每个微服务,都有自己独特的业务配置,这些业务配置是否需要集中到一个微服务统一管理,还是分散到各自的微服务进行单独管理。\n - 全局性统一管理的业务配置对外保留接口应该利用缓存有效的减少数据库访问的频次。甚至可以只使用内存而不是Redis来缓存这部分内容。如果使用内存的话则需要考虑失效时效和重新装载的机制。\n\n3. 无效配置,实际上很多配置说是可以修改,其实自从开始使用的那天开始就没有修改过。比如用来做分布式锁的键值。这部分需要梳理出来直接以常量的形式做在代码里。\n\n# 数据清理以及归档\n\n对于电商行业的零售业务来说每天产生的单据比如订单少做200,300条,多的是上千条。加上明细数据。几个月以后这些数据就会给MySQL的库表增加检索压力。整个`H项目`在这块是相当薄弱的。只依靠云平台的能力,对一些不需要留档的数据进行清理。远远没有能定期归档。\n\n对于数据清理这块大致可以分为这几类\n\n- 定期可以无脑物理删除的数据,比如一些日志类数据。\n- 定期直接归档的数据,归档后即可清理的数据。\n- 业务必须处理完成后才能归档并清理的数据。例如订单数据等。\n\n对于数据归档的时候,还需要考虑一些相关联单据数据一并归档。比如订单归档时,同时需要归档相关联的退换货单,发货单,以及操作日志等等。 归档格式基本上就是考虑单条数据以JSON格式存储,然后一个时间段的数据做成压缩包存储在OSS上,然后提供给客户下载。\n\n而对于归档数据的检索功能,客户选择需要归档的数据,我们可以将归档的数据(每个压缩包内的所有数据)导入到一个主数据库以外的,临时文档数据库(例如MongoDB),然后针对这个临时的文档数据库进行检索。当然提供的检索项目也会远少于热数据的检索项目。这个临时文档数据库基本上保持一两天数据,定期就全部回收。\n\n**未完待续**\n\n","slug":"H项目心路历程记-2","published":1,"updated":"2023-07-12T09:29:36.609Z","comments":1,"layout":"post","photos":[],"link":"","_id":"clmrawzlp00016soicrzra15d","content":"

(书接上文)

\n

前文已经吐槽了产品设计以及中间件上的相关问题。接下来讨论一下技术上的一些问题。

\n

关于配置

对于一个Spring的应用来说,应用配置怎么存取其实并不能说是一个技术问题。更应该算是设计或是架构问题。哪些配置应该进入配置文件,通过微服务配置中心(例如Nacos, Apollo,Kubernetes ConfigMap等)来进行在线配置,哪些配置应该通过其他方式来配置,亦或者哪些配置项目看似是配置项其实本质上就是常量。实际上截止现在H项目的确在有些微服务上是完全没有厘清三者之间的区别,以及界限。以至于现在堆积在配置文件中的内容实在太多。多个运行环境需要统一维护的难度实在太大。

\n

对于配置来说,理想的状态应该是下面这样的。

\n
    \n
  1. 通过微服务配置中心来影响Spring的配置文件来实现动态配置的方案,通常会伴随Spring容器的重启(有些情况下甚至需要整个Docker容器),所以存在这里的配置项应该是随着运行环境改变才会需要改动的配置。例如:

    \n
      \n
    • 外部系统的链接信息(比如MySQL, Redis的连接信息)
    • \n
    • 外部系统的认证凭证(数据库用户密码,第三方系统的密钥等)
    • \n
    • 因为运行环境不同导致的不同变量 (例如消息队列的Topic,Group需要根据运行环境使用不同的名称)
    • \n
    \n
  2. \n
  3. 通过数据库或是Redis存储的配置信息,这类配置主要是应该以业务级别的配置,可以通过页面进行修改,瞬间或是几分钟内生效,且不会造成微服务重启(无论是Spring或是Docker容器的重启)。对于这类配置有几个问题还是需要注意的。

    \n
      \n
    • 配置是否需要集中管理,每个微服务,都有自己独特的业务配置,这些业务配置是否需要集中到一个微服务统一管理,还是分散到各自的微服务进行单独管理。
    • \n
    • 全局性统一管理的业务配置对外保留接口应该利用缓存有效的减少数据库访问的频次。甚至可以只使用内存而不是Redis来缓存这部分内容。如果使用内存的话则需要考虑失效时效和重新装载的机制。
    • \n
    \n
  4. \n
  5. 无效配置,实际上很多配置说是可以修改,其实自从开始使用的那天开始就没有修改过。比如用来做分布式锁的键值。这部分需要梳理出来直接以常量的形式做在代码里。

    \n
  6. \n
\n

数据清理以及归档

对于电商行业的零售业务来说每天产生的单据比如订单少做200,300条,多的是上千条。加上明细数据。几个月以后这些数据就会给MySQL的库表增加检索压力。整个H项目在这块是相当薄弱的。只依靠云平台的能力,对一些不需要留档的数据进行清理。远远没有能定期归档。

\n

对于数据清理这块大致可以分为这几类

\n
    \n
  • 定期可以无脑物理删除的数据,比如一些日志类数据。
  • \n
  • 定期直接归档的数据,归档后即可清理的数据。
  • \n
  • 业务必须处理完成后才能归档并清理的数据。例如订单数据等。
  • \n
\n

对于数据归档的时候,还需要考虑一些相关联单据数据一并归档。比如订单归档时,同时需要归档相关联的退换货单,发货单,以及操作日志等等。 归档格式基本上就是考虑单条数据以JSON格式存储,然后一个时间段的数据做成压缩包存储在OSS上,然后提供给客户下载。

\n

而对于归档数据的检索功能,客户选择需要归档的数据,我们可以将归档的数据(每个压缩包内的所有数据)导入到一个主数据库以外的,临时文档数据库(例如MongoDB),然后针对这个临时的文档数据库进行检索。当然提供的检索项目也会远少于热数据的检索项目。这个临时文档数据库基本上保持一两天数据,定期就全部回收。

\n

未完待续

\n","site":{"data":{}},"excerpt":"","more":"

(书接上文)

\n

前文已经吐槽了产品设计以及中间件上的相关问题。接下来讨论一下技术上的一些问题。

\n

关于配置

对于一个Spring的应用来说,应用配置怎么存取其实并不能说是一个技术问题。更应该算是设计或是架构问题。哪些配置应该进入配置文件,通过微服务配置中心(例如Nacos, Apollo,Kubernetes ConfigMap等)来进行在线配置,哪些配置应该通过其他方式来配置,亦或者哪些配置项目看似是配置项其实本质上就是常量。实际上截止现在H项目的确在有些微服务上是完全没有厘清三者之间的区别,以及界限。以至于现在堆积在配置文件中的内容实在太多。多个运行环境需要统一维护的难度实在太大。

\n

对于配置来说,理想的状态应该是下面这样的。

\n
    \n
  1. 通过微服务配置中心来影响Spring的配置文件来实现动态配置的方案,通常会伴随Spring容器的重启(有些情况下甚至需要整个Docker容器),所以存在这里的配置项应该是随着运行环境改变才会需要改动的配置。例如:

    \n
      \n
    • 外部系统的链接信息(比如MySQL, Redis的连接信息)
    • \n
    • 外部系统的认证凭证(数据库用户密码,第三方系统的密钥等)
    • \n
    • 因为运行环境不同导致的不同变量 (例如消息队列的Topic,Group需要根据运行环境使用不同的名称)
    • \n
    \n
  2. \n
  3. 通过数据库或是Redis存储的配置信息,这类配置主要是应该以业务级别的配置,可以通过页面进行修改,瞬间或是几分钟内生效,且不会造成微服务重启(无论是Spring或是Docker容器的重启)。对于这类配置有几个问题还是需要注意的。

    \n
      \n
    • 配置是否需要集中管理,每个微服务,都有自己独特的业务配置,这些业务配置是否需要集中到一个微服务统一管理,还是分散到各自的微服务进行单独管理。
    • \n
    • 全局性统一管理的业务配置对外保留接口应该利用缓存有效的减少数据库访问的频次。甚至可以只使用内存而不是Redis来缓存这部分内容。如果使用内存的话则需要考虑失效时效和重新装载的机制。
    • \n
    \n
  4. \n
  5. 无效配置,实际上很多配置说是可以修改,其实自从开始使用的那天开始就没有修改过。比如用来做分布式锁的键值。这部分需要梳理出来直接以常量的形式做在代码里。

    \n
  6. \n
\n

数据清理以及归档

对于电商行业的零售业务来说每天产生的单据比如订单少做200,300条,多的是上千条。加上明细数据。几个月以后这些数据就会给MySQL的库表增加检索压力。整个H项目在这块是相当薄弱的。只依靠云平台的能力,对一些不需要留档的数据进行清理。远远没有能定期归档。

\n

对于数据清理这块大致可以分为这几类

\n
    \n
  • 定期可以无脑物理删除的数据,比如一些日志类数据。
  • \n
  • 定期直接归档的数据,归档后即可清理的数据。
  • \n
  • 业务必须处理完成后才能归档并清理的数据。例如订单数据等。
  • \n
\n

对于数据归档的时候,还需要考虑一些相关联单据数据一并归档。比如订单归档时,同时需要归档相关联的退换货单,发货单,以及操作日志等等。 归档格式基本上就是考虑单条数据以JSON格式存储,然后一个时间段的数据做成压缩包存储在OSS上,然后提供给客户下载。

\n

而对于归档数据的检索功能,客户选择需要归档的数据,我们可以将归档的数据(每个压缩包内的所有数据)导入到一个主数据库以外的,临时文档数据库(例如MongoDB),然后针对这个临时的文档数据库进行检索。当然提供的检索项目也会远少于热数据的检索项目。这个临时文档数据库基本上保持一两天数据,定期就全部回收。

\n

未完待续

\n"}],"PostAsset":[],"PostCategory":[{"post_id":"cljo9zkjl00004goifkkd2ktv","category_id":"cljo9zkjo00024goi1kpe1pou","_id":"cljo9zkjr000c4goi0mq29xx4"},{"post_id":"cljo9zkjr000a4goihf3c0c7k","category_id":"cljo9zkjq00074goifz3v6gea","_id":"cljo9zkjs000g4goiet2734as"},{"post_id":"cljo9zkjs000f4goihc0y4xef","category_id":"cljo9zkjq00074goifz3v6gea","_id":"cljo9zkjt000k4goie1pbf53o"},{"post_id":"cljo9zkjp00044goibj6kewct","category_id":"cljo9zkjs000d4goi3nzx2545","_id":"cljo9zkjt000o4goidt4m82tf"},{"post_id":"cljo9zkjp00054goi3n9a45hn","category_id":"cljo9zkjq00074goifz3v6gea","_id":"cljo9zkjt000q4goi0k7wfn7p"},{"post_id":"cljo9zkjq00064goihyn69kzz","category_id":"cljo9zkjs000d4goi3nzx2545","_id":"cljo9zkjt000s4goia8rn0zud"},{"post_id":"cljo9zkjn00014goi59hyfxy3","category_id":"cljo9zkjq00074goifz3v6gea","_id":"cljo9zkju000v4goi6uso8nlj"},{"post_id":"cljo9zkjn00014goi59hyfxy3","category_id":"cljo9zkjt000p4goi5v02detb","_id":"cljo9zkju000x4goies5feufq"},{"post_id":"cljo9zkjr000b4goi8fzg64kj","category_id":"cljo9zkjq00074goifz3v6gea","_id":"cljo9zkju000z4goi9et71czk"},{"post_id":"cljo9zkjr000b4goi8fzg64kj","category_id":"cljo9zkjt000p4goi5v02detb","_id":"cljo9zkju00124goigp2i4zz0"},{"post_id":"cljwneykb0006m3oi131phei7","category_id":"cljo9zkjs000d4goi3nzx2545","_id":"cljwneykd0009m3oicrled1fj"},{"post_id":"clmrawzlm00006soigydjh9x7","category_id":"cljo9zkjs000d4goi3nzx2545","_id":"clmrawzlr00046soieb5b7tr4"},{"post_id":"clmrawzlp00016soicrzra15d","category_id":"cljo9zkjs000d4goi3nzx2545","_id":"clmrawzlr00066soigc6m3gjk"}],"PostTag":[{"post_id":"cljo9zkjl00004goifkkd2ktv","tag_id":"cljo9zkjp00034goi6etuf0oo","_id":"cljo9zkjr00094goi4ps73yw3"},{"post_id":"cljo9zkjn00014goi59hyfxy3","tag_id":"cljo9zkjq00084goidlhtdo5k","_id":"cljo9zkjt000j4goi22tef3sg"},{"post_id":"cljo9zkjn00014goi59hyfxy3","tag_id":"cljo9zkjs000e4goi3yj4hcgl","_id":"cljo9zkjt000l4goibu8x3t4b"},{"post_id":"cljo9zkjp00044goibj6kewct","tag_id":"cljo9zkjs000i4goi109ihmrk","_id":"cljo9zkju000y4goigwbmflsg"},{"post_id":"cljo9zkjp00044goibj6kewct","tag_id":"cljo9zkjs000e4goi3yj4hcgl","_id":"cljo9zkju00104goih2wp813r"},{"post_id":"cljo9zkjp00044goibj6kewct","tag_id":"cljo9zkjq00084goidlhtdo5k","_id":"cljo9zkju00134goi3g3l1ya3"},{"post_id":"cljo9zkjp00044goibj6kewct","tag_id":"cljo9zkjt000u4goih39t9zrk","_id":"cljo9zkju00144goid10catlw"},{"post_id":"cljo9zkjp00054goi3n9a45hn","tag_id":"cljo9zkjt000u4goih39t9zrk","_id":"cljo9zkju00164goi0gv268px"},{"post_id":"cljo9zkjp00054goi3n9a45hn","tag_id":"cljo9zkju00114goieubl5921","_id":"cljo9zkju00174goidmah9yhy"},{"post_id":"cljo9zkjq00064goihyn69kzz","tag_id":"cljo9zkjs000i4goi109ihmrk","_id":"cljo9zkjv001c4goici9xds7j"},{"post_id":"cljo9zkjq00064goihyn69kzz","tag_id":"cljo9zkjs000e4goi3yj4hcgl","_id":"cljo9zkjv001d4goihkj69l7i"},{"post_id":"cljo9zkjq00064goihyn69kzz","tag_id":"cljo9zkjq00084goidlhtdo5k","_id":"cljo9zkjv001f4goihdtue80h"},{"post_id":"cljo9zkjq00064goihyn69kzz","tag_id":"cljo9zkjt000u4goih39t9zrk","_id":"cljo9zkjv001g4goi0huc28cz"},{"post_id":"cljo9zkjr000a4goihf3c0c7k","tag_id":"cljo9zkjv001b4goi1sdxcxze","_id":"cljo9zkjw001k4goie1z56x1s"},{"post_id":"cljo9zkjr000a4goihf3c0c7k","tag_id":"cljo9zkjv001e4goi6oi7esl6","_id":"cljo9zkjw001l4goi2hjw6sry"},{"post_id":"cljo9zkjr000a4goihf3c0c7k","tag_id":"cljo9zkjv001h4goiah5gceqr","_id":"cljo9zkjw001n4goi94n5fctc"},{"post_id":"cljo9zkjr000a4goihf3c0c7k","tag_id":"cljo9zkjw001i4goi4zi09k5x","_id":"cljo9zkjw001o4goi95y7cjn9"},{"post_id":"cljo9zkjr000b4goi8fzg64kj","tag_id":"cljo9zkjq00084goidlhtdo5k","_id":"cljo9zkjx001q4goid5bicl1b"},{"post_id":"cljo9zkjr000b4goi8fzg64kj","tag_id":"cljo9zkjs000e4goi3yj4hcgl","_id":"cljo9zkjx001r4goi536vf7nc"},{"post_id":"cljo9zkjs000f4goihc0y4xef","tag_id":"cljo9zkjw001m4goi642d5x8a","_id":"cljo9zkjx001s4goiean537tz"},{"post_id":"cljo9zkjs000f4goihc0y4xef","tag_id":"cljo9zkjx001p4goihwt36hpf","_id":"cljo9zkjx001t4goic1bw1e2l"},{"post_id":"cljwneykb0006m3oi131phei7","tag_id":"cljo9zkjs000i4goi109ihmrk","_id":"cljwneykc0007m3oi93z1ewpb"},{"post_id":"cljwneykb0006m3oi131phei7","tag_id":"cljo9zkjs000e4goi3yj4hcgl","_id":"cljwneykd0008m3oi1xwy8avl"},{"post_id":"cljwneykb0006m3oi131phei7","tag_id":"cljo9zkjq00084goidlhtdo5k","_id":"cljwneykd000am3oick7zfijn"},{"post_id":"cljwneykb0006m3oi131phei7","tag_id":"cljo9zkjt000u4goih39t9zrk","_id":"cljwneykd000bm3oi0yvtc6ga"},{"post_id":"clmrawzlm00006soigydjh9x7","tag_id":"cljo9zkjs000i4goi109ihmrk","_id":"clmrawzlq00026soibeo02nbx"},{"post_id":"clmrawzlm00006soigydjh9x7","tag_id":"cljo9zkjs000e4goi3yj4hcgl","_id":"clmrawzlr00036soibkzb91cn"},{"post_id":"clmrawzlm00006soigydjh9x7","tag_id":"cljo9zkjq00084goidlhtdo5k","_id":"clmrawzlr00056soi3d6y2tq8"},{"post_id":"clmrawzlm00006soigydjh9x7","tag_id":"cljo9zkjt000u4goih39t9zrk","_id":"clmrawzlr00076soi06v3c5dm"},{"post_id":"clmrawzlp00016soicrzra15d","tag_id":"cljo9zkjs000i4goi109ihmrk","_id":"clmrawzlr00086soids0pf4ej"},{"post_id":"clmrawzlp00016soicrzra15d","tag_id":"cljo9zkjs000e4goi3yj4hcgl","_id":"clmrawzlr00096soi6xu91edv"},{"post_id":"clmrawzlp00016soicrzra15d","tag_id":"cljo9zkjq00084goidlhtdo5k","_id":"clmrawzlr000a6soia4sp43e8"},{"post_id":"clmrawzlp00016soicrzra15d","tag_id":"cljo9zkjt000u4goih39t9zrk","_id":"clmrawzlr000b6soi65xqe8ig"}],"Tag":[{"name":"阅读","_id":"cljo9zkjp00034goi6etuf0oo"},{"name":"Aliyun","_id":"cljo9zkjq00084goidlhtdo5k"},{"name":"Kubernetes","_id":"cljo9zkjs000e4goi3yj4hcgl"},{"name":"架构","_id":"cljo9zkjs000i4goi109ihmrk"},{"name":"Java","_id":"cljo9zkjt000u4goih39t9zrk"},{"name":"Spring","_id":"cljo9zkju00114goieubl5921"},{"name":"ubuntu","_id":"cljo9zkjv001b4goi1sdxcxze"},{"name":"node.js","_id":"cljo9zkjv001e4goi6oi7esl6"},{"name":"nginx","_id":"cljo9zkjv001h4goiah5gceqr"},{"name":"mongodb","_id":"cljo9zkjw001i4goi4zi09k5x"},{"name":"java","_id":"cljo9zkjw001m4goi642d5x8a"},{"name":"generic","_id":"cljo9zkjx001p4goihwt36hpf"}]}} \ No newline at end of file diff --git a/source/images/avatar.jpeg b/images/avatar.jpeg similarity index 100% rename from source/images/avatar.jpeg rename to images/avatar.jpeg diff --git a/img/avatar.png b/img/avatar.png new file mode 100644 index 0000000..baba51e Binary files /dev/null and b/img/avatar.png differ diff --git a/img/favicon.svg b/img/favicon.svg new file mode 100644 index 0000000..32a8683 --- /dev/null +++ b/img/favicon.svg @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/img/logo.svg b/img/logo.svg new file mode 100644 index 0000000..038b593 --- /dev/null +++ b/img/logo.svg @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/img/og_image.png b/img/og_image.png new file mode 100644 index 0000000..3aa9340 Binary files /dev/null and b/img/og_image.png differ diff --git a/img/razor-bottom-black.svg b/img/razor-bottom-black.svg new file mode 100644 index 0000000..a3bcb49 --- /dev/null +++ b/img/razor-bottom-black.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/img/razor-top-black.svg b/img/razor-top-black.svg new file mode 100644 index 0000000..df7d497 --- /dev/null +++ b/img/razor-top-black.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..8c29a86 --- /dev/null +++ b/index.html @@ -0,0 +1,380 @@ + +西门闲话

H项目心路历程记(三)

现状

对于一个基于微服务的中台系统来说,需要以什么样的颗粒度来拆分系统。这一直是基于微服务架构的系统中绕不过去的问题。

+

已H项目来说,当初考量是这么划分微服务的,其中

+
    +
  • 基础服务: 作用主要是负责用户,角色权限,登陆鉴权,以及一些基础主档数据的日常维护功能
  • +
  • 商品服务: 物料,销售商品,款式,平台商品等和实际商品相关的主档数据
  • +
  • 库存服务:负责管理各个库存层级库存(总库存,冻结库存,在途库存等),以及展示各种出入库单据,并向其他微服务提供出入库服务接口。
  • +
  • 订单服务: 负责接收各个公域平台的ToC订单,售后单(含发货前退款,退货,换货单等),并根据配置策略进行单据路由(分仓,分物流,Hold单,加送赠品,通知仓库发货,通知平台发货等)
  • +
  • 接口服务: 主要承揽中台服务和第三方平台间的交互处理(主要是第三方平台发起的主调通知以及回调)
  • +
+

上述五个服务为核心服务,是保证整个中台系统基础业务功能运行的重要组成部分。除此之外还有,

+
    +
  • 采购服务:负责ToB业务线的处理
  • +
  • 财务服务:满足一些企业的财务对账以及开具电子发票的功能
  • +
  • 报表服务: 每天,每周定期从其他服务(主要是订单)
  • +
  • 会员服务:管理各类会员
  • +
+

等7,8个非核心服务,组成非核心业务。来构成一个完整的中台系统。

+

拆分策略以及缺点

当时主要考虑到整个中台系统是可以拆分出售,客户可以只选择购买自身需要的服务,而不需要的服务则无需购买服务,所以当初在拆分服务上在业务领域层面上需要更加低颗粒度;同时考虑到再业务高峰期(下午到晚上,就是各位疯狂刷淘宝,京东的时候)各个服务的负载是各不相同的,如果在高峰期对于特定服务需要进行精准扩容的话,拆分的颗粒度也需要更加细致,所以结果上导致整个系统满血运行起来要至少要十几个服务,外加一些没有云平台的中间件。整个Kubernetes环境中,至少要有十五六个Deployment,考虑到系统稳定性,一个Deployment也至少要有2到3个Pod,那整个系统至少就是50个Pod。

+

对于一个中大型的电商企业再服务器成本上可以负担这样一个能支持50个Pod的Kubernentes集群(考虑到需要一些中间件的云产品,当时估算最小的服务器成本都要到70K/年,一个中大型企业能面对双十一流量的服务器成本都要到300K/年以上)。所以整个系统对于中小型企业来说前期的投入负担相当大(尤其是在后疫情时代的2022年,2023年),以至于早起的好几个客户都是需要我们自己贴补服务器费用的。

+

复盘

现在重新复盘当初服务的拆分策略,个人觉得对于核心业务来说,相似领域的一些服务应该可以适当的聚合。比如上述的五大核心服务上中的商品服务,订单服务,库存服务三个核心服务应该可以合并在一起。首先这3个核心服务是H项目的最重要的竞争力,几乎不可能出现把这3个服务拆分出售的可能性。

+

而且在业务高峰期有订单服务有新订单处理时,势必会产生库存的变化,因此会调用到库存服务。三个服务之间的调用是相当频繁的,尤其是负责处理商品主档数据的商品服务,无论是通过HTTP或是其他方式的远程调用的话,都会有许多浪费的带宽。如果把三个服务整合,那么之间的调用基本上就是线程间的交互,无论是效率和可靠性上都会有更加显著的提升。

+

结尾

怎么拆分服务一直是个难题。虽然事后复盘的时候我的很多想法也不一定是正确,但是什么样的规模的客户需要使用什么样的拆分颗粒度,都是需要仔细考虑的。绝对不是一尘不变的。

+

H项目心路历程记 (二)

(书接上文)

+

前文已经吐槽了产品设计以及中间件上的相关问题。接下来讨论一下技术上的一些问题。

+

关于配置

对于一个Spring的应用来说,应用配置怎么存取其实并不能说是一个技术问题。更应该算是设计或是架构问题。哪些配置应该进入配置文件,通过微服务配置中心(例如Nacos, Apollo,Kubernetes ConfigMap等)来进行在线配置,哪些配置应该通过其他方式来配置,亦或者哪些配置项目看似是配置项其实本质上就是常量。实际上截止现在H项目的确在有些微服务上是完全没有厘清三者之间的区别,以及界限。以至于现在堆积在配置文件中的内容实在太多。多个运行环境需要统一维护的难度实在太大。

+

对于配置来说,理想的状态应该是下面这样的。

+
    +
  1. 通过微服务配置中心来影响Spring的配置文件来实现动态配置的方案,通常会伴随Spring容器的重启(有些情况下甚至需要整个Docker容器),所以存在这里的配置项应该是随着运行环境改变才会需要改动的配置。例如:

    +
      +
    • 外部系统的链接信息(比如MySQL, Redis的连接信息)
    • +
    • 外部系统的认证凭证(数据库用户密码,第三方系统的密钥等)
    • +
    • 因为运行环境不同导致的不同变量 (例如消息队列的Topic,Group需要根据运行环境使用不同的名称)
    • +
    +
  2. +
  3. 通过数据库或是Redis存储的配置信息,这类配置主要是应该以业务级别的配置,可以通过页面进行修改,瞬间或是几分钟内生效,且不会造成微服务重启(无论是Spring或是Docker容器的重启)。对于这类配置有几个问题还是需要注意的。

    +
      +
    • 配置是否需要集中管理,每个微服务,都有自己独特的业务配置,这些业务配置是否需要集中到一个微服务统一管理,还是分散到各自的微服务进行单独管理。
    • +
    • 全局性统一管理的业务配置对外保留接口应该利用缓存有效的减少数据库访问的频次。甚至可以只使用内存而不是Redis来缓存这部分内容。如果使用内存的话则需要考虑失效时效和重新装载的机制。
    • +
    +
  4. +
  5. 无效配置,实际上很多配置说是可以修改,其实自从开始使用的那天开始就没有修改过。比如用来做分布式锁的键值。这部分需要梳理出来直接以常量的形式做在代码里。

    +
  6. +
+

数据清理以及归档

对于电商行业的零售业务来说每天产生的单据比如订单少做200,300条,多的是上千条。加上明细数据。几个月以后这些数据就会给MySQL的库表增加检索压力。整个H项目在这块是相当薄弱的。只依靠云平台的能力,对一些不需要留档的数据进行清理。远远没有能定期归档。

+

对于数据清理这块大致可以分为这几类

+
    +
  • 定期可以无脑物理删除的数据,比如一些日志类数据。
  • +
  • 定期直接归档的数据,归档后即可清理的数据。
  • +
  • 业务必须处理完成后才能归档并清理的数据。例如订单数据等。
  • +
+

对于数据归档的时候,还需要考虑一些相关联单据数据一并归档。比如订单归档时,同时需要归档相关联的退换货单,发货单,以及操作日志等等。 归档格式基本上就是考虑单条数据以JSON格式存储,然后一个时间段的数据做成压缩包存储在OSS上,然后提供给客户下载。

+

而对于归档数据的检索功能,客户选择需要归档的数据,我们可以将归档的数据(每个压缩包内的所有数据)导入到一个主数据库以外的,临时文档数据库(例如MongoDB),然后针对这个临时的文档数据库进行检索。当然提供的检索项目也会远少于热数据的检索项目。这个临时文档数据库基本上保持一两天数据,定期就全部回收。

+

未完待续

+

H项目心路历程记 (一)

前言

2021年年中从前一家公司离职,经过朋友介绍到了现在公司参与了Halfling项目(以下简称H项目),并从零开始的设计开发到第一个客户交付的全部流程。

+

H项目是一个面向零售行业在电商时代的智能一体化中台产品。项目最早是从2021年中开始立项投入,期间经过数个版本迭代,以及2022年初的魔都封城等诸多困难。终于在2023年Q2季度完成了针对首个客户的上线。

+

作为从头开始全程参与项目的技术人员,经过2年多的产品研发期间有不少想吐槽和想说的。

+

业务设计

最初H项目是以打造一个面向零售行业业务中台为目的进行项目立项的,其中一个很重要的点就是希望除核心组件以外都可以独立售卖。所以在架构设计的使用当下最流行的微服务架构(基于Spring Boot + Kubernetes集群的解决方案), 再做业务拆分到服务的时候,进行了更细颗粒度的拆分。所以前前后后一共拆分了近20个微服务。

+

实际上到了2022年Q2前后,两个核心服务订单管理和库存服务实现以后发现,各个业务服务中间的耦合程度比想象中的还要精密,单个服务拆开单独售卖几乎不可能(应该说是完全不可能)。 然而由于服务拆分的过细,导致一个服务需要频繁访问其他服务获取大量数据。尤其类似订单关联数据比如商品的主档数据,会穿越好几个服务。而每个服务都会重复去获取主档数据,造成了大量的额外请求。 虽然最后在所有服务底部兜底使用同一个Redis作为缓存来减少额外的请求调用。

+

随着时间来到2023年产品组负责人离职后,整个H项目的方向有从行业的业务中台降级成中小企业的订单管理系统(Order Management System, OMS)。前期做的很多设计比如用户权限体系(当初需求是要精确到每个页面的每个按钮,如果下拉列表框有外调请求也需要做权限控制)等就显的很重,导致用户在使用以及配置都很不方便。而有些设计就显得稍显欠缺。 所以整个2022年Q4到2023年都是在对业务层上反复横跳,比如库存模型和商品模型的调整。这点是相当影响团队士气,同时也加剧了研发组和产品组之间的矛盾。

+

然后还有一个很重要的问题是多租户的态度上。早起项目是以服务中大体量客户为目的,所以设计之初并没有考虑多租户的问题。但实际项的进展过程中发现部分中大体量的客户也需要类似多租户的功能。这类客户需求与其说是需求多租户,不如说是希望有一个多租户级别的数据隔离,每个业务部门只能操作自己的业务部门的数据(数据包含商品,品牌,属性,平台店铺,订单,售后单据等),而总部依据可以查看一些汇总信息(例如销售对账单据等)。而随着2022年疫情状况严峻,很多中大客户对于内部平台升级和替换都变的更加谨慎和保守。所以当项目方向变成中小企业OMS时,多租户SaaS化的需求就更加迫切了。所以后期如何在原有系统上在扩展一套多租户SaaS化功能也是个课题。

+

中间件

H项目使用的中间件中比较特别的有分布式任务管理系统xxl-job,消息队列RocketMQ,这两个中间件都是第一次使用。关于这两个中间件也有不是可以谈谈的。

+

分布式任务管理系统 XXL-JOB

作为一个开源的分布式任务管理系统,xxl-job足够简单且相当容易上手使用,无论是基于Spring,还是原生Java做开发都是相当简单的。

+

而不足之处也很明显,最近1年版本迭代速度明显变慢,有些功能和BUG都有待处理的。比如想实动态创建定时任务这类功能就必须自己动手去对xxl-job-admin(调度中心)进行修改。

+

对于任务执行日志这块,现在xxl-job也是文件格式在执行器所在服务器上进行输出的,当执行器容器化或是运行在Kubernetes集群中的时候,当容器或是Pod重启后,就会丢失日志。这点在后期运维以及调试上还是有点麻烦的。

+

消息队列 RocketMQ

阿里出品的消息队列如果基于云产品来使用的话问题不大(阿里云卖的价钱也是有点黑的),但是在运维上还是有点问题的比如Topic,Group这些东西都只能人工去添加,阿里云并没有提供导入服务。全系统如果使用消息队列的场景多的话,维护这些Topic和Group还是很酸爽的。还有一点RocketMQ重复消息这一点是无法保证的。用阿里云的原话

+
+

绝大多数情况下,消息是不重复的。作为一款分布式消息中间件,在网络抖动、应用处理超时等异常情况下,无法保证消息不重复,但是能保证消息不丢失。

+
+

实际运用上,在大数据量并发的情况下,我们实际上是遇到了消息重复消费的问题。这点还是需要通过代码干预(例如增加一个唯一MsgId,对MsgId做分布式锁来解决)。

+

还有一点就是在旧版本SDK的默认配置下消费者的负载是有点小问题,总是有一个节点会比其他节点更容易消费到大量消息。据阿里云自己说明在新版本SDK的默认负载配置也已经修复这个问题了。

+

(未完待续)

+

AKS中重写规则踩坑小记录

前言

最近在做标准产品在不同云平台中的部署验证,有幸体验了一下微软的Azure。负责采购的运维部门这次采用了Application Gateway来搭配AKS(Azure Kubernetes Service)对外暴露服务,正好借着这个机会来体验一下Application Gateway

+

应用场景

    +
  1. 域名api.demo.com指向Application Gateway的IP地址
  2. +
  3. AKS内部2个Service, gateway-servicebackend-service分别需要通过Application Gateway对外暴露。
  4. +
  5. /gateway/指向gateway-service, 然后/backend/指向backend-service。而且两个Service都没有context-path,所以需要做一个Rewrite重写URI到Service的根目录上。
  6. +
+

定义重写集

打开AKS对应的应用程序网关设置 > 重写。选择添加重写集。在1. 名称和关联这个Tab上只需要填写名称这项即可(名称后面在做ingress时需要使用), 关联的传递规则不需要选择。2. 重写规则配置里添加一个重写规则,然后填上重写规则的名称,并添加条件(默认新建重写规则时,只会生成操作,不会生成条件)

+

条件做如下设置

    +
  • 要检查的变量类型 : 服务器变量
  • +
  • 服务器变量: request_uri
  • +
  • 区分大小写:
  • +
  • 运算符: 等号(=)
  • +
  • 要匹配的模式: /(gateway|backend)/?(.*)
  • +
+

操作做如下设置

    +
  • 重写类型: URL
  • +
  • 操作类型: 设置
  • +
  • 组件: URL路径和URL查询字符串
  • +
  • URL路径值: /{var_request_uri_2}
  • +
  • 重新计算路径映射: 不选中
  • +
  • URL查询字符串值: 留空不设值
  • +
+

特殊说明

操作里的URL路径值不能使用正则表达式GROUP替换组,例如$1$2之类的。Azure自己定义了一套对应的替换组命名规则。具体可以参考这个网页使用应用程序网关重写 HTTP 标头和 URL

+

另外一个需要注意一点,如果在条件里选择了服务器变量request_uri的时候,注意这个request_uri是完整的原始请求URI(携带了查询参数)。例如: 在请求http://api.demo.com/gateway/search?foo=bar&hello=world中,request_uri的值将为/gateway/search?foo=bar&hello=world。由于request_uri里包含了查询参数,所以在操作组件中建议勾选URL路径和URL查询字符串。如果只选择URL路径的情况下可能出现无法预期的错误。以我们上述的配置来说明。

+

对象URL: http://api.demo.com/gateway/search?foo=bar&hello=world

+ + + + + + + + + + + + + +
组件URL路径和URL查询字符串URL路径
结果/search?foo=bar&hello=world/search?foo=bar&hello=world?foo=bar&hello=world
+

ACK的Ingress设置

当选择了Application Gateway作为对外暴露Service的方式时,Kubernetes集群里(kube-system命名空间里)多一个Application Gateway Ingress Controller(Azure工单时通常会简称为agic)的Deployment,所以对外暴露服务时可以像传统nginx ingress controller一样添加一个Ingress对象即可(甚至配置也和ngic大致相同,只是多了2个annotations)

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
# 这里指定重写规则集(不是重写规则的名字)
appgw.ingress.kubernetes.io/rewrite-rule-set: rule-backend
# 指定说明你这里ingress的类型是agic
kubernetes.io/ingress.class: azure/application-gateway
name: backend-ingress
namespace: default
spec:
rules:
- host: api.demo.com
http:
paths:
- backend:
service:
name: gateway-service
port:
number: 8080
path: /gateway/
pathType: Prefix
- backend:
service:
name: backend-service
port:
number: 8080
path: /backend/
pathType: Prefix
+ +

总结

由于微软云这块文档有部分缺失,导致在配置这块花了一点时间去排查,甚至开了工单。总结下来Ingress的配置主要是根据请求路径路由到对应的Service,重写规则集才是实际负责根据正则来进行匹配重写。

+

Spring Cloud Kubernetes环境下使用Jasypt

前言

最近半年着手开始做了基于微服务的中台项目,整个项目的技术栈采用的是Java + Spring Cloud + Kubernetes + Istio

+

业务开放上还是相当顺利的。但是在安全审核上,运维组提出了一个简易。现在项目一些敏感配置,例如MySQL用户的密码,Redis的密码等现在都是明文保存在Kubernetes的ConfigMap中的(是的,我们并没有Nacos作为微服务的配置中心)。这样可能存在安全隐患。

+

首次尝试

既然有问题,那就解决问题。要给配置文件中的属性项目加密很简单,稍微Google一下,就有现成的方案了。

+

现在比较常用的解决方案就是集成Jasypt,然后通过jasypt-spring-boot-starter来融合进Spring。

+

POM包加入jasypt-spring-boot-starter

1
2
3
4
5
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
+ +

Dockerfile中增加java参数

1
2
...
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar app.jar --jasypt.encryptor.password=helloworld $PARAMS"]
+ +

在ConfigMap中添加加密属性

1
2
3
4
5
6
7
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
data:
application.yaml: |-
test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
+ +

利用actuator接口测试

management.endpoints.web.exposure.include属性中增加env,这样我们就可以通过调用/actuator/env来查看一下env接口返回的整个Spring 容器中所有的PropertySource。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
...
"propertySources": [
{
"name": "bootstrapProperties-configmap.demo.default",
"properties": {
"test2": {
"value": "Hello,world"
}
}
}
...
]
}
+ +

OK, 这下配置项已经加密了。问题解决了。

+

但是…

+

新的问题

自从项目集成了Jayspt以后,出现了一个奇怪的问题。每次项目试图通过修改ConfigMap的配置文件,然后试图通过spring-cloud-starter-kubernetes-fabric8-config来做自动Reload,都失败了。然而查阅应用日志,并没有出现任何异常。无奈只能打开spring-cloudjasypt-spring-bootDEBUG日志。

+

进过几天对日志和两边源代码的分析。终于找到了原因

+

原因

在Spring Boot启动时jasypt-spring-boot会将下面6种配置(并不仅限与这6种配置文件)

+
    +
  • Classpath下的application.yaml
  • +
  • Classpath下的bootstrap.yaml
  • +
  • 集群里名称为${spring.cloud.kubernetes.config.name}的ConfigMap
  • +
  • 集群里名称为${spring.cloud.kubernetes.config.name}-kubernetes的ConfigMap
  • +
  • Java启动参数
  • +
  • 环境变量
  • +
+

转换成jasypt-spring-boot自己的PropertySource实现类EncryptableMapPropertySourceWrapper

+

但是如果使用Kubernetes的ConfigMap来作微服务配置中心的时候,Spring Cloud会在ConfigurationChangeDetector中查找配置类org.springframework.cloud.bootstrap.config.BootstrapPropertySource, 并依据BootstrapPropertySource的类型来判断容器内的配置与集群中ConfigMap里的配置是否有差异,来触发配置reload。

+

由于jasypt-spring-boot已经将所有的配置文件转型成了EncryptableMapPropertySourceWrapper, 所以ConfigurationChangeDetector无法找到BootstrapPropertySource所以会一直任务ConfigMap的里的配置没有变化,导致整个Reload失效(无论是使用polling还是event方式)

+

解决问题

为了保证ConfigMap变化后自动Reload的功能,所以jasypt-spring-boot不能把BootstrapPropertySource转换成EncryptableMapPropertySourceWrapper

+

所以我们需要设置jasypt.encryptor.skip-property-sources配置项, Classpath中的application.yaml需要增加配置

+
1
2
3
jasypt:
encryptor:
skip-property-sources: org.springframework.cloud.bootstrap.config.BootstrapPropertySource
+ +

skip-property-sources配置项配置后,加密项目就不能配置在ConfigMap里了,毕竟已经被我们忽略了。那么我们只能另外找一个PropertySource来存放加密项目了。

+

Classpath中的两个Yaml由于编译时会被Maven打包进Jar文件,会牵涉多个CI/CD多个流程显然不合适,启动参数配置项的也要影响到Docker镜像制作这个流程。所以判断下来最适合的PropertySource就是环境变量了。

+

环境变量增加加密项

在Kubernetes的部署Yaml中,添加加密数据项application.test.str

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: demo
name: demo
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- env:
- name: TZ
value: Asia/Shanghai
- name: application.test.str
value: >-
ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
....
+ +

如果需要更加严密的加密方针的话,我们可以把环境变量的内容放进Kubernetes的Secrets中。

+

在ConfigMap中引用application.test.str

1
2
3
4
5
6
7
8
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
data:
application.yaml: |-
test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
test3: ${application.test.str}
+ +

通过actuator接口来测试

通过actuator\env接口来测试一下

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
...
"propertySources": [
{
"name": "bootstrapProperties-configmap.demo.default",
"properties": {
"test2": {
"value": "ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)"
},
"test3": {
"value": "Hello,world"
}
}
}
...
]
}
+ +

这样ConfigMap中的配置项test3就可以通过环境变量引用并使用加密配置项了。同时修改ConfigMap依然可以触发auto reload了。这下终于算是解决了。

+

重学Java (一) 泛型

1. 前言

泛型编程自从 Java 5.0 中引入后已经超过15个年头了。对于现在的 Java 码农来说熟练使用泛型编程已经是家常便饭的事情了。所以本文就在不对泛型的基础使用在做说明了。 如果你还不会使用泛型的话,可以参考下面两个链接

+ +

这篇文章就简答聊一下,我实际在开发工作中很少用的到泛型方法这个知识点,以及在实际项目中有哪些东西会使用到泛型。

+

2. 泛型方法

在阅读代码的时候我们经常会看到下面这样的方法 (这段代码摘自 java.util.AbstractCollection)

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 public <T> T[] toArray(T[] a) {
// Estimate size of array; be prepared to see more or fewer elements
int size = size();
T[] r = a.length >= size ? a :
(T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
Iterator<E> it = iterator();

for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) { // fewer elements than expected
if (a == r) {
r[i] = null; // null-terminate
} else if (a.length < i) {
return Arrays.copyOf(r, i);
} else {
System.arraycopy(r, 0, a, 0, i);
if (a.length > i) {
a[i] = null;
}
}
return a;
}
r[i] = (T)it.next();
}
// more elements than expected
return it.hasNext() ? finishToArray(r, it) : r;
}
+ +

那么 pulic 关键字后面的那个 <T> 就是用来标记这个方法是一个泛型方法。 那什么是泛型方法呢。

+

官方的解释是这样的

+
1
Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.
+ +

通俗点来将就是将一个方法泛型化,让一个普通的类的某一个方法具有泛型功能。 如果在一个泛型类中增加一个泛型方法,那这个泛型方法就可以有一套独立于这个类的泛型类型。

+

通过一个简单的例子, 我们来看看

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* GenericClass 这个泛型类是一个简单的套皮的 HashMap
*/
public class GenericClass<K, V> {

private Map<K, V> map = new HashMap<>();

public V put(K key, V value) {
return map.put(key, value);
}

public V get(K key) {
return map.get(key);
}

// 泛型方法 genericMethod 可以接受一个全新的、作用域只限本函数的泛型类型T
public <T> T genericMethod(T t) {
return t;
}

}
+ +

实际使用起来

+
1
2
3
4
5
6
GenericClass<String, Integer> map = new GenericClass<>();
// put 和 get 方法的参数必须使用定义时指定的 String 和 Integer
System.out.println(map.put("One", 1));
System.out.println(map.get("One"));
// 泛型方法 genericMethod 就可以接受一个 String 和 Integer 以外的类型
System.out.println(map.genericMethod(new Double(1.0)).getClass());
+ +

我们再来看看 JDK 中使用到泛型方法的例子。我们最常使用的泛型容器 ArrayList 中有个 toArray 方法。JDK 在它的实现中就提供了两个版本,其中一个就是泛型方法的版本

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
// 这是一个普通版本,返回一个Object的数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}

// 这是一个泛型方法的版本,将容器里存储的元素输出到 T[] 数组中。 其中 T 必须是 E 的父类,否则 System.arraycopy 会抛出 ArrayStoreException 异常
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
}
+ +

泛型方法总体上来说就是可以给与现有的方法实现上,增加一个更加灵活的实现可能。

+

3. 实战应用

在实际的项目中,对于泛型的使用,除了像倾倒垃圾一样往泛型容易里塞各种 java bean 和其他泛型对象。还能怎么使用泛型呢?

+

我们在实际的一些项目中,会对数据库中的一些表(多数时候是全部)先实现 CRUD (Create, Read, Update, Delete)的操作,并从这些操作中延伸出一些简单的 REST 风格的 WebAPI 接口,然后才会根据实际业务需要实现一些更复杂的业务接口。

+

大体上会是下面这个样子。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

// 这是一个简单的 Entity 对象
// 通常现在的 Java 应用都会使用到 Lombok 和 Spring Boot
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Entity
@Table(name = "user")
public class User {
@Id
private Long id;
private String username;
private String password;
}

// 然后这个是 DAO 接口继承自 spring-data 的 JpaRepository
public interface UserDao extends JpaRepository<User, Long> {
}

// 在来是一个访问 User 资源的 Service 和他的实现
public interface UserService {
List<User> findAll();
Optional<User> findById(Long id);
User save (User user)
void deleteById(Long id);
}

@Service
public class UserSerivceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}

@Override
public List<User> findAll() {
return this.dao.findAll();
}

@Override
public Optional<User> findById(Long id) {
return this.dao.findById(id);
}

@Override
public User save(User user) {
return this.dao.save(user);
}

@Override
public void deleteById(Long id) {
this.dao.deleteById(id);
}
}

// 最后就是 WebAPI 的接口了
@RestController
@RequestMapping("/user/")
public class UserController{
private UserService userService;
public UserController(userService userService) {
this.userService = userService;
}

@GetMapping
@ResponseBody
public List<User> fetch() {
return this.userService.findAll();
}

@GetMapping("{id}")
@ResponseBody
public User get(@PathVariable("id") Long id) {
// 由于是示例这里就不考虑没有数据的情况了
return this.userService.findById(id).get();
}

@PostMapping
@ResponseBody
public User create(@RequestBody User user) {
return this.userService.save(user);
}

@PutMapping("{id}")
@ResponseBody
public User update(@RequestBody User user) {
return this.userService.save(user);
}

@DeleteMapping("{id}")
@ResponseBody
public User delete(@PathVariable("id") Long id) {
User user = this.userService.findById(id);
this.userService.deleteById(id);
return user;
}
}
+ +

大致一个表的一套相关接口就是这个样子的。如果你的数据库中有大量表的话,而且每个表都需要提供 REST 风格的 WebAPI 接口的话,那么这将是一个相当枯燥的而又及其容易出错的工作。

+

为了不让这项枯燥而又容易犯错的工作占去我们宝贵的私人时间,我们可以通过泛型和继承的技巧来重构从 Service 层到 Controller 的这段代码(感谢 spring-data 提供了 JpaRepository, 让我们不至于从 DAO 层重构)

+

3.1 Service 层的重构

首先是 Service 接口的重构,我们 Service 层接口就是定义了一组 CRUD 的操作,我们可以将这组 CRUD 操作抽象到一个父接口,然后所有 Service 层的接口都将继承自这个父接口。而接口中出现的 Entity 和主键的类型(上例中 User 的主键 id 的类型是 Long)就可以用泛型来展现。

+
1
2
3
4
5
6
7
8
9
10
11
// 这里泛型表示 E 来指代 Entity, ID 用来指代 Entity 主键的类型
public interface ICrudService<E, ID> {
List<E> findAll();
Optional<E> findById(ID id);
E save(E e);
void deleteById(ID id);
}

// 然后 Service 层的接口,就可以简化成这样
public interface UserService extends ICrudService<User, Long> {
}
+ +

同样 Service 层的实现也可以使用相似的方法具体实现可以抽象到一个基类中。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 相比 ICrudService 这里有多了一个泛型 T 来代表 Entity 对应的 DAO, 我们的每一个 DAO 都继承自
// spring-data 的 JpaRepository 所以,这里可以使用到泛型的边界
public abstract class AbstractCrudService<T extends JpaRepository<E, ID>, E, ID> {
private T dao;
public AbstractCrudService(T dao) {
this.dao = dao;
}

public List<E> findAll() {
return this.dao.findAll();
}

public Optional<E> findById(ID id) {
return this.dao.findById(id);
}

public E save(E e) {
return this.dao.save(e);
}

public void deleteById(ID id) {
this.dao.deleteById(id);
}
}

// 那 Service 的实现类可以简化成这样
@Service
public class UserServiceImpl extends AbstractCrudService<UserDao, User, Long> implements UserService {
public UserServiceImpl(UserDao dao) {
supper(dao);
}
}
+ +

同样我们可以通过相同的方法来对 Controller 层进行重构

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Controller 层的基类
public abstract class AbstractCrudController<T extends ICrudService<E, ID>, E, ID> {
private T service;
public AbstractCrudController(T service) {
this.service = service;
}

@GetMapping
@ResponseBody
public List<E> fetch() {
return this.service.findAll();
}

@GetMapping("{id}")
@ResponseBody
public E get(@PathVariable("id") ID id) {
// 由于是示例这里就不考虑没有数据的情况了
return this.service.findById(id).get();
}

@PostMapping
@ResponseBody
public E create(@RequestBody E e) {
return this.service.save(e);
}

@PutMapping("{id}")
@ResponseBody
public E update(@RequestBody E e) {
return this.service.save(e);
}

@DeleteMapping("{id}")
@ResponseBody
public E delete(@PathVariable("id") ID id) {
E e = this.service.findById(id).get();
this.service.deleteById(id);
return e;
}
}

// 具体的 WebAPI
@RestController
@RequestMapping("/user/")
public class UserController extends AbstractCrudController<UserService, User, Long> {
public UserController(UserService service) {
super(service);
}
}
+ +

经过重构可以消减掉 Servcie 和 Controller 中的大量重复代码,使代码更容易维护了。

+

4. 结尾

关于泛型就简单的说这些了,泛型作为 Java 日常开发中一个常用的知识点,其实还有很多知识点可以供我们挖掘,奈何本人才疏学浅,这么多年工作下来,只积累出来这么点内容。

+

文末放上示例代码的代码库:

+ +

2020年阅读总结

多灾多难的2020年已经没有几天了,整个2020年要说还真是像一记刹车,去年很多想法,计划都有因为疫情原因搁置了。平时的生活更是从宅家变成了重度宅家的状态,自然就多出了许多大段大段的连续时间,可以用来好好玩玩大作,看看小说。仔细盘点一下今年总共看了10本小说外加2本科普书籍( 2020年阅读书单
),算是自2018年以来读书最多的一个年份了。其中不乏拖了近3年的那本《西方文化中的数学》。接着就来絮叨絮叨今年看的这12本书吧。

+
+
+

《莱博维茨的赞歌》

+
+

第一次被安利《莱博维茨的赞歌》的机核的电台节目辐射-视觉、音乐以及文学, 当时听完节目就相当好奇,可以被一部载入史册的CRPG游戏誉为精神文本的小说到底是个什么样子的。但是当时新星版的译本几乎就是绝本了,某鱼的二手价也要已经飙到了3位数。直到今年年初在核市节目上听到了中信版译本的消息,才有机会买来拜读。

+

全书通过3个故事讲述了核战灭世后莱博维茨修道院从保卫文明,到重建,然后看着文明再度毁灭的故事。各种无不透露着辐射那股 War, War never changed的味道。

+
+

《占星术杀人事件》《屋顶上的小丑》《亿男》

+
+

这3本书都是2019年上海书展上买的。前两本都是岛田庄司的推理名篇,其中《占星术杀人事件》在本格派推理史上有着非凡的地位,各种被抄袭从未被超越过。《屋顶上的小丑》要是最吸引人的话就是多线叙事,每条线都掐在恰如其分的地方。《亿男》则是挂着悬疑羊头卖着人生哲理。

+
+

《神经漫游者》

+
+

威廉吉布森的蔓生三部曲之一,开创了赛博朋克的科幻流派,说实话这本书我看的很痛苦,很多地方实在读不懂,到现在我还是不明白那些无法读懂的地方到底是原著如此,还是翻译的不行,亦或者是Kindle版排版太烂了。

+
+

《神们自己》,《神的九十亿个名字》

+
+

阿瑟克拉克的中篇小说和短篇小说集,这两本书阅读体验奇佳,尤其是在读完《神经漫游者》后,虽然三本书被Amazon打在同一个包里来卖的,但是光从排版上就比《神经漫游者》高出不少,所以才会有我对后者排版问题的质疑。

+

《神们自己》三线叙事,讲述了2个平行宇宙中的2个物种在能源,种族存续,家庭伦理上的各种讨论和故事。 《神的九十亿个名字》作为短篇小说集收录了好几篇风格题材迥异的小说,花上十几分钟读上一个故事是相当惬意的。

+
+

《佐伊的战争》 《人类决裂》 《万物的终结》

+
+

约翰斯卡尔齐的《老人的战争》系列的后3部,前三部大概是3,4年前读的吧,在这个宇宙观里最吸引人的设定就绿皮的人类防卫军战士,以及跨种族间的政治斗争。系列的六本书相当于在这个大设定下讲的六个故事。每个故事虽说都是独立的故事,但是前后皆有关联,而且最重要的就是每本的长度都算不上很长,相当适合于一口气读完。

+
+

《西方文化中的数学》

+
+

这本书大概是从2017年开始读的,当时意气奋发觉得对于理科生出身的我,书中内容应该很好理解,没想到最后不光是文化还是数学读的都是一知半解。只能说自大了。

+
+

《一想到还有95%的问题留给人类,我就放心了》

+
+

大概是18年还是19年前后对量子物理和宇宙物理开始有了兴趣,虽然各中理论并不是很了解,但是仍然很好奇的买了这本书,全书插画相当有趣,同时使用浅显易懂的语言来解释各种复杂的物理现象,以及当今人类物理知识的边界。强烈推荐给想假装成学霸的朋友。

+
+
+

2021年读什么

+
+

具体读啥还没想好基本上还是以科幻和悬疑为主,但是肯定会读的应该有海明威的《丧钟为谁而鸣》和小林泰三的《醉步男》吧。

+

Largrange项目架构与设计回顾 (二)

Largrange项目架构与设计回顾 (一) 里面我讲了一下项目开始架构和技术选型的一些内容。这一章来聊聊业务设计上的这点事。

+

总体来说项目设计的时候,我们对业务模块的划分和拆解总体上来说都是遵循着高内聚,低耦合的原则来进行划分的。大部分和业务相关的服务,都还是能很好的进行功能划分的。但是有一些和具体业务关联性并不是很高的服务在界定与实现时出现了一些问题。

+

任务调度服务

由于平台方面会有一些控制命令下发给Android设备,而且下发的命令并不一定都是实时的,大部分都是指定一个时间来下发,所以在设计的时候就考虑到需要一个任务调度服务(以下略称cron),来处理这些下发指令以及未来可能会有的定时批处理任务的业务需求。技术选型的时候考虑到整个服务是构建在Kubernetes上的一个分布式的微服务架构,所以就没有选择Spring Scheduler,而是使用了Quarter来支撑整个cron

+

从技术选型上来说cron没有什么问题,但是在设计如何使用cron上还是有点问题的, 我们先来看看已推送服务为例,整个平台中cron的处理流程是怎样的。

+
    +
  1. Platform 调用 cron的创建定时任务Job的接口(接口的参数为推送时使用的相关参数)
  2. +
  3. cron 创建Quartz的Job和Trigger,并将Job和Trigger的Name(Quartz中Job和Trigger的唯一标识符)返回个Platform
  4. +
  5. cron 在指定时间触发推送的Job,即调用Push服务的推送接口
  6. +
  7. Push的推送接口调用第三方服务商的推送服务,并将第三方推送服务的调用结果返回给cron
  8. +
  9. cron通过调用Platform预留的推送服务回调地址将第三方推送服务调用接口返还给Platform
  10. +
  11. Platform 将第三方推送服务的调用结果留档保存,并继续业务处理
  12. +
+

设计之初考虑到不想在cron中牵扯到具体的业务, 所以设计了一个Platform的回调接口来处理推送后的具体业务处理。虽然保证了cron尽量减少了和业务逻辑接触与数据库的访问。但是前前后后要访问集群内部的其他微服务2次,额外增加了网络开销,以及因为网络通信造成额外的通信失败风险。而且针对每种不同的定时任务,都需要额外开发一个QuartzJobBean,很难形成统一的QuartzJobBean来进行处理。今后如果还有相似项目还是需要重新考量一下如何设计一个更加完善的定时任务。

+

总结

暂时就想到了这些东西,今后想到啥还会继续在这里补存。

+

Largrange项目架构与设计回顾 (一)

+

项目背景

+
+

从去年年底开始一个老客户希望在他们的一个传统的机械设备(后面略称 E机关 )上外装一个Android设备。 Android设备和 E机关 之间通过串口或是RJ45接口进行数据交互,主要是Android设备获取 E机关 内部的数据,并不会通过接口来控制 E机关 。 Android设备则通过4G 来和平台交互,上报Android设备的状态数据,同时接受平台的控制。以此来实现让传统机械设备也能拥抱物联网的概念。

+

最后围绕着客户的需求分成了3个项目来并行推进

+
    +
  1. Android设备的硬件设备的设计、选材、样机制作与量产规划
  2. +
  3. 在Android设备上,进行与E机关以及平台进行交互的APP开发
  4. +
  5. 用于Android设备交互的平台的架构、设计与开发
  6. +
+

我们平台Team就负责 3.用于Android设备交互的平台 并命名Lagrange (拉格朗日) 。

+

设计&架构

整个平台这块不仅需要向Android设备的APP提供数据交互接口,还需要有一个供相关运营人员使用的前端Web应用,以及与云平台(主要是Aliyun) 交互的功能。所以考虑到多方面使用微服务的架构来实现整个平台端。
由于E机关的工作工况不能保证长期较的稳定的连接到4G网络,所以我们并不考虑使用Socket长连接的方式来做APP和平台之间的数据交互,要实现平台对Android设备进行反控的话,只能实现推送方式来把控制命令下发给Android设备,所以还需要有一个第三方推送服务商交互的服务。同时控制推送也有实时和非实时以及定期推送的需求,所以可能还需要一个任务调度的服务。

+

根据对上述业务进行梳理,我们将项目分成几个服务

+
    +
  • 提供Android设备的交互接口的 App Service
  • +
  • 提供运营人员使用前端Web应用 Platform Web Service
  • +
  • 前端Web应用使用到的一些接口 Platform Service
  • +
  • 负责第三方推送服务商交互的 Push Service
  • +
  • 提供云平台鉴权用的 Auth Service
  • +
  • 用来管理任务调度的 Cron Service
  • +
+

由于前一个项目实施的时候没有使用容器部署,每当访问量峰值的时候,我们这些码农兼运维就各种加班,所以这次项目决定直接将服务容器化,同时选择了Kubernetes来管理容器。由于客观原因线上的Kubernetes直接购买了Aliyun的托管版Kubernetes服务。 内部的开发测试环境则使用了 Rancher 2.0 来构建Kubernetes集群。

+

技术选型

a. 后端服务选型

由于不考虑长连接的原因,所以在后端服务在技术选型上基本就不考虑Netty了,直接上Spring大礼包。

+
    +
  • Spring Boot 开发接口
  • +
  • Spring Data 配合 JPA 来进行数据的持久化
  • +
  • Spring Cloud Kubenetes 来做数据的Config的autoreload
  • +
  • Quartz 负责处理任务调度
  • +
+

服务之间调用都使用HTTP服务, 服务发现也有Kubernetes的DNS机制支持。服务网格则选择了比较成熟的Istio,主要还是Aliyun的Kubernetes可以集成Istio,部署和使用都相当方便。

+

b. 前端Web应用选型

因为前端Web应用主要是给运营人员使用,所以我们考虑使用Single Page Application来做个前后端分离的Web应用。框架这块由于团队成员基本上没有什么前端开发经验,基本都是后台写Java的码农,所以框架选择有点随性,直接就点名了vue.js。前端控件库则用的是阿里系的Antd。

+

c. 数据持久层

主数据库选择了mysql,缓存用的redis。这些都是团队比较熟悉的。由于一些特殊的业务需求和使用场景,我们还加了一个mongodb来做为一个副数据库,主要存放一些特殊业务使用的数据。

+

d. DevOps

用了相当传统的GitLab CE 加上Jenkins的组合,实现前后端代码的自动编译,推送到私有的镜像仓库(使用Aliyun的镜像仓库服务)

+

最后

以上基本是项目最早做设计时候的各种考量。暂时先写这么多,过两天再回顾一下当初设计上有哪些觉得不足的地方。

+

阿里云Kubernetes上线踩坑记

1
2
Update:
2020-04-08 增加istio-ingressgateway高可用的设置
+ +

最近公司因为项目需要,在阿里云上部署了一个Kubernetes集群。虽然阿里云的文档说的还算细致,但是还是有些没有明确说明的细节。

+

1. 购买篇

申请项目预算的时候,只考虑到Worker节点,1个SLB节点以及域名和证书的预算。但是实际购买的时候发现还有许多额外的开销。

+

1.1 SNAT

这个和EIP一并购买,可以方便通过公网使用kubectl访问集群。关于SNAT网关至今不是很明白需要购买这个服务的意义何在,只是为了一个EIP来访问集群吗?

+

1.2 Ingress

这个选上了后,阿里云会给你买个SLB而且还是带公网访问的,如果你后期考虑使用Istio的话,建议你集群创建后,直接停止这个SLB,以免产生额外的费用。

+

1.3 日志服务

通过阿里云的日志服务来收集应用的的日志,挺好用的。但是另外收费,如果有能力的自建日志服务的可不购买。

+

2. Istio

阿里云的Kubernetes集群完美集成了Istio,根据向导就能很简单的部署成功。

+

2.1 额外的SLB

Istio的Gateway 需要绑定一个新的SLB,和Ingress的SLB不能是同一个,又是一笔额外的开销

+

2.2 集群外访问

这个在阿里云的Istio FAQ中有提到,按照指导很容易解决

+

2.2 SLB的443监听

为了方便443端口的证书绑定,我们直接删除了SLB上原有的443监听(TCP协议), 重新建了一个443监听(HTTPS协议),指向和80端口同样的虚拟服务器组。但是设置健康检查时一直出错,经过排查发现SLB健康检查发送的请求协议是HTTP 1.0的,Istio的envoy直接反悔了426(Upgrade Required)这个状态码,所以我们无奈只能把健康检查的检查返回状态改为http_4xx,这样就能通过SLB的健康检查了。

+

2.3 istio-ingressgateway的高可用

istio-ingressgateway要达成高可用,只需要增加通过伸缩POD就可以实现,于istio-ingressgateway对应的SLB中的虚拟服务器组也会自动增加,完全不需要进行额外的手动设定。

+

由于istio-ingressgateway中挂载了HPAHorizontalPodAutoscaler(简称HPA),通常三节点的集群中最小POD数只有1台,在3节点的集群中,要实现高可用,需要手动修改HPA,增加最小POD数。

+
+

基本上现在遇到了这些坑,再有在总结吧。

+

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/js/algolia.js b/js/algolia.js new file mode 100644 index 0000000..d59f1ef --- /dev/null +++ b/js/algolia.js @@ -0,0 +1,102 @@ +/* global instantsearch, algoliasearch */ +// eslint-disable-next-line no-unused-vars +function loadAlgolia(config, translation) { + const search = instantsearch({ + indexName: config.indexName, + searchClient: algoliasearch(config.applicationId, config.apiKey), + }); + + search.addWidgets([ + instantsearch.widgets.configure({ + attributesToSnippet: ['excerpt'], + }), + ]); + + search.addWidget( + instantsearch.widgets.searchBox({ + container: '#algolia-input', + placeholder: translation.hint, + showReset: false, + showSubmit: false, + showLoadingIndicator: false, + cssClasses: { + root: 'searchbox-input-container', + form: 'searchbox-input-container', + input: 'searchbox-input', + }, + }), + ); + + search.addWidget( + instantsearch.widgets.poweredBy({ + container: '#algolia-poweredby', + }), + ); + + search.addWidget( + instantsearch.widgets.hits({ + container: '.searchbox-body', + escapeHTML: false, + cssClasses: { + root: 'searchbox-result-container', + emptyRoot: ['searchbox-result-item', 'disabled'], + }, + templates: { + empty: function (results) { + return translation.no_result + ': ' + results.query; + }, + item: function (hit) { + let title = instantsearch.highlight({ attribute: 'title', hit }); + let excerpt = instantsearch.highlight({ attribute: 'excerpt', hit }); + title = title ? title : translation.untitled; + excerpt = excerpt + .replace(new RegExp('', 'ig'), '[algolia-highlight]') + .replace(new RegExp('', 'ig'), '[/algolia-highlight]') + .replace(/(<([^>]+)>)/gi, '') + .replace(/(\[algolia-highlight\])/gi, '') + .replace(/(\[\/algolia-highlight\])/gi, ''); + excerpt = excerpt ? excerpt : translation.empty_preview; + return `
+ + + ${title} + ${excerpt} + + +
`; + }, + }, + }), + ); + + search.addWidget( + instantsearch.widgets.pagination({ + container: '.searchbox-footer', + cssClasses: { + list: 'searchbox-pagination', + item: 'searchbox-pagination-item', + link: 'searchbox-pagination-link', + selectedItem: 'active', + disabledItem: 'disabled', + }, + }), + ); + + search.start(); + + if (location.hash.trim() === '#algolia-search') { + $('.searchbox').addClass('show'); + } + + $(document) + .on('click', '.navbar-main .search', () => { + $('.searchbox').toggleClass('show'); + $('.searchbox-input').focus(); + }) + .on('click', '.searchbox .searchbox-mask', () => { + $('.searchbox').removeClass('show'); + }) + .on('click', '.searchbox-close', () => { + $('.searchbox').removeClass('show'); + }); +} diff --git a/js/animation.js b/js/animation.js new file mode 100644 index 0000000..50c7c3f --- /dev/null +++ b/js/animation.js @@ -0,0 +1,45 @@ +(function() { + function $() { + return Array.prototype.slice.call(document.querySelectorAll.apply(document, arguments)); + } + + $('body > .navbar, body > .section, body > .footer').forEach(element => { + element.style.transition = '0s'; + element.style.opacity = '0'; + }); + document.querySelector('body > .navbar').style.transform = 'translateY(-100px)'; + [ + '.column-main > .card, .column-main > .pagination, .column-main > .post-navigation', + '.column-left > .card, .column-right-shadow > .card', + '.column-right > .card' + ].forEach(selector => { + $(selector).forEach(element => { + element.style.transition = '0s'; + element.style.opacity = '0'; + element.style.transform = 'scale(0.8)'; + element.style.transformOrigin = 'center top'; + }); + }); + setTimeout(() => { + $('body > .navbar, body > .section, body > .footer').forEach(element => { + element.style.opacity = '1'; + element.style.transition = 'opacity 0.3s ease-out, transform 0.3s ease-out'; + }); + document.querySelector('body > .navbar').style.transform = 'translateY(0)'; + [ + '.column-main > .card, .column-main > .pagination, .column-main > .post-navigation', + '.column-left > .card, .column-right-shadow > .card', + '.column-right > .card' + ].forEach(selector => { + let i = 1; + $(selector).forEach(element => { + setTimeout(() => { + element.style.opacity = '1'; + element.style.transform = ''; + element.style.transition = 'opacity 0.3s ease-out, transform 0.3s ease-out'; + }, i * 100); + i++; + }); + }); + }); +}()); diff --git a/js/back_to_top.js b/js/back_to_top.js new file mode 100644 index 0000000..cf56af1 --- /dev/null +++ b/js/back_to_top.js @@ -0,0 +1,152 @@ +$(document).ready(() => { + const $button = $('#back-to-top'); + const $footer = $('footer.footer'); + const $mainColumn = $('.column-main'); + const $leftSidebar = $('.column-left'); + const $rightSidebar = $('.column-right'); + let lastScrollTop = 0; + const rightMargin = 20; + const bottomMargin = 20; + let lastState = null; + const state = { + base: { + classname: 'card has-text-centered', + left: '', + width: 64, + bottom: bottomMargin + } + }; + state['desktop-hidden'] = Object.assign({}, state.base, { + classname: state.base.classname + ' rise-up' + }); + state['desktop-visible'] = Object.assign({}, state['desktop-hidden'], { + classname: state['desktop-hidden'].classname + ' fade-in' + }); + state['desktop-dock'] = Object.assign({}, state['desktop-visible'], { + classname: state['desktop-visible'].classname + ' fade-in is-rounded', + width: 40 + }); + state['mobile-hidden'] = Object.assign({}, state.base, { + classname: state.base.classname + ' fade-in', + right: rightMargin + }); + state['mobile-visible'] = Object.assign({}, state['mobile-hidden'], { + classname: state['mobile-hidden'].classname + ' rise-up' + }); + + function isStateEquals(prev, next) { + return ![].concat(Object.keys(prev), Object.keys(next)).some(key => { + return !Object.prototype.hasOwnProperty.call(prev, key) + || !Object.prototype.hasOwnProperty.call(next, key) + || next[key] !== prev[key]; + }); + } + + function applyState(state) { + if (lastState !== null && isStateEquals(lastState, state)) { + return; + } + $button.attr('class', state.classname); + for (const prop in state) { + if (prop === 'classname') { + continue; + } + $button.css(prop, state[prop]); + } + lastState = state; + } + + function isDesktop() { + return window.innerWidth >= 1078; + } + + function isTablet() { + return window.innerWidth >= 768 && !isDesktop(); + } + + function isScrollUp() { + return $(window).scrollTop() < lastScrollTop && $(window).scrollTop() > 0; + } + + function hasLeftSidebar() { + return $leftSidebar.length > 0; + } + + function hasRightSidebar() { + return $rightSidebar.length > 0; + } + + function getRightSidebarBottom() { + if (!hasRightSidebar()) { + return 0; + } + return Math.max.apply(null, $rightSidebar.find('.widget').map(function() { + return $(this).offset().top + $(this).outerHeight(true); + })); + } + + function getScrollTop() { + return $(window).scrollTop(); + } + + function getScrollBottom() { + return $(window).scrollTop() + $(window).height(); + } + + function getButtonWidth() { + return $button.outerWidth(true); + } + + function getButtonHeight() { + return $button.outerHeight(true); + } + + function updateScrollTop() { + lastScrollTop = $(window).scrollTop(); + } + + function update() { + // desktop mode or tablet mode with only right sidebar enabled + if (isDesktop() || (isTablet() && !hasLeftSidebar() && hasRightSidebar())) { + let nextState; + const padding = ($mainColumn.outerWidth() - $mainColumn.width()) / 2; + const maxLeft = $(window).width() - getButtonWidth() - rightMargin; + const maxBottom = $footer.offset().top + (getButtonHeight() / 2) + bottomMargin; + if (getScrollTop() === 0 || getScrollBottom() < getRightSidebarBottom() + padding + getButtonHeight()) { + nextState = state['desktop-hidden']; + } else if (getScrollBottom() < maxBottom) { + nextState = state['desktop-visible']; + } else { + nextState = Object.assign({}, state['desktop-dock'], { + bottom: getScrollBottom() - maxBottom + bottomMargin + }); + } + + const left = $mainColumn.offset().left + $mainColumn.outerWidth() + padding; + nextState = Object.assign({}, nextState, { + left: Math.min(left, maxLeft) + }); + applyState(nextState); + } else { + // mobile and tablet mode + if (!isScrollUp()) { + applyState(state['mobile-hidden']); + } else { + applyState(state['mobile-visible']); + } + updateScrollTop(); + } + } + + update(); + $(window).resize(update); + $(window).scroll(update); + + $('#back-to-top').on('click', () => { + if (CSS && CSS.supports && CSS.supports('(scroll-behavior: smooth)')) { + window.scroll({ top: 0, behavior: 'smooth' }); + } else { + $('body, html').animate({ scrollTop: 0 }, 400); + } + }); +}); diff --git a/js/column.js b/js/column.js new file mode 100644 index 0000000..a39d8aa --- /dev/null +++ b/js/column.js @@ -0,0 +1,12 @@ +(function() { + function $() { + return Array.prototype.slice.call(document.querySelectorAll.apply(document, arguments)); + } + + // copy widgets in the right column, when exist, to the bottom of the left column + if ($('.columns .column-right').length && $('.columns .column-right-shadow').length && !$('.columns .column-right-shadow')[0].children.length) { + for (const child of $('.columns .column-right')[0].children) { + $('.columns .column-right-shadow')[0].append(child.cloneNode(true)); + } + } +}()); diff --git a/js/google_cse.js b/js/google_cse.js new file mode 100644 index 0000000..6c311d1 --- /dev/null +++ b/js/google_cse.js @@ -0,0 +1,44 @@ +/* global google */ +(function (document, $) { + function debounce(func, wait, immediate) { + let timeout; + return function () { + const context = this; + const args = arguments; + const later = function () { + timeout = null; + if (!immediate) func.apply(context, args); + }; + const callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + }; + } + + $(document) + .on('click', '.navbar-main .search', () => { + $('.searchbox').toggleClass('show'); + }) + .on('click', '.searchbox .searchbox-mask', () => { + $('.searchbox').removeClass('show'); + }) + .on('click', '.searchbox-close', () => { + $('.searchbox').removeClass('show'); + }) + .on( + 'keydown', + '.searchbox-input', + debounce(function () { + const value = $(this).val(); + try { + const element = google.search.cse.element.getElement('searchresults-only0'); + if (value.trim() === '') { + element.clearAllResults(); + } else { + element.execute(value); + } + } catch (e) {} + }, 300), + ); +})(document, jQuery); diff --git a/js/insight.js b/js/insight.js new file mode 100644 index 0000000..5112280 --- /dev/null +++ b/js/insight.js @@ -0,0 +1,354 @@ +/** + * Insight search plugin + * @author PPOffice { @link https://github.com/ppoffice } + */ +// eslint-disable-next-line no-unused-vars +function loadInsight(config, translation) { + const $main = $('.searchbox'); + const $input = $main.find('.searchbox-input'); + const $container = $main.find('.searchbox-body'); + + function section(title) { + return $('
').addClass('searchbox-result-section').append($('
').text(title)); + } + + function merge(ranges) { + let last; + const result = []; + + ranges.forEach((r) => { + if (!last || r[0] > last[1]) { + result.push((last = r)); + } else if (r[1] > last[1]) { + last[1] = r[1]; + } + }); + + return result; + } + + function findAndHighlight(text, matches, maxlen) { + if (!Array.isArray(matches) || !matches.length || !text) { + return maxlen ? text.slice(0, maxlen) : text; + } + const testText = text.toLowerCase(); + const indices = matches + .map((match) => { + const index = testText.indexOf(match.toLowerCase()); + if (!match || index === -1) { + return null; + } + return [index, index + match.length]; + }) + .filter((match) => { + return match !== null; + }) + .sort((a, b) => { + return a[0] - b[0] || a[1] - b[1]; + }); + + if (!indices.length) { + return text; + } + + let result = ''; + let last = 0; + const ranges = merge(indices); + const sumRange = [ranges[0][0], ranges[ranges.length - 1][1]]; + if (maxlen && maxlen < sumRange[1]) { + last = sumRange[0]; + } + + for (let i = 0; i < ranges.length; i++) { + const range = ranges[i]; + result += text.slice(last, Math.min(range[0], sumRange[0] + maxlen)); + if (maxlen && range[0] >= sumRange[0] + maxlen) { + break; + } + result += '' + text.slice(range[0], range[1]) + ''; + last = range[1]; + if (i === ranges.length - 1) { + if (maxlen) { + result += text.slice(range[1], Math.min(text.length, sumRange[0] + maxlen + 1)); + } else { + result += text.slice(range[1]); + } + } + } + + return result; + } + + function searchItem(icon, title, slug, preview, url) { + title = title != null && title !== '' ? title : translation.untitled; + const subtitle = slug + ? '(' + slug + ')' + : ''; + + return ` + + + + + + ${title} + ${subtitle} + + ${preview ? '' + preview + '' : ''} + + `; + } + + function sectionFactory(keywords, type, array) { + let $searchItems; + if (array.length === 0) return null; + const sectionTitle = translation[type.toLowerCase()]; + switch (type) { + case 'POSTS': + case 'PAGES': + $searchItems = array.map((item) => { + const title = findAndHighlight(item.title, keywords); + const text = findAndHighlight(item.text, keywords, 100); + return searchItem('file', title, null, text, item.link); + }); + break; + case 'CATEGORIES': + case 'TAGS': + $searchItems = array.map((item) => { + const name = findAndHighlight(item.name, keywords); + const slug = findAndHighlight(item.slug, keywords); + return searchItem(type === 'CATEGORIES' ? 'folder' : 'tag', name, slug, null, item.link); + }); + break; + default: + return null; + } + return section(sectionTitle).append($searchItems); + } + + function parseKeywords(keywords) { + return keywords + .split(' ') + .filter((keyword) => { + return !!keyword; + }) + .map((keyword) => { + return keyword.toLowerCase(); + }); + } + + /** + * Judge if a given post/page/category/tag contains all of the keywords. + * @param Object obj Object to be weighted + * @param Array fields Object's fields to find matches + */ + function filter(keywords, obj, fields) { + const keywordArray = parseKeywords(keywords); + const containKeywords = keywordArray.filter((keyword) => { + const containFields = fields.filter((field) => { + if (!Object.prototype.hasOwnProperty.call(obj, field)) { + return false; + } + if (obj[field].toLowerCase().indexOf(keyword) > -1) { + return true; + } + return false; + }); + if (containFields.length > 0) { + return true; + } + return false; + }); + return containKeywords.length === keywordArray.length; + } + + function filterFactory(keywords) { + return { + post: function (obj) { + return filter(keywords, obj, ['title', 'text']); + }, + page: function (obj) { + return filter(keywords, obj, ['title', 'text']); + }, + category: function (obj) { + return filter(keywords, obj, ['name', 'slug']); + }, + tag: function (obj) { + return filter(keywords, obj, ['name', 'slug']); + }, + }; + } + + /** + * Calculate the weight of a matched post/page/category/tag. + * @param Object obj Object to be weighted + * @param Array fields Object's fields to find matches + * @param Array weights Weight of every field + */ + function weight(keywords, obj, fields, weights) { + let value = 0; + parseKeywords(keywords).forEach((keyword) => { + const pattern = new RegExp(keyword, 'img'); // Global, Multi-line, Case-insensitive + fields.forEach((field, index) => { + if (Object.prototype.hasOwnProperty.call(obj, field)) { + const matches = obj[field].match(pattern); + value += matches ? matches.length * weights[index] : 0; + } + }); + }); + return value; + } + + function weightFactory(keywords) { + return { + post: function (obj) { + return weight(keywords, obj, ['title', 'text'], [3, 1]); + }, + page: function (obj) { + return weight(keywords, obj, ['title', 'text'], [3, 1]); + }, + category: function (obj) { + return weight(keywords, obj, ['name', 'slug'], [1, 1]); + }, + tag: function (obj) { + return weight(keywords, obj, ['name', 'slug'], [1, 1]); + }, + }; + } + + function search(json, keywords) { + const weights = weightFactory(keywords); + const filters = filterFactory(keywords); + const posts = json.posts; + const pages = json.pages; + const tags = json.tags; + const categories = json.categories; + return { + posts: posts + .filter(filters.post) + .sort((a, b) => { + return weights.post(b) - weights.post(a); + }) + .slice(0, 5), + pages: pages + .filter(filters.page) + .sort((a, b) => { + return weights.page(b) - weights.page(a); + }) + .slice(0, 5), + categories: categories + .filter(filters.category) + .sort((a, b) => { + return weights.category(b) - weights.category(a); + }) + .slice(0, 5), + tags: tags + .filter(filters.tag) + .sort((a, b) => { + return weights.tag(b) - weights.tag(a); + }) + .slice(0, 5), + }; + } + + function searchResultToDOM(keywords, searchResult) { + $container.empty(); + for (const key in searchResult) { + $container.append( + sectionFactory(parseKeywords(keywords), key.toUpperCase(), searchResult[key]), + ); + } + } + + function scrollTo($item) { + if ($item.length === 0) return; + const wrapperHeight = $container[0].clientHeight; + const itemTop = $item.position().top - $container.scrollTop(); + const itemBottom = $item[0].clientHeight + $item.position().top; + if (itemBottom > wrapperHeight + $container.scrollTop()) { + $container.scrollTop(itemBottom - $container[0].clientHeight); + } + if (itemTop < 0) { + $container.scrollTop($item.position().top); + } + } + + function selectItemByDiff(value) { + const $items = $.makeArray($container.find('.searchbox-result-item')); + let prevPosition = -1; + $items.forEach((item, index) => { + if ($(item).hasClass('active')) { + prevPosition = index; + } + }); + const nextPosition = ($items.length + prevPosition + value) % $items.length; + $($items[prevPosition]).removeClass('active'); + $($items[nextPosition]).addClass('active'); + scrollTo($($items[nextPosition])); + } + + function gotoLink($item) { + if ($item && $item.length) { + location.href = $item.attr('href'); + } + } + + $.getJSON(config.contentUrl, (json) => { + if (location.hash.trim() === '#insight-search') { + $main.addClass('show'); + } + $input.on('input', function () { + const keywords = $(this).val(); + searchResultToDOM(keywords, search(json, keywords)); + }); + $input.trigger('input'); + }); + + let touch = false; + $(document) + .on('click focus', '.navbar-main .search', () => { + $main.addClass('show'); + $main.find('.searchbox-input').focus(); + }) + .on('click touchend', '.searchbox-result-item', function (e) { + if (e.type !== 'click' && !touch) { + return; + } + gotoLink($(this)); + touch = false; + }) + .on('click touchend', '.searchbox-close', (e) => { + if (e.type !== 'click' && !touch) { + return; + } + $('.navbar-main').css('pointer-events', 'none'); + setTimeout(() => { + $('.navbar-main').css('pointer-events', 'auto'); + }, 400); + $main.removeClass('show'); + touch = false; + }) + .on('keydown', (e) => { + if (!$main.hasClass('show')) return; + switch (e.keyCode) { + case 27: // ESC + $main.removeClass('show'); + break; + case 38: // UP + selectItemByDiff(-1); + break; + case 40: // DOWN + selectItemByDiff(1); + break; + case 13: // ENTER + gotoLink($container.find('.searchbox-result-item.active').eq(0)); + break; + } + }) + .on('touchstart', (e) => { + touch = true; + }) + .on('touchmove', (e) => { + touch = false; + }); +} diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..7f46128 --- /dev/null +++ b/js/main.js @@ -0,0 +1,137 @@ +/* eslint-disable node/no-unsupported-features/node-builtins */ +(function($, moment, ClipboardJS, config) { + $('.article img:not(".not-gallery-item")').each(function() { + // wrap images with link and add caption if possible + if ($(this).parent('a').length === 0) { + $(this).wrap(''); + if (this.alt) { + $(this).after('

' + this.alt + '

'); + } + } + }); + + if (typeof $.fn.lightGallery === 'function') { + $('.article').lightGallery({ selector: '.gallery-item' }); + } + if (typeof $.fn.justifiedGallery === 'function') { + if ($('.justified-gallery > p > .gallery-item').length) { + $('.justified-gallery > p > .gallery-item').unwrap(); + } + $('.justified-gallery').justifiedGallery(); + } + + if (typeof moment === 'function') { + $('.article-meta time').each(function() { + $(this).text(moment($(this).attr('datetime')).fromNow()); + }); + } + + $('.article > .content > table').each(function() { + if ($(this).width() > $(this).parent().width()) { + $(this).wrap('
'); + } + }); + + function adjustNavbar() { + const navbarWidth = $('.navbar-main .navbar-start').outerWidth() + $('.navbar-main .navbar-end').outerWidth(); + if ($(document).outerWidth() < navbarWidth) { + $('.navbar-main .navbar-menu').addClass('justify-content-start'); + } else { + $('.navbar-main .navbar-menu').removeClass('justify-content-start'); + } + } + adjustNavbar(); + $(window).resize(adjustNavbar); + + function toggleFold(codeBlock, isFolded) { + const $toggle = $(codeBlock).find('.fold i'); + !isFolded ? $(codeBlock).removeClass('folded') : $(codeBlock).addClass('folded'); + !isFolded ? $toggle.removeClass('fa-angle-right') : $toggle.removeClass('fa-angle-down'); + !isFolded ? $toggle.addClass('fa-angle-down') : $toggle.addClass('fa-angle-right'); + } + + function createFoldButton(fold) { + return '' + (fold === 'unfolded' ? '' : '') + ''; + } + + $('figure.highlight table').wrap('
'); + if (typeof config !== 'undefined' + && typeof config.article !== 'undefined' + && typeof config.article.highlight !== 'undefined') { + + $('figure.highlight').addClass('hljs'); + $('figure.highlight .code .line span').each(function() { + const classes = $(this).attr('class').split(/\s+/); + if (classes.length === 1) { + $(this).addClass('hljs-' + classes[0]); + $(this).removeClass(classes[0]); + } + }); + + + const clipboard = config.article.highlight.clipboard; + const fold = config.article.highlight.fold.trim(); + + $('figure.highlight').each(function() { + if ($(this).find('figcaption').length) { + $(this).find('figcaption').addClass('level is-mobile'); + $(this).find('figcaption').append('
'); + $(this).find('figcaption').append('
'); + $(this).find('figcaption div.level-left').append($(this).find('figcaption').find('span')); + $(this).find('figcaption div.level-right').append($(this).find('figcaption').find('a')); + } else { + if (clipboard || fold) { + $(this).prepend('
'); + } + } + }); + + if (typeof ClipboardJS !== 'undefined' && clipboard) { + $('figure.highlight').each(function() { + const id = 'code-' + Date.now() + (Math.random() * 1000 | 0); + const button = ''; + $(this).attr('id', id); + $(this).find('figcaption div.level-right').append(button); + }); + new ClipboardJS('.highlight .copy'); // eslint-disable-line no-new + } + + if (fold) { + $('figure.highlight').each(function() { + if ($(this).find('figcaption').find('span').length > 0) { + const span = $(this).find('figcaption').find('span'); + if (span[0].innerText.indexOf('>folded') > -1) { + span[0].innerText = span[0].innerText.replace('>folded', ''); + $(this).find('figcaption div.level-left').prepend(createFoldButton('folded')); + toggleFold(this, true); + return; + } + } + $(this).find('figcaption div.level-left').prepend(createFoldButton(fold)); + toggleFold(this, fold === 'folded'); + }); + + $('figure.highlight figcaption .fold').click(function() { + const $code = $(this).closest('figure.highlight'); + toggleFold($code.eq(0), !$code.hasClass('folded')); + }); + } + } + + const $toc = $('#toc'); + if ($toc.length > 0) { + const $mask = $('
'); + $mask.attr('id', 'toc-mask'); + + $('body').append($mask); + + function toggleToc() { // eslint-disable-line no-inner-declarations + $toc.toggleClass('is-active'); + $mask.toggleClass('is-active'); + } + + $toc.on('click', toggleToc); + $mask.on('click', toggleToc); + $('.navbar-main .catalogue').on('click', toggleToc); + } +}(jQuery, window.moment, window.ClipboardJS, window.IcarusThemeSettings)); diff --git a/js/toc.js b/js/toc.js new file mode 100644 index 0000000..dcdde5e --- /dev/null +++ b/js/toc.js @@ -0,0 +1,80 @@ +(function (window, document) { + function register($toc) { + const currentInView = new Set(); + const headingToMenu = new Map(); + const $menus = Array.from($toc.querySelectorAll('.menu-list > li > a')); + + for (const $menu of $menus) { + const elementId = $menu.getAttribute('href').trim().slice(1); + const $heading = document.getElementById(elementId); + if ($heading) { + headingToMenu.set($heading, $menu); + } + } + + const $headings = Array.from(headingToMenu.keys()); + + const callback = (entries) => { + for (const entry of entries) { + if (entry.isIntersecting) { + currentInView.add(entry.target); + } else { + currentInView.delete(entry.target); + } + } + let $heading; + if (currentInView.size) { + // heading is the first in-view heading + $heading = [...currentInView].sort(($el1, $el2) => $el1.offsetTop - $el2.offsetTop)[0]; + } else if ($headings.length) { + // heading is the closest heading above the viewport top + $heading = $headings + .filter(($heading) => $heading.offsetTop < window.scrollY) + .sort(($el1, $el2) => $el2.offsetTop - $el1.offsetTop)[0]; + } + if ($heading && headingToMenu.has($heading)) { + $menus.forEach(($menu) => $menu.classList.remove('is-active')); + + const $menu = headingToMenu.get($heading); + $menu.classList.add('is-active'); + let $menuList = $menu.parentElement.parentElement; + while ( + $menuList.classList.contains('menu-list') && + $menuList.parentElement.tagName.toLowerCase() === 'li' + ) { + $menuList.parentElement.children[0].classList.add('is-active'); + $menuList = $menuList.parentElement.parentElement; + } + } + }; + const observer = new IntersectionObserver(callback, { threshold: 0 }); + + for (const $heading of $headings) { + observer.observe($heading); + // smooth scroll to the heading + if (headingToMenu.has($heading)) { + const $menu = headingToMenu.get($heading); + $menu.setAttribute('data-href', $menu.getAttribute('href')); + $menu.setAttribute('href', 'javascript:;'); + $menu.addEventListener('click', () => { + if (typeof $heading.scrollIntoView === 'function') { + $heading.scrollIntoView({ behavior: 'smooth' }); + } + const anchor = $menu.getAttribute('data-href'); + if (history.pushState) { + history.pushState(null, null, anchor); + } else { + location.hash = anchor; + } + }); + $heading.style.scrollMargin = '1em'; + } + } + } + + if (typeof window.IntersectionObserver === 'undefined') { + return; + } + + document.querySelectorAll('#toc').forEach(register); +})(window, document); diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..b77c5b5 --- /dev/null +++ b/manifest.json @@ -0,0 +1 @@ +{"name":"西门闲话","short_name":null,"start_url":"/index.html","theme_color":null,"background_color":null,"display":"standalone","icons":[{"src":"","sizes":"","type":null}]} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 144706b..0000000 --- a/package-lock.json +++ /dev/null @@ -1,8927 +0,0 @@ -{ - "name": "hexo-site", - "version": "0.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "hexo-site", - "version": "0.0.0", - "dependencies": { - "bulma-stylus": "0.8.0", - "cheerio": "^1.0.0-rc.12", - "hexo": "^5.4.2", - "hexo-deployer-git": "^4.0.0", - "hexo-filter-mermaid-diagrams": "^1.0.5", - "hexo-generator-archive": "^1.0.0", - "hexo-generator-category": "^1.0.0", - "hexo-generator-feed": "^3.0.0", - "hexo-generator-index": "^2.0.0", - "hexo-generator-tag": "^1.0.0", - "hexo-renderer-ejs": "^2.0.0", - "hexo-renderer-inferno": "^0.1.3", - "hexo-renderer-less": "^1.0.0", - "hexo-renderer-marked": "^6.1.0", - "hexo-renderer-stylus": "^2.0.0", - "hexo-server": "^2.0.0", - "hexo-theme-icarus": "^4.1.2" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/code-frame/download/@babel/code-frame-7.12.13.tgz?cache=0&sync_timestamp=1612314645844&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fcode-frame%2Fdownload%2F%40babel%2Fcode-frame-7.12.13.tgz", - "integrity": "sha1-3PyCa+72XnXFDiHTg319lXmN1lg=", - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.12.13" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.13.11", - "resolved": "https://registry.npm.taobao.org/@babel/compat-data/download/@babel/compat-data-7.13.11.tgz", - "integrity": "sha1-nI/lI8IGl5yagbHhL+UMElTxqjU=", - "license": "MIT" - }, - "node_modules/@babel/core": { - "version": "7.13.10", - "resolved": "https://registry.npm.taobao.org/@babel/core/download/@babel/core-7.13.10.tgz", - "integrity": "sha1-B94FC72Bk/zYo8J5GMCJBhOpRVk=", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.9", - "@babel/helper-compilation-targets": "^7.13.10", - "@babel/helper-module-transforms": "^7.13.0", - "@babel/helpers": "^7.13.10", - "@babel/parser": "^7.13.10", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "lodash": "^4.17.19", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.13.9", - "resolved": "https://registry.npm.taobao.org/@babel/generator/download/@babel/generator-7.13.9.tgz?cache=0&sync_timestamp=1614635380562&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fgenerator%2Fdownload%2F%40babel%2Fgenerator-7.13.9.tgz", - "integrity": "sha1-Onqpb577jivkLTjYDizrTGTY3jk=", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.13.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/helper-annotate-as-pure/download/@babel/helper-annotate-as-pure-7.12.13.tgz?cache=0&sync_timestamp=1612314650267&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-annotate-as-pure%2Fdownload%2F%40babel%2Fhelper-annotate-as-pure-7.12.13.tgz", - "integrity": "sha1-D1jobfxLs7H819uAZXDhd9Q5tqs=", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/helper-builder-binary-assignment-operator-visitor/download/@babel/helper-builder-binary-assignment-operator-visitor-7.12.13.tgz", - "integrity": "sha1-a8IDYciLCnTQUTemXKyNPL9vYfw=", - "license": "MIT", - "dependencies": { - "@babel/helper-explode-assignable-expression": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.13.10", - "resolved": "https://registry.npm.taobao.org/@babel/helper-compilation-targets/download/@babel/helper-compilation-targets-7.13.10.tgz", - "integrity": "sha1-ExChZ4y4QnwHp1N1DaT4zkQr3Qw=", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.13.8", - "@babel/helper-validator-option": "^7.12.17", - "browserslist": "^4.14.5", - "semver": "^6.3.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.13.11", - "resolved": "https://registry.npm.taobao.org/@babel/helper-create-class-features-plugin/download/@babel/helper-create-class-features-plugin-7.13.11.tgz", - "integrity": "sha1-MNMKAFvKLJU/VlP8JQkaSSF39PY=", - "license": "MIT", - "dependencies": { - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-member-expression-to-functions": "^7.13.0", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/helper-replace-supers": "^7.13.0", - "@babel/helper-split-export-declaration": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.12.17", - "resolved": "https://registry.npm.taobao.org/@babel/helper-create-regexp-features-plugin/download/@babel/helper-create-regexp-features-plugin-7.12.17.tgz?cache=0&sync_timestamp=1613661261586&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-create-regexp-features-plugin%2Fdownload%2F%40babel%2Fhelper-create-regexp-features-plugin-7.12.17.tgz", - "integrity": "sha1-oqyH6eMZJprGVbjUQV6U041mPLc=", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "regexpu-core": "^4.7.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.1.5", - "resolved": "https://registry.npm.taobao.org/@babel/helper-define-polyfill-provider/download/@babel/helper-define-polyfill-provider-0.1.5.tgz?cache=0&sync_timestamp=1614675295626&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-define-polyfill-provider%2Fdownload%2F%40babel%2Fhelper-define-polyfill-provider-0.1.5.tgz", - "integrity": "sha1-PC+Rt5cbn8Ef53nJRcAUBl3qNA4=", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.13.0", - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/traverse": "^7.13.0", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0-0" - } - }, - "node_modules/@babel/helper-explode-assignable-expression": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/helper-explode-assignable-expression/download/@babel/helper-explode-assignable-expression-7.13.0.tgz", - "integrity": "sha1-F7XFn/Rz2flW9A71cM86dsoSZX8=", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.13.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/helper-function-name/download/@babel/helper-function-name-7.12.13.tgz", - "integrity": "sha1-k61lbbPDwiMlWf17LD29y+DrN3o=", - "license": "MIT", - "dependencies": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/helper-get-function-arity/download/@babel/helper-get-function-arity-7.12.13.tgz?cache=0&sync_timestamp=1612314643704&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-get-function-arity%2Fdownload%2F%40babel%2Fhelper-get-function-arity-7.12.13.tgz", - "integrity": "sha1-vGNFHUA6OzCCuX4diz/lvUCR5YM=", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/helper-hoist-variables/download/@babel/helper-hoist-variables-7.13.0.tgz", - "integrity": "sha1-XViC6FW1xe2pHgytwmxueiyFk9g=", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/helper-member-expression-to-functions/download/@babel/helper-member-expression-to-functions-7.13.0.tgz", - "integrity": "sha1-aqS7Z44PjCL1jNt5RR0wSURhsJE=", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.13.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/helper-module-imports/download/@babel/helper-module-imports-7.12.13.tgz?cache=0&sync_timestamp=1612314644422&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-module-imports%2Fdownload%2F%40babel%2Fhelper-module-imports-7.12.13.tgz", - "integrity": "sha1-7GfkQE9BdQRj5FXMMgP2oy6T/LA=", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/helper-module-transforms/download/@babel/helper-module-transforms-7.13.0.tgz", - "integrity": "sha1-QutL2O6mi6tGdRISw1e/7YtA9vE=", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-replace-supers": "^7.13.0", - "@babel/helper-simple-access": "^7.12.13", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/helper-validator-identifier": "^7.12.11", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0", - "lodash": "^4.17.19" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/helper-optimise-call-expression/download/@babel/helper-optimise-call-expression-7.12.13.tgz?cache=0&sync_timestamp=1612314645448&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-optimise-call-expression%2Fdownload%2F%40babel%2Fhelper-optimise-call-expression-7.12.13.tgz", - "integrity": "sha1-XALRcbTIYVsecWP4iMHIHDCiquo=", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/helper-plugin-utils/download/@babel/helper-plugin-utils-7.13.0.tgz?cache=0&sync_timestamp=1614035648564&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-plugin-utils%2Fdownload%2F%40babel%2Fhelper-plugin-utils-7.13.0.tgz", - "integrity": "sha1-gGUmzhJa7QM3O8QWqCgyHjpqM68=", - "license": "MIT" - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/helper-remap-async-to-generator/download/@babel/helper-remap-async-to-generator-7.13.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-remap-async-to-generator%2Fdownload%2F%40babel%2Fhelper-remap-async-to-generator-7.13.0.tgz", - "integrity": "sha1-N2p2DZ97SyB3qd0Fqpw5J8rbIgk=", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-wrap-function": "^7.13.0", - "@babel/types": "^7.13.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/helper-replace-supers/download/@babel/helper-replace-supers-7.13.0.tgz?cache=0&sync_timestamp=1614035151077&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-replace-supers%2Fdownload%2F%40babel%2Fhelper-replace-supers-7.13.0.tgz", - "integrity": "sha1-YDS3tRlDCUy0FieEjLIZywK+HSQ=", - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.13.0", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/helper-simple-access/download/@babel/helper-simple-access-7.12.13.tgz?cache=0&sync_timestamp=1612314645475&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-simple-access%2Fdownload%2F%40babel%2Fhelper-simple-access-7.12.13.tgz", - "integrity": "sha1-hHi8xcrPaqFnKyUcHS3eXM1hpsQ=", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.12.1", - "resolved": "https://registry.npm.taobao.org/@babel/helper-skip-transparent-expression-wrappers/download/@babel/helper-skip-transparent-expression-wrappers-7.12.1.tgz?cache=0&sync_timestamp=1602800255982&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-skip-transparent-expression-wrappers%2Fdownload%2F%40babel%2Fhelper-skip-transparent-expression-wrappers-7.12.1.tgz", - "integrity": "sha1-Ri3GOn5DWt6EaDhcY9K4TM5LPL8=", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.12.1" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/helper-split-export-declaration/download/@babel/helper-split-export-declaration-7.12.13.tgz?cache=0&sync_timestamp=1612314645430&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-split-export-declaration%2Fdownload%2F%40babel%2Fhelper-split-export-declaration-7.12.13.tgz", - "integrity": "sha1-6UML4AuvPoiw4T5vnU6vITY3KwU=", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npm.taobao.org/@babel/helper-validator-identifier/download/@babel/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha1-yaHwIZF9y1zPDU5FPjmQIpgfye0=", - "license": "MIT" - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.12.17", - "resolved": "https://registry.npm.taobao.org/@babel/helper-validator-option/download/@babel/helper-validator-option-7.12.17.tgz?cache=0&sync_timestamp=1613661224272&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-validator-option%2Fdownload%2F%40babel%2Fhelper-validator-option-7.12.17.tgz", - "integrity": "sha1-0fvwEuGnm37rv9xtJwuq+NnrmDE=", - "license": "MIT" - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/helper-wrap-function/download/@babel/helper-wrap-function-7.13.0.tgz", - "integrity": "sha1-vbXGb9qFJuwjWriUrVOhI1x5/MQ=", - "license": "MIT", - "dependencies": { - "@babel/helper-function-name": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.13.10", - "resolved": "https://registry.npm.taobao.org/@babel/helpers/download/@babel/helpers-7.13.10.tgz", - "integrity": "sha1-/Y4rp0iFM83qxFzBWOnryl48ffg=", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npm.taobao.org/@babel/highlight/download/@babel/highlight-7.13.10.tgz", - "integrity": "sha1-qLKmYUj1sn1maxXYF3Q0enMdUtE=", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.12.11", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-3.2.1.tgz?cache=0&sync_timestamp=1611325836307&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-styles%2Fdownload%2Fansi-styles-3.2.1.tgz", - "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-2.4.2.tgz?cache=0&sync_timestamp=1591687130777&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchalk%2Fdownload%2Fchalk-2.4.2.tgz", - "integrity": "sha1-zUJUFnelQzPPVBpJEIwUMrRMlCQ=", - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npm.taobao.org/color-convert/download/color-convert-1.9.3.tgz", - "integrity": "sha1-u3GFBpDh8TZWfeYp0tVHHe2kweg=", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npm.taobao.org/color-name/download/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "license": "MIT" - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-5.5.0.tgz?cache=0&sync_timestamp=1611394043517&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-5.5.0.tgz", - "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.13.11", - "resolved": "https://registry.npm.taobao.org/@babel/parser/download/@babel/parser-7.13.11.tgz", - "integrity": "sha1-+T6/yZ0hwXcq+7qhU/R+fOL1C4g=", - "license": "MIT", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-async-generator-functions/download/@babel/plugin-proposal-async-generator-functions-7.13.8.tgz?cache=0&sync_timestamp=1614383121240&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-proposal-async-generator-functions%2Fdownload%2F%40babel%2Fplugin-proposal-async-generator-functions-7.13.8.tgz", - "integrity": "sha1-h6rLV0s7xLVgP2/kFFjXKlouxLE=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-remap-async-to-generator": "^7.13.0", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-class-properties/download/@babel/plugin-proposal-class-properties-7.13.0.tgz", - "integrity": "sha1-FGN2AAuU79AB5XpAqIpSWvqrnzc=", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-dynamic-import/download/@babel/plugin-proposal-dynamic-import-7.13.8.tgz?cache=0&sync_timestamp=1614383120439&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-proposal-dynamic-import%2Fdownload%2F%40babel%2Fplugin-proposal-dynamic-import-7.13.8.tgz", - "integrity": "sha1-h2ofaWbh3sMy6MlFGv2jvrzfLh0=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-export-namespace-from/download/@babel/plugin-proposal-export-namespace-from-7.12.13.tgz", - "integrity": "sha1-OTvkekrNA/oq9uPN6bBuM94bRG0=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-json-strings": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-json-strings/download/@babel/plugin-proposal-json-strings-7.13.8.tgz?cache=0&sync_timestamp=1614383122014&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-proposal-json-strings%2Fdownload%2F%40babel%2Fplugin-proposal-json-strings-7.13.8.tgz", - "integrity": "sha1-vx+zYlRwda/aNjTtMVccWQGv73s=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-logical-assignment-operators/download/@babel/plugin-proposal-logical-assignment-operators-7.13.8.tgz?cache=0&sync_timestamp=1614383123641&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-proposal-logical-assignment-operators%2Fdownload%2F%40babel%2Fplugin-proposal-logical-assignment-operators-7.13.8.tgz", - "integrity": "sha1-k/p41jhXxAzjyMMxUiD9AL+7Tho=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-nullish-coalescing-operator/download/@babel/plugin-proposal-nullish-coalescing-operator-7.13.8.tgz?cache=0&sync_timestamp=1614383122422&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-proposal-nullish-coalescing-operator%2Fdownload%2F%40babel%2Fplugin-proposal-nullish-coalescing-operator-7.13.8.tgz", - "integrity": "sha1-NzCjHa/TwQ2MzRBkjtgKKsVHLvM=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-numeric-separator/download/@babel/plugin-proposal-numeric-separator-7.12.13.tgz", - "integrity": "sha1-vZ2jGI54e1EgtPnUZagmHOZ+0ds=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-object-rest-spread/download/@babel/plugin-proposal-object-rest-spread-7.13.8.tgz?cache=0&sync_timestamp=1614383131892&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-proposal-object-rest-spread%2Fdownload%2F%40babel%2Fplugin-proposal-object-rest-spread-7.13.8.tgz", - "integrity": "sha1-XSEKTXJ9bOOxj53oLMmaOWTu1go=", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.13.8", - "@babel/helper-compilation-targets": "^7.13.8", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-optional-catch-binding/download/@babel/plugin-proposal-optional-catch-binding-7.13.8.tgz?cache=0&sync_timestamp=1614383122839&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-proposal-optional-catch-binding%2Fdownload%2F%40babel%2Fplugin-proposal-optional-catch-binding-7.13.8.tgz", - "integrity": "sha1-Ota9WQFQbqmW/DG9zzzPor7XEQc=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-optional-chaining/download/@babel/plugin-proposal-optional-chaining-7.13.8.tgz?cache=0&sync_timestamp=1614383126788&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-proposal-optional-chaining%2Fdownload%2F%40babel%2Fplugin-proposal-optional-chaining-7.13.8.tgz", - "integrity": "sha1-4535Pv5+fmIYQbq8GXmC4UDpB1Y=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-methods": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-private-methods/download/@babel/plugin-proposal-private-methods-7.13.0.tgz", - "integrity": "sha1-BL1MbUD25rv6L1fi2AlLrZAO94c=", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-unicode-property-regex/download/@babel/plugin-proposal-unicode-property-regex-7.12.13.tgz", - "integrity": "sha1-vr3lEzm+gpwXqqrO0YZB3rYrObo=", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-async-generators/download/@babel/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha1-qYP7Gusuw/btBCohD2QOkOeG/g0=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-class-properties/download/@babel/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha1-tcmHJ0xKOoK4lxR5aTGmtTVErhA=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-dynamic-import/download/@babel/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha1-Yr+Ysto80h1iYVT8lu5bPLaOrLM=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-export-namespace-from/download/@babel/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha1-AolkqbqA28CUyRXEh618TnpmRlo=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-json-strings/download/@babel/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha1-AcohtmjNghjJ5kDLbdiMVBKyyWo=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-jsx/download/@babel/plugin-syntax-jsx-7.12.13.tgz", - "integrity": "sha1-BE+4HrrWaY/mLEeIdVdby7m3DxU=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-logical-assignment-operators/download/@babel/plugin-syntax-logical-assignment-operators-7.10.4.tgz?cache=0&sync_timestamp=1593521628455&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-syntax-logical-assignment-operators%2Fdownload%2F%40babel%2Fplugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha1-ypHvRjA1MESLkGZSusLp/plB9pk=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-nullish-coalescing-operator/download/@babel/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha1-Fn7XA2iIYIH3S1w2xlqIwDtm0ak=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-numeric-separator/download/@babel/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha1-ubBws+M1cM2f0Hun+pHA3Te5r5c=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-object-rest-spread/download/@babel/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha1-YOIl7cvZimQDMqLnLdPmbxr1WHE=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-optional-catch-binding/download/@babel/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha1-YRGiZbz7Ag6579D9/X0mQCue1sE=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-optional-chaining/download/@babel/plugin-syntax-optional-chaining-7.8.3.tgz?cache=0&sync_timestamp=1578977710469&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-syntax-optional-chaining%2Fdownload%2F%40babel%2Fplugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha1-T2nCq5UWfgGAzVM2YT+MV4j31Io=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-top-level-await/download/@babel/plugin-syntax-top-level-await-7.12.13.tgz", - "integrity": "sha1-xfD6biSfW3OXJ/kjVAz3qAYTAXg=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-arrow-functions/download/@babel/plugin-transform-arrow-functions-7.13.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-arrow-functions%2Fdownload%2F%40babel%2Fplugin-transform-arrow-functions-7.13.0.tgz", - "integrity": "sha1-EKWb661S1jegJ6+mkujVzv9ePa4=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-async-to-generator/download/@babel/plugin-transform-async-to-generator-7.13.0.tgz", - "integrity": "sha1-jhEr9ncbgr8el05eJoBsXJmqUW8=", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-remap-async-to-generator": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-block-scoped-functions/download/@babel/plugin-transform-block-scoped-functions-7.12.13.tgz", - "integrity": "sha1-qb8YNvKjm062zwmWdzneKepL9MQ=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-block-scoping/download/@babel/plugin-transform-block-scoping-7.12.13.tgz", - "integrity": "sha1-825VB20G9B39eFV+oDnBtYFkLmE=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-classes/download/@babel/plugin-transform-classes-7.13.0.tgz?cache=0&sync_timestamp=1614035151583&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-classes%2Fdownload%2F%40babel%2Fplugin-transform-classes-7.13.0.tgz", - "integrity": "sha1-AmUVUHXEKRi/TTpAUxNBdq2bUzs=", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-replace-supers": "^7.13.0", - "@babel/helper-split-export-declaration": "^7.12.13", - "globals": "^11.1.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-computed-properties/download/@babel/plugin-transform-computed-properties-7.13.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-computed-properties%2Fdownload%2F%40babel%2Fplugin-transform-computed-properties-7.13.0.tgz", - "integrity": "sha1-hFxui5u1U3ax+guS7wvcjqBmRO0=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-destructuring/download/@babel/plugin-transform-destructuring-7.13.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-destructuring%2Fdownload%2F%40babel%2Fplugin-transform-destructuring-7.13.0.tgz", - "integrity": "sha1-xdzicAFNTh67HYBhFmlMErcCiWM=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-dotall-regex/download/@babel/plugin-transform-dotall-regex-7.12.13.tgz", - "integrity": "sha1-PxYBzCmQW/y2f1ORDxl66v67Ja0=", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-duplicate-keys/download/@babel/plugin-transform-duplicate-keys-7.12.13.tgz", - "integrity": "sha1-bwa4eouAP9ko5UuBwljwoAM5BN4=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-exponentiation-operator/download/@babel/plugin-transform-exponentiation-operator-7.12.13.tgz", - "integrity": "sha1-TVI5C5onPmUeSrpq7knvQOgM0KE=", - "license": "MIT", - "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-for-of/download/@babel/plugin-transform-for-of-7.13.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-for-of%2Fdownload%2F%40babel%2Fplugin-transform-for-of-7.13.0.tgz", - "integrity": "sha1-x5n4gagJGsJrVIZ6hFw+l9JpYGI=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-function-name/download/@babel/plugin-transform-function-name-7.12.13.tgz", - "integrity": "sha1-uwJEUvmq7YYdN0yOeiQlLOOlAFE=", - "license": "MIT", - "dependencies": { - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-literals/download/@babel/plugin-transform-literals-7.12.13.tgz", - "integrity": "sha1-LKRbr+SoIBl88xV5Sk0mVg/kvbk=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-member-expression-literals/download/@babel/plugin-transform-member-expression-literals-7.12.13.tgz", - "integrity": "sha1-X/pmzVm54ZExTJ8fgDuTjowIHkA=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-modules-amd/download/@babel/plugin-transform-modules-amd-7.13.0.tgz", - "integrity": "sha1-GfUR1g49h1PMWm1Od106UYSGbMM=", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-modules-commonjs/download/@babel/plugin-transform-modules-commonjs-7.13.8.tgz?cache=0&sync_timestamp=1614383124878&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-modules-commonjs%2Fdownload%2F%40babel%2Fplugin-transform-modules-commonjs-7.13.8.tgz", - "integrity": "sha1-ewGtfC3PInWwb6F4HgDRPUILPhs=", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-simple-access": "^7.12.13", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-modules-systemjs/download/@babel/plugin-transform-modules-systemjs-7.13.8.tgz?cache=0&sync_timestamp=1614383123237&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-modules-systemjs%2Fdownload%2F%40babel%2Fplugin-transform-modules-systemjs-7.13.8.tgz", - "integrity": "sha1-bQZu4r/zx7PWC/KN7Baa2ZODGuM=", - "license": "MIT", - "dependencies": { - "@babel/helper-hoist-variables": "^7.13.0", - "@babel/helper-module-transforms": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-validator-identifier": "^7.12.11", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-modules-umd/download/@babel/plugin-transform-modules-umd-7.13.0.tgz", - "integrity": "sha1-ij2WqX0ZlwW5/QIVgAgq+BwG5ws=", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-named-capturing-groups-regex/download/@babel/plugin-transform-named-capturing-groups-regex-7.12.13.tgz", - "integrity": "sha1-IhNyWl9bu+NktQw7pZmMlZnFydk=", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-new-target/download/@babel/plugin-transform-new-target-7.12.13.tgz", - "integrity": "sha1-4i2MOvJLFQ3VKMvW5oXnmb8cNRw=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-object-super/download/@babel/plugin-transform-object-super-7.12.13.tgz", - "integrity": "sha1-tEFqLWO4974xTz00m9VanBtRcfc=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13", - "@babel/helper-replace-supers": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-parameters/download/@babel/plugin-transform-parameters-7.13.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-parameters%2Fdownload%2F%40babel%2Fplugin-transform-parameters-7.13.0.tgz", - "integrity": "sha1-j6dgPjCX+cC3yhpIIbwvtS6eUAc=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-property-literals/download/@babel/plugin-transform-property-literals-7.12.13.tgz", - "integrity": "sha1-TmqeN4ZNjxs7wOLc57+IV9uLGoE=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-regenerator/download/@babel/plugin-transform-regenerator-7.12.13.tgz?cache=0&sync_timestamp=1612314644517&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-regenerator%2Fdownload%2F%40babel%2Fplugin-transform-regenerator-7.12.13.tgz", - "integrity": "sha1-tii8ychSYKwa6wW0W94lIQGUovU=", - "license": "MIT", - "dependencies": { - "regenerator-transform": "^0.14.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-reserved-words/download/@babel/plugin-transform-reserved-words-7.12.13.tgz", - "integrity": "sha1-fZmI1PBuD+aX6h2YAxiKoYtHJpU=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-shorthand-properties/download/@babel/plugin-transform-shorthand-properties-7.12.13.tgz", - "integrity": "sha1-23VXMrcMU51QTGOQ2c6Q/mSv960=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-spread/download/@babel/plugin-transform-spread-7.13.0.tgz?cache=0&sync_timestamp=1614035651747&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-spread%2Fdownload%2F%40babel%2Fplugin-transform-spread-7.13.0.tgz", - "integrity": "sha1-hIh3EOJzwYFaznrkWfb0Kl0x1f0=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-sticky-regex/download/@babel/plugin-transform-sticky-regex-7.12.13.tgz", - "integrity": "sha1-dg/9k2+s5z+GCuZG+4bugvPQbR8=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-template-literals/download/@babel/plugin-transform-template-literals-7.13.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-template-literals%2Fdownload%2F%40babel%2Fplugin-transform-template-literals-7.13.0.tgz", - "integrity": "sha1-o2BJEnl3rZRDje50Q1mNHO/fQJ0=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-typeof-symbol/download/@babel/plugin-transform-typeof-symbol-7.12.13.tgz", - "integrity": "sha1-eF3Weh8upXnZwr5yLejITLhfWn8=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-unicode-escapes/download/@babel/plugin-transform-unicode-escapes-7.12.13.tgz", - "integrity": "sha1-hAztO4FtO1En3R0S3O3F3q0aXnQ=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-unicode-regex/download/@babel/plugin-transform-unicode-regex-7.12.13.tgz", - "integrity": "sha1-tSUhaFgE4VWxIC6D/BiNNLtw9aw=", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.13.10", - "resolved": "https://registry.npm.taobao.org/@babel/preset-env/download/@babel/preset-env-7.13.10.tgz", - "integrity": "sha1-tc3jHV/nerKmqz1FO1kEGhs6UlI=", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.13.8", - "@babel/helper-compilation-targets": "^7.13.10", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-validator-option": "^7.12.17", - "@babel/plugin-proposal-async-generator-functions": "^7.13.8", - "@babel/plugin-proposal-class-properties": "^7.13.0", - "@babel/plugin-proposal-dynamic-import": "^7.13.8", - "@babel/plugin-proposal-export-namespace-from": "^7.12.13", - "@babel/plugin-proposal-json-strings": "^7.13.8", - "@babel/plugin-proposal-logical-assignment-operators": "^7.13.8", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8", - "@babel/plugin-proposal-numeric-separator": "^7.12.13", - "@babel/plugin-proposal-object-rest-spread": "^7.13.8", - "@babel/plugin-proposal-optional-catch-binding": "^7.13.8", - "@babel/plugin-proposal-optional-chaining": "^7.13.8", - "@babel/plugin-proposal-private-methods": "^7.13.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.12.13", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.12.13", - "@babel/plugin-transform-arrow-functions": "^7.13.0", - "@babel/plugin-transform-async-to-generator": "^7.13.0", - "@babel/plugin-transform-block-scoped-functions": "^7.12.13", - "@babel/plugin-transform-block-scoping": "^7.12.13", - "@babel/plugin-transform-classes": "^7.13.0", - "@babel/plugin-transform-computed-properties": "^7.13.0", - "@babel/plugin-transform-destructuring": "^7.13.0", - "@babel/plugin-transform-dotall-regex": "^7.12.13", - "@babel/plugin-transform-duplicate-keys": "^7.12.13", - "@babel/plugin-transform-exponentiation-operator": "^7.12.13", - "@babel/plugin-transform-for-of": "^7.13.0", - "@babel/plugin-transform-function-name": "^7.12.13", - "@babel/plugin-transform-literals": "^7.12.13", - "@babel/plugin-transform-member-expression-literals": "^7.12.13", - "@babel/plugin-transform-modules-amd": "^7.13.0", - "@babel/plugin-transform-modules-commonjs": "^7.13.8", - "@babel/plugin-transform-modules-systemjs": "^7.13.8", - "@babel/plugin-transform-modules-umd": "^7.13.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.13", - "@babel/plugin-transform-new-target": "^7.12.13", - "@babel/plugin-transform-object-super": "^7.12.13", - "@babel/plugin-transform-parameters": "^7.13.0", - "@babel/plugin-transform-property-literals": "^7.12.13", - "@babel/plugin-transform-regenerator": "^7.12.13", - "@babel/plugin-transform-reserved-words": "^7.12.13", - "@babel/plugin-transform-shorthand-properties": "^7.12.13", - "@babel/plugin-transform-spread": "^7.13.0", - "@babel/plugin-transform-sticky-regex": "^7.12.13", - "@babel/plugin-transform-template-literals": "^7.13.0", - "@babel/plugin-transform-typeof-symbol": "^7.12.13", - "@babel/plugin-transform-unicode-escapes": "^7.12.13", - "@babel/plugin-transform-unicode-regex": "^7.12.13", - "@babel/preset-modules": "^0.1.4", - "@babel/types": "^7.13.0", - "babel-plugin-polyfill-corejs2": "^0.1.4", - "babel-plugin-polyfill-corejs3": "^0.1.3", - "babel-plugin-polyfill-regenerator": "^0.1.2", - "core-js-compat": "^3.9.0", - "semver": "^6.3.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.4", - "resolved": "https://registry.npm.taobao.org/@babel/preset-modules/download/@babel/preset-modules-0.1.4.tgz", - "integrity": "sha1-Ni8raMZihClw/bXiVP/I/BwuQV4=", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/register": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/register/download/@babel/register-7.13.8.tgz", - "integrity": "sha1-2QUdxoIMtOhjdcwOLVWkhisxGE8=", - "license": "MIT", - "dependencies": { - "find-cache-dir": "^2.0.0", - "lodash": "^4.17.19", - "make-dir": "^2.1.0", - "pirates": "^4.0.0", - "source-map-support": "^0.5.16" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.13.10", - "resolved": "https://registry.npm.taobao.org/@babel/runtime/download/@babel/runtime-7.13.10.tgz", - "integrity": "sha1-R9QqV7YJX0Ro2kQDiP262L6/DX0=", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.13.4" - } - }, - "node_modules/@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/template/download/@babel/template-7.12.13.tgz?cache=0&sync_timestamp=1612314959052&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Ftemplate%2Fdownload%2F%40babel%2Ftemplate-7.12.13.tgz", - "integrity": "sha1-UwJlvooliduzdSOETFvLVZR/syc=", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/traverse": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/traverse/download/@babel/traverse-7.13.0.tgz?cache=0&sync_timestamp=1614035150979&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Ftraverse%2Fdownload%2F%40babel%2Ftraverse-7.13.0.tgz", - "integrity": "sha1-bZV1JHX4bufe0GU23jCaZfyJZsw=", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.0", - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.13.0", - "@babel/types": "^7.13.0", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - } - }, - "node_modules/@babel/types": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/types/download/@babel/types-7.13.0.tgz", - "integrity": "sha1-dEJNKBbwFxtBAPCrNOmjdO/ff4A=", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/a-sync-waterfall": { - "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/a-sync-waterfall/download/a-sync-waterfall-1.0.1.tgz", - "integrity": "sha1-dba2qnJZi0l6El56J3DxT0yKH6c=", - "license": "MIT" - }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npm.taobao.org/abbrev/download/abbrev-1.1.1.tgz", - "integrity": "sha1-+PLIh60Qv2f2NPAFtph/7TF5qsg=", - "license": "ISC" - }, - "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npm.taobao.org/accepts/download/accepts-1.3.7.tgz", - "integrity": "sha1-UxvHJlF6OytB+FACHGzBXqq1B80=", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-globals": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", - "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", - "dependencies": { - "acorn": "^8.1.0", - "acorn-walk": "^8.0.2" - } - }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.6.tgz", - "integrity": "sha1-uvWmLoArB9l3A0WG+MO69a3ybfQ=", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/amdefine/download/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "license": "BSD-3-Clause OR MIT", - "engines": { - "node": ">=0.4.2" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-4.3.0.tgz?cache=0&sync_timestamp=1611325836307&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-styles%2Fdownload%2Fansi-styles-4.3.0.tgz", - "integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/archy/download/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "license": "MIT" - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npm.taobao.org/argparse/download/argparse-1.0.10.tgz", - "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/argparse/node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npm.taobao.org/sprintf-js/download/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "license": "BSD-3-Clause" - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npm.taobao.org/asap/download/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "license": "MIT" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npm.taobao.org/atob/download/atob-2.1.2.tgz", - "integrity": "sha1-bZUX654DDSQ2ZmZR6GvZ9vE1M8k=", - "license": "(MIT OR Apache-2.0)", - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npm.taobao.org/babel-plugin-dynamic-import-node/download/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha1-hP2hnJduxcbe/vV/lCez3vZuF6M=", - "license": "MIT", - "dependencies": { - "object.assign": "^4.1.0" - } - }, - "node_modules/babel-plugin-inferno": { - "version": "6.1.1", - "resolved": "https://registry.npm.taobao.org/babel-plugin-inferno/download/babel-plugin-inferno-6.1.1.tgz", - "integrity": "sha1-Fca/uf/3WmZp6fZBC7NlEuroulQ=", - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-jsx": "^7", - "@babel/types": "^7" - }, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "@babel/core": "^7", - "inferno": ">=7" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.1.10", - "resolved": "https://registry.npm.taobao.org/babel-plugin-polyfill-corejs2/download/babel-plugin-polyfill-corejs2-0.1.10.tgz?cache=0&sync_timestamp=1614681685642&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbabel-plugin-polyfill-corejs2%2Fdownload%2Fbabel-plugin-polyfill-corejs2-0.1.10.tgz", - "integrity": "sha1-osXCRfVsDKw9vdvwcmpGsk8PgdE=", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.13.0", - "@babel/helper-define-polyfill-provider": "^0.1.5", - "semver": "^6.1.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.1.7", - "resolved": "https://registry.npm.taobao.org/babel-plugin-polyfill-corejs3/download/babel-plugin-polyfill-corejs3-0.1.7.tgz", - "integrity": "sha1-gESdnW8idJEuBdnhgrVIFpBL79A=", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.1.5", - "core-js-compat": "^3.8.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.1.6", - "resolved": "https://registry.npm.taobao.org/babel-plugin-polyfill-regenerator/download/babel-plugin-polyfill-regenerator-0.1.6.tgz?cache=0&sync_timestamp=1614675087036&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbabel-plugin-polyfill-regenerator%2Fdownload%2Fbabel-plugin-polyfill-regenerator-0.1.6.tgz", - "integrity": "sha1-D+BqAm/g+qYozMi6MwLaCmzgLz8=", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.1.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "license": "MIT" - }, - "node_modules/basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npm.taobao.org/basic-auth/download/basic-auth-2.0.1.tgz", - "integrity": "sha1-uZgnm/R844NEtPPPkW1Gebv1Hjo=", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npm.taobao.org/binary-extensions/download/binary-extensions-2.2.0.tgz?cache=0&sync_timestamp=1610299373298&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbinary-extensions%2Fdownload%2Fbinary-extensions-2.2.0.tgz", - "integrity": "sha1-dfUC7q+f/eQvyYgpZFvk6na9ni0=", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npm.taobao.org/bluebird/download/bluebird-3.7.2.tgz?cache=0&sync_timestamp=1574981921523&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbluebird%2Fdownload%2Fbluebird-3.7.2.tgz", - "integrity": "sha1-nyKcFb4nJFT/qXOs4NvueaGww28=", - "license": "MIT" - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npm.taobao.org/brace-expansion/download/brace-expansion-1.1.11.tgz?cache=0&sync_timestamp=1614010713935&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbrace-expansion%2Fdownload%2Fbrace-expansion-1.1.11.tgz", - "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npm.taobao.org/braces/download/braces-3.0.2.tgz", - "integrity": "sha1-NFThpGLujVmeI23zNs2epPiv4Qc=", - "license": "MIT", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", - "dependencies": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } - }, - "node_modules/buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npm.taobao.org/buffer-from/download/buffer-from-1.1.1.tgz", - "integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=", - "license": "MIT" - }, - "node_modules/bulma-stylus": { - "version": "0.8.0", - "resolved": "https://registry.npm.taobao.org/bulma-stylus/download/bulma-stylus-0.8.0.tgz", - "integrity": "sha1-E3wYKh/fmsgvLnO9MWOqxFSxBa4=", - "license": "MIT" - }, - "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/bytes/download/bytes-3.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbytes%2Fdownload%2Fbytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npm.taobao.org/call-bind/download/call-bind-1.0.2.tgz?cache=0&sync_timestamp=1610403244794&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcall-bind%2Fdownload%2Fcall-bind-1.0.2.tgz", - "integrity": "sha1-sdTonmiBGcPJqQOtMKuy9qkZvjw=", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npm.taobao.org/camel-case/download/camel-case-4.1.2.tgz", - "integrity": "sha1-lygHKpVPgFIoIlpt7qazhGHhvVo=", - "license": "MIT", - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001512", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001512.tgz", - "integrity": "sha512-2S9nK0G/mE+jasCUsMPlARhRCts1ebcp2Ji8Y8PWi4NDE1iRdLCnEPHkEfeBrGC45L4isBx5ur3IQ6yTE2mRZw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-4.1.0.tgz?cache=0&sync_timestamp=1591687130777&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchalk%2Fdownload%2Fchalk-4.1.0.tgz", - "integrity": "sha1-ThSHCmGNni7dl92DRf2dncMVZGo=", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", - "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", - "dependencies": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", - "parse5": "^7.0.0", - "parse5-htmlparser2-tree-adapter": "^7.0.0" - }, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" - } - }, - "node_modules/cheerio-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", - "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "dependencies": { - "boolbase": "^1.0.0", - "css-select": "^5.1.0", - "css-what": "^6.1.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/cheerio-select/node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/cheerio-select/node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/cheerio-select/node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/cheerio-select/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/cheerio/node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/cheerio/node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/cheerio/node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/cheerio/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npm.taobao.org/color-convert/download/color-convert-2.0.1.tgz", - "integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npm.taobao.org/color-name/download/color-name-1.1.4.tgz", - "integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=", - "license": "MIT" - }, - "node_modules/colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/command-exists": { - "version": "1.2.9", - "resolved": "https://registry.npm.taobao.org/command-exists/download/command-exists-1.2.9.tgz", - "integrity": "sha1-xQclrzgIyKsCYP1gsB+/oluVT2k=", - "license": "MIT" - }, - "node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npm.taobao.org/commander/download/commander-5.1.0.tgz", - "integrity": "sha1-Rqu9FlL44Fm92u+Zu9yyrZzxea4=", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/commondir/download/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "license": "MIT" - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npm.taobao.org/compressible/download/compressible-2.0.18.tgz", - "integrity": "sha1-r1PMprBw1MPAdQ+9dyhqbXzEb7o=", - "license": "MIT", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npm.taobao.org/compression/download/compression-1.7.4.tgz", - "integrity": "sha1-lVI+/xcMpXwpoMpB5v4TH0Hlu48=", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&sync_timestamp=1607566600214&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&sync_timestamp=1607433905910&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npm.taobao.org/concat-map/download/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "license": "MIT" - }, - "node_modules/connect": { - "version": "3.7.0", - "resolved": "https://registry.npm.taobao.org/connect/download/connect-3.7.0.tgz", - "integrity": "sha1-XUk0iRDKpeB6AYALAw0MNfIEhPg=", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/connect/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&sync_timestamp=1607566600214&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/connect/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&sync_timestamp=1607433905910&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npm.taobao.org/convert-source-map/download/convert-source-map-1.7.0.tgz", - "integrity": "sha1-F6LLiC1/d9NJBYXizmxSRCSjpEI=", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/copy-anything": { - "version": "2.0.3", - "resolved": "https://registry.npm.taobao.org/copy-anything/download/copy-anything-2.0.3.tgz", - "integrity": "sha1-hCQHugJGaw34RIGbvjuuu+XUXYc=", - "license": "MIT", - "dependencies": { - "is-what": "^3.12.0" - } - }, - "node_modules/core-js-compat": { - "version": "3.9.1", - "resolved": "https://registry.npm.taobao.org/core-js-compat/download/core-js-compat-3.9.1.tgz", - "integrity": "sha1-Tlcqz+kK/2nXbYw3dZ0hpcWbtFU=", - "license": "MIT", - "dependencies": { - "browserslist": "^4.16.3", - "semver": "7.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-compat/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npm.taobao.org/cross-spawn/download/cross-spawn-7.0.3.tgz", - "integrity": "sha1-9zqFudXUHQRVUcF34ogtSshXKKY=", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css": { - "version": "2.2.4", - "resolved": "https://registry.npm.taobao.org/css/download/css-2.2.4.tgz", - "integrity": "sha1-xkZ1XHOXHyu6amAeLPL9cbEpiSk=", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "source-map": "^0.6.1", - "source-map-resolve": "^0.5.2", - "urix": "^0.1.0" - } - }, - "node_modules/css-parse": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/css-parse/download/css-parse-2.0.0.tgz", - "integrity": "sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=", - "license": "MIT", - "dependencies": { - "css": "^2.0.0" - } - }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-select/node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/css-select/node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/css-select/node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/css-select/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cssom": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", - "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==" - }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dependencies": { - "cssom": "~0.3.6" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" - }, - "node_modules/cuid": { - "version": "2.1.8", - "resolved": "https://registry.npm.taobao.org/cuid/download/cuid-2.1.8.tgz", - "integrity": "sha1-y7iPlUFx4NV0dgbAE5+2XFEB6sA=", - "license": "MIT" - }, - "node_modules/data-urls": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", - "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", - "dependencies": { - "abab": "^2.0.6", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npm.taobao.org/debug/download/debug-4.3.1.tgz?cache=0&sync_timestamp=1607566600214&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.3.1.tgz", - "integrity": "sha1-8NIpxQXgxtjEmsVT0bE9wYP2su4=", - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" - }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/deep-assign": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/deep-assign/download/deep-assign-2.0.0.tgz", - "integrity": "sha1-6+BrHwfwja5ZdiDj3RYi83GhxXI=", - "license": "MIT", - "dependencies": { - "is-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npm.taobao.org/deepmerge/download/deepmerge-4.2.2.tgz", - "integrity": "sha1-RNLqNnm49NT/ujPwPYZfwee/SVU=", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npm.taobao.org/define-properties/download/define-properties-1.1.3.tgz", - "integrity": "sha1-z4jabL7ib+bbcJT2HYcMvYTO6fE=", - "license": "MIT", - "dependencies": { - "object-keys": "^1.0.12" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/depd/download/depd-2.0.0.tgz", - "integrity": "sha1-tpYWPMdXVg0JzyLMj60Vcbeedt8=", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npm.taobao.org/destroy/download/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "license": "MIT" - }, - "node_modules/dom-serializer": { - "version": "1.2.0", - "resolved": "https://registry.npm.taobao.org/dom-serializer/download/dom-serializer-1.2.0.tgz?cache=0&sync_timestamp=1607193465399&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdom-serializer%2Fdownload%2Fdom-serializer-1.2.0.tgz", - "integrity": "sha1-NDPZE2rrPGJ5gdqjhfx/MtJ8SPE=", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domexception": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", - "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", - "dependencies": { - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/dompurify": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.4.tgz", - "integrity": "sha512-ae0mA+Qiqp6C29pqZX3fQgK+F91+F7wobM/v8DRzDqJdZJELXiFUx4PP4pK/mzUS0xkiSEx3Ncd9gr69jg3YsQ==" - }, - "node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "license": "MIT" - }, - "node_modules/ejs": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", - "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.3.739", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.739.tgz", - "integrity": "sha512-+LPJVRsN7hGZ9EIUUiWCpO7l4E3qBYHNadazlucBfsXBbccDFNKUBAgzE68FnkWGJPwD/AfKhSzL+G+Iqb8A4A==" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npm.taobao.org/encodeurl/download/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npm.taobao.org/entities/download/entities-2.1.0.tgz?cache=0&sync_timestamp=1611535878280&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fentities%2Fdownload%2Fentities-2.1.0.tgz", - "integrity": "sha1-mS0xKc999ocLlsV4WMJJoSD4uLU=", - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/errno": { - "version": "0.1.8", - "resolved": "https://registry.npm.taobao.org/errno/download/errno-0.1.8.tgz", - "integrity": "sha1-i7Ppx9Rjvkl2/4iPdrSAnrwugR8=", - "license": "MIT", - "optional": true, - "dependencies": { - "prr": "~1.0.1" - }, - "bin": { - "errno": "cli.js" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npm.taobao.org/escape-html/download/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npm.taobao.org/escape-string-regexp/download/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/escodegen/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npm.taobao.org/esprima/download/esprima-4.0.1.tgz", - "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=", - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npm.taobao.org/esutils/download/esutils-2.0.3.tgz", - "integrity": "sha1-dNLrTeC42hKTcRkQ1Qd1ubcQ72Q=", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npm.taobao.org/etag/download/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", - "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npm.taobao.org/fast-json-stable-stringify/download/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM=", - "license": "MIT" - }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npm.taobao.org/fill-range/download/fill-range-7.0.1.tgz", - "integrity": "sha1-GRmmp8df44ssfHflGYU12prN2kA=", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npm.taobao.org/finalhandler/download/finalhandler-1.1.2.tgz", - "integrity": "sha1-t+fQAP/RGTjQ/bBTUG9uur6fWH0=", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&sync_timestamp=1607566600214&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&sync_timestamp=1607433905910&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "license": "MIT" - }, - "node_modules/find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npm.taobao.org/find-cache-dir/download/find-cache-dir-2.1.0.tgz", - "integrity": "sha1-jQ+UzRP+Q8bHwmGg2GEVypGMBfc=", - "license": "MIT", - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/find-up/download/find-up-3.0.0.tgz", - "integrity": "sha1-SRafHXmTQwZG2mHsxa41XCHJe3M=", - "license": "MIT", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npm.taobao.org/fresh/download/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/fs.realpath/download/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npm.taobao.org/fsevents/download/fsevents-2.3.2.tgz", - "integrity": "sha1-ilJveLj99GI7cJ4Ll1xSwkwC/Ro=", - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz", - "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", - "license": "MIT" - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npm.taobao.org/gensync/download/gensync-1.0.0-beta.2.tgz", - "integrity": "sha1-MqbudsPX9S1GsrGuXZP+qFgKJeA=", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npm.taobao.org/get-intrinsic/download/get-intrinsic-1.1.1.tgz?cache=0&sync_timestamp=1612364411509&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fget-intrinsic%2Fdownload%2Fget-intrinsic-1.1.1.tgz", - "integrity": "sha1-FfWfN2+FXERpY5SPDSTNNje0q8Y=", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npm.taobao.org/glob/download/glob-7.1.6.tgz", - "integrity": "sha1-FB8zuBp8JJLhJVlDB0gMRmeSeKY=", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npm.taobao.org/glob-parent/download/glob-parent-5.1.2.tgz", - "integrity": "sha1-hpgyxYA0/mikCTwX3BXoNA2EAcQ=", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npm.taobao.org/globals/download/globals-11.12.0.tgz", - "integrity": "sha1-q4eVM4hooLq9hSV1gBjCp+uVxC4=", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npm.taobao.org/has/download/has-1.0.3.tgz", - "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-4.0.0.tgz", - "integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npm.taobao.org/has-symbols/download/has-symbols-1.0.2.tgz", - "integrity": "sha1-Fl0wcMADCXUqEjakeTMeOsVvFCM=", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hexo": { - "version": "5.4.2", - "resolved": "https://registry.npmmirror.com/hexo/-/hexo-5.4.2.tgz", - "integrity": "sha512-Af6mxKwx9byalaffKgiQ8/bZfbXPo2SGEn2Q9hFh++15g15/IulvOhu8lQkJdyZNzmj3hOuSrJdqoUvIr3K/qw==", - "license": "MIT", - "dependencies": { - "abbrev": "^1.1.1", - "archy": "^1.0.0", - "bluebird": "^3.5.2", - "chalk": "^4.0.0", - "hexo-cli": "^4.0.0", - "hexo-front-matter": "^2.0.0", - "hexo-fs": "^3.1.0", - "hexo-i18n": "^1.0.0", - "hexo-log": "^2.0.0", - "hexo-util": "^2.4.0", - "js-yaml": "^3.14.1", - "micromatch": "^4.0.2", - "moment": "^2.22.2", - "moment-timezone": "^0.5.21", - "nunjucks": "^3.2.1", - "pretty-hrtime": "^1.0.3", - "resolve": "^1.8.1", - "strip-ansi": "^6.0.0", - "text-table": "^0.2.0", - "tildify": "^2.0.0", - "titlecase": "^1.1.2", - "warehouse": "^4.0.0" - }, - "bin": { - "hexo": "bin/hexo" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/hexo" - } - }, - "node_modules/hexo-cli": { - "version": "4.2.0", - "resolved": "https://registry.npm.taobao.org/hexo-cli/download/hexo-cli-4.2.0.tgz", - "integrity": "sha1-dGR7xyqQuc6/BK9bxwg5qygaMF0=", - "license": "MIT", - "dependencies": { - "abbrev": "^1.1.1", - "bluebird": "^3.5.5", - "chalk": "^4.0.0", - "command-exists": "^1.2.8", - "hexo-fs": "^3.0.1", - "hexo-log": "^2.0.0", - "hexo-util": "^2.0.0", - "minimist": "^1.2.5", - "resolve": "^1.11.0", - "tildify": "^2.0.0" - }, - "bin": { - "hexo": "bin/hexo" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/hexo-component-inferno": { - "version": "0.10.5", - "resolved": "https://registry.npm.taobao.org/hexo-component-inferno/download/hexo-component-inferno-0.10.5.tgz", - "integrity": "sha1-D7+kJ6U54jQOW4s3CI3xLcyBqYg=", - "license": "MIT", - "dependencies": { - "ajv": "^6.10.2", - "inferno": "^7.3.3", - "inferno-create-element": "^7.3.3", - "js-yaml": "^3.13.1", - "moment": "^2.24.0", - "semver": "^7.1.1" - }, - "peerDependencies": { - "hexo": "^5.0.0", - "hexo-pagination": "^1.0.0", - "hexo-util": "^2.2.0" - } - }, - "node_modules/hexo-component-inferno/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/hexo-deployer-git": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hexo-deployer-git/-/hexo-deployer-git-4.0.0.tgz", - "integrity": "sha512-28t1Q+4taB/UaBAP52W3mD/wcCwa2y2zBieUfBJFBZudbmVgiKJB5YedYILeyI5QByaUKAOwoupmdTbocdQ+CQ==", - "dependencies": { - "bluebird": "^3.7.2", - "hexo-fs": "^4.0.0", - "hexo-util": "^2.7.0", - "luxon": "^3.0.4", - "nunjucks": "^3.2.3", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/hexo-deployer-git/node_modules/hexo-fs": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/hexo-fs/-/hexo-fs-4.1.1.tgz", - "integrity": "sha512-aDysNTyv8ElcerbFVbPLRXnYt+QDY6gAOZZ5DLbCxudY0Ywppqd+uZ03gZ2BDypIBvmNB27WYWYz76M+Yv/YXw==", - "dependencies": { - "bluebird": "^3.7.2", - "chokidar": "^3.5.3", - "graceful-fs": "^4.2.10", - "hexo-util": "^2.7.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/hexo-filter-mermaid-diagrams": { - "version": "1.0.5", - "resolved": "https://registry.npm.taobao.org/hexo-filter-mermaid-diagrams/download/hexo-filter-mermaid-diagrams-1.0.5.tgz", - "integrity": "sha1-HjL8TkbquQzzo3W1yDCp8q7xZA8=", - "license": "MIT", - "dependencies": { - "deep-assign": "^2.0.0" - } - }, - "node_modules/hexo-front-matter": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/hexo-front-matter/download/hexo-front-matter-2.0.0.tgz", - "integrity": "sha1-A/Oanu2xbFRI27O+x9/yxOIkAGw=", - "license": "MIT", - "dependencies": { - "js-yaml": "^3.13.1" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/hexo-fs": { - "version": "3.1.0", - "resolved": "https://registry.npm.taobao.org/hexo-fs/download/hexo-fs-3.1.0.tgz", - "integrity": "sha1-IFL/csaM2ixXeoeqDqeYCuUD+pk=", - "license": "MIT", - "dependencies": { - "bluebird": "^3.5.1", - "chokidar": "^3.0.0", - "graceful-fs": "^4.1.11", - "hexo-util": "^2.0.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/hexo-generator-archive": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/hexo-generator-archive/download/hexo-generator-archive-1.0.0.tgz", - "integrity": "sha1-rSr7EiMqZeL4YI/Byj8ZFi+2N4Y=", - "license": "MIT", - "dependencies": { - "hexo-pagination": "1.0.0" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/hexo-generator-category": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/hexo-generator-category/download/hexo-generator-category-1.0.0.tgz", - "integrity": "sha1-zSorA+sybaPvgh1uKUCEiMwTLrU=", - "license": "MIT", - "dependencies": { - "hexo-pagination": "1.0.0" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/hexo-generator-feed": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/hexo-generator-feed/download/hexo-generator-feed-3.0.0.tgz", - "integrity": "sha1-QSbvXjCCZMQlmfsO/a+I7RH6WZ4=", - "license": "MIT", - "dependencies": { - "hexo-util": "^2.1.0", - "nunjucks": "^3.0.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/hexo-generator-index": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/hexo-generator-index/download/hexo-generator-index-2.0.0.tgz", - "integrity": "sha1-FecfTSjunSFy4td+jR26BIgu3I8=", - "license": "MIT", - "dependencies": { - "hexo-pagination": "1.0.0", - "timsort": "^0.3.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/hexo-generator-tag": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/hexo-generator-tag/download/hexo-generator-tag-1.0.0.tgz", - "integrity": "sha1-VOwj3pQJx1WE6oHjYFelkDGwIvE=", - "license": "MIT", - "dependencies": { - "hexo-pagination": "1.0.0" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/hexo-i18n": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/hexo-i18n/download/hexo-i18n-1.0.0.tgz", - "integrity": "sha1-eYP7OjE+kGFbhN2PqUanHEie9b0=", - "license": "MIT", - "dependencies": { - "sprintf-js": "^1.0.3" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/hexo-log": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/hexo-log/download/hexo-log-2.0.0.tgz", - "integrity": "sha1-HcyOlgApsIshmU/ixWxOPWNG6JM=", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/hexo-pagination": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexo-pagination/-/hexo-pagination-1.0.0.tgz", - "integrity": "sha512-miEVFgxchPr2qNWxw0JWpJ9R/Yaf7HjHBZVjvCCcqfbsLyYtCvIfJDxcEwz1sDOC/fLzYPqNnhUI73uNxBHRSA==", - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/hexo-renderer-ejs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hexo-renderer-ejs/-/hexo-renderer-ejs-2.0.0.tgz", - "integrity": "sha512-qCjE1IdwgDgv65qyb0KMVCwCdSVAkH0vwAe9XihjvaKWkmb9dtt8DgErOdqCXn0HReSyWiEVP2BrLRj3gyHwOQ==", - "dependencies": { - "ejs": "^3.1.6" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/hexo-renderer-inferno": { - "version": "0.1.3", - "resolved": "https://registry.npm.taobao.org/hexo-renderer-inferno/download/hexo-renderer-inferno-0.1.3.tgz", - "integrity": "sha1-moOs80LBReeB5IEilvZteOLY8j4=", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.7.7", - "@babel/preset-env": "^7.7.7", - "@babel/register": "^7.7.7", - "babel-plugin-inferno": "^6.1.0", - "inferno": "^7.3.3", - "inferno-create-element": "^7.3.3", - "inferno-server": "^7.3.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/hexo-renderer-less": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/hexo-renderer-less/download/hexo-renderer-less-1.0.0.tgz", - "integrity": "sha1-BfJGcezNBAxgoa1knaHPtN8WS24=", - "license": "MIT", - "dependencies": { - "less": "^3.9.0" - } - }, - "node_modules/hexo-renderer-marked": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/hexo-renderer-marked/-/hexo-renderer-marked-6.1.0.tgz", - "integrity": "sha512-KuVuaYFyKfbJj5dtwoWMwTNbe7rk+UuVR/tGzLXbQ05AWO5ob6xpFHbfeSyzSDt4ccYLzaKJBRduDWHd2f6bSQ==", - "dependencies": { - "dompurify": "^3.0.3", - "hexo-util": "^3.0.1", - "jsdom": "^20.0.1", - "marked": "^4.3.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/hexo-renderer-marked/node_modules/hexo-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/hexo-util/-/hexo-util-3.0.1.tgz", - "integrity": "sha512-ri3WsEUWSfrjydYPeOaWvrYIzfVBiaXUy0846051MkuJxBcNtG2o87q0KFGiniSMmi0XxLQhSl415anU3+FFlA==", - "dependencies": { - "bluebird": "^3.7.2", - "camel-case": "^4.1.2", - "cross-spawn": "^7.0.3", - "deepmerge": "^4.2.2", - "highlight.js": "^11.6.0", - "htmlparser2": "^8.0.1", - "prismjs": "^1.29.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/hexo-renderer-stylus": { - "version": "2.0.1", - "resolved": "https://registry.npm.taobao.org/hexo-renderer-stylus/download/hexo-renderer-stylus-2.0.1.tgz", - "integrity": "sha1-xlVm6X6QWKUuRsgrDEXPVh9LlMs=", - "license": "MIT", - "dependencies": { - "nib": "^1.1.2", - "stylus": "^0.54.8" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/hexo-server": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/hexo-server/download/hexo-server-2.0.0.tgz", - "integrity": "sha1-GtksyKfTUo/sb/pl/4RT8kL3mD8=", - "license": "MIT", - "dependencies": { - "bluebird": "^3.5.5", - "chalk": "^4.0.0", - "compression": "^1.7.4", - "connect": "^3.7.0", - "mime": "^2.4.3", - "morgan": "^1.9.1", - "open": "^7.0.0", - "serve-static": "^1.14.1" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/hexo-theme-icarus": { - "version": "4.1.2", - "resolved": "https://registry.npm.taobao.org/hexo-theme-icarus/download/hexo-theme-icarus-4.1.2.tgz", - "integrity": "sha1-WJdAbVO3MKk9l1vHje5BFNzwQ4M=", - "license": "MIT", - "dependencies": { - "deepmerge": "^4.2.2", - "hexo-component-inferno": "^0.10.5", - "inferno": "^7.3.3", - "inferno-create-element": "^7.3.3", - "moment": "^2.22.2", - "semver": ">=5.0.0" - }, - "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "bulma-stylus": "0.8.0", - "hexo": "^5.0.2", - "hexo-log": "^2.0.0", - "hexo-pagination": "^1.0.0", - "hexo-renderer-inferno": "^0.1.3", - "hexo-renderer-stylus": "^2.0.0", - "hexo-util": "^2.2.0" - } - }, - "node_modules/hexo-util": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/hexo-util/-/hexo-util-2.7.0.tgz", - "integrity": "sha512-hQM3h34nhDg0bSe/Tg1lnpODvNkz7h2u0+lZGzlKL0Oufp+5KCAEUX9wal7/xC7ax3/cwEn8IuoU75kNpZLpJQ==", - "dependencies": { - "bluebird": "^3.5.2", - "camel-case": "^4.0.0", - "cross-spawn": "^7.0.0", - "deepmerge": "^4.2.2", - "highlight.js": "^11.0.1", - "htmlparser2": "^7.0.0", - "prismjs": "^1.17.1", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=12.4.0" - } - }, - "node_modules/hexo-util/node_modules/entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/hexo-util/node_modules/htmlparser2": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" - } - }, - "node_modules/highlight.js": { - "version": "11.8.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.8.0.tgz", - "integrity": "sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "dependencies": { - "whatwg-encoding": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } - }, - "node_modules/htmlparser2/node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/htmlparser2/node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/htmlparser2/node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/htmlparser2/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npm.taobao.org/http-errors/download/http-errors-1.7.3.tgz", - "integrity": "sha1-bGGeT5xgMIw4UZSYwU+7EKrOuwY=", - "license": "MIT", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-errors/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npm.taobao.org/depd/download/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/image-size": { - "version": "0.5.5", - "resolved": "https://registry.npm.taobao.org/image-size/download/image-size-0.5.5.tgz?cache=0&sync_timestamp=1615833926443&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fimage-size%2Fdownload%2Fimage-size-0.5.5.tgz", - "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", - "license": "MIT", - "optional": true, - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inferno": { - "version": "7.4.8", - "resolved": "https://registry.npm.taobao.org/inferno/download/inferno-7.4.8.tgz", - "integrity": "sha1-DVUEdT55kDsOS77/dvwR/Quf/pI=", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "inferno-shared": "7.4.8", - "inferno-vnode-flags": "7.4.8", - "opencollective-postinstall": "^2.0.3" - } - }, - "node_modules/inferno-create-element": { - "version": "7.4.8", - "resolved": "https://registry.npm.taobao.org/inferno-create-element/download/inferno-create-element-7.4.8.tgz", - "integrity": "sha1-d7vyQohkXDWc9ltIIaOTjGU3614=", - "license": "MIT", - "dependencies": { - "inferno": "7.4.8" - } - }, - "node_modules/inferno-server": { - "version": "7.4.8", - "resolved": "https://registry.npm.taobao.org/inferno-server/download/inferno-server-7.4.8.tgz", - "integrity": "sha1-Ma18IJKroW5CNKECzErD80bRfuQ=", - "license": "MIT", - "dependencies": { - "inferno": "7.4.8" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/inferno-shared": { - "version": "7.4.8", - "resolved": "https://registry.npm.taobao.org/inferno-shared/download/inferno-shared-7.4.8.tgz", - "integrity": "sha1-K1VKNmg7dwM5AIdJCW2XBIRt0zc=", - "license": "MIT" - }, - "node_modules/inferno-vnode-flags": { - "version": "7.4.8", - "resolved": "https://registry.npm.taobao.org/inferno-vnode-flags/download/inferno-vnode-flags-7.4.8.tgz", - "integrity": "sha1-J11w48iys/TrVgQcybjIMs4fsm0=", - "license": "MIT" - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npm.taobao.org/inflight/download/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.4.tgz", - "integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=", - "license": "ISC" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npm.taobao.org/is-binary-path/download/is-binary-path-2.1.0.tgz", - "integrity": "sha1-6h9/O4DwZCNug0cPhsCcJU+0Wwk=", - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.2.0", - "resolved": "https://registry.npm.taobao.org/is-core-module/download/is-core-module-2.2.0.tgz?cache=0&sync_timestamp=1606411931775&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-core-module%2Fdownload%2Fis-core-module-2.2.0.tgz", - "integrity": "sha1-lwN+89UiJNhRY/VZeytj2a/tmBo=", - "license": "MIT", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "2.1.1", - "resolved": "https://registry.npm.taobao.org/is-docker/download/is-docker-2.1.1.tgz?cache=0&sync_timestamp=1596559619667&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-docker%2Fdownload%2Fis-docker-2.1.1.tgz", - "integrity": "sha1-QSWojkTkUNOE4JBH7eca3C0UQVY=", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npm.taobao.org/is-extglob/download/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npm.taobao.org/is-glob/download/is-glob-4.0.1.tgz", - "integrity": "sha1-dWfb6fL14kZ7x3q4PEopSCQHpdw=", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npm.taobao.org/is-number/download/is-number-7.0.0.tgz", - "integrity": "sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/is-obj/download/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-plain-object": { - "version": "3.0.1", - "resolved": "https://registry.npm.taobao.org/is-plain-object/download/is-plain-object-3.0.1.tgz?cache=0&sync_timestamp=1599667374673&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-plain-object%2Fdownload%2Fis-plain-object-3.0.1.tgz", - "integrity": "sha1-Zi2S0kwKpDAkB7DUXSHyJRyF+Fs=", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" - }, - "node_modules/is-what": { - "version": "3.14.1", - "resolved": "https://registry.npm.taobao.org/is-what/download/is-what-3.14.1.tgz", - "integrity": "sha1-4SIvRt3ahd6tD9HJ3xMXYOd3VcE=", - "license": "MIT" - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npm.taobao.org/is-wsl/download/is-wsl-2.2.0.tgz", - "integrity": "sha1-dKTHbnfKn9P5MvKQwX6jJs0VcnE=", - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/isexe/download/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "license": "ISC" - }, - "node_modules/jake": { - "version": "10.8.7", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", - "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jake/node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" - }, - "node_modules/jake/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npm.taobao.org/js-tokens/download/js-tokens-4.0.0.tgz", - "integrity": "sha1-GSA/tZmR35jjoocFDUZHzerzJJk=", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npm.taobao.org/js-yaml/download/js-yaml-3.14.1.tgz?cache=0&sync_timestamp=1609680193223&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjs-yaml%2Fdownload%2Fjs-yaml-3.14.1.tgz", - "integrity": "sha1-2ugS/bOCX6MGYJqHFzg8UMNqBTc=", - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsdom": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", - "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", - "dependencies": { - "abab": "^2.0.6", - "acorn": "^8.8.1", - "acorn-globals": "^7.0.0", - "cssom": "^0.5.0", - "cssstyle": "^2.3.0", - "data-urls": "^3.0.2", - "decimal.js": "^10.4.2", - "domexception": "^4.0.0", - "escodegen": "^2.0.0", - "form-data": "^4.0.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.1", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.2", - "parse5": "^7.1.1", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.1.2", - "w3c-xmlserializer": "^4.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^2.0.0", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0", - "ws": "^8.11.0", - "xml-name-validator": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npm.taobao.org/jsesc/download/jsesc-2.5.2.tgz?cache=0&sync_timestamp=1603891236757&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjsesc%2Fdownload%2Fjsesc-2.5.2.tgz", - "integrity": "sha1-gFZNLkg9rPbo7yCWUKZ98/DCg6Q=", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz", - "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npm.taobao.org/jsonparse/download/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", - "engines": [ - "node >= 0.2.0" - ], - "license": "MIT" - }, - "node_modules/JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npm.taobao.org/JSONStream/download/JSONStream-1.3.5.tgz", - "integrity": "sha1-MgjB8I06TZkmGrZPkjArwV4RHKA=", - "license": "(MIT OR Apache-2.0)", - "dependencies": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - }, - "bin": { - "JSONStream": "bin.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/less": { - "version": "3.13.1", - "resolved": "https://registry.npm.taobao.org/less/download/less-3.13.1.tgz", - "integrity": "sha1-DryR0qDpwMZzW4PUlrCrBYMHeQk=", - "license": "Apache-2.0", - "dependencies": { - "copy-anything": "^2.0.1", - "tslib": "^1.10.0" - }, - "bin": { - "lessc": "bin/lessc" - }, - "engines": { - "node": ">=6" - }, - "optionalDependencies": { - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "make-dir": "^2.1.0", - "mime": "^1.4.1", - "native-request": "^1.0.5", - "source-map": "~0.6.0" - } - }, - "node_modules/less/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npm.taobao.org/mime/download/mime-1.6.0.tgz?cache=0&sync_timestamp=1613584832862&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmime%2Fdownload%2Fmime-1.6.0.tgz", - "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=", - "license": "MIT", - "optional": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/less/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", - "license": "BSD-3-Clause", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/less/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npm.taobao.org/tslib/download/tslib-1.14.1.tgz?cache=0&sync_timestamp=1609887769758&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftslib%2Fdownload%2Ftslib-1.14.1.tgz", - "integrity": "sha1-zy04vcNKE0vK8QkcQfZhni9nLQA=", - "license": "0BSD" - }, - "node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/locate-path/download/locate-path-3.0.0.tgz", - "integrity": "sha1-2+w7OrdZdYBxtY/ln8QYca8hQA4=", - "license": "MIT", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npm.taobao.org/lodash/download/lodash-4.17.21.tgz?cache=0&sync_timestamp=1613835891789&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash%2Fdownload%2Flodash-4.17.21.tgz", - "integrity": "sha1-Z5WRxWTDv/quhFTPCz3zcMPWkRw=", - "license": "MIT" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npm.taobao.org/lodash.debounce/download/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "license": "MIT" - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npm.taobao.org/lower-case/download/lower-case-2.0.2.tgz", - "integrity": "sha1-b6I3xj29xKgsoP2ILkci3F5jTig=", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npm.taobao.org/lru-cache/download/lru-cache-6.0.0.tgz?cache=0&sync_timestamp=1594427606170&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flru-cache%2Fdownload%2Flru-cache-6.0.0.tgz", - "integrity": "sha1-bW/mVw69lqr5D8rR2vo7JWbbOpQ=", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/luxon": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz", - "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npm.taobao.org/make-dir/download/make-dir-2.1.0.tgz", - "integrity": "sha1-XwMQ4YuL6JjMBwCSlaMK5B6R5vU=", - "license": "MIT", - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/marked": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npm.taobao.org/micromatch/download/micromatch-4.0.2.tgz", - "integrity": "sha1-T8sJmb+fvC/L3SEvbWKbmlbDklk=", - "license": "MIT", - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mime": { - "version": "2.5.2", - "resolved": "https://registry.npm.taobao.org/mime/download/mime-2.5.2.tgz?cache=0&sync_timestamp=1613584832862&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmime%2Fdownload%2Fmime-2.5.2.tgz", - "integrity": "sha1-bj3GzCuVEGQ4MOXxnVy3U9pe6r4=", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mime-db": { - "version": "1.46.0", - "resolved": "https://registry.npm.taobao.org/mime-db/download/mime-db-1.46.0.tgz?cache=0&sync_timestamp=1613194712759&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmime-db%2Fdownload%2Fmime-db-1.46.0.tgz", - "integrity": "sha1-Ymd0in95lZTePLyM3pHe80lmHO4=", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.29", - "resolved": "https://registry.npm.taobao.org/mime-types/download/mime-types-2.1.29.tgz", - "integrity": "sha1-HUq3faZLkfX3JInfKSNlY3VLsbI=", - "license": "MIT", - "dependencies": { - "mime-db": "1.46.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/min-indent/download/min-indent-1.0.1.tgz", - "integrity": "sha1-pj9oFnOzBXH76LwlaGrnRu76mGk=", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npm.taobao.org/minimatch/download/minimatch-3.0.4.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fminimatch%2Fdownload%2Fminimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npm.taobao.org/mkdirp/download/mkdirp-1.0.4.tgz?cache=0&sync_timestamp=1587535418745&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmkdirp%2Fdownload%2Fmkdirp-1.0.4.tgz", - "integrity": "sha1-PrXtYmInVteaXw4qIh3+utdcL34=", - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", - "engines": { - "node": "*" - } - }, - "node_modules/moment-timezone": { - "version": "0.5.37", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.37.tgz", - "integrity": "sha512-uEDzDNFhfaywRl+vwXxffjjq1q0Vzr+fcQpQ1bU0kbzorfS7zVtZnCnGc8mhWmF39d4g4YriF6kwA75mJKE/Zg==", - "dependencies": { - "moment": ">= 2.9.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/morgan": { - "version": "1.10.0", - "resolved": "https://registry.npm.taobao.org/morgan/download/morgan-1.10.0.tgz?cache=0&sync_timestamp=1584728007537&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmorgan%2Fdownload%2Fmorgan-1.10.0.tgz", - "integrity": "sha1-CRd4q8H8R801CYJGU9rh+qtrF9c=", - "license": "MIT", - "dependencies": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/morgan/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&sync_timestamp=1607566600214&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/morgan/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&sync_timestamp=1607433905910&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "license": "MIT" - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz?cache=0&sync_timestamp=1607433905910&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.1.2.tgz", - "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", - "license": "MIT" - }, - "node_modules/native-request": { - "version": "1.0.8", - "resolved": "https://registry.npm.taobao.org/native-request/download/native-request-1.0.8.tgz", - "integrity": "sha1-j2a/YG4PfqJ8DlmV6y9dA+M65vs=", - "license": "MIT", - "optional": true - }, - "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npm.taobao.org/negotiator/download/negotiator-0.6.2.tgz", - "integrity": "sha1-/qz3zPUlp3rpY0Q2pkiD/+yjRvs=", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/nib": { - "version": "1.1.2", - "resolved": "https://registry.npm.taobao.org/nib/download/nib-1.1.2.tgz", - "integrity": "sha1-amnt5AgblcDe+L4CSkyK4MLLtsc=", - "license": "MIT", - "dependencies": { - "stylus": "0.54.5" - }, - "engines": { - "node": "*" - } - }, - "node_modules/nib/node_modules/css-parse": { - "version": "1.7.0", - "resolved": "https://registry.npm.taobao.org/css-parse/download/css-parse-1.7.0.tgz", - "integrity": "sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs=", - "license": "MIT" - }, - "node_modules/nib/node_modules/glob": { - "version": "7.0.6", - "resolved": "https://registry.npm.taobao.org/glob/download/glob-7.0.6.tgz", - "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.2", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/nib/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npm.taobao.org/mkdirp/download/mkdirp-0.5.5.tgz?cache=0&sync_timestamp=1587535418745&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmkdirp%2Fdownload%2Fmkdirp-0.5.5.tgz", - "integrity": "sha1-2Rzv1i0UNsoPQWIOJRKI1CAJne8=", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/nib/node_modules/sax": { - "version": "0.5.8", - "resolved": "https://registry.npm.taobao.org/sax/download/sax-0.5.8.tgz", - "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=", - "license": "BSD" - }, - "node_modules/nib/node_modules/source-map": { - "version": "0.1.43", - "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.1.43.tgz", - "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", - "dependencies": { - "amdefine": ">=0.0.4" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/nib/node_modules/stylus": { - "version": "0.54.5", - "resolved": "https://registry.npm.taobao.org/stylus/download/stylus-0.54.5.tgz?cache=0&sync_timestamp=1594901587949&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstylus%2Fdownload%2Fstylus-0.54.5.tgz", - "integrity": "sha1-QrlWCTHKcJDOhRWnmLqeaqPW3Hk=", - "license": "MIT", - "dependencies": { - "css-parse": "1.7.x", - "debug": "*", - "glob": "7.0.x", - "mkdirp": "0.5.x", - "sax": "0.5.x", - "source-map": "0.1.x" - }, - "bin": { - "stylus": "bin/stylus" - }, - "engines": { - "node": "*" - } - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npm.taobao.org/no-case/download/no-case-3.0.4.tgz", - "integrity": "sha1-02H9XJgA9VhVGoNp/A3NRmK2Ek0=", - "license": "MIT", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-modules-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/node-modules-regexp/download/node-modules-regexp-1.0.0.tgz", - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/node-releases": { - "version": "1.1.72", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz", - "integrity": "sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/nunjucks": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz", - "integrity": "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==", - "dependencies": { - "a-sync-waterfall": "^1.0.0", - "asap": "^2.0.3", - "commander": "^5.1.0" - }, - "bin": { - "nunjucks-precompile": "bin/precompile" - }, - "engines": { - "node": ">= 6.9.0" - }, - "peerDependencies": { - "chokidar": "^3.3.0" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/nwsapi": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", - "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==" - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npm.taobao.org/object-keys/download/object-keys-1.1.1.tgz", - "integrity": "sha1-HEfyct8nfzsdrwYWd9nILiMixg4=", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npm.taobao.org/object.assign/download/object.assign-4.1.2.tgz?cache=0&sync_timestamp=1604115263846&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fobject.assign%2Fdownload%2Fobject.assign-4.1.2.tgz", - "integrity": "sha1-DtVKNC7Os3s4/3brgxoOeIy2OUA=", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npm.taobao.org/on-finished/download/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npm.taobao.org/on-headers/download/on-headers-1.0.2.tgz", - "integrity": "sha1-dysK5qqlJcOZ5Imt+tkMQD6zwo8=", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npm.taobao.org/once/download/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/open": { - "version": "7.4.2", - "resolved": "https://registry.npm.taobao.org/open/download/open-7.4.2.tgz", - "integrity": "sha1-uBR+Jtzz5CYxbHMAif1x7dKcIyE=", - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/opencollective-postinstall": { - "version": "2.0.3", - "resolved": "https://registry.npm.taobao.org/opencollective-postinstall/download/opencollective-postinstall-2.0.3.tgz", - "integrity": "sha1-eg//l49tv6TQBiOPusmO1BmMMlk=", - "license": "MIT", - "bin": { - "opencollective-postinstall": "index.js" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npm.taobao.org/p-limit/download/p-limit-2.3.0.tgz", - "integrity": "sha1-PdM8ZHohT9//2DWTPrCG2g3CHbE=", - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/p-locate/download/p-locate-3.0.0.tgz", - "integrity": "sha1-Mi1poFwCZLJZl9n0DNiokasAZKQ=", - "license": "MIT", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npm.taobao.org/p-try/download/p-try-2.2.0.tgz", - "integrity": "sha1-yyhoVA4xPWHeWPr741zpAE1VQOY=", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dependencies": { - "entities": "^4.4.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", - "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", - "dependencies": { - "domhandler": "^5.0.2", - "parse5": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5-htmlparser2-tree-adapter/node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/parse5/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npm.taobao.org/parseurl/download/parseurl-1.3.3.tgz", - "integrity": "sha1-naGee+6NEt/wUT7Vt2lXeTvC6NQ=", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npm.taobao.org/pascal-case/download/pascal-case-3.1.2.tgz", - "integrity": "sha1-tI4O8rmOIF58Ha50fQsVCCN2YOs=", - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/path-exists/download/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/path-is-absolute/download/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npm.taobao.org/path-key/download/path-key-3.1.1.tgz", - "integrity": "sha1-WB9q3mWMu6ZaDTOA3ndTKVBU83U=", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "node_modules/picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npm.taobao.org/picomatch/download/picomatch-2.2.2.tgz?cache=0&sync_timestamp=1584791322335&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpicomatch%2Fdownload%2Fpicomatch-2.2.2.tgz", - "integrity": "sha1-IfMz6ba46v8CRo9RRupAbTRfTa0=", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npm.taobao.org/pify/download/pify-4.0.1.tgz", - "integrity": "sha1-SyzSXFDVmHNcUCkiJP2MbfQeMjE=", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pirates": { - "version": "4.0.1", - "resolved": "https://registry.npm.taobao.org/pirates/download/pirates-4.0.1.tgz", - "integrity": "sha1-ZDqSyviUVm+RsrmG0sZpUKji+4c=", - "license": "MIT", - "dependencies": { - "node-modules-regexp": "^1.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/pkg-dir/download/pkg-dir-3.0.0.tgz", - "integrity": "sha1-J0kCDyOe2ZCIGx9xIQ1R62UjvqM=", - "license": "MIT", - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pretty-hrtime": { - "version": "1.0.3", - "resolved": "https://registry.npm.taobao.org/pretty-hrtime/download/pretty-hrtime-1.0.3.tgz", - "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/prismjs": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", - "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", - "engines": { - "node": ">=6" - } - }, - "node_modules/prr": { - "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/prr/download/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "license": "MIT", - "optional": true - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npm.taobao.org/punycode/download/punycode-2.1.1.tgz", - "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npm.taobao.org/range-parser/download/range-parser-1.2.1.tgz", - "integrity": "sha1-PPNwI9GZ4cJNGlW4SADC8+ZGgDE=", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npm.taobao.org/regenerate/download/regenerate-1.4.2.tgz?cache=0&sync_timestamp=1604220147658&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fregenerate%2Fdownload%2Fregenerate-1.4.2.tgz", - "integrity": "sha1-uTRtiCfo9aMve6KWN9OYtpAUhIo=", - "license": "MIT" - }, - "node_modules/regenerate-unicode-properties": { - "version": "8.2.0", - "resolved": "https://registry.npm.taobao.org/regenerate-unicode-properties/download/regenerate-unicode-properties-8.2.0.tgz", - "integrity": "sha1-5d5xEdZV57pgwFfb6f83yH5lzew=", - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npm.taobao.org/regenerator-runtime/download/regenerator-runtime-0.13.7.tgz", - "integrity": "sha1-ysLazIoepnX+qrrriugziYrkb1U=", - "license": "MIT" - }, - "node_modules/regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npm.taobao.org/regenerator-transform/download/regenerator-transform-0.14.5.tgz", - "integrity": "sha1-yY2hVGg2ccnE3LFuznNlF+G3/rQ=", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, - "node_modules/regexpu-core": { - "version": "4.7.1", - "resolved": "https://registry.npm.taobao.org/regexpu-core/download/regexpu-core-4.7.1.tgz", - "integrity": "sha1-LepamgcjMpj78NuR+pq8TG4PitY=", - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.2.0", - "regjsgen": "^0.5.1", - "regjsparser": "^0.6.4", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsgen": { - "version": "0.5.2", - "resolved": "https://registry.npm.taobao.org/regjsgen/download/regjsgen-0.5.2.tgz", - "integrity": "sha1-kv8pX7He7L9uzaslQ9IH6RqjNzM=", - "license": "MIT" - }, - "node_modules/regjsparser": { - "version": "0.6.7", - "resolved": "https://registry.npm.taobao.org/regjsparser/download/regjsparser-0.6.7.tgz", - "integrity": "sha1-wAFk4eZxPC4+5kHxcBxLeqCn+Gw=", - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npm.taobao.org/jsesc/download/jsesc-0.5.0.tgz?cache=0&sync_timestamp=1603891236757&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjsesc%2Fdownload%2Fjsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" - }, - "node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npm.taobao.org/resolve/download/resolve-1.20.0.tgz", - "integrity": "sha1-YpoBP7P3B1XW8LeTXMHCxTeLGXU=", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npm.taobao.org/resolve-url/download/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "license": "MIT" - }, - "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npm.taobao.org/rfdc/download/rfdc-1.3.0.tgz", - "integrity": "sha1-0LfEQasnINBdxM8m4ByJYx2doIs=", - "license": "MIT" - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.2.tgz", - "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=", - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz", - "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=", - "license": "MIT" - }, - "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npm.taobao.org/sax/download/sax-1.2.4.tgz", - "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=", - "license": "ISC" - }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=v12.22.7" - } - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npm.taobao.org/send/download/send-0.17.1.tgz", - "integrity": "sha1-wdiwWfeQD3Rm3Uk4vcROEd2zdsg=", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&sync_timestamp=1607566600214&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&sync_timestamp=1607433905910&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "license": "MIT" - }, - "node_modules/send/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npm.taobao.org/depd/download/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/send/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npm.taobao.org/mime/download/mime-1.6.0.tgz?cache=0&sync_timestamp=1613584832862&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmime%2Fdownload%2Fmime-1.6.0.tgz", - "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.1.tgz?cache=0&sync_timestamp=1607433905910&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.1.1.tgz", - "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=", - "license": "MIT" - }, - "node_modules/serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npm.taobao.org/serve-static/download/serve-static-1.14.1.tgz", - "integrity": "sha1-Zm5jbcTwEPfvKZcKiKZ0MgiYsvk=", - "license": "MIT", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npm.taobao.org/setprototypeof/download/setprototypeof-1.1.1.tgz", - "integrity": "sha1-fpWsskqpL1iF4KvvW6ExMw1K5oM=", - "license": "ISC" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/shebang-command/download/shebang-command-2.0.0.tgz", - "integrity": "sha1-zNCvT4g1+9wmW4JGGq8MNmY/NOo=", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/shebang-regex/download/shebang-regex-3.0.0.tgz", - "integrity": "sha1-rhbxZE2HPsrYQ7AwexQzYtTEIXI=", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npm.taobao.org/source-map-resolve/download/source-map-resolve-0.5.3.tgz", - "integrity": "sha1-GQhmvs51U+H48mei7oLGBrVQmho=", - "license": "MIT", - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npm.taobao.org/source-map-support/download/source-map-support-0.5.19.tgz", - "integrity": "sha1-qYti+G3K9PZzmWSMCFKRq56P7WE=", - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npm.taobao.org/source-map-url/download/source-map-url-0.4.1.tgz?cache=0&sync_timestamp=1612210621814&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsource-map-url%2Fdownload%2Fsource-map-url-0.4.1.tgz", - "integrity": "sha1-CvZmBadFpaL5HPG7+KevvCg97FY=", - "license": "MIT" - }, - "node_modules/sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npm.taobao.org/sprintf-js/download/sprintf-js-1.1.2.tgz", - "integrity": "sha1-2hdlJiv4wPVxdJ8q1sJjACB65nM=", - "license": "BSD-3-Clause" - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npm.taobao.org/statuses/download/statuses-1.5.0.tgz?cache=0&sync_timestamp=1609654218656&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstatuses%2Fdownload%2Fstatuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npm.taobao.org/strip-ansi/download/strip-ansi-6.0.0.tgz", - "integrity": "sha1-CxVx3XZpzNTz4G4U7x7tJiJa5TI=", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/strip-indent/download/strip-indent-3.0.0.tgz", - "integrity": "sha1-wy4c7pQLazQyx3G8LFS8znPNMAE=", - "license": "MIT", - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/stylus": { - "version": "0.54.8", - "resolved": "https://registry.npm.taobao.org/stylus/download/stylus-0.54.8.tgz?cache=0&sync_timestamp=1594901587949&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstylus%2Fdownload%2Fstylus-0.54.8.tgz", - "integrity": "sha1-PaPmWWa8Vnp7BEv+DuzmU+CZ0Uc=", - "license": "MIT", - "dependencies": { - "css-parse": "~2.0.0", - "debug": "~3.1.0", - "glob": "^7.1.6", - "mkdirp": "~1.0.4", - "safer-buffer": "^2.1.2", - "sax": "~1.2.4", - "semver": "^6.3.0", - "source-map": "^0.7.3" - }, - "bin": { - "stylus": "bin/stylus" - }, - "engines": { - "node": "*" - } - }, - "node_modules/stylus/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz?cache=0&sync_timestamp=1607566600214&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/stylus/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&sync_timestamp=1607433905910&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "license": "MIT" - }, - "node_modules/stylus/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.7.3.tgz", - "integrity": "sha1-UwL4FpAxc1ImVECS5kmB91F1A4M=", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-7.2.0.tgz?cache=0&sync_timestamp=1611394043517&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-7.2.0.tgz", - "integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npm.taobao.org/text-table/download/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "license": "MIT" - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npm.taobao.org/through/download/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "license": "MIT" - }, - "node_modules/tildify": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/tildify/download/tildify-2.0.0.tgz", - "integrity": "sha1-8gXzZ01nfOaYtwZ6melJzgO0dUo=", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/timsort": { - "version": "0.3.0", - "resolved": "https://registry.npm.taobao.org/timsort/download/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", - "license": "MIT" - }, - "node_modules/titlecase": { - "version": "1.1.3", - "resolved": "https://registry.npm.taobao.org/titlecase/download/titlecase-1.1.3.tgz", - "integrity": "sha1-/G1l/1grBgJBB2jvGgm3BQYxPcM=", - "license": "MIT", - "bin": { - "to-title-case": "bin.js" - } - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/to-fast-properties/download/to-fast-properties-2.0.0.tgz?cache=0&sync_timestamp=1580550651593&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fto-fast-properties%2Fdownload%2Fto-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npm.taobao.org/to-regex-range/download/to-regex-range-5.0.1.tgz", - "integrity": "sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ=", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/toidentifier/download/toidentifier-1.0.0.tgz", - "integrity": "sha1-fhvjRw8ed5SLxD2Uo8j013UrpVM=", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tr46": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", - "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/tslib": { - "version": "2.1.0", - "resolved": "https://registry.npm.taobao.org/tslib/download/tslib-2.1.0.tgz?cache=0&sync_timestamp=1609887769758&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftslib%2Fdownload%2Ftslib-2.1.0.tgz", - "integrity": "sha1-2mCGDxwuyqVwOrfTm8Bba/mIuXo=", - "license": "0BSD" - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npm.taobao.org/unicode-canonical-property-names-ecmascript/download/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha1-JhmADEyCWADv3YNDr33Zkzy+KBg=", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npm.taobao.org/unicode-match-property-ecmascript/download/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha1-jtKjJWmWG86SJ9Cc0/+7j+1fAgw=", - "license": "MIT", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "1.2.0", - "resolved": "https://registry.npm.taobao.org/unicode-match-property-value-ecmascript/download/unicode-match-property-value-ecmascript-1.2.0.tgz?cache=0&sync_timestamp=1583948699384&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Funicode-match-property-value-ecmascript%2Fdownload%2Funicode-match-property-value-ecmascript-1.2.0.tgz", - "integrity": "sha1-DZH2AO7rMJaqlisdb8iIduZOpTE=", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npm.taobao.org/unicode-property-aliases-ecmascript/download/unicode-property-aliases-ecmascript-1.1.0.tgz", - "integrity": "sha1-3Vepn2IHvt/0Yoq++5TFDblByPQ=", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/unpipe/download/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npm.taobao.org/uri-js/download/uri-js-4.4.1.tgz?cache=0&sync_timestamp=1610240086113&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Furi-js%2Fdownload%2Furi-js-4.4.1.tgz", - "integrity": "sha1-mxpSWVIlhZ5V9mnZKPiMbFfyp34=", - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npm.taobao.org/urix/download/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "license": "MIT" - }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/utils-merge/download/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npm.taobao.org/vary/download/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/w3c-xmlserializer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", - "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", - "dependencies": { - "xml-name-validator": "^4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/warehouse": { - "version": "4.0.0", - "resolved": "https://registry.npm.taobao.org/warehouse/download/warehouse-4.0.0.tgz", - "integrity": "sha1-RYMrxD4f0yO9nTyGsnYUMLoNHns=", - "license": "MIT", - "dependencies": { - "bluebird": "^3.2.2", - "cuid": "^2.1.4", - "graceful-fs": "^4.1.3", - "is-plain-object": "^3.0.0", - "JSONStream": "^1.0.7", - "rfdc": "^1.1.4" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-mimetype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-url": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", - "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", - "dependencies": { - "tr46": "^3.0.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npm.taobao.org/which/download/which-2.0.2.tgz", - "integrity": "sha1-fGqN0KY2oDJ+ELWckobu6T8/UbE=", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npm.taobao.org/wrappy/download/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "license": "ISC" - }, - "node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml-name-validator": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", - "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npm.taobao.org/yallist/download/yallist-4.0.0.tgz", - "integrity": "sha1-m7knkNnA7/7GO+c1GeEaNQGaOnI=", - "license": "ISC" - } - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/code-frame/download/@babel/code-frame-7.12.13.tgz?cache=0&sync_timestamp=1612314645844&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fcode-frame%2Fdownload%2F%40babel%2Fcode-frame-7.12.13.tgz", - "integrity": "sha1-3PyCa+72XnXFDiHTg319lXmN1lg=", - "requires": { - "@babel/highlight": "^7.12.13" - } - }, - "@babel/compat-data": { - "version": "7.13.11", - "resolved": "https://registry.npm.taobao.org/@babel/compat-data/download/@babel/compat-data-7.13.11.tgz", - "integrity": "sha1-nI/lI8IGl5yagbHhL+UMElTxqjU=" - }, - "@babel/core": { - "version": "7.13.10", - "resolved": "https://registry.npm.taobao.org/@babel/core/download/@babel/core-7.13.10.tgz", - "integrity": "sha1-B94FC72Bk/zYo8J5GMCJBhOpRVk=", - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.9", - "@babel/helper-compilation-targets": "^7.13.10", - "@babel/helper-module-transforms": "^7.13.0", - "@babel/helpers": "^7.13.10", - "@babel/parser": "^7.13.10", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "lodash": "^4.17.19", - "semver": "^6.3.0", - "source-map": "^0.5.0" - } - }, - "@babel/generator": { - "version": "7.13.9", - "resolved": "https://registry.npm.taobao.org/@babel/generator/download/@babel/generator-7.13.9.tgz?cache=0&sync_timestamp=1614635380562&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fgenerator%2Fdownload%2F%40babel%2Fgenerator-7.13.9.tgz", - "integrity": "sha1-Onqpb577jivkLTjYDizrTGTY3jk=", - "requires": { - "@babel/types": "^7.13.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/helper-annotate-as-pure/download/@babel/helper-annotate-as-pure-7.12.13.tgz?cache=0&sync_timestamp=1612314650267&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-annotate-as-pure%2Fdownload%2F%40babel%2Fhelper-annotate-as-pure-7.12.13.tgz", - "integrity": "sha1-D1jobfxLs7H819uAZXDhd9Q5tqs=", - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/helper-builder-binary-assignment-operator-visitor/download/@babel/helper-builder-binary-assignment-operator-visitor-7.12.13.tgz", - "integrity": "sha1-a8IDYciLCnTQUTemXKyNPL9vYfw=", - "requires": { - "@babel/helper-explode-assignable-expression": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.13.10", - "resolved": "https://registry.npm.taobao.org/@babel/helper-compilation-targets/download/@babel/helper-compilation-targets-7.13.10.tgz", - "integrity": "sha1-ExChZ4y4QnwHp1N1DaT4zkQr3Qw=", - "requires": { - "@babel/compat-data": "^7.13.8", - "@babel/helper-validator-option": "^7.12.17", - "browserslist": "^4.14.5", - "semver": "^6.3.0" - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.13.11", - "resolved": "https://registry.npm.taobao.org/@babel/helper-create-class-features-plugin/download/@babel/helper-create-class-features-plugin-7.13.11.tgz", - "integrity": "sha1-MNMKAFvKLJU/VlP8JQkaSSF39PY=", - "requires": { - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-member-expression-to-functions": "^7.13.0", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/helper-replace-supers": "^7.13.0", - "@babel/helper-split-export-declaration": "^7.12.13" - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.12.17", - "resolved": "https://registry.npm.taobao.org/@babel/helper-create-regexp-features-plugin/download/@babel/helper-create-regexp-features-plugin-7.12.17.tgz?cache=0&sync_timestamp=1613661261586&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-create-regexp-features-plugin%2Fdownload%2F%40babel%2Fhelper-create-regexp-features-plugin-7.12.17.tgz", - "integrity": "sha1-oqyH6eMZJprGVbjUQV6U041mPLc=", - "requires": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "regexpu-core": "^4.7.1" - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.1.5", - "resolved": "https://registry.npm.taobao.org/@babel/helper-define-polyfill-provider/download/@babel/helper-define-polyfill-provider-0.1.5.tgz?cache=0&sync_timestamp=1614675295626&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-define-polyfill-provider%2Fdownload%2F%40babel%2Fhelper-define-polyfill-provider-0.1.5.tgz", - "integrity": "sha1-PC+Rt5cbn8Ef53nJRcAUBl3qNA4=", - "requires": { - "@babel/helper-compilation-targets": "^7.13.0", - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/traverse": "^7.13.0", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - } - }, - "@babel/helper-explode-assignable-expression": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/helper-explode-assignable-expression/download/@babel/helper-explode-assignable-expression-7.13.0.tgz", - "integrity": "sha1-F7XFn/Rz2flW9A71cM86dsoSZX8=", - "requires": { - "@babel/types": "^7.13.0" - } - }, - "@babel/helper-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/helper-function-name/download/@babel/helper-function-name-7.12.13.tgz", - "integrity": "sha1-k61lbbPDwiMlWf17LD29y+DrN3o=", - "requires": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/helper-get-function-arity/download/@babel/helper-get-function-arity-7.12.13.tgz?cache=0&sync_timestamp=1612314643704&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-get-function-arity%2Fdownload%2F%40babel%2Fhelper-get-function-arity-7.12.13.tgz", - "integrity": "sha1-vGNFHUA6OzCCuX4diz/lvUCR5YM=", - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/helper-hoist-variables/download/@babel/helper-hoist-variables-7.13.0.tgz", - "integrity": "sha1-XViC6FW1xe2pHgytwmxueiyFk9g=", - "requires": { - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/helper-member-expression-to-functions/download/@babel/helper-member-expression-to-functions-7.13.0.tgz", - "integrity": "sha1-aqS7Z44PjCL1jNt5RR0wSURhsJE=", - "requires": { - "@babel/types": "^7.13.0" - } - }, - "@babel/helper-module-imports": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/helper-module-imports/download/@babel/helper-module-imports-7.12.13.tgz?cache=0&sync_timestamp=1612314644422&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-module-imports%2Fdownload%2F%40babel%2Fhelper-module-imports-7.12.13.tgz", - "integrity": "sha1-7GfkQE9BdQRj5FXMMgP2oy6T/LA=", - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-module-transforms": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/helper-module-transforms/download/@babel/helper-module-transforms-7.13.0.tgz", - "integrity": "sha1-QutL2O6mi6tGdRISw1e/7YtA9vE=", - "requires": { - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-replace-supers": "^7.13.0", - "@babel/helper-simple-access": "^7.12.13", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/helper-validator-identifier": "^7.12.11", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0", - "lodash": "^4.17.19" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/helper-optimise-call-expression/download/@babel/helper-optimise-call-expression-7.12.13.tgz?cache=0&sync_timestamp=1612314645448&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-optimise-call-expression%2Fdownload%2F%40babel%2Fhelper-optimise-call-expression-7.12.13.tgz", - "integrity": "sha1-XALRcbTIYVsecWP4iMHIHDCiquo=", - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/helper-plugin-utils/download/@babel/helper-plugin-utils-7.13.0.tgz?cache=0&sync_timestamp=1614035648564&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-plugin-utils%2Fdownload%2F%40babel%2Fhelper-plugin-utils-7.13.0.tgz", - "integrity": "sha1-gGUmzhJa7QM3O8QWqCgyHjpqM68=" - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/helper-remap-async-to-generator/download/@babel/helper-remap-async-to-generator-7.13.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-remap-async-to-generator%2Fdownload%2F%40babel%2Fhelper-remap-async-to-generator-7.13.0.tgz", - "integrity": "sha1-N2p2DZ97SyB3qd0Fqpw5J8rbIgk=", - "requires": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-wrap-function": "^7.13.0", - "@babel/types": "^7.13.0" - } - }, - "@babel/helper-replace-supers": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/helper-replace-supers/download/@babel/helper-replace-supers-7.13.0.tgz?cache=0&sync_timestamp=1614035151077&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-replace-supers%2Fdownload%2F%40babel%2Fhelper-replace-supers-7.13.0.tgz", - "integrity": "sha1-YDS3tRlDCUy0FieEjLIZywK+HSQ=", - "requires": { - "@babel/helper-member-expression-to-functions": "^7.13.0", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0" - } - }, - "@babel/helper-simple-access": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/helper-simple-access/download/@babel/helper-simple-access-7.12.13.tgz?cache=0&sync_timestamp=1612314645475&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-simple-access%2Fdownload%2F%40babel%2Fhelper-simple-access-7.12.13.tgz", - "integrity": "sha1-hHi8xcrPaqFnKyUcHS3eXM1hpsQ=", - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.12.1", - "resolved": "https://registry.npm.taobao.org/@babel/helper-skip-transparent-expression-wrappers/download/@babel/helper-skip-transparent-expression-wrappers-7.12.1.tgz?cache=0&sync_timestamp=1602800255982&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-skip-transparent-expression-wrappers%2Fdownload%2F%40babel%2Fhelper-skip-transparent-expression-wrappers-7.12.1.tgz", - "integrity": "sha1-Ri3GOn5DWt6EaDhcY9K4TM5LPL8=", - "requires": { - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/helper-split-export-declaration/download/@babel/helper-split-export-declaration-7.12.13.tgz?cache=0&sync_timestamp=1612314645430&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-split-export-declaration%2Fdownload%2F%40babel%2Fhelper-split-export-declaration-7.12.13.tgz", - "integrity": "sha1-6UML4AuvPoiw4T5vnU6vITY3KwU=", - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npm.taobao.org/@babel/helper-validator-identifier/download/@babel/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha1-yaHwIZF9y1zPDU5FPjmQIpgfye0=" - }, - "@babel/helper-validator-option": { - "version": "7.12.17", - "resolved": "https://registry.npm.taobao.org/@babel/helper-validator-option/download/@babel/helper-validator-option-7.12.17.tgz?cache=0&sync_timestamp=1613661224272&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fhelper-validator-option%2Fdownload%2F%40babel%2Fhelper-validator-option-7.12.17.tgz", - "integrity": "sha1-0fvwEuGnm37rv9xtJwuq+NnrmDE=" - }, - "@babel/helper-wrap-function": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/helper-wrap-function/download/@babel/helper-wrap-function-7.13.0.tgz", - "integrity": "sha1-vbXGb9qFJuwjWriUrVOhI1x5/MQ=", - "requires": { - "@babel/helper-function-name": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0" - } - }, - "@babel/helpers": { - "version": "7.13.10", - "resolved": "https://registry.npm.taobao.org/@babel/helpers/download/@babel/helpers-7.13.10.tgz", - "integrity": "sha1-/Y4rp0iFM83qxFzBWOnryl48ffg=", - "requires": { - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0" - } - }, - "@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npm.taobao.org/@babel/highlight/download/@babel/highlight-7.13.10.tgz", - "integrity": "sha1-qLKmYUj1sn1maxXYF3Q0enMdUtE=", - "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-3.2.1.tgz?cache=0&sync_timestamp=1611325836307&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-styles%2Fdownload%2Fansi-styles-3.2.1.tgz", - "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-2.4.2.tgz?cache=0&sync_timestamp=1591687130777&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchalk%2Fdownload%2Fchalk-2.4.2.tgz", - "integrity": "sha1-zUJUFnelQzPPVBpJEIwUMrRMlCQ=", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npm.taobao.org/color-convert/download/color-convert-1.9.3.tgz", - "integrity": "sha1-u3GFBpDh8TZWfeYp0tVHHe2kweg=", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npm.taobao.org/color-name/download/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-5.5.0.tgz?cache=0&sync_timestamp=1611394043517&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-5.5.0.tgz", - "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.13.11", - "resolved": "https://registry.npm.taobao.org/@babel/parser/download/@babel/parser-7.13.11.tgz", - "integrity": "sha1-+T6/yZ0hwXcq+7qhU/R+fOL1C4g=" - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-async-generator-functions/download/@babel/plugin-proposal-async-generator-functions-7.13.8.tgz?cache=0&sync_timestamp=1614383121240&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-proposal-async-generator-functions%2Fdownload%2F%40babel%2Fplugin-proposal-async-generator-functions-7.13.8.tgz", - "integrity": "sha1-h6rLV0s7xLVgP2/kFFjXKlouxLE=", - "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-remap-async-to-generator": "^7.13.0", - "@babel/plugin-syntax-async-generators": "^7.8.4" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-class-properties/download/@babel/plugin-proposal-class-properties-7.13.0.tgz", - "integrity": "sha1-FGN2AAuU79AB5XpAqIpSWvqrnzc=", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0" - } - }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-dynamic-import/download/@babel/plugin-proposal-dynamic-import-7.13.8.tgz?cache=0&sync_timestamp=1614383120439&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-proposal-dynamic-import%2Fdownload%2F%40babel%2Fplugin-proposal-dynamic-import-7.13.8.tgz", - "integrity": "sha1-h2ofaWbh3sMy6MlFGv2jvrzfLh0=", - "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - } - }, - "@babel/plugin-proposal-export-namespace-from": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-export-namespace-from/download/@babel/plugin-proposal-export-namespace-from-7.12.13.tgz", - "integrity": "sha1-OTvkekrNA/oq9uPN6bBuM94bRG0=", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-json-strings/download/@babel/plugin-proposal-json-strings-7.13.8.tgz?cache=0&sync_timestamp=1614383122014&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-proposal-json-strings%2Fdownload%2F%40babel%2Fplugin-proposal-json-strings-7.13.8.tgz", - "integrity": "sha1-vx+zYlRwda/aNjTtMVccWQGv73s=", - "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-json-strings": "^7.8.3" - } - }, - "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-logical-assignment-operators/download/@babel/plugin-proposal-logical-assignment-operators-7.13.8.tgz?cache=0&sync_timestamp=1614383123641&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-proposal-logical-assignment-operators%2Fdownload%2F%40babel%2Fplugin-proposal-logical-assignment-operators-7.13.8.tgz", - "integrity": "sha1-k/p41jhXxAzjyMMxUiD9AL+7Tho=", - "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-nullish-coalescing-operator/download/@babel/plugin-proposal-nullish-coalescing-operator-7.13.8.tgz?cache=0&sync_timestamp=1614383122422&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-proposal-nullish-coalescing-operator%2Fdownload%2F%40babel%2Fplugin-proposal-nullish-coalescing-operator-7.13.8.tgz", - "integrity": "sha1-NzCjHa/TwQ2MzRBkjtgKKsVHLvM=", - "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - } - }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-numeric-separator/download/@babel/plugin-proposal-numeric-separator-7.12.13.tgz", - "integrity": "sha1-vZ2jGI54e1EgtPnUZagmHOZ+0ds=", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-object-rest-spread/download/@babel/plugin-proposal-object-rest-spread-7.13.8.tgz?cache=0&sync_timestamp=1614383131892&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-proposal-object-rest-spread%2Fdownload%2F%40babel%2Fplugin-proposal-object-rest-spread-7.13.8.tgz", - "integrity": "sha1-XSEKTXJ9bOOxj53oLMmaOWTu1go=", - "requires": { - "@babel/compat-data": "^7.13.8", - "@babel/helper-compilation-targets": "^7.13.8", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.13.0" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-optional-catch-binding/download/@babel/plugin-proposal-optional-catch-binding-7.13.8.tgz?cache=0&sync_timestamp=1614383122839&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-proposal-optional-catch-binding%2Fdownload%2F%40babel%2Fplugin-proposal-optional-catch-binding-7.13.8.tgz", - "integrity": "sha1-Ota9WQFQbqmW/DG9zzzPor7XEQc=", - "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-optional-chaining/download/@babel/plugin-proposal-optional-chaining-7.13.8.tgz?cache=0&sync_timestamp=1614383126788&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-proposal-optional-chaining%2Fdownload%2F%40babel%2Fplugin-proposal-optional-chaining-7.13.8.tgz", - "integrity": "sha1-4535Pv5+fmIYQbq8GXmC4UDpB1Y=", - "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "@babel/plugin-proposal-private-methods": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-private-methods/download/@babel/plugin-proposal-private-methods-7.13.0.tgz", - "integrity": "sha1-BL1MbUD25rv6L1fi2AlLrZAO94c=", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-unicode-property-regex/download/@babel/plugin-proposal-unicode-property-regex-7.12.13.tgz", - "integrity": "sha1-vr3lEzm+gpwXqqrO0YZB3rYrObo=", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-async-generators/download/@babel/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha1-qYP7Gusuw/btBCohD2QOkOeG/g0=", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-class-properties/download/@babel/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha1-tcmHJ0xKOoK4lxR5aTGmtTVErhA=", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-dynamic-import/download/@babel/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha1-Yr+Ysto80h1iYVT8lu5bPLaOrLM=", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-export-namespace-from/download/@babel/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha1-AolkqbqA28CUyRXEh618TnpmRlo=", - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-json-strings/download/@babel/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha1-AcohtmjNghjJ5kDLbdiMVBKyyWo=", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-jsx/download/@babel/plugin-syntax-jsx-7.12.13.tgz", - "integrity": "sha1-BE+4HrrWaY/mLEeIdVdby7m3DxU=", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-logical-assignment-operators/download/@babel/plugin-syntax-logical-assignment-operators-7.10.4.tgz?cache=0&sync_timestamp=1593521628455&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-syntax-logical-assignment-operators%2Fdownload%2F%40babel%2Fplugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha1-ypHvRjA1MESLkGZSusLp/plB9pk=", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-nullish-coalescing-operator/download/@babel/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha1-Fn7XA2iIYIH3S1w2xlqIwDtm0ak=", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-numeric-separator/download/@babel/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha1-ubBws+M1cM2f0Hun+pHA3Te5r5c=", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-object-rest-spread/download/@babel/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha1-YOIl7cvZimQDMqLnLdPmbxr1WHE=", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-optional-catch-binding/download/@babel/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha1-YRGiZbz7Ag6579D9/X0mQCue1sE=", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-optional-chaining/download/@babel/plugin-syntax-optional-chaining-7.8.3.tgz?cache=0&sync_timestamp=1578977710469&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-syntax-optional-chaining%2Fdownload%2F%40babel%2Fplugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha1-T2nCq5UWfgGAzVM2YT+MV4j31Io=", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-syntax-top-level-await/download/@babel/plugin-syntax-top-level-await-7.12.13.tgz", - "integrity": "sha1-xfD6biSfW3OXJ/kjVAz3qAYTAXg=", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-arrow-functions/download/@babel/plugin-transform-arrow-functions-7.13.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-arrow-functions%2Fdownload%2F%40babel%2Fplugin-transform-arrow-functions-7.13.0.tgz", - "integrity": "sha1-EKWb661S1jegJ6+mkujVzv9ePa4=", - "requires": { - "@babel/helper-plugin-utils": "^7.13.0" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-async-to-generator/download/@babel/plugin-transform-async-to-generator-7.13.0.tgz", - "integrity": "sha1-jhEr9ncbgr8el05eJoBsXJmqUW8=", - "requires": { - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-remap-async-to-generator": "^7.13.0" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-block-scoped-functions/download/@babel/plugin-transform-block-scoped-functions-7.12.13.tgz", - "integrity": "sha1-qb8YNvKjm062zwmWdzneKepL9MQ=", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-block-scoping/download/@babel/plugin-transform-block-scoping-7.12.13.tgz", - "integrity": "sha1-825VB20G9B39eFV+oDnBtYFkLmE=", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-classes/download/@babel/plugin-transform-classes-7.13.0.tgz?cache=0&sync_timestamp=1614035151583&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-classes%2Fdownload%2F%40babel%2Fplugin-transform-classes-7.13.0.tgz", - "integrity": "sha1-AmUVUHXEKRi/TTpAUxNBdq2bUzs=", - "requires": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-replace-supers": "^7.13.0", - "@babel/helper-split-export-declaration": "^7.12.13", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-computed-properties/download/@babel/plugin-transform-computed-properties-7.13.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-computed-properties%2Fdownload%2F%40babel%2Fplugin-transform-computed-properties-7.13.0.tgz", - "integrity": "sha1-hFxui5u1U3ax+guS7wvcjqBmRO0=", - "requires": { - "@babel/helper-plugin-utils": "^7.13.0" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-destructuring/download/@babel/plugin-transform-destructuring-7.13.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-destructuring%2Fdownload%2F%40babel%2Fplugin-transform-destructuring-7.13.0.tgz", - "integrity": "sha1-xdzicAFNTh67HYBhFmlMErcCiWM=", - "requires": { - "@babel/helper-plugin-utils": "^7.13.0" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-dotall-regex/download/@babel/plugin-transform-dotall-regex-7.12.13.tgz", - "integrity": "sha1-PxYBzCmQW/y2f1ORDxl66v67Ja0=", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-duplicate-keys/download/@babel/plugin-transform-duplicate-keys-7.12.13.tgz", - "integrity": "sha1-bwa4eouAP9ko5UuBwljwoAM5BN4=", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-exponentiation-operator/download/@babel/plugin-transform-exponentiation-operator-7.12.13.tgz", - "integrity": "sha1-TVI5C5onPmUeSrpq7knvQOgM0KE=", - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-for-of/download/@babel/plugin-transform-for-of-7.13.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-for-of%2Fdownload%2F%40babel%2Fplugin-transform-for-of-7.13.0.tgz", - "integrity": "sha1-x5n4gagJGsJrVIZ6hFw+l9JpYGI=", - "requires": { - "@babel/helper-plugin-utils": "^7.13.0" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-function-name/download/@babel/plugin-transform-function-name-7.12.13.tgz", - "integrity": "sha1-uwJEUvmq7YYdN0yOeiQlLOOlAFE=", - "requires": { - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-literals/download/@babel/plugin-transform-literals-7.12.13.tgz", - "integrity": "sha1-LKRbr+SoIBl88xV5Sk0mVg/kvbk=", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-member-expression-literals/download/@babel/plugin-transform-member-expression-literals-7.12.13.tgz", - "integrity": "sha1-X/pmzVm54ZExTJ8fgDuTjowIHkA=", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-modules-amd/download/@babel/plugin-transform-modules-amd-7.13.0.tgz", - "integrity": "sha1-GfUR1g49h1PMWm1Od106UYSGbMM=", - "requires": { - "@babel/helper-module-transforms": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-modules-commonjs/download/@babel/plugin-transform-modules-commonjs-7.13.8.tgz?cache=0&sync_timestamp=1614383124878&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-modules-commonjs%2Fdownload%2F%40babel%2Fplugin-transform-modules-commonjs-7.13.8.tgz", - "integrity": "sha1-ewGtfC3PInWwb6F4HgDRPUILPhs=", - "requires": { - "@babel/helper-module-transforms": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-simple-access": "^7.12.13", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-modules-systemjs/download/@babel/plugin-transform-modules-systemjs-7.13.8.tgz?cache=0&sync_timestamp=1614383123237&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-modules-systemjs%2Fdownload%2F%40babel%2Fplugin-transform-modules-systemjs-7.13.8.tgz", - "integrity": "sha1-bQZu4r/zx7PWC/KN7Baa2ZODGuM=", - "requires": { - "@babel/helper-hoist-variables": "^7.13.0", - "@babel/helper-module-transforms": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-validator-identifier": "^7.12.11", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-modules-umd/download/@babel/plugin-transform-modules-umd-7.13.0.tgz", - "integrity": "sha1-ij2WqX0ZlwW5/QIVgAgq+BwG5ws=", - "requires": { - "@babel/helper-module-transforms": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-named-capturing-groups-regex/download/@babel/plugin-transform-named-capturing-groups-regex-7.12.13.tgz", - "integrity": "sha1-IhNyWl9bu+NktQw7pZmMlZnFydk=", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-new-target/download/@babel/plugin-transform-new-target-7.12.13.tgz", - "integrity": "sha1-4i2MOvJLFQ3VKMvW5oXnmb8cNRw=", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-object-super/download/@babel/plugin-transform-object-super-7.12.13.tgz", - "integrity": "sha1-tEFqLWO4974xTz00m9VanBtRcfc=", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13", - "@babel/helper-replace-supers": "^7.12.13" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-parameters/download/@babel/plugin-transform-parameters-7.13.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-parameters%2Fdownload%2F%40babel%2Fplugin-transform-parameters-7.13.0.tgz", - "integrity": "sha1-j6dgPjCX+cC3yhpIIbwvtS6eUAc=", - "requires": { - "@babel/helper-plugin-utils": "^7.13.0" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-property-literals/download/@babel/plugin-transform-property-literals-7.12.13.tgz", - "integrity": "sha1-TmqeN4ZNjxs7wOLc57+IV9uLGoE=", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-regenerator/download/@babel/plugin-transform-regenerator-7.12.13.tgz?cache=0&sync_timestamp=1612314644517&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-regenerator%2Fdownload%2F%40babel%2Fplugin-transform-regenerator-7.12.13.tgz", - "integrity": "sha1-tii8ychSYKwa6wW0W94lIQGUovU=", - "requires": { - "regenerator-transform": "^0.14.2" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-reserved-words/download/@babel/plugin-transform-reserved-words-7.12.13.tgz", - "integrity": "sha1-fZmI1PBuD+aX6h2YAxiKoYtHJpU=", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-shorthand-properties/download/@babel/plugin-transform-shorthand-properties-7.12.13.tgz", - "integrity": "sha1-23VXMrcMU51QTGOQ2c6Q/mSv960=", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-spread/download/@babel/plugin-transform-spread-7.13.0.tgz?cache=0&sync_timestamp=1614035651747&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-spread%2Fdownload%2F%40babel%2Fplugin-transform-spread-7.13.0.tgz", - "integrity": "sha1-hIh3EOJzwYFaznrkWfb0Kl0x1f0=", - "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-sticky-regex/download/@babel/plugin-transform-sticky-regex-7.12.13.tgz", - "integrity": "sha1-dg/9k2+s5z+GCuZG+4bugvPQbR8=", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-template-literals/download/@babel/plugin-transform-template-literals-7.13.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-template-literals%2Fdownload%2F%40babel%2Fplugin-transform-template-literals-7.13.0.tgz", - "integrity": "sha1-o2BJEnl3rZRDje50Q1mNHO/fQJ0=", - "requires": { - "@babel/helper-plugin-utils": "^7.13.0" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-typeof-symbol/download/@babel/plugin-transform-typeof-symbol-7.12.13.tgz", - "integrity": "sha1-eF3Weh8upXnZwr5yLejITLhfWn8=", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-unicode-escapes/download/@babel/plugin-transform-unicode-escapes-7.12.13.tgz", - "integrity": "sha1-hAztO4FtO1En3R0S3O3F3q0aXnQ=", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-unicode-regex/download/@babel/plugin-transform-unicode-regex-7.12.13.tgz", - "integrity": "sha1-tSUhaFgE4VWxIC6D/BiNNLtw9aw=", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/preset-env": { - "version": "7.13.10", - "resolved": "https://registry.npm.taobao.org/@babel/preset-env/download/@babel/preset-env-7.13.10.tgz", - "integrity": "sha1-tc3jHV/nerKmqz1FO1kEGhs6UlI=", - "requires": { - "@babel/compat-data": "^7.13.8", - "@babel/helper-compilation-targets": "^7.13.10", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-validator-option": "^7.12.17", - "@babel/plugin-proposal-async-generator-functions": "^7.13.8", - "@babel/plugin-proposal-class-properties": "^7.13.0", - "@babel/plugin-proposal-dynamic-import": "^7.13.8", - "@babel/plugin-proposal-export-namespace-from": "^7.12.13", - "@babel/plugin-proposal-json-strings": "^7.13.8", - "@babel/plugin-proposal-logical-assignment-operators": "^7.13.8", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8", - "@babel/plugin-proposal-numeric-separator": "^7.12.13", - "@babel/plugin-proposal-object-rest-spread": "^7.13.8", - "@babel/plugin-proposal-optional-catch-binding": "^7.13.8", - "@babel/plugin-proposal-optional-chaining": "^7.13.8", - "@babel/plugin-proposal-private-methods": "^7.13.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.12.13", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.12.13", - "@babel/plugin-transform-arrow-functions": "^7.13.0", - "@babel/plugin-transform-async-to-generator": "^7.13.0", - "@babel/plugin-transform-block-scoped-functions": "^7.12.13", - "@babel/plugin-transform-block-scoping": "^7.12.13", - "@babel/plugin-transform-classes": "^7.13.0", - "@babel/plugin-transform-computed-properties": "^7.13.0", - "@babel/plugin-transform-destructuring": "^7.13.0", - "@babel/plugin-transform-dotall-regex": "^7.12.13", - "@babel/plugin-transform-duplicate-keys": "^7.12.13", - "@babel/plugin-transform-exponentiation-operator": "^7.12.13", - "@babel/plugin-transform-for-of": "^7.13.0", - "@babel/plugin-transform-function-name": "^7.12.13", - "@babel/plugin-transform-literals": "^7.12.13", - "@babel/plugin-transform-member-expression-literals": "^7.12.13", - "@babel/plugin-transform-modules-amd": "^7.13.0", - "@babel/plugin-transform-modules-commonjs": "^7.13.8", - "@babel/plugin-transform-modules-systemjs": "^7.13.8", - "@babel/plugin-transform-modules-umd": "^7.13.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.13", - "@babel/plugin-transform-new-target": "^7.12.13", - "@babel/plugin-transform-object-super": "^7.12.13", - "@babel/plugin-transform-parameters": "^7.13.0", - "@babel/plugin-transform-property-literals": "^7.12.13", - "@babel/plugin-transform-regenerator": "^7.12.13", - "@babel/plugin-transform-reserved-words": "^7.12.13", - "@babel/plugin-transform-shorthand-properties": "^7.12.13", - "@babel/plugin-transform-spread": "^7.13.0", - "@babel/plugin-transform-sticky-regex": "^7.12.13", - "@babel/plugin-transform-template-literals": "^7.13.0", - "@babel/plugin-transform-typeof-symbol": "^7.12.13", - "@babel/plugin-transform-unicode-escapes": "^7.12.13", - "@babel/plugin-transform-unicode-regex": "^7.12.13", - "@babel/preset-modules": "^0.1.4", - "@babel/types": "^7.13.0", - "babel-plugin-polyfill-corejs2": "^0.1.4", - "babel-plugin-polyfill-corejs3": "^0.1.3", - "babel-plugin-polyfill-regenerator": "^0.1.2", - "core-js-compat": "^3.9.0", - "semver": "^6.3.0" - } - }, - "@babel/preset-modules": { - "version": "0.1.4", - "resolved": "https://registry.npm.taobao.org/@babel/preset-modules/download/@babel/preset-modules-0.1.4.tgz", - "integrity": "sha1-Ni8raMZihClw/bXiVP/I/BwuQV4=", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/register": { - "version": "7.13.8", - "resolved": "https://registry.npm.taobao.org/@babel/register/download/@babel/register-7.13.8.tgz", - "integrity": "sha1-2QUdxoIMtOhjdcwOLVWkhisxGE8=", - "requires": { - "find-cache-dir": "^2.0.0", - "lodash": "^4.17.19", - "make-dir": "^2.1.0", - "pirates": "^4.0.0", - "source-map-support": "^0.5.16" - } - }, - "@babel/runtime": { - "version": "7.13.10", - "resolved": "https://registry.npm.taobao.org/@babel/runtime/download/@babel/runtime-7.13.10.tgz", - "integrity": "sha1-R9QqV7YJX0Ro2kQDiP262L6/DX0=", - "requires": { - "regenerator-runtime": "^0.13.4" - } - }, - "@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npm.taobao.org/@babel/template/download/@babel/template-7.12.13.tgz?cache=0&sync_timestamp=1612314959052&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Ftemplate%2Fdownload%2F%40babel%2Ftemplate-7.12.13.tgz", - "integrity": "sha1-UwJlvooliduzdSOETFvLVZR/syc=", - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "@babel/traverse": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/traverse/download/@babel/traverse-7.13.0.tgz?cache=0&sync_timestamp=1614035150979&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Ftraverse%2Fdownload%2F%40babel%2Ftraverse-7.13.0.tgz", - "integrity": "sha1-bZV1JHX4bufe0GU23jCaZfyJZsw=", - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.0", - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.13.0", - "@babel/types": "^7.13.0", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - } - }, - "@babel/types": { - "version": "7.13.0", - "resolved": "https://registry.npm.taobao.org/@babel/types/download/@babel/types-7.13.0.tgz", - "integrity": "sha1-dEJNKBbwFxtBAPCrNOmjdO/ff4A=", - "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==" - }, - "a-sync-waterfall": { - "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/a-sync-waterfall/download/a-sync-waterfall-1.0.1.tgz", - "integrity": "sha1-dba2qnJZi0l6El56J3DxT0yKH6c=" - }, - "abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npm.taobao.org/abbrev/download/abbrev-1.1.1.tgz", - "integrity": "sha1-+PLIh60Qv2f2NPAFtph/7TF5qsg=" - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npm.taobao.org/accepts/download/accepts-1.3.7.tgz", - "integrity": "sha1-UxvHJlF6OytB+FACHGzBXqq1B80=", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==" - }, - "acorn-globals": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", - "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", - "requires": { - "acorn": "^8.1.0", - "acorn-walk": "^8.0.2" - } - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "requires": { - "debug": "4" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.6.tgz", - "integrity": "sha1-uvWmLoArB9l3A0WG+MO69a3ybfQ=", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/amdefine/download/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-4.3.0.tgz?cache=0&sync_timestamp=1611325836307&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-styles%2Fdownload%2Fansi-styles-4.3.0.tgz", - "integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=", - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/archy/download/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npm.taobao.org/argparse/download/argparse-1.0.10.tgz", - "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", - "requires": { - "sprintf-js": "~1.0.2" - }, - "dependencies": { - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npm.taobao.org/sprintf-js/download/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - } - } - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npm.taobao.org/asap/download/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npm.taobao.org/atob/download/atob-2.1.2.tgz", - "integrity": "sha1-bZUX654DDSQ2ZmZR6GvZ9vE1M8k=" - }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npm.taobao.org/babel-plugin-dynamic-import-node/download/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha1-hP2hnJduxcbe/vV/lCez3vZuF6M=", - "requires": { - "object.assign": "^4.1.0" - } - }, - "babel-plugin-inferno": { - "version": "6.1.1", - "resolved": "https://registry.npm.taobao.org/babel-plugin-inferno/download/babel-plugin-inferno-6.1.1.tgz", - "integrity": "sha1-Fca/uf/3WmZp6fZBC7NlEuroulQ=", - "requires": { - "@babel/plugin-syntax-jsx": "^7", - "@babel/types": "^7" - } - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.1.10", - "resolved": "https://registry.npm.taobao.org/babel-plugin-polyfill-corejs2/download/babel-plugin-polyfill-corejs2-0.1.10.tgz?cache=0&sync_timestamp=1614681685642&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbabel-plugin-polyfill-corejs2%2Fdownload%2Fbabel-plugin-polyfill-corejs2-0.1.10.tgz", - "integrity": "sha1-osXCRfVsDKw9vdvwcmpGsk8PgdE=", - "requires": { - "@babel/compat-data": "^7.13.0", - "@babel/helper-define-polyfill-provider": "^0.1.5", - "semver": "^6.1.1" - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.1.7", - "resolved": "https://registry.npm.taobao.org/babel-plugin-polyfill-corejs3/download/babel-plugin-polyfill-corejs3-0.1.7.tgz", - "integrity": "sha1-gESdnW8idJEuBdnhgrVIFpBL79A=", - "requires": { - "@babel/helper-define-polyfill-provider": "^0.1.5", - "core-js-compat": "^3.8.1" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.1.6", - "resolved": "https://registry.npm.taobao.org/babel-plugin-polyfill-regenerator/download/babel-plugin-polyfill-regenerator-0.1.6.tgz?cache=0&sync_timestamp=1614675087036&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbabel-plugin-polyfill-regenerator%2Fdownload%2Fbabel-plugin-polyfill-regenerator-0.1.6.tgz", - "integrity": "sha1-D+BqAm/g+qYozMi6MwLaCmzgLz8=", - "requires": { - "@babel/helper-define-polyfill-provider": "^0.1.5" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npm.taobao.org/basic-auth/download/basic-auth-2.0.1.tgz", - "integrity": "sha1-uZgnm/R844NEtPPPkW1Gebv1Hjo=", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npm.taobao.org/binary-extensions/download/binary-extensions-2.2.0.tgz?cache=0&sync_timestamp=1610299373298&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbinary-extensions%2Fdownload%2Fbinary-extensions-2.2.0.tgz", - "integrity": "sha1-dfUC7q+f/eQvyYgpZFvk6na9ni0=" - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npm.taobao.org/bluebird/download/bluebird-3.7.2.tgz?cache=0&sync_timestamp=1574981921523&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbluebird%2Fdownload%2Fbluebird-3.7.2.tgz", - "integrity": "sha1-nyKcFb4nJFT/qXOs4NvueaGww28=" - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npm.taobao.org/brace-expansion/download/brace-expansion-1.1.11.tgz?cache=0&sync_timestamp=1614010713935&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbrace-expansion%2Fdownload%2Fbrace-expansion-1.1.11.tgz", - "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npm.taobao.org/braces/download/braces-3.0.2.tgz", - "integrity": "sha1-NFThpGLujVmeI23zNs2epPiv4Qc=", - "requires": { - "fill-range": "^7.0.1" - } - }, - "browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", - "requires": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npm.taobao.org/buffer-from/download/buffer-from-1.1.1.tgz", - "integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=" - }, - "bulma-stylus": { - "version": "0.8.0", - "resolved": "https://registry.npm.taobao.org/bulma-stylus/download/bulma-stylus-0.8.0.tgz", - "integrity": "sha1-E3wYKh/fmsgvLnO9MWOqxFSxBa4=" - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/bytes/download/bytes-3.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbytes%2Fdownload%2Fbytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npm.taobao.org/call-bind/download/call-bind-1.0.2.tgz?cache=0&sync_timestamp=1610403244794&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcall-bind%2Fdownload%2Fcall-bind-1.0.2.tgz", - "integrity": "sha1-sdTonmiBGcPJqQOtMKuy9qkZvjw=", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npm.taobao.org/camel-case/download/camel-case-4.1.2.tgz", - "integrity": "sha1-lygHKpVPgFIoIlpt7qazhGHhvVo=", - "requires": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "caniuse-lite": { - "version": "1.0.30001512", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001512.tgz", - "integrity": "sha512-2S9nK0G/mE+jasCUsMPlARhRCts1ebcp2Ji8Y8PWi4NDE1iRdLCnEPHkEfeBrGC45L4isBx5ur3IQ6yTE2mRZw==" - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-4.1.0.tgz?cache=0&sync_timestamp=1591687130777&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchalk%2Fdownload%2Fchalk-4.1.0.tgz", - "integrity": "sha1-ThSHCmGNni7dl92DRf2dncMVZGo=", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", - "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", - "requires": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", - "parse5": "^7.0.0", - "parse5-htmlparser2-tree-adapter": "^7.0.0" - }, - "dependencies": { - "dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - } - }, - "domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "requires": { - "domelementtype": "^2.3.0" - } - }, - "domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "requires": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - } - }, - "entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" - } - } - }, - "cheerio-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", - "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "requires": { - "boolbase": "^1.0.0", - "css-select": "^5.1.0", - "css-what": "^6.1.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1" - }, - "dependencies": { - "dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - } - }, - "domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "requires": { - "domelementtype": "^2.3.0" - } - }, - "domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "requires": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - } - }, - "entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" - } - } - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npm.taobao.org/color-convert/download/color-convert-2.0.1.tgz", - "integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npm.taobao.org/color-name/download/color-name-1.1.4.tgz", - "integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=" - }, - "colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "command-exists": { - "version": "1.2.9", - "resolved": "https://registry.npm.taobao.org/command-exists/download/command-exists-1.2.9.tgz", - "integrity": "sha1-xQclrzgIyKsCYP1gsB+/oluVT2k=" - }, - "commander": { - "version": "5.1.0", - "resolved": "https://registry.npm.taobao.org/commander/download/commander-5.1.0.tgz", - "integrity": "sha1-Rqu9FlL44Fm92u+Zu9yyrZzxea4=" - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/commondir/download/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" - }, - "compressible": { - "version": "2.0.18", - "resolved": "https://registry.npm.taobao.org/compressible/download/compressible-2.0.18.tgz", - "integrity": "sha1-r1PMprBw1MPAdQ+9dyhqbXzEb7o=", - "requires": { - "mime-db": ">= 1.43.0 < 2" - } - }, - "compression": { - "version": "1.7.4", - "resolved": "https://registry.npm.taobao.org/compression/download/compression-1.7.4.tgz", - "integrity": "sha1-lVI+/xcMpXwpoMpB5v4TH0Hlu48=", - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&sync_timestamp=1607566600214&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&sync_timestamp=1607433905910&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npm.taobao.org/concat-map/download/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "connect": { - "version": "3.7.0", - "resolved": "https://registry.npm.taobao.org/connect/download/connect-3.7.0.tgz", - "integrity": "sha1-XUk0iRDKpeB6AYALAw0MNfIEhPg=", - "requires": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&sync_timestamp=1607566600214&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&sync_timestamp=1607433905910&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npm.taobao.org/convert-source-map/download/convert-source-map-1.7.0.tgz", - "integrity": "sha1-F6LLiC1/d9NJBYXizmxSRCSjpEI=", - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "copy-anything": { - "version": "2.0.3", - "resolved": "https://registry.npm.taobao.org/copy-anything/download/copy-anything-2.0.3.tgz", - "integrity": "sha1-hCQHugJGaw34RIGbvjuuu+XUXYc=", - "requires": { - "is-what": "^3.12.0" - } - }, - "core-js-compat": { - "version": "3.9.1", - "resolved": "https://registry.npm.taobao.org/core-js-compat/download/core-js-compat-3.9.1.tgz", - "integrity": "sha1-Tlcqz+kK/2nXbYw3dZ0hpcWbtFU=", - "requires": { - "browserslist": "^4.16.3", - "semver": "7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" - } - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npm.taobao.org/cross-spawn/download/cross-spawn-7.0.3.tgz", - "integrity": "sha1-9zqFudXUHQRVUcF34ogtSshXKKY=", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "css": { - "version": "2.2.4", - "resolved": "https://registry.npm.taobao.org/css/download/css-2.2.4.tgz", - "integrity": "sha1-xkZ1XHOXHyu6amAeLPL9cbEpiSk=", - "requires": { - "inherits": "^2.0.3", - "source-map": "^0.6.1", - "source-map-resolve": "^0.5.2", - "urix": "^0.1.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=" - } - } - }, - "css-parse": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/css-parse/download/css-parse-2.0.0.tgz", - "integrity": "sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=", - "requires": { - "css": "^2.0.0" - } - }, - "css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "requires": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "dependencies": { - "dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - } - }, - "domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "requires": { - "domelementtype": "^2.3.0" - } - }, - "domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "requires": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - } - }, - "entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" - } - } - }, - "css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" - }, - "cssom": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", - "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==" - }, - "cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "requires": { - "cssom": "~0.3.6" - }, - "dependencies": { - "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" - } - } - }, - "cuid": { - "version": "2.1.8", - "resolved": "https://registry.npm.taobao.org/cuid/download/cuid-2.1.8.tgz", - "integrity": "sha1-y7iPlUFx4NV0dgbAE5+2XFEB6sA=" - }, - "data-urls": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", - "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", - "requires": { - "abab": "^2.0.6", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0" - } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npm.taobao.org/debug/download/debug-4.3.1.tgz?cache=0&sync_timestamp=1607566600214&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.3.1.tgz", - "integrity": "sha1-8NIpxQXgxtjEmsVT0bE9wYP2su4=", - "requires": { - "ms": "2.1.2" - } - }, - "decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" - }, - "decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" - }, - "deep-assign": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/deep-assign/download/deep-assign-2.0.0.tgz", - "integrity": "sha1-6+BrHwfwja5ZdiDj3RYi83GhxXI=", - "requires": { - "is-obj": "^1.0.0" - } - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npm.taobao.org/deepmerge/download/deepmerge-4.2.2.tgz", - "integrity": "sha1-RNLqNnm49NT/ujPwPYZfwee/SVU=" - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npm.taobao.org/define-properties/download/define-properties-1.1.3.tgz", - "integrity": "sha1-z4jabL7ib+bbcJT2HYcMvYTO6fE=", - "requires": { - "object-keys": "^1.0.12" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/depd/download/depd-2.0.0.tgz", - "integrity": "sha1-tpYWPMdXVg0JzyLMj60Vcbeedt8=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npm.taobao.org/destroy/download/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "dom-serializer": { - "version": "1.2.0", - "resolved": "https://registry.npm.taobao.org/dom-serializer/download/dom-serializer-1.2.0.tgz?cache=0&sync_timestamp=1607193465399&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdom-serializer%2Fdownload%2Fdom-serializer-1.2.0.tgz", - "integrity": "sha1-NDPZE2rrPGJ5gdqjhfx/MtJ8SPE=", - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "entities": "^2.0.0" - } - }, - "domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" - }, - "domexception": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", - "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", - "requires": { - "webidl-conversions": "^7.0.0" - } - }, - "domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "requires": { - "domelementtype": "^2.2.0" - } - }, - "dompurify": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.4.tgz", - "integrity": "sha512-ae0mA+Qiqp6C29pqZX3fQgK+F91+F7wobM/v8DRzDqJdZJELXiFUx4PP4pK/mzUS0xkiSEx3Ncd9gr69jg3YsQ==" - }, - "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "ejs": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", - "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", - "requires": { - "jake": "^10.8.5" - } - }, - "electron-to-chromium": { - "version": "1.3.739", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.739.tgz", - "integrity": "sha512-+LPJVRsN7hGZ9EIUUiWCpO7l4E3qBYHNadazlucBfsXBbccDFNKUBAgzE68FnkWGJPwD/AfKhSzL+G+Iqb8A4A==" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npm.taobao.org/encodeurl/download/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "entities": { - "version": "2.1.0", - "resolved": "https://registry.npm.taobao.org/entities/download/entities-2.1.0.tgz?cache=0&sync_timestamp=1611535878280&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fentities%2Fdownload%2Fentities-2.1.0.tgz", - "integrity": "sha1-mS0xKc999ocLlsV4WMJJoSD4uLU=" - }, - "errno": { - "version": "0.1.8", - "resolved": "https://registry.npm.taobao.org/errno/download/errno-0.1.8.tgz", - "integrity": "sha1-i7Ppx9Rjvkl2/4iPdrSAnrwugR8=", - "optional": true, - "requires": { - "prr": "~1.0.1" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npm.taobao.org/escape-html/download/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npm.taobao.org/escape-string-regexp/download/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "requires": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true - } - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npm.taobao.org/esprima/download/esprima-4.0.1.tgz", - "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=" - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npm.taobao.org/esutils/download/esutils-2.0.3.tgz", - "integrity": "sha1-dNLrTeC42hKTcRkQ1Qd1ubcQ72Q=" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npm.taobao.org/etag/download/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", - "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=" - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npm.taobao.org/fast-json-stable-stringify/download/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM=" - }, - "filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "requires": { - "minimatch": "^5.0.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npm.taobao.org/fill-range/download/fill-range-7.0.1.tgz", - "integrity": "sha1-GRmmp8df44ssfHflGYU12prN2kA=", - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npm.taobao.org/finalhandler/download/finalhandler-1.1.2.tgz", - "integrity": "sha1-t+fQAP/RGTjQ/bBTUG9uur6fWH0=", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&sync_timestamp=1607566600214&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&sync_timestamp=1607433905910&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npm.taobao.org/find-cache-dir/download/find-cache-dir-2.1.0.tgz", - "integrity": "sha1-jQ+UzRP+Q8bHwmGg2GEVypGMBfc=", - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/find-up/download/find-up-3.0.0.tgz", - "integrity": "sha1-SRafHXmTQwZG2mHsxa41XCHJe3M=", - "requires": { - "locate-path": "^3.0.0" - } - }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npm.taobao.org/fresh/download/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/fs.realpath/download/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npm.taobao.org/fsevents/download/fsevents-2.3.2.tgz", - "integrity": "sha1-ilJveLj99GI7cJ4Ll1xSwkwC/Ro=", - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz", - "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=" - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npm.taobao.org/gensync/download/gensync-1.0.0-beta.2.tgz", - "integrity": "sha1-MqbudsPX9S1GsrGuXZP+qFgKJeA=" - }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npm.taobao.org/get-intrinsic/download/get-intrinsic-1.1.1.tgz?cache=0&sync_timestamp=1612364411509&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fget-intrinsic%2Fdownload%2Fget-intrinsic-1.1.1.tgz", - "integrity": "sha1-FfWfN2+FXERpY5SPDSTNNje0q8Y=", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npm.taobao.org/glob/download/glob-7.1.6.tgz", - "integrity": "sha1-FB8zuBp8JJLhJVlDB0gMRmeSeKY=", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npm.taobao.org/glob-parent/download/glob-parent-5.1.2.tgz", - "integrity": "sha1-hpgyxYA0/mikCTwX3BXoNA2EAcQ=", - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npm.taobao.org/globals/download/globals-11.12.0.tgz", - "integrity": "sha1-q4eVM4hooLq9hSV1gBjCp+uVxC4=" - }, - "graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npm.taobao.org/has/download/has-1.0.3.tgz", - "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-4.0.0.tgz", - "integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=" - }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npm.taobao.org/has-symbols/download/has-symbols-1.0.2.tgz", - "integrity": "sha1-Fl0wcMADCXUqEjakeTMeOsVvFCM=" - }, - "hexo": { - "version": "5.4.2", - "resolved": "https://registry.npmmirror.com/hexo/-/hexo-5.4.2.tgz", - "integrity": "sha512-Af6mxKwx9byalaffKgiQ8/bZfbXPo2SGEn2Q9hFh++15g15/IulvOhu8lQkJdyZNzmj3hOuSrJdqoUvIr3K/qw==", - "requires": { - "abbrev": "^1.1.1", - "archy": "^1.0.0", - "bluebird": "^3.5.2", - "chalk": "^4.0.0", - "hexo-cli": "^4.0.0", - "hexo-front-matter": "^2.0.0", - "hexo-fs": "^3.1.0", - "hexo-i18n": "^1.0.0", - "hexo-log": "^2.0.0", - "hexo-util": "^2.4.0", - "js-yaml": "^3.14.1", - "micromatch": "^4.0.2", - "moment": "^2.22.2", - "moment-timezone": "^0.5.21", - "nunjucks": "^3.2.1", - "pretty-hrtime": "^1.0.3", - "resolve": "^1.8.1", - "strip-ansi": "^6.0.0", - "text-table": "^0.2.0", - "tildify": "^2.0.0", - "titlecase": "^1.1.2", - "warehouse": "^4.0.0" - } - }, - "hexo-cli": { - "version": "4.2.0", - "resolved": "https://registry.npm.taobao.org/hexo-cli/download/hexo-cli-4.2.0.tgz", - "integrity": "sha1-dGR7xyqQuc6/BK9bxwg5qygaMF0=", - "requires": { - "abbrev": "^1.1.1", - "bluebird": "^3.5.5", - "chalk": "^4.0.0", - "command-exists": "^1.2.8", - "hexo-fs": "^3.0.1", - "hexo-log": "^2.0.0", - "hexo-util": "^2.0.0", - "minimist": "^1.2.5", - "resolve": "^1.11.0", - "tildify": "^2.0.0" - } - }, - "hexo-component-inferno": { - "version": "0.10.5", - "resolved": "https://registry.npm.taobao.org/hexo-component-inferno/download/hexo-component-inferno-0.10.5.tgz", - "integrity": "sha1-D7+kJ6U54jQOW4s3CI3xLcyBqYg=", - "requires": { - "ajv": "^6.10.2", - "inferno": "^7.3.3", - "inferno-create-element": "^7.3.3", - "js-yaml": "^3.13.1", - "moment": "^2.24.0", - "semver": "^7.1.1" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "hexo-deployer-git": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hexo-deployer-git/-/hexo-deployer-git-4.0.0.tgz", - "integrity": "sha512-28t1Q+4taB/UaBAP52W3mD/wcCwa2y2zBieUfBJFBZudbmVgiKJB5YedYILeyI5QByaUKAOwoupmdTbocdQ+CQ==", - "requires": { - "bluebird": "^3.7.2", - "hexo-fs": "^4.0.0", - "hexo-util": "^2.7.0", - "luxon": "^3.0.4", - "nunjucks": "^3.2.3", - "picocolors": "^1.0.0" - }, - "dependencies": { - "hexo-fs": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/hexo-fs/-/hexo-fs-4.1.1.tgz", - "integrity": "sha512-aDysNTyv8ElcerbFVbPLRXnYt+QDY6gAOZZ5DLbCxudY0Ywppqd+uZ03gZ2BDypIBvmNB27WYWYz76M+Yv/YXw==", - "requires": { - "bluebird": "^3.7.2", - "chokidar": "^3.5.3", - "graceful-fs": "^4.2.10", - "hexo-util": "^2.7.0" - } - } - } - }, - "hexo-filter-mermaid-diagrams": { - "version": "1.0.5", - "resolved": "https://registry.npm.taobao.org/hexo-filter-mermaid-diagrams/download/hexo-filter-mermaid-diagrams-1.0.5.tgz", - "integrity": "sha1-HjL8TkbquQzzo3W1yDCp8q7xZA8=", - "requires": { - "deep-assign": "^2.0.0" - } - }, - "hexo-front-matter": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/hexo-front-matter/download/hexo-front-matter-2.0.0.tgz", - "integrity": "sha1-A/Oanu2xbFRI27O+x9/yxOIkAGw=", - "requires": { - "js-yaml": "^3.13.1" - } - }, - "hexo-fs": { - "version": "3.1.0", - "resolved": "https://registry.npm.taobao.org/hexo-fs/download/hexo-fs-3.1.0.tgz", - "integrity": "sha1-IFL/csaM2ixXeoeqDqeYCuUD+pk=", - "requires": { - "bluebird": "^3.5.1", - "chokidar": "^3.0.0", - "graceful-fs": "^4.1.11", - "hexo-util": "^2.0.0" - } - }, - "hexo-generator-archive": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/hexo-generator-archive/download/hexo-generator-archive-1.0.0.tgz", - "integrity": "sha1-rSr7EiMqZeL4YI/Byj8ZFi+2N4Y=", - "requires": { - "hexo-pagination": "1.0.0" - } - }, - "hexo-generator-category": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/hexo-generator-category/download/hexo-generator-category-1.0.0.tgz", - "integrity": "sha1-zSorA+sybaPvgh1uKUCEiMwTLrU=", - "requires": { - "hexo-pagination": "1.0.0" - } - }, - "hexo-generator-feed": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/hexo-generator-feed/download/hexo-generator-feed-3.0.0.tgz", - "integrity": "sha1-QSbvXjCCZMQlmfsO/a+I7RH6WZ4=", - "requires": { - "hexo-util": "^2.1.0", - "nunjucks": "^3.0.0" - } - }, - "hexo-generator-index": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/hexo-generator-index/download/hexo-generator-index-2.0.0.tgz", - "integrity": "sha1-FecfTSjunSFy4td+jR26BIgu3I8=", - "requires": { - "hexo-pagination": "1.0.0", - "timsort": "^0.3.0" - } - }, - "hexo-generator-tag": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/hexo-generator-tag/download/hexo-generator-tag-1.0.0.tgz", - "integrity": "sha1-VOwj3pQJx1WE6oHjYFelkDGwIvE=", - "requires": { - "hexo-pagination": "1.0.0" - } - }, - "hexo-i18n": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/hexo-i18n/download/hexo-i18n-1.0.0.tgz", - "integrity": "sha1-eYP7OjE+kGFbhN2PqUanHEie9b0=", - "requires": { - "sprintf-js": "^1.0.3" - } - }, - "hexo-log": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/hexo-log/download/hexo-log-2.0.0.tgz", - "integrity": "sha1-HcyOlgApsIshmU/ixWxOPWNG6JM=", - "requires": { - "chalk": "^4.0.0" - } - }, - "hexo-pagination": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexo-pagination/-/hexo-pagination-1.0.0.tgz", - "integrity": "sha512-miEVFgxchPr2qNWxw0JWpJ9R/Yaf7HjHBZVjvCCcqfbsLyYtCvIfJDxcEwz1sDOC/fLzYPqNnhUI73uNxBHRSA==" - }, - "hexo-renderer-ejs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hexo-renderer-ejs/-/hexo-renderer-ejs-2.0.0.tgz", - "integrity": "sha512-qCjE1IdwgDgv65qyb0KMVCwCdSVAkH0vwAe9XihjvaKWkmb9dtt8DgErOdqCXn0HReSyWiEVP2BrLRj3gyHwOQ==", - "requires": { - "ejs": "^3.1.6" - } - }, - "hexo-renderer-inferno": { - "version": "0.1.3", - "resolved": "https://registry.npm.taobao.org/hexo-renderer-inferno/download/hexo-renderer-inferno-0.1.3.tgz", - "integrity": "sha1-moOs80LBReeB5IEilvZteOLY8j4=", - "requires": { - "@babel/core": "^7.7.7", - "@babel/preset-env": "^7.7.7", - "@babel/register": "^7.7.7", - "babel-plugin-inferno": "^6.1.0", - "inferno": "^7.3.3", - "inferno-create-element": "^7.3.3", - "inferno-server": "^7.3.3" - } - }, - "hexo-renderer-less": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/hexo-renderer-less/download/hexo-renderer-less-1.0.0.tgz", - "integrity": "sha1-BfJGcezNBAxgoa1knaHPtN8WS24=", - "requires": { - "less": "^3.9.0" - } - }, - "hexo-renderer-marked": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/hexo-renderer-marked/-/hexo-renderer-marked-6.1.0.tgz", - "integrity": "sha512-KuVuaYFyKfbJj5dtwoWMwTNbe7rk+UuVR/tGzLXbQ05AWO5ob6xpFHbfeSyzSDt4ccYLzaKJBRduDWHd2f6bSQ==", - "requires": { - "dompurify": "^3.0.3", - "hexo-util": "^3.0.1", - "jsdom": "^20.0.1", - "marked": "^4.3.0" - }, - "dependencies": { - "hexo-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/hexo-util/-/hexo-util-3.0.1.tgz", - "integrity": "sha512-ri3WsEUWSfrjydYPeOaWvrYIzfVBiaXUy0846051MkuJxBcNtG2o87q0KFGiniSMmi0XxLQhSl415anU3+FFlA==", - "requires": { - "bluebird": "^3.7.2", - "camel-case": "^4.1.2", - "cross-spawn": "^7.0.3", - "deepmerge": "^4.2.2", - "highlight.js": "^11.6.0", - "htmlparser2": "^8.0.1", - "prismjs": "^1.29.0", - "strip-indent": "^3.0.0" - } - } - } - }, - "hexo-renderer-stylus": { - "version": "2.0.1", - "resolved": "https://registry.npm.taobao.org/hexo-renderer-stylus/download/hexo-renderer-stylus-2.0.1.tgz", - "integrity": "sha1-xlVm6X6QWKUuRsgrDEXPVh9LlMs=", - "requires": { - "nib": "^1.1.2", - "stylus": "^0.54.8" - } - }, - "hexo-server": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/hexo-server/download/hexo-server-2.0.0.tgz", - "integrity": "sha1-GtksyKfTUo/sb/pl/4RT8kL3mD8=", - "requires": { - "bluebird": "^3.5.5", - "chalk": "^4.0.0", - "compression": "^1.7.4", - "connect": "^3.7.0", - "mime": "^2.4.3", - "morgan": "^1.9.1", - "open": "^7.0.0", - "serve-static": "^1.14.1" - } - }, - "hexo-theme-icarus": { - "version": "4.1.2", - "resolved": "https://registry.npm.taobao.org/hexo-theme-icarus/download/hexo-theme-icarus-4.1.2.tgz", - "integrity": "sha1-WJdAbVO3MKk9l1vHje5BFNzwQ4M=", - "requires": { - "deepmerge": "^4.2.2", - "hexo-component-inferno": "^0.10.5", - "inferno": "^7.3.3", - "inferno-create-element": "^7.3.3", - "moment": "^2.22.2", - "semver": ">=5.0.0" - } - }, - "hexo-util": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/hexo-util/-/hexo-util-2.7.0.tgz", - "integrity": "sha512-hQM3h34nhDg0bSe/Tg1lnpODvNkz7h2u0+lZGzlKL0Oufp+5KCAEUX9wal7/xC7ax3/cwEn8IuoU75kNpZLpJQ==", - "requires": { - "bluebird": "^3.5.2", - "camel-case": "^4.0.0", - "cross-spawn": "^7.0.0", - "deepmerge": "^4.2.2", - "highlight.js": "^11.0.1", - "htmlparser2": "^7.0.0", - "prismjs": "^1.17.1", - "strip-indent": "^3.0.0" - }, - "dependencies": { - "entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==" - }, - "htmlparser2": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" - } - } - } - }, - "highlight.js": { - "version": "11.8.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.8.0.tgz", - "integrity": "sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==" - }, - "html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "requires": { - "whatwg-encoding": "^2.0.0" - } - }, - "htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - }, - "dependencies": { - "dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - } - }, - "domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "requires": { - "domelementtype": "^2.3.0" - } - }, - "domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "requires": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - } - }, - "entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" - } - } - }, - "http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npm.taobao.org/http-errors/download/http-errors-1.7.3.tgz", - "integrity": "sha1-bGGeT5xgMIw4UZSYwU+7EKrOuwY=", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "dependencies": { - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npm.taobao.org/depd/download/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - } - } - }, - "http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "requires": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - } - }, - "https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - }, - "image-size": { - "version": "0.5.5", - "resolved": "https://registry.npm.taobao.org/image-size/download/image-size-0.5.5.tgz?cache=0&sync_timestamp=1615833926443&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fimage-size%2Fdownload%2Fimage-size-0.5.5.tgz", - "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", - "optional": true - }, - "inferno": { - "version": "7.4.8", - "resolved": "https://registry.npm.taobao.org/inferno/download/inferno-7.4.8.tgz", - "integrity": "sha1-DVUEdT55kDsOS77/dvwR/Quf/pI=", - "requires": { - "inferno-shared": "7.4.8", - "inferno-vnode-flags": "7.4.8", - "opencollective-postinstall": "^2.0.3" - } - }, - "inferno-create-element": { - "version": "7.4.8", - "resolved": "https://registry.npm.taobao.org/inferno-create-element/download/inferno-create-element-7.4.8.tgz", - "integrity": "sha1-d7vyQohkXDWc9ltIIaOTjGU3614=", - "requires": { - "inferno": "7.4.8" - } - }, - "inferno-server": { - "version": "7.4.8", - "resolved": "https://registry.npm.taobao.org/inferno-server/download/inferno-server-7.4.8.tgz", - "integrity": "sha1-Ma18IJKroW5CNKECzErD80bRfuQ=", - "requires": { - "inferno": "7.4.8" - } - }, - "inferno-shared": { - "version": "7.4.8", - "resolved": "https://registry.npm.taobao.org/inferno-shared/download/inferno-shared-7.4.8.tgz", - "integrity": "sha1-K1VKNmg7dwM5AIdJCW2XBIRt0zc=" - }, - "inferno-vnode-flags": { - "version": "7.4.8", - "resolved": "https://registry.npm.taobao.org/inferno-vnode-flags/download/inferno-vnode-flags-7.4.8.tgz", - "integrity": "sha1-J11w48iys/TrVgQcybjIMs4fsm0=" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npm.taobao.org/inflight/download/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.4.tgz", - "integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=" - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npm.taobao.org/is-binary-path/download/is-binary-path-2.1.0.tgz", - "integrity": "sha1-6h9/O4DwZCNug0cPhsCcJU+0Wwk=", - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.2.0", - "resolved": "https://registry.npm.taobao.org/is-core-module/download/is-core-module-2.2.0.tgz?cache=0&sync_timestamp=1606411931775&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-core-module%2Fdownload%2Fis-core-module-2.2.0.tgz", - "integrity": "sha1-lwN+89UiJNhRY/VZeytj2a/tmBo=", - "requires": { - "has": "^1.0.3" - } - }, - "is-docker": { - "version": "2.1.1", - "resolved": "https://registry.npm.taobao.org/is-docker/download/is-docker-2.1.1.tgz?cache=0&sync_timestamp=1596559619667&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-docker%2Fdownload%2Fis-docker-2.1.1.tgz", - "integrity": "sha1-QSWojkTkUNOE4JBH7eca3C0UQVY=" - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npm.taobao.org/is-extglob/download/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npm.taobao.org/is-glob/download/is-glob-4.0.1.tgz", - "integrity": "sha1-dWfb6fL14kZ7x3q4PEopSCQHpdw=", - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npm.taobao.org/is-number/download/is-number-7.0.0.tgz", - "integrity": "sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=" - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/is-obj/download/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" - }, - "is-plain-object": { - "version": "3.0.1", - "resolved": "https://registry.npm.taobao.org/is-plain-object/download/is-plain-object-3.0.1.tgz?cache=0&sync_timestamp=1599667374673&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-plain-object%2Fdownload%2Fis-plain-object-3.0.1.tgz", - "integrity": "sha1-Zi2S0kwKpDAkB7DUXSHyJRyF+Fs=" - }, - "is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" - }, - "is-what": { - "version": "3.14.1", - "resolved": "https://registry.npm.taobao.org/is-what/download/is-what-3.14.1.tgz", - "integrity": "sha1-4SIvRt3ahd6tD9HJ3xMXYOd3VcE=" - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npm.taobao.org/is-wsl/download/is-wsl-2.2.0.tgz", - "integrity": "sha1-dKTHbnfKn9P5MvKQwX6jJs0VcnE=", - "requires": { - "is-docker": "^2.0.0" - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/isexe/download/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "jake": { - "version": "10.8.7", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", - "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", - "requires": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "dependencies": { - "async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npm.taobao.org/js-tokens/download/js-tokens-4.0.0.tgz", - "integrity": "sha1-GSA/tZmR35jjoocFDUZHzerzJJk=" - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npm.taobao.org/js-yaml/download/js-yaml-3.14.1.tgz?cache=0&sync_timestamp=1609680193223&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjs-yaml%2Fdownload%2Fjs-yaml-3.14.1.tgz", - "integrity": "sha1-2ugS/bOCX6MGYJqHFzg8UMNqBTc=", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsdom": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", - "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", - "requires": { - "abab": "^2.0.6", - "acorn": "^8.8.1", - "acorn-globals": "^7.0.0", - "cssom": "^0.5.0", - "cssstyle": "^2.3.0", - "data-urls": "^3.0.2", - "decimal.js": "^10.4.2", - "domexception": "^4.0.0", - "escodegen": "^2.0.0", - "form-data": "^4.0.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.1", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.2", - "parse5": "^7.1.1", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.1.2", - "w3c-xmlserializer": "^4.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^2.0.0", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0", - "ws": "^8.11.0", - "xml-name-validator": "^4.0.0" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npm.taobao.org/jsesc/download/jsesc-2.5.2.tgz?cache=0&sync_timestamp=1603891236757&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjsesc%2Fdownload%2Fjsesc-2.5.2.tgz", - "integrity": "sha1-gFZNLkg9rPbo7yCWUKZ98/DCg6Q=" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz", - "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=" - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" - }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npm.taobao.org/jsonparse/download/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" - }, - "JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npm.taobao.org/JSONStream/download/JSONStream-1.3.5.tgz", - "integrity": "sha1-MgjB8I06TZkmGrZPkjArwV4RHKA=", - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - } - }, - "less": { - "version": "3.13.1", - "resolved": "https://registry.npm.taobao.org/less/download/less-3.13.1.tgz", - "integrity": "sha1-DryR0qDpwMZzW4PUlrCrBYMHeQk=", - "requires": { - "copy-anything": "^2.0.1", - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "make-dir": "^2.1.0", - "mime": "^1.4.1", - "native-request": "^1.0.5", - "source-map": "~0.6.0", - "tslib": "^1.10.0" - }, - "dependencies": { - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npm.taobao.org/mime/download/mime-1.6.0.tgz?cache=0&sync_timestamp=1613584832862&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmime%2Fdownload%2Fmime-1.6.0.tgz", - "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=", - "optional": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", - "optional": true - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npm.taobao.org/tslib/download/tslib-1.14.1.tgz?cache=0&sync_timestamp=1609887769758&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftslib%2Fdownload%2Ftslib-1.14.1.tgz", - "integrity": "sha1-zy04vcNKE0vK8QkcQfZhni9nLQA=" - } - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/locate-path/download/locate-path-3.0.0.tgz", - "integrity": "sha1-2+w7OrdZdYBxtY/ln8QYca8hQA4=", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npm.taobao.org/lodash/download/lodash-4.17.21.tgz?cache=0&sync_timestamp=1613835891789&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash%2Fdownload%2Flodash-4.17.21.tgz", - "integrity": "sha1-Z5WRxWTDv/quhFTPCz3zcMPWkRw=" - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npm.taobao.org/lodash.debounce/download/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" - }, - "lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npm.taobao.org/lower-case/download/lower-case-2.0.2.tgz", - "integrity": "sha1-b6I3xj29xKgsoP2ILkci3F5jTig=", - "requires": { - "tslib": "^2.0.3" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npm.taobao.org/lru-cache/download/lru-cache-6.0.0.tgz?cache=0&sync_timestamp=1594427606170&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flru-cache%2Fdownload%2Flru-cache-6.0.0.tgz", - "integrity": "sha1-bW/mVw69lqr5D8rR2vo7JWbbOpQ=", - "requires": { - "yallist": "^4.0.0" - } - }, - "luxon": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz", - "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==" - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npm.taobao.org/make-dir/download/make-dir-2.1.0.tgz", - "integrity": "sha1-XwMQ4YuL6JjMBwCSlaMK5B6R5vU=", - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "dependencies": { - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" - } - } - }, - "marked": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==" - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npm.taobao.org/micromatch/download/micromatch-4.0.2.tgz", - "integrity": "sha1-T8sJmb+fvC/L3SEvbWKbmlbDklk=", - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "mime": { - "version": "2.5.2", - "resolved": "https://registry.npm.taobao.org/mime/download/mime-2.5.2.tgz?cache=0&sync_timestamp=1613584832862&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmime%2Fdownload%2Fmime-2.5.2.tgz", - "integrity": "sha1-bj3GzCuVEGQ4MOXxnVy3U9pe6r4=" - }, - "mime-db": { - "version": "1.46.0", - "resolved": "https://registry.npm.taobao.org/mime-db/download/mime-db-1.46.0.tgz?cache=0&sync_timestamp=1613194712759&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmime-db%2Fdownload%2Fmime-db-1.46.0.tgz", - "integrity": "sha1-Ymd0in95lZTePLyM3pHe80lmHO4=" - }, - "mime-types": { - "version": "2.1.29", - "resolved": "https://registry.npm.taobao.org/mime-types/download/mime-types-2.1.29.tgz", - "integrity": "sha1-HUq3faZLkfX3JInfKSNlY3VLsbI=", - "requires": { - "mime-db": "1.46.0" - } - }, - "min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/min-indent/download/min-indent-1.0.1.tgz", - "integrity": "sha1-pj9oFnOzBXH76LwlaGrnRu76mGk=" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npm.taobao.org/minimatch/download/minimatch-3.0.4.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fminimatch%2Fdownload%2Fminimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npm.taobao.org/mkdirp/download/mkdirp-1.0.4.tgz?cache=0&sync_timestamp=1587535418745&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmkdirp%2Fdownload%2Fmkdirp-1.0.4.tgz", - "integrity": "sha1-PrXtYmInVteaXw4qIh3+utdcL34=" - }, - "moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" - }, - "moment-timezone": { - "version": "0.5.37", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.37.tgz", - "integrity": "sha512-uEDzDNFhfaywRl+vwXxffjjq1q0Vzr+fcQpQ1bU0kbzorfS7zVtZnCnGc8mhWmF39d4g4YriF6kwA75mJKE/Zg==", - "requires": { - "moment": ">= 2.9.0" - } - }, - "morgan": { - "version": "1.10.0", - "resolved": "https://registry.npm.taobao.org/morgan/download/morgan-1.10.0.tgz?cache=0&sync_timestamp=1584728007537&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmorgan%2Fdownload%2Fmorgan-1.10.0.tgz", - "integrity": "sha1-CRd4q8H8R801CYJGU9rh+qtrF9c=", - "requires": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&sync_timestamp=1607566600214&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&sync_timestamp=1607433905910&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz?cache=0&sync_timestamp=1607433905910&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.1.2.tgz", - "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=" - }, - "native-request": { - "version": "1.0.8", - "resolved": "https://registry.npm.taobao.org/native-request/download/native-request-1.0.8.tgz", - "integrity": "sha1-j2a/YG4PfqJ8DlmV6y9dA+M65vs=", - "optional": true - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npm.taobao.org/negotiator/download/negotiator-0.6.2.tgz", - "integrity": "sha1-/qz3zPUlp3rpY0Q2pkiD/+yjRvs=" - }, - "nib": { - "version": "1.1.2", - "resolved": "https://registry.npm.taobao.org/nib/download/nib-1.1.2.tgz", - "integrity": "sha1-amnt5AgblcDe+L4CSkyK4MLLtsc=", - "requires": { - "stylus": "0.54.5" - }, - "dependencies": { - "css-parse": { - "version": "1.7.0", - "resolved": "https://registry.npm.taobao.org/css-parse/download/css-parse-1.7.0.tgz", - "integrity": "sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs=" - }, - "glob": { - "version": "7.0.6", - "resolved": "https://registry.npm.taobao.org/glob/download/glob-7.0.6.tgz", - "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.2", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npm.taobao.org/mkdirp/download/mkdirp-0.5.5.tgz?cache=0&sync_timestamp=1587535418745&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmkdirp%2Fdownload%2Fmkdirp-0.5.5.tgz", - "integrity": "sha1-2Rzv1i0UNsoPQWIOJRKI1CAJne8=", - "requires": { - "minimist": "^1.2.5" - } - }, - "sax": { - "version": "0.5.8", - "resolved": "https://registry.npm.taobao.org/sax/download/sax-0.5.8.tgz", - "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=" - }, - "source-map": { - "version": "0.1.43", - "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.1.43.tgz", - "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", - "requires": { - "amdefine": ">=0.0.4" - } - }, - "stylus": { - "version": "0.54.5", - "resolved": "https://registry.npm.taobao.org/stylus/download/stylus-0.54.5.tgz?cache=0&sync_timestamp=1594901587949&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstylus%2Fdownload%2Fstylus-0.54.5.tgz", - "integrity": "sha1-QrlWCTHKcJDOhRWnmLqeaqPW3Hk=", - "requires": { - "css-parse": "1.7.x", - "debug": "*", - "glob": "7.0.x", - "mkdirp": "0.5.x", - "sax": "0.5.x", - "source-map": "0.1.x" - } - } - } - }, - "no-case": { - "version": "3.0.4", - "resolved": "https://registry.npm.taobao.org/no-case/download/no-case-3.0.4.tgz", - "integrity": "sha1-02H9XJgA9VhVGoNp/A3NRmK2Ek0=", - "requires": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node-modules-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/node-modules-regexp/download/node-modules-regexp-1.0.0.tgz", - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=" - }, - "node-releases": { - "version": "1.1.72", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz", - "integrity": "sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==" - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" - }, - "nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "requires": { - "boolbase": "^1.0.0" - } - }, - "nunjucks": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz", - "integrity": "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==", - "requires": { - "a-sync-waterfall": "^1.0.0", - "asap": "^2.0.3", - "commander": "^5.1.0" - } - }, - "nwsapi": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", - "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==" - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npm.taobao.org/object-keys/download/object-keys-1.1.1.tgz", - "integrity": "sha1-HEfyct8nfzsdrwYWd9nILiMixg4=" - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npm.taobao.org/object.assign/download/object.assign-4.1.2.tgz?cache=0&sync_timestamp=1604115263846&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fobject.assign%2Fdownload%2Fobject.assign-4.1.2.tgz", - "integrity": "sha1-DtVKNC7Os3s4/3brgxoOeIy2OUA=", - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npm.taobao.org/on-finished/download/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npm.taobao.org/on-headers/download/on-headers-1.0.2.tgz", - "integrity": "sha1-dysK5qqlJcOZ5Imt+tkMQD6zwo8=" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npm.taobao.org/once/download/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "open": { - "version": "7.4.2", - "resolved": "https://registry.npm.taobao.org/open/download/open-7.4.2.tgz", - "integrity": "sha1-uBR+Jtzz5CYxbHMAif1x7dKcIyE=", - "requires": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - } - }, - "opencollective-postinstall": { - "version": "2.0.3", - "resolved": "https://registry.npm.taobao.org/opencollective-postinstall/download/opencollective-postinstall-2.0.3.tgz", - "integrity": "sha1-eg//l49tv6TQBiOPusmO1BmMMlk=" - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npm.taobao.org/p-limit/download/p-limit-2.3.0.tgz", - "integrity": "sha1-PdM8ZHohT9//2DWTPrCG2g3CHbE=", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/p-locate/download/p-locate-3.0.0.tgz", - "integrity": "sha1-Mi1poFwCZLJZl9n0DNiokasAZKQ=", - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npm.taobao.org/p-try/download/p-try-2.2.0.tgz", - "integrity": "sha1-yyhoVA4xPWHeWPr741zpAE1VQOY=" - }, - "parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "requires": { - "entities": "^4.4.0" - }, - "dependencies": { - "entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" - } - } - }, - "parse5-htmlparser2-tree-adapter": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", - "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", - "requires": { - "domhandler": "^5.0.2", - "parse5": "^7.0.0" - }, - "dependencies": { - "domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "requires": { - "domelementtype": "^2.3.0" - } - } - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npm.taobao.org/parseurl/download/parseurl-1.3.3.tgz", - "integrity": "sha1-naGee+6NEt/wUT7Vt2lXeTvC6NQ=" - }, - "pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npm.taobao.org/pascal-case/download/pascal-case-3.1.2.tgz", - "integrity": "sha1-tI4O8rmOIF58Ha50fQsVCCN2YOs=", - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/path-exists/download/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/path-is-absolute/download/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npm.taobao.org/path-key/download/path-key-3.1.1.tgz", - "integrity": "sha1-WB9q3mWMu6ZaDTOA3ndTKVBU83U=" - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npm.taobao.org/picomatch/download/picomatch-2.2.2.tgz?cache=0&sync_timestamp=1584791322335&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpicomatch%2Fdownload%2Fpicomatch-2.2.2.tgz", - "integrity": "sha1-IfMz6ba46v8CRo9RRupAbTRfTa0=" - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npm.taobao.org/pify/download/pify-4.0.1.tgz", - "integrity": "sha1-SyzSXFDVmHNcUCkiJP2MbfQeMjE=" - }, - "pirates": { - "version": "4.0.1", - "resolved": "https://registry.npm.taobao.org/pirates/download/pirates-4.0.1.tgz", - "integrity": "sha1-ZDqSyviUVm+RsrmG0sZpUKji+4c=", - "requires": { - "node-modules-regexp": "^1.0.0" - } - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/pkg-dir/download/pkg-dir-3.0.0.tgz", - "integrity": "sha1-J0kCDyOe2ZCIGx9xIQ1R62UjvqM=", - "requires": { - "find-up": "^3.0.0" - } - }, - "pretty-hrtime": { - "version": "1.0.3", - "resolved": "https://registry.npm.taobao.org/pretty-hrtime/download/pretty-hrtime-1.0.3.tgz", - "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=" - }, - "prismjs": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", - "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==" - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/prr/download/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "optional": true - }, - "psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npm.taobao.org/punycode/download/punycode-2.1.1.tgz", - "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=" - }, - "querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npm.taobao.org/range-parser/download/range-parser-1.2.1.tgz", - "integrity": "sha1-PPNwI9GZ4cJNGlW4SADC8+ZGgDE=" - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "requires": { - "picomatch": "^2.2.1" - } - }, - "regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npm.taobao.org/regenerate/download/regenerate-1.4.2.tgz?cache=0&sync_timestamp=1604220147658&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fregenerate%2Fdownload%2Fregenerate-1.4.2.tgz", - "integrity": "sha1-uTRtiCfo9aMve6KWN9OYtpAUhIo=" - }, - "regenerate-unicode-properties": { - "version": "8.2.0", - "resolved": "https://registry.npm.taobao.org/regenerate-unicode-properties/download/regenerate-unicode-properties-8.2.0.tgz", - "integrity": "sha1-5d5xEdZV57pgwFfb6f83yH5lzew=", - "requires": { - "regenerate": "^1.4.0" - } - }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npm.taobao.org/regenerator-runtime/download/regenerator-runtime-0.13.7.tgz", - "integrity": "sha1-ysLazIoepnX+qrrriugziYrkb1U=" - }, - "regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npm.taobao.org/regenerator-transform/download/regenerator-transform-0.14.5.tgz", - "integrity": "sha1-yY2hVGg2ccnE3LFuznNlF+G3/rQ=", - "requires": { - "@babel/runtime": "^7.8.4" - } - }, - "regexpu-core": { - "version": "4.7.1", - "resolved": "https://registry.npm.taobao.org/regexpu-core/download/regexpu-core-4.7.1.tgz", - "integrity": "sha1-LepamgcjMpj78NuR+pq8TG4PitY=", - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.2.0", - "regjsgen": "^0.5.1", - "regjsparser": "^0.6.4", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.2.0" - } - }, - "regjsgen": { - "version": "0.5.2", - "resolved": "https://registry.npm.taobao.org/regjsgen/download/regjsgen-0.5.2.tgz", - "integrity": "sha1-kv8pX7He7L9uzaslQ9IH6RqjNzM=" - }, - "regjsparser": { - "version": "0.6.7", - "resolved": "https://registry.npm.taobao.org/regjsparser/download/regjsparser-0.6.7.tgz", - "integrity": "sha1-wAFk4eZxPC4+5kHxcBxLeqCn+Gw=", - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npm.taobao.org/jsesc/download/jsesc-0.5.0.tgz?cache=0&sync_timestamp=1603891236757&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjsesc%2Fdownload%2Fjsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" - } - } - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" - }, - "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npm.taobao.org/resolve/download/resolve-1.20.0.tgz", - "integrity": "sha1-YpoBP7P3B1XW8LeTXMHCxTeLGXU=", - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npm.taobao.org/resolve-url/download/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" - }, - "rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npm.taobao.org/rfdc/download/rfdc-1.3.0.tgz", - "integrity": "sha1-0LfEQasnINBdxM8m4ByJYx2doIs=" - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.2.tgz", - "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz", - "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npm.taobao.org/sax/download/sax-1.2.4.tgz", - "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=" - }, - "saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "requires": { - "xmlchars": "^2.2.0" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npm.taobao.org/send/download/send-0.17.1.tgz", - "integrity": "sha1-wdiwWfeQD3Rm3Uk4vcROEd2zdsg=", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&sync_timestamp=1607566600214&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&sync_timestamp=1607433905910&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npm.taobao.org/depd/download/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npm.taobao.org/mime/download/mime-1.6.0.tgz?cache=0&sync_timestamp=1613584832862&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmime%2Fdownload%2Fmime-1.6.0.tgz", - "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=" - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.1.tgz?cache=0&sync_timestamp=1607433905910&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.1.1.tgz", - "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=" - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npm.taobao.org/serve-static/download/serve-static-1.14.1.tgz", - "integrity": "sha1-Zm5jbcTwEPfvKZcKiKZ0MgiYsvk=", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npm.taobao.org/setprototypeof/download/setprototypeof-1.1.1.tgz", - "integrity": "sha1-fpWsskqpL1iF4KvvW6ExMw1K5oM=" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/shebang-command/download/shebang-command-2.0.0.tgz", - "integrity": "sha1-zNCvT4g1+9wmW4JGGq8MNmY/NOo=", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/shebang-regex/download/shebang-regex-3.0.0.tgz", - "integrity": "sha1-rhbxZE2HPsrYQ7AwexQzYtTEIXI=" - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - }, - "source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npm.taobao.org/source-map-resolve/download/source-map-resolve-0.5.3.tgz", - "integrity": "sha1-GQhmvs51U+H48mei7oLGBrVQmho=", - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npm.taobao.org/source-map-support/download/source-map-support-0.5.19.tgz", - "integrity": "sha1-qYti+G3K9PZzmWSMCFKRq56P7WE=", - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=" - } - } - }, - "source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npm.taobao.org/source-map-url/download/source-map-url-0.4.1.tgz?cache=0&sync_timestamp=1612210621814&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsource-map-url%2Fdownload%2Fsource-map-url-0.4.1.tgz", - "integrity": "sha1-CvZmBadFpaL5HPG7+KevvCg97FY=" - }, - "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npm.taobao.org/sprintf-js/download/sprintf-js-1.1.2.tgz", - "integrity": "sha1-2hdlJiv4wPVxdJ8q1sJjACB65nM=" - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npm.taobao.org/statuses/download/statuses-1.5.0.tgz?cache=0&sync_timestamp=1609654218656&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstatuses%2Fdownload%2Fstatuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npm.taobao.org/strip-ansi/download/strip-ansi-6.0.0.tgz", - "integrity": "sha1-CxVx3XZpzNTz4G4U7x7tJiJa5TI=", - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/strip-indent/download/strip-indent-3.0.0.tgz", - "integrity": "sha1-wy4c7pQLazQyx3G8LFS8znPNMAE=", - "requires": { - "min-indent": "^1.0.0" - } - }, - "stylus": { - "version": "0.54.8", - "resolved": "https://registry.npm.taobao.org/stylus/download/stylus-0.54.8.tgz?cache=0&sync_timestamp=1594901587949&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstylus%2Fdownload%2Fstylus-0.54.8.tgz", - "integrity": "sha1-PaPmWWa8Vnp7BEv+DuzmU+CZ0Uc=", - "requires": { - "css-parse": "~2.0.0", - "debug": "~3.1.0", - "glob": "^7.1.6", - "mkdirp": "~1.0.4", - "safer-buffer": "^2.1.2", - "sax": "~1.2.4", - "semver": "^6.3.0", - "source-map": "^0.7.3" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz?cache=0&sync_timestamp=1607566600214&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&sync_timestamp=1607433905910&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.7.3.tgz", - "integrity": "sha1-UwL4FpAxc1ImVECS5kmB91F1A4M=" - } - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-7.2.0.tgz?cache=0&sync_timestamp=1611394043517&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-7.2.0.tgz", - "integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=", - "requires": { - "has-flag": "^4.0.0" - } - }, - "symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npm.taobao.org/text-table/download/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npm.taobao.org/through/download/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "tildify": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/tildify/download/tildify-2.0.0.tgz", - "integrity": "sha1-8gXzZ01nfOaYtwZ6melJzgO0dUo=" - }, - "timsort": { - "version": "0.3.0", - "resolved": "https://registry.npm.taobao.org/timsort/download/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" - }, - "titlecase": { - "version": "1.1.3", - "resolved": "https://registry.npm.taobao.org/titlecase/download/titlecase-1.1.3.tgz", - "integrity": "sha1-/G1l/1grBgJBB2jvGgm3BQYxPcM=" - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/to-fast-properties/download/to-fast-properties-2.0.0.tgz?cache=0&sync_timestamp=1580550651593&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fto-fast-properties%2Fdownload%2Fto-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npm.taobao.org/to-regex-range/download/to-regex-range-5.0.1.tgz", - "integrity": "sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ=", - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/toidentifier/download/toidentifier-1.0.0.tgz", - "integrity": "sha1-fhvjRw8ed5SLxD2Uo8j013UrpVM=" - }, - "tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", - "requires": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - } - }, - "tr46": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", - "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", - "requires": { - "punycode": "^2.1.1" - } - }, - "tslib": { - "version": "2.1.0", - "resolved": "https://registry.npm.taobao.org/tslib/download/tslib-2.1.0.tgz?cache=0&sync_timestamp=1609887769758&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftslib%2Fdownload%2Ftslib-2.1.0.tgz", - "integrity": "sha1-2mCGDxwuyqVwOrfTm8Bba/mIuXo=" - }, - "unicode-canonical-property-names-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npm.taobao.org/unicode-canonical-property-names-ecmascript/download/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha1-JhmADEyCWADv3YNDr33Zkzy+KBg=" - }, - "unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npm.taobao.org/unicode-match-property-ecmascript/download/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha1-jtKjJWmWG86SJ9Cc0/+7j+1fAgw=", - "requires": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "1.2.0", - "resolved": "https://registry.npm.taobao.org/unicode-match-property-value-ecmascript/download/unicode-match-property-value-ecmascript-1.2.0.tgz?cache=0&sync_timestamp=1583948699384&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Funicode-match-property-value-ecmascript%2Fdownload%2Funicode-match-property-value-ecmascript-1.2.0.tgz", - "integrity": "sha1-DZH2AO7rMJaqlisdb8iIduZOpTE=" - }, - "unicode-property-aliases-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npm.taobao.org/unicode-property-aliases-ecmascript/download/unicode-property-aliases-ecmascript-1.1.0.tgz", - "integrity": "sha1-3Vepn2IHvt/0Yoq++5TFDblByPQ=" - }, - "universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==" - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/unpipe/download/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npm.taobao.org/uri-js/download/uri-js-4.4.1.tgz?cache=0&sync_timestamp=1610240086113&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Furi-js%2Fdownload%2Furi-js-4.4.1.tgz", - "integrity": "sha1-mxpSWVIlhZ5V9mnZKPiMbFfyp34=", - "requires": { - "punycode": "^2.1.0" - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npm.taobao.org/urix/download/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" - }, - "url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/utils-merge/download/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npm.taobao.org/vary/download/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "w3c-xmlserializer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", - "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", - "requires": { - "xml-name-validator": "^4.0.0" - } - }, - "warehouse": { - "version": "4.0.0", - "resolved": "https://registry.npm.taobao.org/warehouse/download/warehouse-4.0.0.tgz", - "integrity": "sha1-RYMrxD4f0yO9nTyGsnYUMLoNHns=", - "requires": { - "bluebird": "^3.2.2", - "cuid": "^2.1.4", - "graceful-fs": "^4.1.3", - "is-plain-object": "^3.0.0", - "JSONStream": "^1.0.7", - "rfdc": "^1.1.4" - } - }, - "webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" - }, - "whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "requires": { - "iconv-lite": "0.6.3" - } - }, - "whatwg-mimetype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==" - }, - "whatwg-url": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", - "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", - "requires": { - "tr46": "^3.0.0", - "webidl-conversions": "^7.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npm.taobao.org/which/download/which-2.0.2.tgz", - "integrity": "sha1-fGqN0KY2oDJ+ELWckobu6T8/UbE=", - "requires": { - "isexe": "^2.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npm.taobao.org/wrappy/download/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "requires": {} - }, - "xml-name-validator": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", - "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==" - }, - "xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npm.taobao.org/yallist/download/yallist-4.0.0.tgz", - "integrity": "sha1-m7knkNnA7/7GO+c1GeEaNQGaOnI=" - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 37009a1..0000000 --- a/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "hexo-site", - "version": "0.0.0", - "private": true, - "scripts": { - "build": "hexo generate", - "clean": "hexo clean", - "deploy": "hexo deploy", - "server": "cp -ip _config.theme.yml themes/icarus/_config.yml & hexo server" - }, - "hexo": { - "version": "5.4.2" - }, - "dependencies": { - "bulma-stylus": "0.8.0", - "cheerio": "^1.0.0-rc.12", - "hexo": "^5.4.2", - "hexo-deployer-git": "^4.0.0", - "hexo-filter-mermaid-diagrams": "^1.0.5", - "hexo-generator-archive": "^1.0.0", - "hexo-generator-category": "^1.0.0", - "hexo-generator-feed": "^3.0.0", - "hexo-generator-index": "^2.0.0", - "hexo-generator-tag": "^1.0.0", - "hexo-renderer-ejs": "^2.0.0", - "hexo-renderer-inferno": "^0.1.3", - "hexo-renderer-less": "^1.0.0", - "hexo-renderer-marked": "^6.1.0", - "hexo-renderer-stylus": "^2.0.0", - "hexo-server": "^2.0.0", - "hexo-theme-icarus": "^4.1.2" - } -} diff --git a/page/2/index.html b/page/2/index.html new file mode 100644 index 0000000..0415978 --- /dev/null +++ b/page/2/index.html @@ -0,0 +1,99 @@ + +西门闲话

在ubuntu上从零搭建node.js + nginx + mongodb环境

说到后端开发环境,最有名的莫过于LAMP和LNMP,最近由于node.js的强势崛起,越来越多的后端开发也开始试水node.js了。我最近也因为各种原因,前前后后总够构建了好几台node.js + nginx + mongodb的Linux服务器。

+

首先关于Linux服务器,比起CentOS来说,我更加喜欢ubuntu一点。所以无论是阿里云还是一些海外的vps服务器上,我也倾向选用ubuntu服务器,本贴也是基于ubuntu服务器里说明的。

+

1.开始前的一些准备

首先还是需要刷新一下ubuntu的包索引并安装build-essential和libssl-dev这2个包以及curl这个工具。

+
1
2
3
sudo apt-get update
sudo apt-get install build-essential libssl-dev
sudo apt-get isntall curl
+ +

2.安装node.js

关于安装node.js这一点,我不是很推荐使用apt-get 来安装node.js的环境。主要是因为node.js和io.js合并以后,版本迭代速度相当频繁(主要还是因为更多ES6的特性得到了支持)。今后很有可能会有在一台服务器上使用不同版本的node.js的需求。

+

这里推荐一个管理不同版本node.js的工具:nvm,官网: https://github.com/creationix/nvm  。安装nvm,如果前面你安装了curl的话可以

+
1
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
+

如果没有按照curl的话,也可以使用wget来进行安装

+
1
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
+ +

然后nvm就会自动安装到home目录下面的.nvm目录里,并会在.bashrc里自动添加nvm的环境变量。为了让环境变量生效,最简单的方法就是通过ssh或是telnet重新连接你的服务器。

+

安装完nvm后,就可以通过nvm来安装指定版本的node.js了。

+
1
2
3
4
5
# 列出可以安装的node版本号
nvm ls-remote

# 安装指定版本的node (当前最新版本为v5.7.1, LTS版是v4.3.2)
nvm install v4.3.2
+ +

3.安装nginx

由于ubuntu源(尤其是阿里云的源)上的nginx经常不是最新的,如果需要安装最新版本nginx的时候需要手动添加nginx的源。

+
1
2
3
4
5
6
7
8
9
10
11
# 添加nginx的mainline仓库
cd /tmp/ && wget http://nginx.org/keys/nginx_signing.key
sudo apt-key add nginx_signing.key

# 编辑/etc/apt/sources.list.d/nginx.list 添加下面2行内容,井号不需要
# deb http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
# deb-src http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
sudo vi /etc/apt/sources.list.d/nginx.list

# 更新源,并安装nginx
sudo apt-get update && sudo apt-get install nginx
+ +

在编辑/etc/apt/sources.list.d/nginx.list的时候需要注意,“ubuntu代号”需要根据ubuntu服务器的版本不同手动调整的,比如14.04是trusty。通过下面的命令可以获取ubuntu的代号。

+
1
lsb_release -cs
+ +

4.安装mongodb

同样和nginx有同样的问题,要安装最新3.2版本的mongodb也需要手动添加ubuntu的源。

+
1
2
3
4
5
6
7
8
9
10
11
# 导入mongodb的public key
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927

# 生成mongodb的源list
echo "deb http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list

# 更新源
sudo apt-get update

# 安装最新版本的mongodb
sudo apt-get install -y mongodb-org
+ +

以上一台node.js + nginx + mongodb的ubuntu服务器就完成了。

+

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/rss2.xml b/rss2.xml new file mode 100644 index 0000000..aa3b893 --- /dev/null +++ b/rss2.xml @@ -0,0 +1,338 @@ + + + 西门闲话 + + 旧居魔都西门外,年岁已长仍爱吹牛,故曰西门闲话 + + + + 2023-09-21T10:00:16.318Z + https://hashmaparraylist.github.io/ + + + Sebastian Qu + + + + Hexo + + + H项目心路历程记(三) + + https://hashmaparraylist.github.io/2023/09/21/H%E9%A1%B9%E7%9B%AE%E5%BF%83%E8%B7%AF%E5%8E%86%E7%A8%8B%E8%AE%B0-3/ + 2023-09-21T09:58:33.000Z + 2023-09-21T10:00:16.318Z + + 现状

对于一个基于微服务的中台系统来说,需要以什么样的颗粒度来拆分系统。这一直是基于微服务架构的系统中绕不过去的问题。

已H项目来说,当初考量是这么划分微服务的,其中

  • 基础服务: 作用主要是负责用户,角色权限,登陆鉴权,以及一些基础主档数据的日常维护功能
  • 商品服务: 物料,销售商品,款式,平台商品等和实际商品相关的主档数据
  • 库存服务:负责管理各个库存层级库存(总库存,冻结库存,在途库存等),以及展示各种出入库单据,并向其他微服务提供出入库服务接口。
  • 订单服务: 负责接收各个公域平台的ToC订单,售后单(含发货前退款,退货,换货单等),并根据配置策略进行单据路由(分仓,分物流,Hold单,加送赠品,通知仓库发货,通知平台发货等)
  • 接口服务: 主要承揽中台服务和第三方平台间的交互处理(主要是第三方平台发起的主调通知以及回调)

上述五个服务为核心服务,是保证整个中台系统基础业务功能运行的重要组成部分。除此之外还有,

  • 采购服务:负责ToB业务线的处理
  • 财务服务:满足一些企业的财务对账以及开具电子发票的功能
  • 报表服务: 每天,每周定期从其他服务(主要是订单)
  • 会员服务:管理各类会员

等7,8个非核心服务,组成非核心业务。来构成一个完整的中台系统。

拆分策略以及缺点

当时主要考虑到整个中台系统是可以拆分出售,客户可以只选择购买自身需要的服务,而不需要的服务则无需购买服务,所以当初在拆分服务上在业务领域层面上需要更加低颗粒度;同时考虑到再业务高峰期(下午到晚上,就是各位疯狂刷淘宝,京东的时候)各个服务的负载是各不相同的,如果在高峰期对于特定服务需要进行精准扩容的话,拆分的颗粒度也需要更加细致,所以结果上导致整个系统满血运行起来要至少要十几个服务,外加一些没有云平台的中间件。整个Kubernetes环境中,至少要有十五六个Deployment,考虑到系统稳定性,一个Deployment也至少要有2到3个Pod,那整个系统至少就是50个Pod。

对于一个中大型的电商企业再服务器成本上可以负担这样一个能支持50个Pod的Kubernentes集群(考虑到需要一些中间件的云产品,当时估算最小的服务器成本都要到70K/年,一个中大型企业能面对双十一流量的服务器成本都要到300K/年以上)。所以整个系统对于中小型企业来说前期的投入负担相当大(尤其是在后疫情时代的2022年,2023年),以至于早起的好几个客户都是需要我们自己贴补服务器费用的。

复盘

现在重新复盘当初服务的拆分策略,个人觉得对于核心业务来说,相似领域的一些服务应该可以适当的聚合。比如上述的五大核心服务上中的商品服务,订单服务,库存服务三个核心服务应该可以合并在一起。首先这3个核心服务是H项目的最重要的竞争力,几乎不可能出现把这3个服务拆分出售的可能性。

而且在业务高峰期有订单服务有新订单处理时,势必会产生库存的变化,因此会调用到库存服务。三个服务之间的调用是相当频繁的,尤其是负责处理商品主档数据的商品服务,无论是通过HTTP或是其他方式的远程调用的话,都会有许多浪费的带宽。如果把三个服务整合,那么之间的调用基本上就是线程间的交互,无论是效率和可靠性上都会有更加显著的提升。

结尾

怎么拆分服务一直是个难题。虽然事后复盘的时候我的很多想法也不一定是正确,但是什么样的规模的客户需要使用什么样的拆分颗粒度,都是需要仔细考虑的。绝对不是一尘不变的。

]]>
+ + + + + <h1 id="现状"><a href="#现状" class="headerlink" title="现状"></a>现状</h1><p>对于一个基于微服务的中台系统来说,需要以什么样的颗粒度来拆分系统。这一直是基于微服务架构的系统中绕不过去的问题。</p> +<p>已H项目来说 + + + + + + + + + + + + + + + +
+ + + H项目心路历程记 (二) + + https://hashmaparraylist.github.io/2023/07/12/H%E9%A1%B9%E7%9B%AE%E5%BF%83%E8%B7%AF%E5%8E%86%E7%A8%8B%E8%AE%B0-2/ + 2023-07-12T09:29:36.000Z + 2023-07-12T09:29:36.609Z + + (书接上文)

前文已经吐槽了产品设计以及中间件上的相关问题。接下来讨论一下技术上的一些问题。

关于配置

对于一个Spring的应用来说,应用配置怎么存取其实并不能说是一个技术问题。更应该算是设计或是架构问题。哪些配置应该进入配置文件,通过微服务配置中心(例如Nacos, Apollo,Kubernetes ConfigMap等)来进行在线配置,哪些配置应该通过其他方式来配置,亦或者哪些配置项目看似是配置项其实本质上就是常量。实际上截止现在H项目的确在有些微服务上是完全没有厘清三者之间的区别,以及界限。以至于现在堆积在配置文件中的内容实在太多。多个运行环境需要统一维护的难度实在太大。

对于配置来说,理想的状态应该是下面这样的。

  1. 通过微服务配置中心来影响Spring的配置文件来实现动态配置的方案,通常会伴随Spring容器的重启(有些情况下甚至需要整个Docker容器),所以存在这里的配置项应该是随着运行环境改变才会需要改动的配置。例如:

    • 外部系统的链接信息(比如MySQL, Redis的连接信息)
    • 外部系统的认证凭证(数据库用户密码,第三方系统的密钥等)
    • 因为运行环境不同导致的不同变量 (例如消息队列的Topic,Group需要根据运行环境使用不同的名称)
  2. 通过数据库或是Redis存储的配置信息,这类配置主要是应该以业务级别的配置,可以通过页面进行修改,瞬间或是几分钟内生效,且不会造成微服务重启(无论是Spring或是Docker容器的重启)。对于这类配置有几个问题还是需要注意的。

    • 配置是否需要集中管理,每个微服务,都有自己独特的业务配置,这些业务配置是否需要集中到一个微服务统一管理,还是分散到各自的微服务进行单独管理。
    • 全局性统一管理的业务配置对外保留接口应该利用缓存有效的减少数据库访问的频次。甚至可以只使用内存而不是Redis来缓存这部分内容。如果使用内存的话则需要考虑失效时效和重新装载的机制。
  3. 无效配置,实际上很多配置说是可以修改,其实自从开始使用的那天开始就没有修改过。比如用来做分布式锁的键值。这部分需要梳理出来直接以常量的形式做在代码里。

数据清理以及归档

对于电商行业的零售业务来说每天产生的单据比如订单少做200,300条,多的是上千条。加上明细数据。几个月以后这些数据就会给MySQL的库表增加检索压力。整个H项目在这块是相当薄弱的。只依靠云平台的能力,对一些不需要留档的数据进行清理。远远没有能定期归档。

对于数据清理这块大致可以分为这几类

  • 定期可以无脑物理删除的数据,比如一些日志类数据。
  • 定期直接归档的数据,归档后即可清理的数据。
  • 业务必须处理完成后才能归档并清理的数据。例如订单数据等。

对于数据归档的时候,还需要考虑一些相关联单据数据一并归档。比如订单归档时,同时需要归档相关联的退换货单,发货单,以及操作日志等等。 归档格式基本上就是考虑单条数据以JSON格式存储,然后一个时间段的数据做成压缩包存储在OSS上,然后提供给客户下载。

而对于归档数据的检索功能,客户选择需要归档的数据,我们可以将归档的数据(每个压缩包内的所有数据)导入到一个主数据库以外的,临时文档数据库(例如MongoDB),然后针对这个临时的文档数据库进行检索。当然提供的检索项目也会远少于热数据的检索项目。这个临时文档数据库基本上保持一两天数据,定期就全部回收。

未完待续

]]>
+ + + + + <p><strong>(书接上文)</strong></p> +<p>前文已经吐槽了产品设计以及中间件上的相关问题。接下来讨论一下技术上的一些问题。</p> +<h1 id="关于配置"><a href="#关于配置" class="headerlink" title="关于配置"> + + + + + + + + + + + + + + + +
+ + + H项目心路历程记 (一) + + https://hashmaparraylist.github.io/2023/07/10/H%E9%A1%B9%E7%9B%AE%E5%BF%83%E8%B7%AF%E5%8E%86%E7%A8%8B%E8%AE%B0-1/ + 2023-07-10T03:19:00.000Z + 2023-07-10T09:17:01.579Z + + 前言

2021年年中从前一家公司离职,经过朋友介绍到了现在公司参与了Halfling项目(以下简称H项目),并从零开始的设计开发到第一个客户交付的全部流程。

H项目是一个面向零售行业在电商时代的智能一体化中台产品。项目最早是从2021年中开始立项投入,期间经过数个版本迭代,以及2022年初的魔都封城等诸多困难。终于在2023年Q2季度完成了针对首个客户的上线。

作为从头开始全程参与项目的技术人员,经过2年多的产品研发期间有不少想吐槽和想说的。

业务设计

最初H项目是以打造一个面向零售行业业务中台为目的进行项目立项的,其中一个很重要的点就是希望除核心组件以外都可以独立售卖。所以在架构设计的使用当下最流行的微服务架构(基于Spring Boot + Kubernetes集群的解决方案), 再做业务拆分到服务的时候,进行了更细颗粒度的拆分。所以前前后后一共拆分了近20个微服务。

实际上到了2022年Q2前后,两个核心服务订单管理和库存服务实现以后发现,各个业务服务中间的耦合程度比想象中的还要精密,单个服务拆开单独售卖几乎不可能(应该说是完全不可能)。 然而由于服务拆分的过细,导致一个服务需要频繁访问其他服务获取大量数据。尤其类似订单关联数据比如商品的主档数据,会穿越好几个服务。而每个服务都会重复去获取主档数据,造成了大量的额外请求。 虽然最后在所有服务底部兜底使用同一个Redis作为缓存来减少额外的请求调用。

随着时间来到2023年产品组负责人离职后,整个H项目的方向有从行业的业务中台降级成中小企业的订单管理系统(Order Management System, OMS)。前期做的很多设计比如用户权限体系(当初需求是要精确到每个页面的每个按钮,如果下拉列表框有外调请求也需要做权限控制)等就显的很重,导致用户在使用以及配置都很不方便。而有些设计就显得稍显欠缺。 所以整个2022年Q4到2023年都是在对业务层上反复横跳,比如库存模型和商品模型的调整。这点是相当影响团队士气,同时也加剧了研发组和产品组之间的矛盾。

然后还有一个很重要的问题是多租户的态度上。早起项目是以服务中大体量客户为目的,所以设计之初并没有考虑多租户的问题。但实际项的进展过程中发现部分中大体量的客户也需要类似多租户的功能。这类客户需求与其说是需求多租户,不如说是希望有一个多租户级别的数据隔离,每个业务部门只能操作自己的业务部门的数据(数据包含商品,品牌,属性,平台店铺,订单,售后单据等),而总部依据可以查看一些汇总信息(例如销售对账单据等)。而随着2022年疫情状况严峻,很多中大客户对于内部平台升级和替换都变的更加谨慎和保守。所以当项目方向变成中小企业OMS时,多租户SaaS化的需求就更加迫切了。所以后期如何在原有系统上在扩展一套多租户SaaS化功能也是个课题。

中间件

H项目使用的中间件中比较特别的有分布式任务管理系统xxl-job,消息队列RocketMQ,这两个中间件都是第一次使用。关于这两个中间件也有不是可以谈谈的。

分布式任务管理系统 XXL-JOB

作为一个开源的分布式任务管理系统,xxl-job足够简单且相当容易上手使用,无论是基于Spring,还是原生Java做开发都是相当简单的。

而不足之处也很明显,最近1年版本迭代速度明显变慢,有些功能和BUG都有待处理的。比如想实动态创建定时任务这类功能就必须自己动手去对xxl-job-admin(调度中心)进行修改。

对于任务执行日志这块,现在xxl-job也是文件格式在执行器所在服务器上进行输出的,当执行器容器化或是运行在Kubernetes集群中的时候,当容器或是Pod重启后,就会丢失日志。这点在后期运维以及调试上还是有点麻烦的。

消息队列 RocketMQ

阿里出品的消息队列如果基于云产品来使用的话问题不大(阿里云卖的价钱也是有点黑的),但是在运维上还是有点问题的比如Topic,Group这些东西都只能人工去添加,阿里云并没有提供导入服务。全系统如果使用消息队列的场景多的话,维护这些Topic和Group还是很酸爽的。还有一点RocketMQ重复消息这一点是无法保证的。用阿里云的原话

绝大多数情况下,消息是不重复的。作为一款分布式消息中间件,在网络抖动、应用处理超时等异常情况下,无法保证消息不重复,但是能保证消息不丢失。

实际运用上,在大数据量并发的情况下,我们实际上是遇到了消息重复消费的问题。这点还是需要通过代码干预(例如增加一个唯一MsgId,对MsgId做分布式锁来解决)。

还有一点就是在旧版本SDK的默认配置下消费者的负载是有点小问题,总是有一个节点会比其他节点更容易消费到大量消息。据阿里云自己说明在新版本SDK的默认负载配置也已经修复这个问题了。

(未完待续)

]]>
+ + + + + <h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>2021年年中从前一家公司离职,经过朋友介绍到了现在公司参与了<code>Halfling</code>项目(以下简称<code>H项目</ + + + + + + + + + + + + + + + +
+ + + AKS中重写规则踩坑小记录 + + https://hashmaparraylist.github.io/2022/09/23/AKS%E4%B8%AD%E9%87%8D%E5%86%99%E8%A7%84%E5%88%99%E8%B8%A9%E5%9D%91%E5%B0%8F%E8%AE%B0%E5%BD%95/ + 2022-09-23T05:51:49.000Z + 2023-07-04T12:26:10.184Z + + 前言

最近在做标准产品在不同云平台中的部署验证,有幸体验了一下微软的Azure。负责采购的运维部门这次采用了Application Gateway来搭配AKS(Azure Kubernetes Service)对外暴露服务,正好借着这个机会来体验一下Application Gateway

应用场景

  1. 域名api.demo.com指向Application Gateway的IP地址
  2. AKS内部2个Service, gateway-servicebackend-service分别需要通过Application Gateway对外暴露。
  3. /gateway/指向gateway-service, 然后/backend/指向backend-service。而且两个Service都没有context-path,所以需要做一个Rewrite重写URI到Service的根目录上。

定义重写集

打开AKS对应的应用程序网关设置 > 重写。选择添加重写集。在1. 名称和关联这个Tab上只需要填写名称这项即可(名称后面在做ingress时需要使用), 关联的传递规则不需要选择。2. 重写规则配置里添加一个重写规则,然后填上重写规则的名称,并添加条件(默认新建重写规则时,只会生成操作,不会生成条件)

条件做如下设置

  • 要检查的变量类型 : 服务器变量
  • 服务器变量: request_uri
  • 区分大小写:
  • 运算符: 等号(=)
  • 要匹配的模式: /(gateway|backend)/?(.*)

操作做如下设置

  • 重写类型: URL
  • 操作类型: 设置
  • 组件: URL路径和URL查询字符串
  • URL路径值: /{var_request_uri_2}
  • 重新计算路径映射: 不选中
  • URL查询字符串值: 留空不设值

特殊说明

操作里的URL路径值不能使用正则表达式GROUP替换组,例如$1$2之类的。Azure自己定义了一套对应的替换组命名规则。具体可以参考这个网页使用应用程序网关重写 HTTP 标头和 URL

另外一个需要注意一点,如果在条件里选择了服务器变量request_uri的时候,注意这个request_uri是完整的原始请求URI(携带了查询参数)。例如: 在请求http://api.demo.com/gateway/search?foo=bar&hello=world中,request_uri的值将为/gateway/search?foo=bar&hello=world。由于request_uri里包含了查询参数,所以在操作组件中建议勾选URL路径和URL查询字符串。如果只选择URL路径的情况下可能出现无法预期的错误。以我们上述的配置来说明。

对象URL: http://api.demo.com/gateway/search?foo=bar&hello=world

组件URL路径和URL查询字符串URL路径
结果/search?foo=bar&hello=world/search?foo=bar&hello=world?foo=bar&hello=world

ACK的Ingress设置

当选择了Application Gateway作为对外暴露Service的方式时,Kubernetes集群里(kube-system命名空间里)多一个Application Gateway Ingress Controller(Azure工单时通常会简称为agic)的Deployment,所以对外暴露服务时可以像传统nginx ingress controller一样添加一个Ingress对象即可(甚至配置也和ngic大致相同,只是多了2个annotations)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
# 这里指定重写规则集(不是重写规则的名字)
appgw.ingress.kubernetes.io/rewrite-rule-set: rule-backend
# 指定说明你这里ingress的类型是agic
kubernetes.io/ingress.class: azure/application-gateway
name: backend-ingress
namespace: default
spec:
rules:
- host: api.demo.com
http:
paths:
- backend:
service:
name: gateway-service
port:
number: 8080
path: /gateway/
pathType: Prefix
- backend:
service:
name: backend-service
port:
number: 8080
path: /backend/
pathType: Prefix

总结

由于微软云这块文档有部分缺失,导致在配置这块花了一点时间去排查,甚至开了工单。总结下来Ingress的配置主要是根据请求路径路由到对应的Service,重写规则集才是实际负责根据正则来进行匹配重写。

]]>
+ + + + + <h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>最近在做标准产品在不同云平台中的部署验证,有幸体验了一下微软的Azure。负责采购的运维部门这次采用了<code>Application G + + + + + + + + + + + + + +
+ + + Spring Cloud Kubernetes环境下使用Jasypt + + https://hashmaparraylist.github.io/2021/09/29/Spring-Cloud-Kubernetes%E7%8E%AF%E5%A2%83%E4%B8%8B%E4%BD%BF%E7%94%A8Jasypt/ + 2021-09-29T03:28:11.000Z + 2023-07-04T12:26:10.184Z + + 前言

最近半年着手开始做了基于微服务的中台项目,整个项目的技术栈采用的是Java + Spring Cloud + Kubernetes + Istio

业务开放上还是相当顺利的。但是在安全审核上,运维组提出了一个简易。现在项目一些敏感配置,例如MySQL用户的密码,Redis的密码等现在都是明文保存在Kubernetes的ConfigMap中的(是的,我们并没有Nacos作为微服务的配置中心)。这样可能存在安全隐患。

首次尝试

既然有问题,那就解决问题。要给配置文件中的属性项目加密很简单,稍微Google一下,就有现成的方案了。

现在比较常用的解决方案就是集成Jasypt,然后通过jasypt-spring-boot-starter来融合进Spring。

POM包加入jasypt-spring-boot-starter

1
2
3
4
5
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>

Dockerfile中增加java参数

1
2
...
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar app.jar --jasypt.encryptor.password=helloworld $PARAMS"]

在ConfigMap中添加加密属性

1
2
3
4
5
6
7
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
data:
application.yaml: |-
test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)

利用actuator接口测试

management.endpoints.web.exposure.include属性中增加env,这样我们就可以通过调用/actuator/env来查看一下env接口返回的整个Spring 容器中所有的PropertySource。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
...
"propertySources": [
{
"name": "bootstrapProperties-configmap.demo.default",
"properties": {
"test2": {
"value": "Hello,world"
}
}
}
...
]
}

OK, 这下配置项已经加密了。问题解决了。

但是…

新的问题

自从项目集成了Jayspt以后,出现了一个奇怪的问题。每次项目试图通过修改ConfigMap的配置文件,然后试图通过spring-cloud-starter-kubernetes-fabric8-config来做自动Reload,都失败了。然而查阅应用日志,并没有出现任何异常。无奈只能打开spring-cloudjasypt-spring-bootDEBUG日志。

进过几天对日志和两边源代码的分析。终于找到了原因

原因

在Spring Boot启动时jasypt-spring-boot会将下面6种配置(并不仅限与这6种配置文件)

  • Classpath下的application.yaml
  • Classpath下的bootstrap.yaml
  • 集群里名称为${spring.cloud.kubernetes.config.name}的ConfigMap
  • 集群里名称为${spring.cloud.kubernetes.config.name}-kubernetes的ConfigMap
  • Java启动参数
  • 环境变量

转换成jasypt-spring-boot自己的PropertySource实现类EncryptableMapPropertySourceWrapper

但是如果使用Kubernetes的ConfigMap来作微服务配置中心的时候,Spring Cloud会在ConfigurationChangeDetector中查找配置类org.springframework.cloud.bootstrap.config.BootstrapPropertySource, 并依据BootstrapPropertySource的类型来判断容器内的配置与集群中ConfigMap里的配置是否有差异,来触发配置reload。

由于jasypt-spring-boot已经将所有的配置文件转型成了EncryptableMapPropertySourceWrapper, 所以ConfigurationChangeDetector无法找到BootstrapPropertySource所以会一直任务ConfigMap的里的配置没有变化,导致整个Reload失效(无论是使用polling还是event方式)

解决问题

为了保证ConfigMap变化后自动Reload的功能,所以jasypt-spring-boot不能把BootstrapPropertySource转换成EncryptableMapPropertySourceWrapper

所以我们需要设置jasypt.encryptor.skip-property-sources配置项, Classpath中的application.yaml需要增加配置

1
2
3
jasypt:
encryptor:
skip-property-sources: org.springframework.cloud.bootstrap.config.BootstrapPropertySource

skip-property-sources配置项配置后,加密项目就不能配置在ConfigMap里了,毕竟已经被我们忽略了。那么我们只能另外找一个PropertySource来存放加密项目了。

Classpath中的两个Yaml由于编译时会被Maven打包进Jar文件,会牵涉多个CI/CD多个流程显然不合适,启动参数配置项的也要影响到Docker镜像制作这个流程。所以判断下来最适合的PropertySource就是环境变量了。

环境变量增加加密项

在Kubernetes的部署Yaml中,添加加密数据项application.test.str

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: demo
name: demo
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- env:
- name: TZ
value: Asia/Shanghai
- name: application.test.str
value: >-
ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
....

如果需要更加严密的加密方针的话,我们可以把环境变量的内容放进Kubernetes的Secrets中。

在ConfigMap中引用application.test.str

1
2
3
4
5
6
7
8
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
data:
application.yaml: |-
test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
test3: ${application.test.str}

通过actuator接口来测试

通过actuator\env接口来测试一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
...
"propertySources": [
{
"name": "bootstrapProperties-configmap.demo.default",
"properties": {
"test2": {
"value": "ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)"
},
"test3": {
"value": "Hello,world"
}
}
}
...
]
}

这样ConfigMap中的配置项test3就可以通过环境变量引用并使用加密配置项了。同时修改ConfigMap依然可以触发auto reload了。这下终于算是解决了。

]]>
+ + + + + <h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>最近半年着手开始做了基于微服务的中台项目,整个项目的技术栈采用的是<code>Java</code> + <code>Spring Clou + + + + + + + + + + + +
+ + + 重学Java (一) 泛型 + + https://hashmaparraylist.github.io/2021/03/15/%E9%87%8D%E5%AD%A6Java-%E4%B8%80-%E6%B3%9B%E5%9E%8B/ + 2021-03-15T08:01:46.000Z + 2023-07-04T12:26:10.184Z + + 1. 前言

泛型编程自从 Java 5.0 中引入后已经超过15个年头了。对于现在的 Java 码农来说熟练使用泛型编程已经是家常便饭的事情了。所以本文就在不对泛型的基础使用在做说明了。 如果你还不会使用泛型的话,可以参考下面两个链接

这篇文章就简答聊一下,我实际在开发工作中很少用的到泛型方法这个知识点,以及在实际项目中有哪些东西会使用到泛型。

2. 泛型方法

在阅读代码的时候我们经常会看到下面这样的方法 (这段代码摘自 java.util.AbstractCollection)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 public <T> T[] toArray(T[] a) {
// Estimate size of array; be prepared to see more or fewer elements
int size = size();
T[] r = a.length >= size ? a :
(T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
Iterator<E> it = iterator();

for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) { // fewer elements than expected
if (a == r) {
r[i] = null; // null-terminate
} else if (a.length < i) {
return Arrays.copyOf(r, i);
} else {
System.arraycopy(r, 0, a, 0, i);
if (a.length > i) {
a[i] = null;
}
}
return a;
}
r[i] = (T)it.next();
}
// more elements than expected
return it.hasNext() ? finishToArray(r, it) : r;
}

那么 pulic 关键字后面的那个 <T> 就是用来标记这个方法是一个泛型方法。 那什么是泛型方法呢。

官方的解释是这样的

1
Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.

通俗点来将就是将一个方法泛型化,让一个普通的类的某一个方法具有泛型功能。 如果在一个泛型类中增加一个泛型方法,那这个泛型方法就可以有一套独立于这个类的泛型类型。

通过一个简单的例子, 我们来看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* GenericClass 这个泛型类是一个简单的套皮的 HashMap
*/
public class GenericClass<K, V> {

private Map<K, V> map = new HashMap<>();

public V put(K key, V value) {
return map.put(key, value);
}

public V get(K key) {
return map.get(key);
}

// 泛型方法 genericMethod 可以接受一个全新的、作用域只限本函数的泛型类型T
public <T> T genericMethod(T t) {
return t;
}

}

实际使用起来

1
2
3
4
5
6
GenericClass<String, Integer> map = new GenericClass<>();
// put 和 get 方法的参数必须使用定义时指定的 String 和 Integer
System.out.println(map.put("One", 1));
System.out.println(map.get("One"));
// 泛型方法 genericMethod 就可以接受一个 String 和 Integer 以外的类型
System.out.println(map.genericMethod(new Double(1.0)).getClass());

我们再来看看 JDK 中使用到泛型方法的例子。我们最常使用的泛型容器 ArrayList 中有个 toArray 方法。JDK 在它的实现中就提供了两个版本,其中一个就是泛型方法的版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
// 这是一个普通版本,返回一个Object的数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}

// 这是一个泛型方法的版本,将容器里存储的元素输出到 T[] 数组中。 其中 T 必须是 E 的父类,否则 System.arraycopy 会抛出 ArrayStoreException 异常
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
}

泛型方法总体上来说就是可以给与现有的方法实现上,增加一个更加灵活的实现可能。

3. 实战应用

在实际的项目中,对于泛型的使用,除了像倾倒垃圾一样往泛型容易里塞各种 java bean 和其他泛型对象。还能怎么使用泛型呢?

我们在实际的一些项目中,会对数据库中的一些表(多数时候是全部)先实现 CRUD (Create, Read, Update, Delete)的操作,并从这些操作中延伸出一些简单的 REST 风格的 WebAPI 接口,然后才会根据实际业务需要实现一些更复杂的业务接口。

大体上会是下面这个样子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

// 这是一个简单的 Entity 对象
// 通常现在的 Java 应用都会使用到 Lombok 和 Spring Boot
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Entity
@Table(name = "user")
public class User {
@Id
private Long id;
private String username;
private String password;
}

// 然后这个是 DAO 接口继承自 spring-data 的 JpaRepository
public interface UserDao extends JpaRepository<User, Long> {
}

// 在来是一个访问 User 资源的 Service 和他的实现
public interface UserService {
List<User> findAll();
Optional<User> findById(Long id);
User save (User user)
void deleteById(Long id);
}

@Service
public class UserSerivceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}

@Override
public List<User> findAll() {
return this.dao.findAll();
}

@Override
public Optional<User> findById(Long id) {
return this.dao.findById(id);
}

@Override
public User save(User user) {
return this.dao.save(user);
}

@Override
public void deleteById(Long id) {
this.dao.deleteById(id);
}
}

// 最后就是 WebAPI 的接口了
@RestController
@RequestMapping("/user/")
public class UserController{
private UserService userService;
public UserController(userService userService) {
this.userService = userService;
}

@GetMapping
@ResponseBody
public List<User> fetch() {
return this.userService.findAll();
}

@GetMapping("{id}")
@ResponseBody
public User get(@PathVariable("id") Long id) {
// 由于是示例这里就不考虑没有数据的情况了
return this.userService.findById(id).get();
}

@PostMapping
@ResponseBody
public User create(@RequestBody User user) {
return this.userService.save(user);
}

@PutMapping("{id}")
@ResponseBody
public User update(@RequestBody User user) {
return this.userService.save(user);
}

@DeleteMapping("{id}")
@ResponseBody
public User delete(@PathVariable("id") Long id) {
User user = this.userService.findById(id);
this.userService.deleteById(id);
return user;
}
}

大致一个表的一套相关接口就是这个样子的。如果你的数据库中有大量表的话,而且每个表都需要提供 REST 风格的 WebAPI 接口的话,那么这将是一个相当枯燥的而又及其容易出错的工作。

为了不让这项枯燥而又容易犯错的工作占去我们宝贵的私人时间,我们可以通过泛型和继承的技巧来重构从 Service 层到 Controller 的这段代码(感谢 spring-data 提供了 JpaRepository, 让我们不至于从 DAO 层重构)

3.1 Service 层的重构

首先是 Service 接口的重构,我们 Service 层接口就是定义了一组 CRUD 的操作,我们可以将这组 CRUD 操作抽象到一个父接口,然后所有 Service 层的接口都将继承自这个父接口。而接口中出现的 Entity 和主键的类型(上例中 User 的主键 id 的类型是 Long)就可以用泛型来展现。

1
2
3
4
5
6
7
8
9
10
11
// 这里泛型表示 E 来指代 Entity, ID 用来指代 Entity 主键的类型
public interface ICrudService<E, ID> {
List<E> findAll();
Optional<E> findById(ID id);
E save(E e);
void deleteById(ID id);
}

// 然后 Service 层的接口,就可以简化成这样
public interface UserService extends ICrudService<User, Long> {
}

同样 Service 层的实现也可以使用相似的方法具体实现可以抽象到一个基类中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 相比 ICrudService 这里有多了一个泛型 T 来代表 Entity 对应的 DAO, 我们的每一个 DAO 都继承自
// spring-data 的 JpaRepository 所以,这里可以使用到泛型的边界
public abstract class AbstractCrudService<T extends JpaRepository<E, ID>, E, ID> {
private T dao;
public AbstractCrudService(T dao) {
this.dao = dao;
}

public List<E> findAll() {
return this.dao.findAll();
}

public Optional<E> findById(ID id) {
return this.dao.findById(id);
}

public E save(E e) {
return this.dao.save(e);
}

public void deleteById(ID id) {
this.dao.deleteById(id);
}
}

// 那 Service 的实现类可以简化成这样
@Service
public class UserServiceImpl extends AbstractCrudService<UserDao, User, Long> implements UserService {
public UserServiceImpl(UserDao dao) {
supper(dao);
}
}

同样我们可以通过相同的方法来对 Controller 层进行重构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Controller 层的基类
public abstract class AbstractCrudController<T extends ICrudService<E, ID>, E, ID> {
private T service;
public AbstractCrudController(T service) {
this.service = service;
}

@GetMapping
@ResponseBody
public List<E> fetch() {
return this.service.findAll();
}

@GetMapping("{id}")
@ResponseBody
public E get(@PathVariable("id") ID id) {
// 由于是示例这里就不考虑没有数据的情况了
return this.service.findById(id).get();
}

@PostMapping
@ResponseBody
public E create(@RequestBody E e) {
return this.service.save(e);
}

@PutMapping("{id}")
@ResponseBody
public E update(@RequestBody E e) {
return this.service.save(e);
}

@DeleteMapping("{id}")
@ResponseBody
public E delete(@PathVariable("id") ID id) {
E e = this.service.findById(id).get();
this.service.deleteById(id);
return e;
}
}

// 具体的 WebAPI
@RestController
@RequestMapping("/user/")
public class UserController extends AbstractCrudController<UserService, User, Long> {
public UserController(UserService service) {
super(service);
}
}

经过重构可以消减掉 Servcie 和 Controller 中的大量重复代码,使代码更容易维护了。

4. 结尾

关于泛型就简单的说这些了,泛型作为 Java 日常开发中一个常用的知识点,其实还有很多知识点可以供我们挖掘,奈何本人才疏学浅,这么多年工作下来,只积累出来这么点内容。

文末放上示例代码的代码库:

]]>
+ + + + + <h2 id="1-前言"><a href="#1-前言" class="headerlink" title="1. 前言"></a>1. 前言</h2><p>泛型编程自从 Java 5.0 中引入后已经超过15个年头了。对于现在的 Java 码农来说熟练使用泛型编程已经是家常便 + + + + + + + + + + + +
+ + + 2020年阅读总结 + + https://hashmaparraylist.github.io/2020/12/29/2020%E5%B9%B4%E9%98%85%E8%AF%BB%E6%80%BB%E7%BB%93/ + 2020-12-29T02:45:41.000Z + 2023-07-04T12:26:10.184Z + + 多灾多难的2020年已经没有几天了,整个2020年要说还真是像一记刹车,去年很多想法,计划都有因为疫情原因搁置了。平时的生活更是从宅家变成了重度宅家的状态,自然就多出了许多大段大段的连续时间,可以用来好好玩玩大作,看看小说。仔细盘点一下今年总共看了10本小说外加2本科普书籍( 2020年阅读书单
),算是自2018年以来读书最多的一个年份了。其中不乏拖了近3年的那本《西方文化中的数学》。接着就来絮叨絮叨今年看的这12本书吧。


《莱博维茨的赞歌》

第一次被安利《莱博维茨的赞歌》的机核的电台节目辐射-视觉、音乐以及文学, 当时听完节目就相当好奇,可以被一部载入史册的CRPG游戏誉为精神文本的小说到底是个什么样子的。但是当时新星版的译本几乎就是绝本了,某鱼的二手价也要已经飙到了3位数。直到今年年初在核市节目上听到了中信版译本的消息,才有机会买来拜读。

全书通过3个故事讲述了核战灭世后莱博维茨修道院从保卫文明,到重建,然后看着文明再度毁灭的故事。各种无不透露着辐射那股 War, War never changed的味道。

《占星术杀人事件》《屋顶上的小丑》《亿男》

这3本书都是2019年上海书展上买的。前两本都是岛田庄司的推理名篇,其中《占星术杀人事件》在本格派推理史上有着非凡的地位,各种被抄袭从未被超越过。《屋顶上的小丑》要是最吸引人的话就是多线叙事,每条线都掐在恰如其分的地方。《亿男》则是挂着悬疑羊头卖着人生哲理。

《神经漫游者》

威廉吉布森的蔓生三部曲之一,开创了赛博朋克的科幻流派,说实话这本书我看的很痛苦,很多地方实在读不懂,到现在我还是不明白那些无法读懂的地方到底是原著如此,还是翻译的不行,亦或者是Kindle版排版太烂了。

《神们自己》,《神的九十亿个名字》

阿瑟克拉克的中篇小说和短篇小说集,这两本书阅读体验奇佳,尤其是在读完《神经漫游者》后,虽然三本书被Amazon打在同一个包里来卖的,但是光从排版上就比《神经漫游者》高出不少,所以才会有我对后者排版问题的质疑。

《神们自己》三线叙事,讲述了2个平行宇宙中的2个物种在能源,种族存续,家庭伦理上的各种讨论和故事。 《神的九十亿个名字》作为短篇小说集收录了好几篇风格题材迥异的小说,花上十几分钟读上一个故事是相当惬意的。

《佐伊的战争》 《人类决裂》 《万物的终结》

约翰斯卡尔齐的《老人的战争》系列的后3部,前三部大概是3,4年前读的吧,在这个宇宙观里最吸引人的设定就绿皮的人类防卫军战士,以及跨种族间的政治斗争。系列的六本书相当于在这个大设定下讲的六个故事。每个故事虽说都是独立的故事,但是前后皆有关联,而且最重要的就是每本的长度都算不上很长,相当适合于一口气读完。

《西方文化中的数学》

这本书大概是从2017年开始读的,当时意气奋发觉得对于理科生出身的我,书中内容应该很好理解,没想到最后不光是文化还是数学读的都是一知半解。只能说自大了。

《一想到还有95%的问题留给人类,我就放心了》

大概是18年还是19年前后对量子物理和宇宙物理开始有了兴趣,虽然各中理论并不是很了解,但是仍然很好奇的买了这本书,全书插画相当有趣,同时使用浅显易懂的语言来解释各种复杂的物理现象,以及当今人类物理知识的边界。强烈推荐给想假装成学霸的朋友。


2021年读什么

具体读啥还没想好基本上还是以科幻和悬疑为主,但是肯定会读的应该有海明威的《丧钟为谁而鸣》和小林泰三的《醉步男》吧。

]]>
+ + + + + <p>多灾多难的2020年已经没有几天了,整个2020年要说还真是像一记刹车,去年很多想法,计划都有因为疫情原因搁置了。平时的生活更是从宅家变成了重度宅家的状态,自然就多出了许多大段大段的连续时间,可以用来好好玩玩大作,看看小说。仔细盘点一下今年总共看了10本小说外加2本科普书籍 + + + + + + + + + +
+ + + Largrange项目架构与设计回顾 (二) + + https://hashmaparraylist.github.io/2020/05/21/Lagrange%E9%A1%B9%E7%9B%AE%E5%9B%9E%E9%A1%BE2/ + 2020-05-21T02:49:41.000Z + 2023-07-04T12:26:10.184Z + + Largrange项目架构与设计回顾 (一) 里面我讲了一下项目开始架构和技术选型的一些内容。这一章来聊聊业务设计上的这点事。

总体来说项目设计的时候,我们对业务模块的划分和拆解总体上来说都是遵循着高内聚,低耦合的原则来进行划分的。大部分和业务相关的服务,都还是能很好的进行功能划分的。但是有一些和具体业务关联性并不是很高的服务在界定与实现时出现了一些问题。

任务调度服务

由于平台方面会有一些控制命令下发给Android设备,而且下发的命令并不一定都是实时的,大部分都是指定一个时间来下发,所以在设计的时候就考虑到需要一个任务调度服务(以下略称cron),来处理这些下发指令以及未来可能会有的定时批处理任务的业务需求。技术选型的时候考虑到整个服务是构建在Kubernetes上的一个分布式的微服务架构,所以就没有选择Spring Scheduler,而是使用了Quarter来支撑整个cron

从技术选型上来说cron没有什么问题,但是在设计如何使用cron上还是有点问题的, 我们先来看看已推送服务为例,整个平台中cron的处理流程是怎样的。

  1. Platform 调用 cron的创建定时任务Job的接口(接口的参数为推送时使用的相关参数)
  2. cron 创建Quartz的Job和Trigger,并将Job和Trigger的Name(Quartz中Job和Trigger的唯一标识符)返回个Platform
  3. cron 在指定时间触发推送的Job,即调用Push服务的推送接口
  4. Push的推送接口调用第三方服务商的推送服务,并将第三方推送服务的调用结果返回给cron
  5. cron通过调用Platform预留的推送服务回调地址将第三方推送服务调用接口返还给Platform
  6. Platform 将第三方推送服务的调用结果留档保存,并继续业务处理

设计之初考虑到不想在cron中牵扯到具体的业务, 所以设计了一个Platform的回调接口来处理推送后的具体业务处理。虽然保证了cron尽量减少了和业务逻辑接触与数据库的访问。但是前前后后要访问集群内部的其他微服务2次,额外增加了网络开销,以及因为网络通信造成额外的通信失败风险。而且针对每种不同的定时任务,都需要额外开发一个QuartzJobBean,很难形成统一的QuartzJobBean来进行处理。今后如果还有相似项目还是需要重新考量一下如何设计一个更加完善的定时任务。

总结

暂时就想到了这些东西,今后想到啥还会继续在这里补存。

]]>
+ + + + + <p>在<a href="/2020/05/09/lagrange%E9%A1%B9%E7%9B%AE%E5%9B%9E%E9%A1%BE/">Largrange项目架构与设计回顾 (一)</a> 里面我讲了一下项目开始架构和技术选型的一些内容。这一章来聊聊业务设计上的这点事。< + + + + + + + + + + + + + + + +
+ + + Largrange项目架构与设计回顾 (一) + + https://hashmaparraylist.github.io/2020/05/09/lagrange%E9%A1%B9%E7%9B%AE%E5%9B%9E%E9%A1%BE/ + 2020-05-09T02:34:47.000Z + 2023-07-04T12:26:10.184Z + +

项目背景

从去年年底开始一个老客户希望在他们的一个传统的机械设备(后面略称 E机关 )上外装一个Android设备。 Android设备和 E机关 之间通过串口或是RJ45接口进行数据交互,主要是Android设备获取 E机关 内部的数据,并不会通过接口来控制 E机关 。 Android设备则通过4G 来和平台交互,上报Android设备的状态数据,同时接受平台的控制。以此来实现让传统机械设备也能拥抱物联网的概念。

最后围绕着客户的需求分成了3个项目来并行推进

  1. Android设备的硬件设备的设计、选材、样机制作与量产规划
  2. 在Android设备上,进行与E机关以及平台进行交互的APP开发
  3. 用于Android设备交互的平台的架构、设计与开发

我们平台Team就负责 3.用于Android设备交互的平台 并命名Lagrange (拉格朗日) 。

设计&架构

整个平台这块不仅需要向Android设备的APP提供数据交互接口,还需要有一个供相关运营人员使用的前端Web应用,以及与云平台(主要是Aliyun) 交互的功能。所以考虑到多方面使用微服务的架构来实现整个平台端。
由于E机关的工作工况不能保证长期较的稳定的连接到4G网络,所以我们并不考虑使用Socket长连接的方式来做APP和平台之间的数据交互,要实现平台对Android设备进行反控的话,只能实现推送方式来把控制命令下发给Android设备,所以还需要有一个第三方推送服务商交互的服务。同时控制推送也有实时和非实时以及定期推送的需求,所以可能还需要一个任务调度的服务。

根据对上述业务进行梳理,我们将项目分成几个服务

  • 提供Android设备的交互接口的 App Service
  • 提供运营人员使用前端Web应用 Platform Web Service
  • 前端Web应用使用到的一些接口 Platform Service
  • 负责第三方推送服务商交互的 Push Service
  • 提供云平台鉴权用的 Auth Service
  • 用来管理任务调度的 Cron Service

由于前一个项目实施的时候没有使用容器部署,每当访问量峰值的时候,我们这些码农兼运维就各种加班,所以这次项目决定直接将服务容器化,同时选择了Kubernetes来管理容器。由于客观原因线上的Kubernetes直接购买了Aliyun的托管版Kubernetes服务。 内部的开发测试环境则使用了 Rancher 2.0 来构建Kubernetes集群。

技术选型

a. 后端服务选型

由于不考虑长连接的原因,所以在后端服务在技术选型上基本就不考虑Netty了,直接上Spring大礼包。

  • Spring Boot 开发接口
  • Spring Data 配合 JPA 来进行数据的持久化
  • Spring Cloud Kubenetes 来做数据的Config的autoreload
  • Quartz 负责处理任务调度

服务之间调用都使用HTTP服务, 服务发现也有Kubernetes的DNS机制支持。服务网格则选择了比较成熟的Istio,主要还是Aliyun的Kubernetes可以集成Istio,部署和使用都相当方便。

b. 前端Web应用选型

因为前端Web应用主要是给运营人员使用,所以我们考虑使用Single Page Application来做个前后端分离的Web应用。框架这块由于团队成员基本上没有什么前端开发经验,基本都是后台写Java的码农,所以框架选择有点随性,直接就点名了vue.js。前端控件库则用的是阿里系的Antd。

c. 数据持久层

主数据库选择了mysql,缓存用的redis。这些都是团队比较熟悉的。由于一些特殊的业务需求和使用场景,我们还加了一个mongodb来做为一个副数据库,主要存放一些特殊业务使用的数据。

d. DevOps

用了相当传统的GitLab CE 加上Jenkins的组合,实现前后端代码的自动编译,推送到私有的镜像仓库(使用Aliyun的镜像仓库服务)

最后

以上基本是项目最早做设计时候的各种考量。暂时先写这么多,过两天再回顾一下当初设计上有哪些觉得不足的地方。

]]>
+ + + + + <blockquote> +<p>项目背景</p> +</blockquote> +<p>从去年年底开始一个老客户希望在他们的一个传统的机械设备(后面略称 <code>E机关</code> )上外装一个Android设备。 Android设备和 <code>E机关</code> 之间通 + + + + + + + + + + + + + + + +
+ + + 阿里云Kubernetes上线踩坑记 + + https://hashmaparraylist.github.io/2020/04/01/%E9%98%BF%E9%87%8C%E4%BA%91Kubernetes%E4%B8%8A%E7%BA%BF%E8%B8%A9%E5%9D%91%E8%AE%B0/ + 2020-04-01T01:47:38.000Z + 2023-07-04T12:26:10.184Z + +
1
2
Update:
2020-04-08 增加istio-ingressgateway高可用的设置

最近公司因为项目需要,在阿里云上部署了一个Kubernetes集群。虽然阿里云的文档说的还算细致,但是还是有些没有明确说明的细节。

1. 购买篇

申请项目预算的时候,只考虑到Worker节点,1个SLB节点以及域名和证书的预算。但是实际购买的时候发现还有许多额外的开销。

1.1 SNAT

这个和EIP一并购买,可以方便通过公网使用kubectl访问集群。关于SNAT网关至今不是很明白需要购买这个服务的意义何在,只是为了一个EIP来访问集群吗?

1.2 Ingress

这个选上了后,阿里云会给你买个SLB而且还是带公网访问的,如果你后期考虑使用Istio的话,建议你集群创建后,直接停止这个SLB,以免产生额外的费用。

1.3 日志服务

通过阿里云的日志服务来收集应用的的日志,挺好用的。但是另外收费,如果有能力的自建日志服务的可不购买。

2. Istio

阿里云的Kubernetes集群完美集成了Istio,根据向导就能很简单的部署成功。

2.1 额外的SLB

Istio的Gateway 需要绑定一个新的SLB,和Ingress的SLB不能是同一个,又是一笔额外的开销

2.2 集群外访问

这个在阿里云的Istio FAQ中有提到,按照指导很容易解决

2.2 SLB的443监听

为了方便443端口的证书绑定,我们直接删除了SLB上原有的443监听(TCP协议), 重新建了一个443监听(HTTPS协议),指向和80端口同样的虚拟服务器组。但是设置健康检查时一直出错,经过排查发现SLB健康检查发送的请求协议是HTTP 1.0的,Istio的envoy直接反悔了426(Upgrade Required)这个状态码,所以我们无奈只能把健康检查的检查返回状态改为http_4xx,这样就能通过SLB的健康检查了。

2.3 istio-ingressgateway的高可用

istio-ingressgateway要达成高可用,只需要增加通过伸缩POD就可以实现,于istio-ingressgateway对应的SLB中的虚拟服务器组也会自动增加,完全不需要进行额外的手动设定。

由于istio-ingressgateway中挂载了HPAHorizontalPodAutoscaler(简称HPA),通常三节点的集群中最小POD数只有1台,在3节点的集群中,要实现高可用,需要手动修改HPA,增加最小POD数。


基本上现在遇到了这些坑,再有在总结吧。

]]>
+ + + + + <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre>< + + + + + + + + + + + + + +
+ + + 在ubuntu上从零搭建node.js + nginx + mongodb环境 + + https://hashmaparraylist.github.io/2019/12/09/hot-to-install-nodejs-nginx-mongodb-on-ubuntu/ + 2019-12-09T02:40:05.000Z + 2023-07-04T12:26:10.184Z + + 说到后端开发环境,最有名的莫过于LAMP和LNMP,最近由于node.js的强势崛起,越来越多的后端开发也开始试水node.js了。我最近也因为各种原因,前前后后总够构建了好几台node.js + nginx + mongodb的Linux服务器。

首先关于Linux服务器,比起CentOS来说,我更加喜欢ubuntu一点。所以无论是阿里云还是一些海外的vps服务器上,我也倾向选用ubuntu服务器,本贴也是基于ubuntu服务器里说明的。

1.开始前的一些准备

首先还是需要刷新一下ubuntu的包索引并安装build-essential和libssl-dev这2个包以及curl这个工具。

1
2
3
sudo apt-get update
sudo apt-get install build-essential libssl-dev
sudo apt-get isntall curl

2.安装node.js

关于安装node.js这一点,我不是很推荐使用apt-get 来安装node.js的环境。主要是因为node.js和io.js合并以后,版本迭代速度相当频繁(主要还是因为更多ES6的特性得到了支持)。今后很有可能会有在一台服务器上使用不同版本的node.js的需求。

这里推荐一个管理不同版本node.js的工具:nvm,官网: https://github.com/creationix/nvm  。安装nvm,如果前面你安装了curl的话可以

1
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash

如果没有按照curl的话,也可以使用wget来进行安装

1
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash

然后nvm就会自动安装到home目录下面的.nvm目录里,并会在.bashrc里自动添加nvm的环境变量。为了让环境变量生效,最简单的方法就是通过ssh或是telnet重新连接你的服务器。

安装完nvm后,就可以通过nvm来安装指定版本的node.js了。

1
2
3
4
5
# 列出可以安装的node版本号
nvm ls-remote

# 安装指定版本的node (当前最新版本为v5.7.1, LTS版是v4.3.2)
nvm install v4.3.2

3.安装nginx

由于ubuntu源(尤其是阿里云的源)上的nginx经常不是最新的,如果需要安装最新版本nginx的时候需要手动添加nginx的源。

1
2
3
4
5
6
7
8
9
10
11
# 添加nginx的mainline仓库
cd /tmp/ && wget http://nginx.org/keys/nginx_signing.key
sudo apt-key add nginx_signing.key

# 编辑/etc/apt/sources.list.d/nginx.list 添加下面2行内容,井号不需要
# deb http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
# deb-src http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
sudo vi /etc/apt/sources.list.d/nginx.list

# 更新源,并安装nginx
sudo apt-get update && sudo apt-get install nginx

在编辑/etc/apt/sources.list.d/nginx.list的时候需要注意,“ubuntu代号”需要根据ubuntu服务器的版本不同手动调整的,比如14.04是trusty。通过下面的命令可以获取ubuntu的代号。

1
lsb_release -cs

4.安装mongodb

同样和nginx有同样的问题,要安装最新3.2版本的mongodb也需要手动添加ubuntu的源。

1
2
3
4
5
6
7
8
9
10
11
# 导入mongodb的public key
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927

# 生成mongodb的源list
echo "deb http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list

# 更新源
sudo apt-get update

# 安装最新版本的mongodb
sudo apt-get install -y mongodb-org

以上一台node.js + nginx + mongodb的ubuntu服务器就完成了。

]]>
+ + + + + <p>说到后端开发环境,最有名的莫过于LAMP和LNMP,最近由于node.js的强势崛起,越来越多的后端开发也开始试水node.js了。我最近也因为各种原因,前前后后总够构建了好几台node.js + nginx + mongodb的Linux服务器。</p> +<p>首先关于L + + + + + + + + + + + + + + + +
+ +
diff --git a/scaffolds/draft.md b/scaffolds/draft.md deleted file mode 100644 index 498e95b..0000000 --- a/scaffolds/draft.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: {{ title }} -tags: ---- diff --git a/scaffolds/page.md b/scaffolds/page.md deleted file mode 100644 index f01ba3c..0000000 --- a/scaffolds/page.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: {{ title }} -date: {{ date }} ---- diff --git a/scaffolds/post.md b/scaffolds/post.md deleted file mode 100644 index 1f9b9a4..0000000 --- a/scaffolds/post.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: {{ title }} -date: {{ date }} -tags: ---- diff --git "a/source/_posts/2020\345\271\264\351\230\205\350\257\273\346\200\273\347\273\223.md" "b/source/_posts/2020\345\271\264\351\230\205\350\257\273\346\200\273\347\273\223.md" deleted file mode 100644 index 7546738..0000000 --- "a/source/_posts/2020\345\271\264\351\230\205\350\257\273\346\200\273\347\273\223.md" +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: 2020年阅读总结 -tags: - - 阅读 -categories: - - 杂记 -date: 2020-12-29 10:45:41 ---- - - -多灾多难的2020年已经没有几天了,整个2020年要说还真是像一记刹车,去年很多想法,计划都有因为疫情原因搁置了。平时的生活更是从宅家变成了重度宅家的状态,自然就多出了许多大段大段的连续时间,可以用来好好玩玩大作,看看小说。仔细盘点一下今年总共看了10本小说外加2本科普书籍( [2020年阅读书单](https://github.com/hashmaparraylist/ReadList/blob/master/ReadList2020.md) -),算是自2018年以来读书最多的一个年份了。其中不乏拖了近3年的那本《西方文化中的数学》。接着就来絮叨絮叨今年看的这12本书吧。 - ---- - -> 《莱博维茨的赞歌》 - -第一次被安利《莱博维茨的赞歌》的机核的电台节目[辐射-视觉、音乐以及文学](https://www.gcores.com/radios/16773), 当时听完节目就相当好奇,可以被一部载入史册的CRPG游戏誉为精神文本的小说到底是个什么样子的。但是当时新星版的译本几乎就是绝本了,某鱼的二手价也要已经飙到了3位数。直到今年年初在核市节目上听到了中信版译本的消息,才有机会买来拜读。 - -全书通过3个故事讲述了核战灭世后莱博维茨修道院从保卫文明,到重建,然后看着文明再度毁灭的故事。各种无不透露着辐射那股 `War, War never changed`的味道。 - -> 《占星术杀人事件》《屋顶上的小丑》《亿男》 - -这3本书都是2019年上海书展上买的。前两本都是岛田庄司的推理名篇,其中《占星术杀人事件》在本格派推理史上有着非凡的地位,各种被抄袭从未被超越过。《屋顶上的小丑》要是最吸引人的话就是多线叙事,每条线都掐在恰如其分的地方。《亿男》则是挂着悬疑羊头卖着人生哲理。 - -> 《神经漫游者》 - -威廉吉布森的蔓生三部曲之一,开创了赛博朋克的科幻流派,说实话这本书我看的很痛苦,很多地方实在读不懂,到现在我还是不明白那些无法读懂的地方到底是原著如此,还是翻译的不行,亦或者是Kindle版排版太烂了。 - -> 《神们自己》,《神的九十亿个名字》 - -阿瑟克拉克的中篇小说和短篇小说集,这两本书阅读体验奇佳,尤其是在读完《神经漫游者》后,虽然三本书被Amazon打在同一个包里来卖的,但是光从排版上就比《神经漫游者》高出不少,所以才会有我对后者排版问题的质疑。 - -《神们自己》三线叙事,讲述了2个平行宇宙中的2个物种在能源,种族存续,家庭伦理上的各种讨论和故事。 《神的九十亿个名字》作为短篇小说集收录了好几篇风格题材迥异的小说,花上十几分钟读上一个故事是相当惬意的。 - -> 《佐伊的战争》 《人类决裂》 《万物的终结》 - -约翰斯卡尔齐的《老人的战争》系列的后3部,前三部大概是3,4年前读的吧,在这个宇宙观里最吸引人的设定就绿皮的人类防卫军战士,以及跨种族间的政治斗争。系列的六本书相当于在这个大设定下讲的六个故事。每个故事虽说都是独立的故事,但是前后皆有关联,而且最重要的就是每本的长度都算不上很长,相当适合于一口气读完。 - -> 《西方文化中的数学》 - -这本书大概是从2017年开始读的,当时意气奋发觉得对于理科生出身的我,书中内容应该很好理解,没想到最后不光是文化还是数学读的都是一知半解。只能说自大了。 - -> 《一想到还有95%的问题留给人类,我就放心了》 - -大概是18年还是19年前后对量子物理和宇宙物理开始有了兴趣,虽然各中理论并不是很了解,但是仍然很好奇的买了这本书,全书插画相当有趣,同时使用浅显易懂的语言来解释各种复杂的物理现象,以及当今人类物理知识的边界。强烈推荐给想假装成学霸的朋友。 - ---- - -> 2021年读什么 - -具体读啥还没想好基本上还是以科幻和悬疑为主,但是肯定会读的应该有海明威的《丧钟为谁而鸣》和小林泰三的《醉步男》吧。 - diff --git "a/source/_posts/AKS\344\270\255\351\207\215\345\206\231\350\247\204\345\210\231\350\270\251\345\235\221\345\260\217\350\256\260\345\275\225.md" "b/source/_posts/AKS\344\270\255\351\207\215\345\206\231\350\247\204\345\210\231\350\270\251\345\235\221\345\260\217\350\256\260\345\275\225.md" deleted file mode 100644 index e3df640..0000000 --- "a/source/_posts/AKS\344\270\255\351\207\215\345\206\231\350\247\204\345\210\231\350\270\251\345\235\221\345\260\217\350\256\260\345\275\225.md" +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: AKS中重写规则踩坑小记录 -date: 2022-09-23 13:51:49 -tags: - - Aliyun - - Kubernetes -categories: - - 后端 - - Cloud ---- - -# 前言 - -最近在做标准产品在不同云平台中的部署验证,有幸体验了一下微软的Azure。负责采购的运维部门这次采用了`Application Gateway`来搭配`AKS`(`Azure Kubernetes Service`)对外暴露服务,正好借着这个机会来体验一下`Application Gateway`。 - -# 应用场景 - -1. 域名`api.demo.com`指向`Application Gateway`的IP地址 -2. 在`AKS`内部2个Service, `gateway-service`和`backend-service`分别需要通过`Application Gateway`对外暴露。 -3. `/gateway/`指向`gateway-service`, 然后`/backend/`指向`backend-service`。而且两个Service都没有context-path,所以需要做一个Rewrite重写URI到Service的根目录上。 - -# 定义重写集 - -打开`AKS`对应的应用程序网关`设置` > `重写`。选择`添加重写集`。在`1. 名称和关联`这个Tab上只需要填写名称这项即可(名称后面在做ingress时需要使用), `关联的传递规则`不需要选择。`2. 重写规则配置`里添加一个重写规则,然后填上重写规则的名称,并添加条件(默认新建重写规则时,只会生成操作,不会生成条件) - -## `条件`做如下设置 - -- **要检查的变量类型** : `服务器变量` -- **服务器变量**: `request_uri` -- **区分大小写**: `否` -- **运算符**: `等号(=)` -- **要匹配的模式**: `/(gateway|backend)/?(.*)` - -## `操作`做如下设置 - -- **重写类型**: `URL` -- **操作类型**: `设置` -- **组件**: `URL路径和URL查询字符串` -- **URL路径值**: `/{var_request_uri_2}` -- **重新计算路径映射**: `不选中` -- **URL查询字符串值**: `留空不设值` - -## 特殊说明 - -`操作`里的`URL路径值`不能使用正则表达式GROUP替换组,例如`$1`和`$2`之类的。Azure自己定义了一套对应的替换组命名规则。具体可以参考这个网页[使用应用程序网关重写 HTTP 标头和 URL](https://docs.azure.cn/zh-cn/application-gateway/rewrite-http-headers-url)。 - -另外一个需要注意一点,如果在`条件`里选择了`服务器变量`的`request_uri`的时候,注意这个`request_uri`是完整的原始请求URI(携带了查询参数)。例如: 在请求`http://api.demo.com/gateway/search?foo=bar&hello=world`中,`request_uri`的值将为`/gateway/search?foo=bar&hello=world`。由于`request_uri`里包含了查询参数,所以在`操作`的`组件`中建议勾选`URL路径和URL查询字符串`。如果只选择`URL路径`的情况下可能出现无法预期的错误。以我们上述的配置来说明。 - -对象URL: `http://api.demo.com/gateway/search?foo=bar&hello=world` - -**组件** | `URL路径和URL查询字符串` | `URL路径` ---- | --- | --- -**结果** | `/search?foo=bar&hello=world` | `/search?foo=bar&hello=world?foo=bar&hello=world` - -# `ACK`的Ingress设置 - -当选择了`Application Gateway`作为对外暴露Service的方式时,Kubernetes集群里(`kube-system`命名空间里)多一个`Application Gateway Ingress Controller`(Azure工单时通常会简称为`agic`)的Deployment,所以对外暴露服务时可以像传统`nginx ingress controller`一样添加一个`Ingress`对象即可(甚至配置也和ngic大致相同,只是多了2个annotations) - -```yaml -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - annotations: - # 这里指定重写规则集(不是重写规则的名字) - appgw.ingress.kubernetes.io/rewrite-rule-set: rule-backend - # 指定说明你这里ingress的类型是agic - kubernetes.io/ingress.class: azure/application-gateway - name: backend-ingress - namespace: default -spec: - rules: - - host: api.demo.com - http: - paths: - - backend: - service: - name: gateway-service - port: - number: 8080 - path: /gateway/ - pathType: Prefix - - backend: - service: - name: backend-service - port: - number: 8080 - path: /backend/ - pathType: Prefix -``` - -# 总结 - -由于微软云这块文档有部分缺失,导致在配置这块花了一点时间去排查,甚至开了工单。总结下来Ingress的配置主要是根据请求路径路由到对应的Service,重写规则集才是实际负责根据正则来进行匹配重写。 \ No newline at end of file diff --git "a/source/_posts/H\351\241\271\347\233\256\345\277\203\350\267\257\345\216\206\347\250\213\350\256\260-1.md" "b/source/_posts/H\351\241\271\347\233\256\345\277\203\350\267\257\345\216\206\347\250\213\350\256\260-1.md" deleted file mode 100644 index dda6e5e..0000000 --- "a/source/_posts/H\351\241\271\347\233\256\345\277\203\350\267\257\345\216\206\347\250\213\350\256\260-1.md" +++ /dev/null @@ -1,56 +0,0 @@ ---- -title: H项目心路历程记 (一) -tags: - - 架构 - - Kubernetes - - Aliyun - - Java -categories: - - 设计 -date: 2023-07-10 11:19:00 ---- - - -# 前言 - -2021年年中从前一家公司离职,经过朋友介绍到了现在公司参与了`Halfling`项目(以下简称`H项目`),并从零开始的设计开发到第一个客户交付的全部流程。 - -`H项目`是一个面向零售行业在电商时代的智能一体化中台产品。项目最早是从2021年中开始立项投入,期间经过数个版本迭代,以及2022年初的魔都封城等诸多困难。终于在2023年Q2季度完成了针对首个客户的上线。 - -作为从头开始全程参与项目的技术人员,经过2年多的产品研发期间有不少想吐槽和想说的。 - -# 业务设计 - -最初`H项目`是以打造一个面向零售行业业务中台为目的进行项目立项的,其中一个很重要的点就是希望除核心组件以外都可以独立售卖。所以在架构设计的使用当下最流行的微服务架构(基于Spring Boot + Kubernetes集群的解决方案), 再做业务拆分到服务的时候,进行了更细颗粒度的拆分。所以前前后后一共拆分了近20个微服务。 - -实际上到了2022年Q2前后,两个核心服务订单管理和库存服务实现以后发现,各个业务服务中间的耦合程度比想象中的还要精密,单个服务拆开单独售卖几乎不可能(应该说是完全不可能)。 然而由于服务拆分的过细,导致一个服务需要频繁访问其他服务获取大量数据。尤其类似订单关联数据比如商品的主档数据,会穿越好几个服务。而每个服务都会重复去获取主档数据,造成了大量的额外请求。 虽然最后在所有服务底部兜底使用同一个Redis作为缓存来减少额外的请求调用。 - -随着时间来到2023年产品组负责人离职后,整个`H项目`的方向有从行业的业务中台降级成中小企业的订单管理系统(`Order Management System`, OMS)。前期做的很多设计比如用户权限体系(当初需求是要精确到每个页面的每个按钮,如果下拉列表框有外调请求也需要做权限控制)等就显的很重,导致用户在使用以及配置都很不方便。而有些设计就显得稍显欠缺。 所以整个2022年Q4到2023年都是在对业务层上反复横跳,比如库存模型和商品模型的调整。这点是相当影响团队士气,同时也加剧了研发组和产品组之间的矛盾。 - -然后还有一个很重要的问题是多租户的态度上。早起项目是以服务中大体量客户为目的,所以设计之初并没有考虑多租户的问题。但实际项的进展过程中发现部分中大体量的客户也需要类似多租户的功能。这类客户需求与其说是需求多租户,不如说是希望有一个多租户级别的数据隔离,每个业务部门只能操作自己的业务部门的数据(数据包含商品,品牌,属性,平台店铺,订单,售后单据等),而总部依据可以查看一些汇总信息(例如销售对账单据等)。而随着2022年疫情状况严峻,很多中大客户对于内部平台升级和替换都变的更加谨慎和保守。所以当项目方向变成中小企业OMS时,多租户SaaS化的需求就更加迫切了。所以后期如何在原有系统上在扩展一套多租户SaaS化功能也是个课题。 - -# 中间件 - -`H项目`使用的中间件中比较特别的有分布式任务管理系统`xxl-job`,消息队列`RocketMQ`,这两个中间件都是第一次使用。关于这两个中间件也有不是可以谈谈的。 - -## 分布式任务管理系统 `XXL-JOB` - -作为一个开源的分布式任务管理系统,`xxl-job`足够简单且相当容易上手使用,无论是基于Spring,还是原生Java做开发都是相当简单的。 - -而不足之处也很明显,最近1年版本迭代速度明显变慢,有些功能和BUG都有待处理的。比如想实动态创建定时任务这类功能就必须自己动手去对`xxl-job-admin`(调度中心)进行修改。 - -对于任务执行日志这块,现在`xxl-job`也是文件格式在执行器所在服务器上进行输出的,当执行器容器化或是运行在Kubernetes集群中的时候,当容器或是Pod重启后,就会丢失日志。这点在后期运维以及调试上还是有点麻烦的。 - -## 消息队列 `RocketMQ` - -阿里出品的消息队列如果基于云产品来使用的话问题不大(阿里云卖的价钱也是有点黑的),但是在运维上还是有点问题的比如Topic,Group这些东西都只能人工去添加,阿里云并没有提供导入服务。全系统如果使用消息队列的场景多的话,维护这些Topic和Group还是很酸爽的。还有一点`RocketMQ`重复消息这一点是无法保证的。用阿里云的原话 - -> 绝大多数情况下,消息是不重复的。作为一款分布式消息中间件,在网络抖动、应用处理超时等异常情况下,无法保证消息不重复,但是能保证消息不丢失。 - -实际运用上,在大数据量并发的情况下,我们实际上是遇到了消息重复消费的问题。这点还是需要通过代码干预(例如增加一个唯一MsgId,对MsgId做分布式锁来解决)。 - -还有一点就是在旧版本SDK的默认配置下消费者的负载是有点小问题,总是有一个节点会比其他节点更容易消费到大量消息。据阿里云自己说明在新版本SDK的默认负载配置也已经修复这个问题了。 - - -**(未完待续)** - diff --git "a/source/_posts/H\351\241\271\347\233\256\345\277\203\350\267\257\345\216\206\347\250\213\350\256\260-2.md" "b/source/_posts/H\351\241\271\347\233\256\345\277\203\350\267\257\345\216\206\347\250\213\350\256\260-2.md" deleted file mode 100644 index 8a73609..0000000 --- "a/source/_posts/H\351\241\271\347\233\256\345\277\203\350\267\257\345\216\206\347\250\213\350\256\260-2.md" +++ /dev/null @@ -1,51 +0,0 @@ ---- -title: H项目心路历程记 (二) -tags: - - 架构 - - Kubernetes - - Aliyun - - Java -categories: - - 设计 -date: 2023-07-12 17:29:36 ---- - - -**(书接上文)** - -前文已经吐槽了产品设计以及中间件上的相关问题。接下来讨论一下技术上的一些问题。 - -# 关于配置 - -对于一个Spring的应用来说,应用配置怎么存取其实并不能说是一个技术问题。更应该算是设计或是架构问题。哪些配置应该进入配置文件,通过微服务配置中心(例如Nacos, Apollo,Kubernetes ConfigMap等)来进行在线配置,哪些配置应该通过其他方式来配置,亦或者哪些配置项目看似是配置项其实本质上就是常量。实际上截止现在`H项目`的确在有些微服务上是完全没有厘清三者之间的区别,以及界限。以至于现在堆积在配置文件中的内容实在太多。多个运行环境需要统一维护的难度实在太大。 - -对于配置来说,理想的状态应该是下面这样的。 - -1. 通过微服务配置中心来影响Spring的配置文件来实现动态配置的方案,通常会伴随Spring容器的重启(有些情况下甚至需要整个Docker容器),所以存在这里的配置项应该是随着运行环境改变才会需要改动的配置。例如: - - - 外部系统的链接信息(比如MySQL, Redis的连接信息) - - 外部系统的认证凭证(数据库用户密码,第三方系统的密钥等) - - 因为运行环境不同导致的不同变量 (例如消息队列的Topic,Group需要根据运行环境使用不同的名称) - -2. 通过数据库或是Redis存储的配置信息,这类配置主要是应该以业务级别的配置,可以通过页面进行修改,瞬间或是几分钟内生效,且不会造成微服务重启(无论是Spring或是Docker容器的重启)。对于这类配置有几个问题还是需要注意的。 - - 配置是否需要集中管理,每个微服务,都有自己独特的业务配置,这些业务配置是否需要集中到一个微服务统一管理,还是分散到各自的微服务进行单独管理。 - - 全局性统一管理的业务配置对外保留接口应该利用缓存有效的减少数据库访问的频次。甚至可以只使用内存而不是Redis来缓存这部分内容。如果使用内存的话则需要考虑失效时效和重新装载的机制。 - -3. 无效配置,实际上很多配置说是可以修改,其实自从开始使用的那天开始就没有修改过。比如用来做分布式锁的键值。这部分需要梳理出来直接以常量的形式做在代码里。 - -# 数据清理以及归档 - -对于电商行业的零售业务来说每天产生的单据比如订单少做200,300条,多的是上千条。加上明细数据。几个月以后这些数据就会给MySQL的库表增加检索压力。整个`H项目`在这块是相当薄弱的。只依靠云平台的能力,对一些不需要留档的数据进行清理。远远没有能定期归档。 - -对于数据清理这块大致可以分为这几类 - -- 定期可以无脑物理删除的数据,比如一些日志类数据。 -- 定期直接归档的数据,归档后即可清理的数据。 -- 业务必须处理完成后才能归档并清理的数据。例如订单数据等。 - -对于数据归档的时候,还需要考虑一些相关联单据数据一并归档。比如订单归档时,同时需要归档相关联的退换货单,发货单,以及操作日志等等。 归档格式基本上就是考虑单条数据以JSON格式存储,然后一个时间段的数据做成压缩包存储在OSS上,然后提供给客户下载。 - -而对于归档数据的检索功能,客户选择需要归档的数据,我们可以将归档的数据(每个压缩包内的所有数据)导入到一个主数据库以外的,临时文档数据库(例如MongoDB),然后针对这个临时的文档数据库进行检索。当然提供的检索项目也会远少于热数据的检索项目。这个临时文档数据库基本上保持一两天数据,定期就全部回收。 - -**未完待续** - diff --git "a/source/_posts/H\351\241\271\347\233\256\345\277\203\350\267\257\345\216\206\347\250\213\350\256\260-3.md" "b/source/_posts/H\351\241\271\347\233\256\345\277\203\350\267\257\345\216\206\347\250\213\350\256\260-3.md" deleted file mode 100644 index 4674401..0000000 --- "a/source/_posts/H\351\241\271\347\233\256\345\277\203\350\267\257\345\216\206\347\250\213\350\256\260-3.md" +++ /dev/null @@ -1,49 +0,0 @@ ---- -title: H项目心路历程记(三) -tags: - - 架构 - - Kubernetes - - Aliyun - - Java -categories: - - 设计 -date: 2023-09-21 17:58:33 ---- - - -# 现状 - -对于一个基于微服务的中台系统来说,需要以什么样的颗粒度来拆分系统。这一直是基于微服务架构的系统中绕不过去的问题。 - -已H项目来说,当初考量是这么划分微服务的,其中 - -- `基础服务`: 作用主要是负责用户,角色权限,登陆鉴权,以及一些基础主档数据的日常维护功能 -- `商品服务`: 物料,销售商品,款式,平台商品等和实际商品相关的主档数据 -- `库存服务`:负责管理各个库存层级库存(总库存,冻结库存,在途库存等),以及展示各种出入库单据,并向其他微服务提供出入库服务接口。 -- `订单服务`: 负责接收各个公域平台的ToC订单,售后单(含发货前退款,退货,换货单等),并根据配置策略进行单据路由(分仓,分物流,Hold单,加送赠品,通知仓库发货,通知平台发货等) -- `接口服务`: 主要承揽中台服务和第三方平台间的交互处理(主要是第三方平台发起的主调通知以及回调) - -上述五个服务为核心服务,是保证整个中台系统基础业务功能运行的重要组成部分。除此之外还有, - -- `采购服务`:负责ToB业务线的处理 -- `财务服务`:满足一些企业的财务对账以及开具电子发票的功能 -- `报表服务`: 每天,每周定期从其他服务(主要是订单) -- `会员服务`:管理各类会员 - -等7,8个非核心服务,组成非核心业务。来构成一个完整的中台系统。 - -# 拆分策略以及缺点 - -当时主要考虑到整个中台系统是可以拆分出售,客户可以只选择购买自身需要的服务,而不需要的服务则无需购买服务,所以当初在拆分服务上在业务领域层面上需要更加低颗粒度;同时考虑到再业务高峰期(下午到晚上,就是各位疯狂刷淘宝,京东的时候)各个服务的负载是各不相同的,如果在高峰期对于特定服务需要进行精准扩容的话,拆分的颗粒度也需要更加细致,所以结果上导致整个系统满血运行起来要至少要十几个服务,外加一些没有云平台的中间件。整个Kubernetes环境中,至少要有十五六个Deployment,考虑到系统稳定性,一个Deployment也至少要有2到3个Pod,那整个系统至少就是50个Pod。 - -对于一个中大型的电商企业再服务器成本上可以负担这样一个能支持50个Pod的Kubernentes集群(考虑到需要一些中间件的云产品,当时估算最小的服务器成本都要到70K/年,一个中大型企业能面对双十一流量的服务器成本都要到300K/年以上)。所以整个系统对于中小型企业来说前期的投入负担相当大(尤其是在后疫情时代的2022年,2023年),以至于早起的好几个客户都是需要我们自己贴补服务器费用的。 - -# 复盘 - -现在重新复盘当初服务的拆分策略,个人觉得对于核心业务来说,相似领域的一些服务应该可以适当的聚合。比如上述的五大核心服务上中的`商品服务`,`订单服务`,`库存服务`三个核心服务应该可以合并在一起。首先这3个核心服务是`H项目`的最重要的竞争力,几乎不可能出现把这3个服务拆分出售的可能性。 - -而且在业务高峰期有`订单服务`有新订单处理时,势必会产生库存的变化,因此会调用到`库存服务`。三个服务之间的调用是相当频繁的,尤其是负责处理商品主档数据的`商品服务`,无论是通过HTTP或是其他方式的远程调用的话,都会有许多浪费的带宽。如果把三个服务整合,那么之间的调用基本上就是线程间的交互,无论是效率和可靠性上都会有更加显著的提升。 - -# 结尾 - -怎么拆分服务一直是个难题。虽然事后复盘的时候我的很多想法也不一定是正确,但是什么样的规模的客户需要使用什么样的拆分颗粒度,都是需要仔细考虑的。绝对不是一尘不变的。 \ No newline at end of file diff --git "a/source/_posts/Lagrange\351\241\271\347\233\256\345\233\236\351\241\2762.md" "b/source/_posts/Lagrange\351\241\271\347\233\256\345\233\236\351\241\2762.md" deleted file mode 100644 index 51178c5..0000000 --- "a/source/_posts/Lagrange\351\241\271\347\233\256\345\233\236\351\241\2762.md" +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: Largrange项目架构与设计回顾 (二) -tags: - - 架构 - - Kubernetes - - Aliyun - - Java -categories: - - 设计 -date: 2020-05-21 10:49:41 ---- - - -在[Largrange项目架构与设计回顾 (一)](/2020/05/09/lagrange项目回顾/) 里面我讲了一下项目开始架构和技术选型的一些内容。这一章来聊聊业务设计上的这点事。 - -总体来说项目设计的时候,我们对业务模块的划分和拆解总体上来说都是遵循着高内聚,低耦合的原则来进行划分的。大部分和业务相关的服务,都还是能很好的进行功能划分的。但是有一些和具体业务关联性并不是很高的服务在界定与实现时出现了一些问题。 - -## 任务调度服务 - -由于平台方面会有一些控制命令下发给Android设备,而且下发的命令并不一定都是实时的,大部分都是指定一个时间来下发,所以在设计的时候就考虑到需要一个任务调度服务(以下略称`cron`),来处理这些下发指令以及未来可能会有的定时批处理任务的业务需求。技术选型的时候考虑到整个服务是构建在Kubernetes上的一个分布式的微服务架构,所以就没有选择Spring Scheduler,而是使用了Quarter来支撑整个`cron`。 - -从技术选型上来说`cron`没有什么问题,但是在设计如何使用`cron`上还是有点问题的, 我们先来看看已推送服务为例,整个平台中`cron`的处理流程是怎样的。 - -1. `Platform` 调用 `cron`的创建定时任务Job的接口(接口的参数为推送时使用的相关参数) -2. `cron` 创建Quartz的Job和Trigger,并将Job和Trigger的Name(Quartz中Job和Trigger的唯一标识符)返回个`Platform` -3. `cron` 在指定时间触发推送的Job,即调用`Push`服务的推送接口 -4. `Push`的推送接口调用第三方服务商的推送服务,并将第三方推送服务的调用结果返回给`cron` -5. `cron`通过调用`Platform`预留的推送服务回调地址将第三方推送服务调用接口返还给`Platform` -6. `Platform` 将第三方推送服务的调用结果留档保存,并继续业务处理 - -设计之初考虑到不想在`cron`中牵扯到具体的业务, 所以设计了一个`Platform`的回调接口来处理推送后的具体业务处理。虽然保证了`cron`尽量减少了和业务逻辑接触与数据库的访问。但是前前后后要访问集群内部的其他微服务2次,额外增加了网络开销,以及因为网络通信造成额外的通信失败风险。而且针对每种不同的定时任务,都需要额外开发一个QuartzJobBean,很难形成统一的QuartzJobBean来进行处理。今后如果还有相似项目还是需要重新考量一下如何设计一个更加完善的定时任务。 - -## 总结 - -暂时就想到了这些东西,今后想到啥还会继续在这里补存。 diff --git "a/source/_posts/Spring-Cloud-Kubernetes\347\216\257\345\242\203\344\270\213\344\275\277\347\224\250Jasypt.md" "b/source/_posts/Spring-Cloud-Kubernetes\347\216\257\345\242\203\344\270\213\344\275\277\347\224\250Jasypt.md" deleted file mode 100644 index 29be9db..0000000 --- "a/source/_posts/Spring-Cloud-Kubernetes\347\216\257\345\242\203\344\270\213\344\275\277\347\224\250Jasypt.md" +++ /dev/null @@ -1,187 +0,0 @@ ---- -title: Spring Cloud Kubernetes环境下使用Jasypt -tags: - - Java - - Spring -categories: - - 后端 -date: 2021-09-29 11:28:11 ---- - - -# 前言 - -最近半年着手开始做了基于微服务的中台项目,整个项目的技术栈采用的是`Java` + `Spring Cloud` + `Kubernetes` + `Istio`。 - -业务开放上还是相当顺利的。但是在安全审核上,运维组提出了一个简易。现在项目一些敏感配置,例如MySQL用户的密码,Redis的密码等现在都是明文保存在Kubernetes的ConfigMap中的(是的,我们并没有Nacos作为微服务的配置中心)。这样可能存在安全隐患。 - -# 首次尝试 - -既然有问题,那就解决问题。要给配置文件中的属性项目加密很简单,稍微Google一下,就有现成的方案了。 - -现在比较常用的解决方案就是集成`Jasypt`,然后通过`jasypt-spring-boot-starter`来融合进Spring。 - -### POM包加入`jasypt-spring-boot-starter` - -```xml - - com.github.ulisesbocchio - jasypt-spring-boot-starter - 3.0.4 - -``` - -### Dockerfile中增加java参数 - -``` -... -ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar app.jar --jasypt.encryptor.password=helloworld $PARAMS"] -``` - -### 在ConfigMap中添加加密属性 - -```yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: demo -data: - application.yaml: |- - test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7) -``` - -### 利用`actuator`接口测试 - -在`management.endpoints.web.exposure.include`属性中增加`env`,这样我们就可以通过调用`/actuator/env`来查看一下`env`接口返回的整个Spring 容器中所有的PropertySource。 - -```json -{ - ... - "propertySources": [ - { - "name": "bootstrapProperties-configmap.demo.default", - "properties": { - "test2": { - "value": "Hello,world" - } - } - } - ... - ] -} -``` - -OK, 这下配置项已经加密了。问题解决了。 - -但是... - -## 新的问题 - -自从项目集成了`Jayspt`以后,出现了一个奇怪的问题。每次项目试图通过修改ConfigMap的配置文件,然后试图通过`spring-cloud-starter-kubernetes-fabric8-config`来做自动Reload,都失败了。然而查阅应用日志,并没有出现任何异常。无奈只能打开`spring-cloud`和`jasypt-spring-boot`的`DEBUG`日志。 - -进过几天对日志和两边源代码的分析。终于找到了原因 - -### 原因 - -在Spring Boot启动时`jasypt-spring-boot`会将下面6种配置(并不仅限与这6种配置文件) - -- `Classpath`下的`application.yaml` -- `Classpath`下的`bootstrap.yaml` -- 集群里名称为`${spring.cloud.kubernetes.config.name}`的ConfigMap -- 集群里名称为`${spring.cloud.kubernetes.config.name}-kubernetes`的ConfigMap -- Java启动参数 -- 环境变量 - -转换成`jasypt-spring-boot`自己的PropertySource实现类`EncryptableMapPropertySourceWrapper`。 - -但是如果使用Kubernetes的ConfigMap来作微服务配置中心的时候,Spring Cloud会在`ConfigurationChangeDetector`中查找配置类`org.springframework.cloud.bootstrap.config.BootstrapPropertySource`, 并依据`BootstrapPropertySource`的类型来判断容器内的配置与集群中ConfigMap里的配置是否有差异,来触发配置reload。 - -由于`jasypt-spring-boot`已经将所有的配置文件转型成了`EncryptableMapPropertySourceWrapper`, 所以`ConfigurationChangeDetector`无法找到`BootstrapPropertySource`所以会一直任务ConfigMap的里的配置没有变化,导致整个Reload失效(无论是使用polling还是event方式) - -### 解决问题 - -为了保证ConfigMap变化后自动Reload的功能,所以`jasypt-spring-boot`不能把`BootstrapPropertySource`转换成`EncryptableMapPropertySourceWrapper` - -所以我们需要设置`jasypt.encryptor.skip-property-sources`配置项, Classpath中的application.yaml需要增加配置 - -```yaml -jasypt: - encryptor: - skip-property-sources: org.springframework.cloud.bootstrap.config.BootstrapPropertySource -``` - -`skip-property-sources`配置项配置后,加密项目就不能配置在ConfigMap里了,毕竟已经被我们忽略了。那么我们只能另外找一个PropertySource来存放加密项目了。 - -`Classpath`中的两个Yaml由于编译时会被Maven打包进Jar文件,会牵涉多个CI/CD多个流程显然不合适,启动参数配置项的也要影响到Docker镜像制作这个流程。所以判断下来最适合的PropertySource就是环境变量了。 - -### 环境变量增加加密项 - -在Kubernetes的部署Yaml中,添加加密数据项`application.test.str` - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: demo - name: demo -spec: - replicas: 1 - selector: - matchLabels: - app: demo - template: - metadata: - labels: - app: demo - spec: - containers: - - env: - - name: TZ - value: Asia/Shanghai - - name: application.test.str - value: >- - ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7) - .... -``` - -如果需要更加严密的加密方针的话,我们可以把环境变量的内容放进Kubernetes的Secrets中。 - -### 在ConfigMap中引用`application.test.str` - -```yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: demo -data: - application.yaml: |- - test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7) - test3: ${application.test.str} -``` - -### 通过`actuator`接口来测试 - -通过`actuator\env`接口来测试一下 - -```json -{ - ... - "propertySources": [ - { - "name": "bootstrapProperties-configmap.demo.default", - "properties": { - "test2": { - "value": "ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)" - }, - "test3": { - "value": "Hello,world" - } - } - } - ... - ] -} -``` - -这样ConfigMap中的配置项`test3`就可以通过环境变量引用并使用加密配置项了。同时修改ConfigMap依然可以触发auto reload了。这下终于算是解决了。 \ No newline at end of file diff --git a/source/_posts/hot-to-install-nodejs-nginx-mongodb-on-ubuntu.md b/source/_posts/hot-to-install-nodejs-nginx-mongodb-on-ubuntu.md deleted file mode 100644 index e5e9eb0..0000000 --- a/source/_posts/hot-to-install-nodejs-nginx-mongodb-on-ubuntu.md +++ /dev/null @@ -1,97 +0,0 @@ ---- -title: 在ubuntu上从零搭建node.js + nginx + mongodb环境 -tags: - - ubuntu - - node.js - - nginx - - mongodb -categories: - - 后端 -date: 2019-12-09 10:40:05 ---- - - -说到后端开发环境,最有名的莫过于LAMP和LNMP,最近由于node.js的强势崛起,越来越多的后端开发也开始试水node.js了。我最近也因为各种原因,前前后后总够构建了好几台node.js + nginx + mongodb的Linux服务器。 - -首先关于Linux服务器,比起CentOS来说,我更加喜欢ubuntu一点。所以无论是阿里云还是一些海外的vps服务器上,我也倾向选用ubuntu服务器,本贴也是基于ubuntu服务器里说明的。 - -## 1.开始前的一些准备 - -首先还是需要刷新一下ubuntu的包索引并安装build-essential和libssl-dev这2个包以及curl这个工具。 - -```sh -sudo apt-get update -sudo apt-get install build-essential libssl-dev -sudo apt-get isntall curl -``` - -## 2.安装node.js - -关于安装node.js这一点,我不是很推荐使用apt-get 来安装node.js的环境。主要是因为node.js和io.js合并以后,版本迭代速度相当频繁(主要还是因为更多ES6的特性得到了支持)。今后很有可能会有在一台服务器上使用不同版本的node.js的需求。 - -这里推荐一个管理不同版本node.js的工具:nvm,官网: [https://github.com/creationix/nvm](https://github.com/creationix/nvm)  。安装nvm,如果前面你安装了curl的话可以 - -```sh -curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash -``` -如果没有按照curl的话,也可以使用wget来进行安装 - -```sh -wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash -``` - -然后nvm就会自动安装到home目录下面的.nvm目录里,并会在.bashrc里自动添加nvm的环境变量。为了让环境变量生效,最简单的方法就是通过ssh或是telnet重新连接你的服务器。 - -安装完nvm后,就可以通过nvm来安装指定版本的node.js了。 - -```sh -# 列出可以安装的node版本号 -nvm ls-remote - -# 安装指定版本的node (当前最新版本为v5.7.1, LTS版是v4.3.2) -nvm install v4.3.2 -``` - -## 3.安装nginx - -由于ubuntu源(尤其是阿里云的源)上的nginx经常不是最新的,如果需要安装最新版本nginx的时候需要手动添加nginx的源。 - -```sh -# 添加nginx的mainline仓库 -cd /tmp/ && wget http://nginx.org/keys/nginx_signing.key -sudo apt-key add nginx_signing.key - -# 编辑/etc/apt/sources.list.d/nginx.list 添加下面2行内容,井号不需要 -# deb http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx -# deb-src http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx -sudo vi /etc/apt/sources.list.d/nginx.list - -# 更新源,并安装nginx -sudo apt-get update && sudo apt-get install nginx -``` - -在编辑/etc/apt/sources.list.d/nginx.list的时候需要注意,“ubuntu代号”需要根据ubuntu服务器的版本不同手动调整的,比如14.04是trusty。通过下面的命令可以获取ubuntu的代号。 -```sh -lsb_release -cs -``` - -## 4.安装mongodb - -同样和nginx有同样的问题,要安装最新3.2版本的mongodb也需要手动添加ubuntu的源。 - -```sh -# 导入mongodb的public key -sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927 - -# 生成mongodb的源list -echo "deb http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list - -# 更新源 -sudo apt-get update - -# 安装最新版本的mongodb -sudo apt-get install -y mongodb-org -``` - -以上一台node.js + nginx + mongodb的ubuntu服务器就完成了。 - diff --git "a/source/_posts/lagrange\351\241\271\347\233\256\345\233\236\351\241\276.md" "b/source/_posts/lagrange\351\241\271\347\233\256\345\233\236\351\241\276.md" deleted file mode 100644 index f52ca4b..0000000 --- "a/source/_posts/lagrange\351\241\271\347\233\256\345\233\236\351\241\276.md" +++ /dev/null @@ -1,71 +0,0 @@ ---- -title: Largrange项目架构与设计回顾 (一) -tags: - - 架构 - - Kubernetes - - Aliyun - - Java -categories: - - 设计 -date: 2020-05-09 10:34:47 ---- - - -> 项目背景 - -从去年年底开始一个老客户希望在他们的一个传统的机械设备(后面略称 `E机关` )上外装一个Android设备。 Android设备和 `E机关` 之间通过串口或是RJ45接口进行数据交互,主要是Android设备获取 `E机关` 内部的数据,并不会通过接口来控制 `E机关` 。 Android设备则通过4G 来和平台交互,上报Android设备的状态数据,同时接受平台的控制。以此来实现让传统机械设备也能拥抱物联网的概念。 - -最后围绕着客户的需求分成了3个项目来并行推进 - -1. Android设备的硬件设备的设计、选材、样机制作与量产规划 -2. 在Android设备上,进行与`E机关`以及平台进行交互的APP开发 -3. 用于Android设备交互的平台的架构、设计与开发 - -我们平台Team就负责 `3.用于Android设备交互的平台` 并命名Lagrange (拉格朗日) 。 - -# 设计&架构 - -整个平台这块不仅需要向Android设备的APP提供数据交互接口,还需要有一个供相关运营人员使用的前端Web应用,以及与云平台(主要是Aliyun) 交互的功能。所以考虑到多方面使用微服务的架构来实现整个平台端。 -由于`E机关`的工作工况不能保证长期较的稳定的连接到4G网络,所以我们并不考虑使用Socket长连接的方式来做APP和平台之间的数据交互,要实现平台对Android设备进行反控的话,只能实现推送方式来把控制命令下发给Android设备,所以还需要有一个第三方推送服务商交互的服务。同时控制推送也有实时和非实时以及定期推送的需求,所以可能还需要一个任务调度的服务。 - -根据对上述业务进行梳理,我们将项目分成几个服务 - -- 提供Android设备的交互接口的 `App Service` -- 提供运营人员使用前端Web应用 `Platform Web Service` -- 前端Web应用使用到的一些接口 `Platform Service` -- 负责第三方推送服务商交互的 `Push Service` -- 提供云平台鉴权用的 `Auth Service` -- 用来管理任务调度的 `Cron Service` - -由于前一个项目实施的时候没有使用容器部署,每当访问量峰值的时候,我们这些码农兼运维就各种加班,所以这次项目决定直接将服务容器化,同时选择了`Kubernetes`来管理容器。由于客观原因线上的`Kubernetes`直接购买了Aliyun的托管版`Kubernetes`服务。 内部的开发测试环境则使用了 `Rancher 2.0` 来构建`Kubernetes`集群。 - - -# 技术选型 - -## a. 后端服务选型 - -由于不考虑长连接的原因,所以在后端服务在技术选型上基本就不考虑Netty了,直接上Spring大礼包。 - -- Spring Boot 开发接口 -- Spring Data 配合 JPA 来进行数据的持久化 -- Spring Cloud Kubenetes 来做数据的Config的autoreload -- Quartz 负责处理任务调度 - -服务之间调用都使用HTTP服务, 服务发现也有`Kubernetes`的DNS机制支持。服务网格则选择了比较成熟的`Istio`,主要还是Aliyun的`Kubernetes`可以集成`Istio`,部署和使用都相当方便。 - -## b. 前端Web应用选型 - -因为前端Web应用主要是给运营人员使用,所以我们考虑使用Single Page Application来做个前后端分离的Web应用。框架这块由于团队成员基本上没有什么前端开发经验,基本都是后台写Java的码农,所以框架选择有点随性,直接就点名了vue.js。前端控件库则用的是阿里系的Antd。 - -## c. 数据持久层 - -主数据库选择了mysql,缓存用的redis。这些都是团队比较熟悉的。由于一些特殊的业务需求和使用场景,我们还加了一个mongodb来做为一个副数据库,主要存放一些特殊业务使用的数据。 - -## d. DevOps - -用了相当传统的GitLab CE 加上Jenkins的组合,实现前后端代码的自动编译,推送到私有的镜像仓库(使用Aliyun的镜像仓库服务) - -# 最后 - -以上基本是项目最早做设计时候的各种考量。暂时先写这么多,过两天再回顾一下当初设计上有哪些觉得不足的地方。 - diff --git "a/source/_posts/\351\207\215\345\255\246Java-\344\270\200-\346\263\233\345\236\213.md" "b/source/_posts/\351\207\215\345\255\246Java-\344\270\200-\346\263\233\345\236\213.md" deleted file mode 100644 index 70f6b25..0000000 --- "a/source/_posts/\351\207\215\345\255\246Java-\344\270\200-\346\263\233\345\236\213.md" +++ /dev/null @@ -1,359 +0,0 @@ ---- -title: 重学Java (一) 泛型 -date: 2021-03-15 16:01:46 -tags: - - java - - generic -categories: - - 后端 ---- - -## 1. 前言 - -泛型编程自从 Java 5.0 中引入后已经超过15个年头了。对于现在的 Java 码农来说熟练使用泛型编程已经是家常便饭的事情了。所以本文就在不对泛型的基础使用在做说明了。 如果你还不会使用泛型的话,可以参考下面两个链接 - -- [Java 泛型详解](https://blog.csdn.net/qq_24084925/article/details/68491132) -- [The Java™ Tutorials (Lesson: Generics)](https://docs.oracle.com/javase/tutorial/java/generics/index.html) - -这篇文章就简答聊一下,我实际在开发工作中很少用的到泛型方法这个知识点,以及在实际项目中有哪些东西会使用到泛型。 - -## 2. 泛型方法 - -在阅读代码的时候我们经常会看到下面这样的方法 (这段代码摘自 `java.util.AbstractCollection`) - -```java - public T[] toArray(T[] a) { - // Estimate size of array; be prepared to see more or fewer elements - int size = size(); - T[] r = a.length >= size ? a : - (T[])java.lang.reflect.Array - .newInstance(a.getClass().getComponentType(), size); - Iterator it = iterator(); - - for (int i = 0; i < r.length; i++) { - if (! it.hasNext()) { // fewer elements than expected - if (a == r) { - r[i] = null; // null-terminate - } else if (a.length < i) { - return Arrays.copyOf(r, i); - } else { - System.arraycopy(r, 0, a, 0, i); - if (a.length > i) { - a[i] = null; - } - } - return a; - } - r[i] = (T)it.next(); - } - // more elements than expected - return it.hasNext() ? finishToArray(r, it) : r; -} -``` - -那么 `pulic` 关键字后面的那个 `` 就是用来标记这个方法是一个泛型方法。 那什么是泛型方法呢。 - -官方的解释是这样的 - -``` -Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors. -``` - -通俗点来将就是将一个方法泛型化,让一个普通的类的某一个方法具有泛型功能。 如果在一个泛型类中增加一个泛型方法,那这个泛型方法就可以有一套独立于这个类的泛型类型。 - -通过一个简单的例子, 我们来看看 - -```java -/** - * GenericClass 这个泛型类是一个简单的套皮的 HashMap - */ -public class GenericClass { - - private Map map = new HashMap<>(); - - public V put(K key, V value) { - return map.put(key, value); - } - - public V get(K key) { - return map.get(key); - } - - // 泛型方法 genericMethod 可以接受一个全新的、作用域只限本函数的泛型类型T - public T genericMethod(T t) { - return t; - } - -} -``` - -实际使用起来 - -```java -GenericClass map = new GenericClass<>(); -// put 和 get 方法的参数必须使用定义时指定的 String 和 Integer -System.out.println(map.put("One", 1)); -System.out.println(map.get("One")); -// 泛型方法 genericMethod 就可以接受一个 String 和 Integer 以外的类型 -System.out.println(map.genericMethod(new Double(1.0)).getClass()); -``` - -我们再来看看 JDK 中使用到泛型方法的例子。我们最常使用的泛型容器 `ArrayList` 中有个 `toArray` 方法。JDK 在它的实现中就提供了两个版本,其中一个就是泛型方法的版本 - -```java -public class ArrayList extends AbstractList - implements List, RandomAccess, Cloneable, java.io.Serializable -{ - // 这是一个普通版本,返回一个Object的数组 - public Object[] toArray() { - return Arrays.copyOf(elementData, size); - } - - // 这是一个泛型方法的版本,将容器里存储的元素输出到 T[] 数组中。 其中 T 必须是 E 的父类,否则 System.arraycopy 会抛出 ArrayStoreException 异常 - public T[] toArray(T[] a) { - if (a.length < size) - // Make a new array of a's runtime type, but my contents: - return (T[]) Arrays.copyOf(elementData, size, a.getClass()); - System.arraycopy(elementData, 0, a, 0, size); - if (a.length > size) - a[size] = null; - return a; - } -} -``` - -泛型方法总体上来说就是可以给与现有的方法实现上,增加一个更加灵活的实现可能。 - -## 3. 实战应用 - -在实际的项目中,对于泛型的使用,除了像倾倒垃圾一样往泛型容易里塞各种 java bean 和其他泛型对象。还能怎么使用泛型呢? - -我们在实际的一些项目中,会对数据库中的一些表(多数时候是全部)先实现 CRUD (Create, Read, Update, Delete)的操作,并从这些操作中延伸出一些简单的 REST 风格的 WebAPI 接口,然后才会根据实际业务需要实现一些更复杂的业务接口。 - -大体上会是下面这个样子。 - -```java - -// 这是一个简单的 Entity 对象 -// 通常现在的 Java 应用都会使用到 Lombok 和 Spring Boot -@Data -@AllArgsConstructor -@NoArgsConstructor -@ToString -@Entity -@Table(name = "user") -public class User { - @Id - private Long id; - private String username; - private String password; -} - -// 然后这个是 DAO 接口继承自 spring-data 的 JpaRepository -public interface UserDao extends JpaRepository { -} - -// 在来是一个访问 User 资源的 Service 和他的实现 -public interface UserService { - List findAll(); - Optional findById(Long id); - User save (User user) - void deleteById(Long id); -} - -@Service -public class UserSerivceImpl implements UserService { - private UserDao userDao; - public UserServiceImpl(UserDao userDao) { - this.userDao = userDao; - } - - @Override - public List findAll() { - return this.dao.findAll(); - } - - @Override - public Optional findById(Long id) { - return this.dao.findById(id); - } - - @Override - public User save(User user) { - return this.dao.save(user); - } - - @Override - public void deleteById(Long id) { - this.dao.deleteById(id); - } -} - -// 最后就是 WebAPI 的接口了 -@RestController -@RequestMapping("/user/") -public class UserController{ - private UserService userService; - public UserController(userService userService) { - this.userService = userService; - } - - @GetMapping - @ResponseBody - public List fetch() { - return this.userService.findAll(); - } - - @GetMapping("{id}") - @ResponseBody - public User get(@PathVariable("id") Long id) { - // 由于是示例这里就不考虑没有数据的情况了 - return this.userService.findById(id).get(); - } - - @PostMapping - @ResponseBody - public User create(@RequestBody User user) { - return this.userService.save(user); - } - - @PutMapping("{id}") - @ResponseBody - public User update(@RequestBody User user) { - return this.userService.save(user); - } - - @DeleteMapping("{id}") - @ResponseBody - public User delete(@PathVariable("id") Long id) { - User user = this.userService.findById(id); - this.userService.deleteById(id); - return user; - } -} -``` - -大致一个表的一套相关接口就是这个样子的。如果你的数据库中有大量表的话,而且每个表都需要提供 REST 风格的 WebAPI 接口的话,那么这将是一个相当枯燥的而又及其容易出错的工作。 - -为了不让这项枯燥而又容易犯错的工作占去我们宝贵的私人时间,我们可以通过泛型和继承的技巧来重构从 Service 层到 Controller 的这段代码(感谢 spring-data 提供了 `JpaRepository`, 让我们不至于从 DAO 层重构) - -## 3.1 Service 层的重构 - -首先是 Service 接口的重构,我们 Service 层接口就是定义了一组 CRUD 的操作,我们可以将这组 CRUD 操作抽象到一个父接口,然后所有 Service 层的接口都将继承自这个父接口。而接口中出现的 Entity 和主键的类型(上例中 User 的主键 id 的类型是 Long)就可以用泛型来展现。 - -```java -// 这里泛型表示 E 来指代 Entity, ID 用来指代 Entity 主键的类型 -public interface ICrudService { - List findAll(); - Optional findById(ID id); - E save(E e); - void deleteById(ID id); -} - -// 然后 Service 层的接口,就可以简化成这样 -public interface UserService extends ICrudService { -} -``` - -同样 Service 层的实现也可以使用相似的方法具体实现可以抽象到一个基类中。 - -```java -// 相比 ICrudService 这里有多了一个泛型 T 来代表 Entity 对应的 DAO, 我们的每一个 DAO 都继承自 -// spring-data 的 JpaRepository 所以,这里可以使用到泛型的边界 -public abstract class AbstractCrudService, E, ID> { - private T dao; - public AbstractCrudService(T dao) { - this.dao = dao; - } - - public List findAll() { - return this.dao.findAll(); - } - - public Optional findById(ID id) { - return this.dao.findById(id); - } - - public E save(E e) { - return this.dao.save(e); - } - - public void deleteById(ID id) { - this.dao.deleteById(id); - } -} - -// 那 Service 的实现类可以简化成这样 -@Service -public class UserServiceImpl extends AbstractCrudService implements UserService { - public UserServiceImpl(UserDao dao) { - supper(dao); - } -} -``` - -同样我们可以通过相同的方法来对 Controller 层进行重构 - -```java -// Controller 层的基类 -public abstract class AbstractCrudController, E, ID> { - private T service; - public AbstractCrudController(T service) { - this.service = service; - } - - @GetMapping - @ResponseBody - public List fetch() { - return this.service.findAll(); - } - - @GetMapping("{id}") - @ResponseBody - public E get(@PathVariable("id") ID id) { - // 由于是示例这里就不考虑没有数据的情况了 - return this.service.findById(id).get(); - } - - @PostMapping - @ResponseBody - public E create(@RequestBody E e) { - return this.service.save(e); - } - - @PutMapping("{id}") - @ResponseBody - public E update(@RequestBody E e) { - return this.service.save(e); - } - - @DeleteMapping("{id}") - @ResponseBody - public E delete(@PathVariable("id") ID id) { - E e = this.service.findById(id).get(); - this.service.deleteById(id); - return e; - } -} - -// 具体的 WebAPI -@RestController -@RequestMapping("/user/") -public class UserController extends AbstractCrudController { - public UserController(UserService service) { - super(service); - } -} -``` - -经过重构可以消减掉 Servcie 和 Controller 中的大量重复代码,使代码更容易维护了。 - -## 4. 结尾 - -关于泛型就简单的说这些了,泛型作为 Java 日常开发中一个常用的知识点,其实还有很多知识点可以供我们挖掘,奈何本人才疏学浅,这么多年工作下来,只积累出来这么点内容。 - -文末放上示例代码的代码库: - -- [GitHub入口](https://github.com/hashmaparraylist/re-study-java) -- [gitee入口](https://gitee.com/hashmaparraylist/re-study-java) - diff --git "a/source/_posts/\351\230\277\351\207\214\344\272\221Kubernetes\344\270\212\347\272\277\350\270\251\345\235\221\350\256\260.md" "b/source/_posts/\351\230\277\351\207\214\344\272\221Kubernetes\344\270\212\347\272\277\350\270\251\345\235\221\350\256\260.md" deleted file mode 100644 index 8708c23..0000000 --- "a/source/_posts/\351\230\277\351\207\214\344\272\221Kubernetes\344\270\212\347\272\277\350\270\251\345\235\221\350\256\260.md" +++ /dev/null @@ -1,59 +0,0 @@ ---- -title: 阿里云Kubernetes上线踩坑记 -date: 2020-04-01 09:47:38 -tags: - - Aliyun - - Kubernetes -categories: - - 后端 - - Cloud ---- - -``` -Update: -2020-04-08 增加istio-ingressgateway高可用的设置 -``` - -最近公司因为项目需要,在阿里云上部署了一个Kubernetes集群。虽然阿里云的文档说的还算细致,但是还是有些没有明确说明的细节。 - -# 1. 购买篇 - -申请项目预算的时候,只考虑到Worker节点,1个SLB节点以及域名和证书的预算。但是实际购买的时候发现还有许多额外的开销。 - -## 1.1 SNAT - -这个和EIP一并购买,可以方便通过公网使用kubectl访问集群。关于SNAT网关至今不是很明白需要购买这个服务的意义何在,只是为了一个EIP来访问集群吗? - -## 1.2 Ingress - -这个选上了后,阿里云会给你买个SLB而且还是带公网访问的,如果你后期考虑使用Istio的话,建议你集群创建后,直接停止这个SLB,以免产生额外的费用。 - -## 1.3 日志服务 - -通过阿里云的日志服务来收集应用的的日志,挺好用的。但是另外收费,如果有能力的自建日志服务的可不购买。 - -# 2. Istio - -阿里云的Kubernetes集群完美集成了Istio,根据向导就能很简单的部署成功。 - -## 2.1 额外的SLB - -Istio的Gateway 需要绑定一个新的SLB,和Ingress的SLB不能是同一个,又是一笔额外的开销 - -## 2.2 集群外访问 - -这个在阿里云的Istio FAQ中有提到,按照指导很容易解决 - -## 2.2 SLB的443监听 - -为了方便443端口的证书绑定,我们直接删除了SLB上原有的443监听(TCP协议), 重新建了一个443监听(HTTPS协议),指向和80端口同样的虚拟服务器组。但是设置健康检查时一直出错,经过排查发现SLB健康检查发送的请求协议是HTTP 1.0的,Istio的envoy直接反悔了`426(Upgrade Required)`这个状态码,所以我们无奈只能把健康检查的检查返回状态改为http_4xx,这样就能通过SLB的健康检查了。 - -## 2.3 istio-ingressgateway的高可用 - -`istio-ingressgateway`要达成高可用,只需要增加通过伸缩POD就可以实现,于`istio-ingressgateway`对应的SLB中的虚拟服务器组也会自动增加,完全不需要进行额外的手动设定。 - -由于`istio-ingressgateway`中挂载了HPA`HorizontalPodAutoscaler`(简称HPA),通常三节点的集群中最小POD数只有1台,在3节点的集群中,要实现高可用,需要手动修改HPA,增加最小POD数。 - ---- - -基本上现在遇到了这些坑,再有在总结吧。 diff --git a/tags/Aliyun/index.html b/tags/Aliyun/index.html new file mode 100644 index 0000000..325dfa4 --- /dev/null +++ b/tags/Aliyun/index.html @@ -0,0 +1,251 @@ + +标签: Aliyun - 西门闲话

H项目心路历程记(三)

现状

对于一个基于微服务的中台系统来说,需要以什么样的颗粒度来拆分系统。这一直是基于微服务架构的系统中绕不过去的问题。

+

已H项目来说,当初考量是这么划分微服务的,其中

+
    +
  • 基础服务: 作用主要是负责用户,角色权限,登陆鉴权,以及一些基础主档数据的日常维护功能
  • +
  • 商品服务: 物料,销售商品,款式,平台商品等和实际商品相关的主档数据
  • +
  • 库存服务:负责管理各个库存层级库存(总库存,冻结库存,在途库存等),以及展示各种出入库单据,并向其他微服务提供出入库服务接口。
  • +
  • 订单服务: 负责接收各个公域平台的ToC订单,售后单(含发货前退款,退货,换货单等),并根据配置策略进行单据路由(分仓,分物流,Hold单,加送赠品,通知仓库发货,通知平台发货等)
  • +
  • 接口服务: 主要承揽中台服务和第三方平台间的交互处理(主要是第三方平台发起的主调通知以及回调)
  • +
+

上述五个服务为核心服务,是保证整个中台系统基础业务功能运行的重要组成部分。除此之外还有,

+
    +
  • 采购服务:负责ToB业务线的处理
  • +
  • 财务服务:满足一些企业的财务对账以及开具电子发票的功能
  • +
  • 报表服务: 每天,每周定期从其他服务(主要是订单)
  • +
  • 会员服务:管理各类会员
  • +
+

等7,8个非核心服务,组成非核心业务。来构成一个完整的中台系统。

+

拆分策略以及缺点

当时主要考虑到整个中台系统是可以拆分出售,客户可以只选择购买自身需要的服务,而不需要的服务则无需购买服务,所以当初在拆分服务上在业务领域层面上需要更加低颗粒度;同时考虑到再业务高峰期(下午到晚上,就是各位疯狂刷淘宝,京东的时候)各个服务的负载是各不相同的,如果在高峰期对于特定服务需要进行精准扩容的话,拆分的颗粒度也需要更加细致,所以结果上导致整个系统满血运行起来要至少要十几个服务,外加一些没有云平台的中间件。整个Kubernetes环境中,至少要有十五六个Deployment,考虑到系统稳定性,一个Deployment也至少要有2到3个Pod,那整个系统至少就是50个Pod。

+

对于一个中大型的电商企业再服务器成本上可以负担这样一个能支持50个Pod的Kubernentes集群(考虑到需要一些中间件的云产品,当时估算最小的服务器成本都要到70K/年,一个中大型企业能面对双十一流量的服务器成本都要到300K/年以上)。所以整个系统对于中小型企业来说前期的投入负担相当大(尤其是在后疫情时代的2022年,2023年),以至于早起的好几个客户都是需要我们自己贴补服务器费用的。

+

复盘

现在重新复盘当初服务的拆分策略,个人觉得对于核心业务来说,相似领域的一些服务应该可以适当的聚合。比如上述的五大核心服务上中的商品服务,订单服务,库存服务三个核心服务应该可以合并在一起。首先这3个核心服务是H项目的最重要的竞争力,几乎不可能出现把这3个服务拆分出售的可能性。

+

而且在业务高峰期有订单服务有新订单处理时,势必会产生库存的变化,因此会调用到库存服务。三个服务之间的调用是相当频繁的,尤其是负责处理商品主档数据的商品服务,无论是通过HTTP或是其他方式的远程调用的话,都会有许多浪费的带宽。如果把三个服务整合,那么之间的调用基本上就是线程间的交互,无论是效率和可靠性上都会有更加显著的提升。

+

结尾

怎么拆分服务一直是个难题。虽然事后复盘的时候我的很多想法也不一定是正确,但是什么样的规模的客户需要使用什么样的拆分颗粒度,都是需要仔细考虑的。绝对不是一尘不变的。

+

H项目心路历程记 (二)

(书接上文)

+

前文已经吐槽了产品设计以及中间件上的相关问题。接下来讨论一下技术上的一些问题。

+

关于配置

对于一个Spring的应用来说,应用配置怎么存取其实并不能说是一个技术问题。更应该算是设计或是架构问题。哪些配置应该进入配置文件,通过微服务配置中心(例如Nacos, Apollo,Kubernetes ConfigMap等)来进行在线配置,哪些配置应该通过其他方式来配置,亦或者哪些配置项目看似是配置项其实本质上就是常量。实际上截止现在H项目的确在有些微服务上是完全没有厘清三者之间的区别,以及界限。以至于现在堆积在配置文件中的内容实在太多。多个运行环境需要统一维护的难度实在太大。

+

对于配置来说,理想的状态应该是下面这样的。

+
    +
  1. 通过微服务配置中心来影响Spring的配置文件来实现动态配置的方案,通常会伴随Spring容器的重启(有些情况下甚至需要整个Docker容器),所以存在这里的配置项应该是随着运行环境改变才会需要改动的配置。例如:

    +
      +
    • 外部系统的链接信息(比如MySQL, Redis的连接信息)
    • +
    • 外部系统的认证凭证(数据库用户密码,第三方系统的密钥等)
    • +
    • 因为运行环境不同导致的不同变量 (例如消息队列的Topic,Group需要根据运行环境使用不同的名称)
    • +
    +
  2. +
  3. 通过数据库或是Redis存储的配置信息,这类配置主要是应该以业务级别的配置,可以通过页面进行修改,瞬间或是几分钟内生效,且不会造成微服务重启(无论是Spring或是Docker容器的重启)。对于这类配置有几个问题还是需要注意的。

    +
      +
    • 配置是否需要集中管理,每个微服务,都有自己独特的业务配置,这些业务配置是否需要集中到一个微服务统一管理,还是分散到各自的微服务进行单独管理。
    • +
    • 全局性统一管理的业务配置对外保留接口应该利用缓存有效的减少数据库访问的频次。甚至可以只使用内存而不是Redis来缓存这部分内容。如果使用内存的话则需要考虑失效时效和重新装载的机制。
    • +
    +
  4. +
  5. 无效配置,实际上很多配置说是可以修改,其实自从开始使用的那天开始就没有修改过。比如用来做分布式锁的键值。这部分需要梳理出来直接以常量的形式做在代码里。

    +
  6. +
+

数据清理以及归档

对于电商行业的零售业务来说每天产生的单据比如订单少做200,300条,多的是上千条。加上明细数据。几个月以后这些数据就会给MySQL的库表增加检索压力。整个H项目在这块是相当薄弱的。只依靠云平台的能力,对一些不需要留档的数据进行清理。远远没有能定期归档。

+

对于数据清理这块大致可以分为这几类

+
    +
  • 定期可以无脑物理删除的数据,比如一些日志类数据。
  • +
  • 定期直接归档的数据,归档后即可清理的数据。
  • +
  • 业务必须处理完成后才能归档并清理的数据。例如订单数据等。
  • +
+

对于数据归档的时候,还需要考虑一些相关联单据数据一并归档。比如订单归档时,同时需要归档相关联的退换货单,发货单,以及操作日志等等。 归档格式基本上就是考虑单条数据以JSON格式存储,然后一个时间段的数据做成压缩包存储在OSS上,然后提供给客户下载。

+

而对于归档数据的检索功能,客户选择需要归档的数据,我们可以将归档的数据(每个压缩包内的所有数据)导入到一个主数据库以外的,临时文档数据库(例如MongoDB),然后针对这个临时的文档数据库进行检索。当然提供的检索项目也会远少于热数据的检索项目。这个临时文档数据库基本上保持一两天数据,定期就全部回收。

+

未完待续

+

H项目心路历程记 (一)

前言

2021年年中从前一家公司离职,经过朋友介绍到了现在公司参与了Halfling项目(以下简称H项目),并从零开始的设计开发到第一个客户交付的全部流程。

+

H项目是一个面向零售行业在电商时代的智能一体化中台产品。项目最早是从2021年中开始立项投入,期间经过数个版本迭代,以及2022年初的魔都封城等诸多困难。终于在2023年Q2季度完成了针对首个客户的上线。

+

作为从头开始全程参与项目的技术人员,经过2年多的产品研发期间有不少想吐槽和想说的。

+

业务设计

最初H项目是以打造一个面向零售行业业务中台为目的进行项目立项的,其中一个很重要的点就是希望除核心组件以外都可以独立售卖。所以在架构设计的使用当下最流行的微服务架构(基于Spring Boot + Kubernetes集群的解决方案), 再做业务拆分到服务的时候,进行了更细颗粒度的拆分。所以前前后后一共拆分了近20个微服务。

+

实际上到了2022年Q2前后,两个核心服务订单管理和库存服务实现以后发现,各个业务服务中间的耦合程度比想象中的还要精密,单个服务拆开单独售卖几乎不可能(应该说是完全不可能)。 然而由于服务拆分的过细,导致一个服务需要频繁访问其他服务获取大量数据。尤其类似订单关联数据比如商品的主档数据,会穿越好几个服务。而每个服务都会重复去获取主档数据,造成了大量的额外请求。 虽然最后在所有服务底部兜底使用同一个Redis作为缓存来减少额外的请求调用。

+

随着时间来到2023年产品组负责人离职后,整个H项目的方向有从行业的业务中台降级成中小企业的订单管理系统(Order Management System, OMS)。前期做的很多设计比如用户权限体系(当初需求是要精确到每个页面的每个按钮,如果下拉列表框有外调请求也需要做权限控制)等就显的很重,导致用户在使用以及配置都很不方便。而有些设计就显得稍显欠缺。 所以整个2022年Q4到2023年都是在对业务层上反复横跳,比如库存模型和商品模型的调整。这点是相当影响团队士气,同时也加剧了研发组和产品组之间的矛盾。

+

然后还有一个很重要的问题是多租户的态度上。早起项目是以服务中大体量客户为目的,所以设计之初并没有考虑多租户的问题。但实际项的进展过程中发现部分中大体量的客户也需要类似多租户的功能。这类客户需求与其说是需求多租户,不如说是希望有一个多租户级别的数据隔离,每个业务部门只能操作自己的业务部门的数据(数据包含商品,品牌,属性,平台店铺,订单,售后单据等),而总部依据可以查看一些汇总信息(例如销售对账单据等)。而随着2022年疫情状况严峻,很多中大客户对于内部平台升级和替换都变的更加谨慎和保守。所以当项目方向变成中小企业OMS时,多租户SaaS化的需求就更加迫切了。所以后期如何在原有系统上在扩展一套多租户SaaS化功能也是个课题。

+

中间件

H项目使用的中间件中比较特别的有分布式任务管理系统xxl-job,消息队列RocketMQ,这两个中间件都是第一次使用。关于这两个中间件也有不是可以谈谈的。

+

分布式任务管理系统 XXL-JOB

作为一个开源的分布式任务管理系统,xxl-job足够简单且相当容易上手使用,无论是基于Spring,还是原生Java做开发都是相当简单的。

+

而不足之处也很明显,最近1年版本迭代速度明显变慢,有些功能和BUG都有待处理的。比如想实动态创建定时任务这类功能就必须自己动手去对xxl-job-admin(调度中心)进行修改。

+

对于任务执行日志这块,现在xxl-job也是文件格式在执行器所在服务器上进行输出的,当执行器容器化或是运行在Kubernetes集群中的时候,当容器或是Pod重启后,就会丢失日志。这点在后期运维以及调试上还是有点麻烦的。

+

消息队列 RocketMQ

阿里出品的消息队列如果基于云产品来使用的话问题不大(阿里云卖的价钱也是有点黑的),但是在运维上还是有点问题的比如Topic,Group这些东西都只能人工去添加,阿里云并没有提供导入服务。全系统如果使用消息队列的场景多的话,维护这些Topic和Group还是很酸爽的。还有一点RocketMQ重复消息这一点是无法保证的。用阿里云的原话

+
+

绝大多数情况下,消息是不重复的。作为一款分布式消息中间件,在网络抖动、应用处理超时等异常情况下,无法保证消息不重复,但是能保证消息不丢失。

+
+

实际运用上,在大数据量并发的情况下,我们实际上是遇到了消息重复消费的问题。这点还是需要通过代码干预(例如增加一个唯一MsgId,对MsgId做分布式锁来解决)。

+

还有一点就是在旧版本SDK的默认配置下消费者的负载是有点小问题,总是有一个节点会比其他节点更容易消费到大量消息。据阿里云自己说明在新版本SDK的默认负载配置也已经修复这个问题了。

+

(未完待续)

+

AKS中重写规则踩坑小记录

前言

最近在做标准产品在不同云平台中的部署验证,有幸体验了一下微软的Azure。负责采购的运维部门这次采用了Application Gateway来搭配AKS(Azure Kubernetes Service)对外暴露服务,正好借着这个机会来体验一下Application Gateway

+

应用场景

    +
  1. 域名api.demo.com指向Application Gateway的IP地址
  2. +
  3. AKS内部2个Service, gateway-servicebackend-service分别需要通过Application Gateway对外暴露。
  4. +
  5. /gateway/指向gateway-service, 然后/backend/指向backend-service。而且两个Service都没有context-path,所以需要做一个Rewrite重写URI到Service的根目录上。
  6. +
+

定义重写集

打开AKS对应的应用程序网关设置 > 重写。选择添加重写集。在1. 名称和关联这个Tab上只需要填写名称这项即可(名称后面在做ingress时需要使用), 关联的传递规则不需要选择。2. 重写规则配置里添加一个重写规则,然后填上重写规则的名称,并添加条件(默认新建重写规则时,只会生成操作,不会生成条件)

+

条件做如下设置

    +
  • 要检查的变量类型 : 服务器变量
  • +
  • 服务器变量: request_uri
  • +
  • 区分大小写:
  • +
  • 运算符: 等号(=)
  • +
  • 要匹配的模式: /(gateway|backend)/?(.*)
  • +
+

操作做如下设置

    +
  • 重写类型: URL
  • +
  • 操作类型: 设置
  • +
  • 组件: URL路径和URL查询字符串
  • +
  • URL路径值: /{var_request_uri_2}
  • +
  • 重新计算路径映射: 不选中
  • +
  • URL查询字符串值: 留空不设值
  • +
+

特殊说明

操作里的URL路径值不能使用正则表达式GROUP替换组,例如$1$2之类的。Azure自己定义了一套对应的替换组命名规则。具体可以参考这个网页使用应用程序网关重写 HTTP 标头和 URL

+

另外一个需要注意一点,如果在条件里选择了服务器变量request_uri的时候,注意这个request_uri是完整的原始请求URI(携带了查询参数)。例如: 在请求http://api.demo.com/gateway/search?foo=bar&hello=world中,request_uri的值将为/gateway/search?foo=bar&hello=world。由于request_uri里包含了查询参数,所以在操作组件中建议勾选URL路径和URL查询字符串。如果只选择URL路径的情况下可能出现无法预期的错误。以我们上述的配置来说明。

+

对象URL: http://api.demo.com/gateway/search?foo=bar&hello=world

+ + + + + + + + + + + + + +
组件URL路径和URL查询字符串URL路径
结果/search?foo=bar&hello=world/search?foo=bar&hello=world?foo=bar&hello=world
+

ACK的Ingress设置

当选择了Application Gateway作为对外暴露Service的方式时,Kubernetes集群里(kube-system命名空间里)多一个Application Gateway Ingress Controller(Azure工单时通常会简称为agic)的Deployment,所以对外暴露服务时可以像传统nginx ingress controller一样添加一个Ingress对象即可(甚至配置也和ngic大致相同,只是多了2个annotations)

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
# 这里指定重写规则集(不是重写规则的名字)
appgw.ingress.kubernetes.io/rewrite-rule-set: rule-backend
# 指定说明你这里ingress的类型是agic
kubernetes.io/ingress.class: azure/application-gateway
name: backend-ingress
namespace: default
spec:
rules:
- host: api.demo.com
http:
paths:
- backend:
service:
name: gateway-service
port:
number: 8080
path: /gateway/
pathType: Prefix
- backend:
service:
name: backend-service
port:
number: 8080
path: /backend/
pathType: Prefix
+ +

总结

由于微软云这块文档有部分缺失,导致在配置这块花了一点时间去排查,甚至开了工单。总结下来Ingress的配置主要是根据请求路径路由到对应的Service,重写规则集才是实际负责根据正则来进行匹配重写。

+

Largrange项目架构与设计回顾 (二)

Largrange项目架构与设计回顾 (一) 里面我讲了一下项目开始架构和技术选型的一些内容。这一章来聊聊业务设计上的这点事。

+

总体来说项目设计的时候,我们对业务模块的划分和拆解总体上来说都是遵循着高内聚,低耦合的原则来进行划分的。大部分和业务相关的服务,都还是能很好的进行功能划分的。但是有一些和具体业务关联性并不是很高的服务在界定与实现时出现了一些问题。

+

任务调度服务

由于平台方面会有一些控制命令下发给Android设备,而且下发的命令并不一定都是实时的,大部分都是指定一个时间来下发,所以在设计的时候就考虑到需要一个任务调度服务(以下略称cron),来处理这些下发指令以及未来可能会有的定时批处理任务的业务需求。技术选型的时候考虑到整个服务是构建在Kubernetes上的一个分布式的微服务架构,所以就没有选择Spring Scheduler,而是使用了Quarter来支撑整个cron

+

从技术选型上来说cron没有什么问题,但是在设计如何使用cron上还是有点问题的, 我们先来看看已推送服务为例,整个平台中cron的处理流程是怎样的。

+
    +
  1. Platform 调用 cron的创建定时任务Job的接口(接口的参数为推送时使用的相关参数)
  2. +
  3. cron 创建Quartz的Job和Trigger,并将Job和Trigger的Name(Quartz中Job和Trigger的唯一标识符)返回个Platform
  4. +
  5. cron 在指定时间触发推送的Job,即调用Push服务的推送接口
  6. +
  7. Push的推送接口调用第三方服务商的推送服务,并将第三方推送服务的调用结果返回给cron
  8. +
  9. cron通过调用Platform预留的推送服务回调地址将第三方推送服务调用接口返还给Platform
  10. +
  11. Platform 将第三方推送服务的调用结果留档保存,并继续业务处理
  12. +
+

设计之初考虑到不想在cron中牵扯到具体的业务, 所以设计了一个Platform的回调接口来处理推送后的具体业务处理。虽然保证了cron尽量减少了和业务逻辑接触与数据库的访问。但是前前后后要访问集群内部的其他微服务2次,额外增加了网络开销,以及因为网络通信造成额外的通信失败风险。而且针对每种不同的定时任务,都需要额外开发一个QuartzJobBean,很难形成统一的QuartzJobBean来进行处理。今后如果还有相似项目还是需要重新考量一下如何设计一个更加完善的定时任务。

+

总结

暂时就想到了这些东西,今后想到啥还会继续在这里补存。

+

Largrange项目架构与设计回顾 (一)

+

项目背景

+
+

从去年年底开始一个老客户希望在他们的一个传统的机械设备(后面略称 E机关 )上外装一个Android设备。 Android设备和 E机关 之间通过串口或是RJ45接口进行数据交互,主要是Android设备获取 E机关 内部的数据,并不会通过接口来控制 E机关 。 Android设备则通过4G 来和平台交互,上报Android设备的状态数据,同时接受平台的控制。以此来实现让传统机械设备也能拥抱物联网的概念。

+

最后围绕着客户的需求分成了3个项目来并行推进

+
    +
  1. Android设备的硬件设备的设计、选材、样机制作与量产规划
  2. +
  3. 在Android设备上,进行与E机关以及平台进行交互的APP开发
  4. +
  5. 用于Android设备交互的平台的架构、设计与开发
  6. +
+

我们平台Team就负责 3.用于Android设备交互的平台 并命名Lagrange (拉格朗日) 。

+

设计&架构

整个平台这块不仅需要向Android设备的APP提供数据交互接口,还需要有一个供相关运营人员使用的前端Web应用,以及与云平台(主要是Aliyun) 交互的功能。所以考虑到多方面使用微服务的架构来实现整个平台端。
由于E机关的工作工况不能保证长期较的稳定的连接到4G网络,所以我们并不考虑使用Socket长连接的方式来做APP和平台之间的数据交互,要实现平台对Android设备进行反控的话,只能实现推送方式来把控制命令下发给Android设备,所以还需要有一个第三方推送服务商交互的服务。同时控制推送也有实时和非实时以及定期推送的需求,所以可能还需要一个任务调度的服务。

+

根据对上述业务进行梳理,我们将项目分成几个服务

+
    +
  • 提供Android设备的交互接口的 App Service
  • +
  • 提供运营人员使用前端Web应用 Platform Web Service
  • +
  • 前端Web应用使用到的一些接口 Platform Service
  • +
  • 负责第三方推送服务商交互的 Push Service
  • +
  • 提供云平台鉴权用的 Auth Service
  • +
  • 用来管理任务调度的 Cron Service
  • +
+

由于前一个项目实施的时候没有使用容器部署,每当访问量峰值的时候,我们这些码农兼运维就各种加班,所以这次项目决定直接将服务容器化,同时选择了Kubernetes来管理容器。由于客观原因线上的Kubernetes直接购买了Aliyun的托管版Kubernetes服务。 内部的开发测试环境则使用了 Rancher 2.0 来构建Kubernetes集群。

+

技术选型

a. 后端服务选型

由于不考虑长连接的原因,所以在后端服务在技术选型上基本就不考虑Netty了,直接上Spring大礼包。

+
    +
  • Spring Boot 开发接口
  • +
  • Spring Data 配合 JPA 来进行数据的持久化
  • +
  • Spring Cloud Kubenetes 来做数据的Config的autoreload
  • +
  • Quartz 负责处理任务调度
  • +
+

服务之间调用都使用HTTP服务, 服务发现也有Kubernetes的DNS机制支持。服务网格则选择了比较成熟的Istio,主要还是Aliyun的Kubernetes可以集成Istio,部署和使用都相当方便。

+

b. 前端Web应用选型

因为前端Web应用主要是给运营人员使用,所以我们考虑使用Single Page Application来做个前后端分离的Web应用。框架这块由于团队成员基本上没有什么前端开发经验,基本都是后台写Java的码农,所以框架选择有点随性,直接就点名了vue.js。前端控件库则用的是阿里系的Antd。

+

c. 数据持久层

主数据库选择了mysql,缓存用的redis。这些都是团队比较熟悉的。由于一些特殊的业务需求和使用场景,我们还加了一个mongodb来做为一个副数据库,主要存放一些特殊业务使用的数据。

+

d. DevOps

用了相当传统的GitLab CE 加上Jenkins的组合,实现前后端代码的自动编译,推送到私有的镜像仓库(使用Aliyun的镜像仓库服务)

+

最后

以上基本是项目最早做设计时候的各种考量。暂时先写这么多,过两天再回顾一下当初设计上有哪些觉得不足的地方。

+

阿里云Kubernetes上线踩坑记

1
2
Update:
2020-04-08 增加istio-ingressgateway高可用的设置
+ +

最近公司因为项目需要,在阿里云上部署了一个Kubernetes集群。虽然阿里云的文档说的还算细致,但是还是有些没有明确说明的细节。

+

1. 购买篇

申请项目预算的时候,只考虑到Worker节点,1个SLB节点以及域名和证书的预算。但是实际购买的时候发现还有许多额外的开销。

+

1.1 SNAT

这个和EIP一并购买,可以方便通过公网使用kubectl访问集群。关于SNAT网关至今不是很明白需要购买这个服务的意义何在,只是为了一个EIP来访问集群吗?

+

1.2 Ingress

这个选上了后,阿里云会给你买个SLB而且还是带公网访问的,如果你后期考虑使用Istio的话,建议你集群创建后,直接停止这个SLB,以免产生额外的费用。

+

1.3 日志服务

通过阿里云的日志服务来收集应用的的日志,挺好用的。但是另外收费,如果有能力的自建日志服务的可不购买。

+

2. Istio

阿里云的Kubernetes集群完美集成了Istio,根据向导就能很简单的部署成功。

+

2.1 额外的SLB

Istio的Gateway 需要绑定一个新的SLB,和Ingress的SLB不能是同一个,又是一笔额外的开销

+

2.2 集群外访问

这个在阿里云的Istio FAQ中有提到,按照指导很容易解决

+

2.2 SLB的443监听

为了方便443端口的证书绑定,我们直接删除了SLB上原有的443监听(TCP协议), 重新建了一个443监听(HTTPS协议),指向和80端口同样的虚拟服务器组。但是设置健康检查时一直出错,经过排查发现SLB健康检查发送的请求协议是HTTP 1.0的,Istio的envoy直接反悔了426(Upgrade Required)这个状态码,所以我们无奈只能把健康检查的检查返回状态改为http_4xx,这样就能通过SLB的健康检查了。

+

2.3 istio-ingressgateway的高可用

istio-ingressgateway要达成高可用,只需要增加通过伸缩POD就可以实现,于istio-ingressgateway对应的SLB中的虚拟服务器组也会自动增加,完全不需要进行额外的手动设定。

+

由于istio-ingressgateway中挂载了HPAHorizontalPodAutoscaler(简称HPA),通常三节点的集群中最小POD数只有1台,在3节点的集群中,要实现高可用,需要手动修改HPA,增加最小POD数。

+
+

基本上现在遇到了这些坑,再有在总结吧。

+

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/tags/Java/index.html b/tags/Java/index.html new file mode 100644 index 0000000..f718222 --- /dev/null +++ b/tags/Java/index.html @@ -0,0 +1,238 @@ + +标签: Java - 西门闲话

H项目心路历程记(三)

现状

对于一个基于微服务的中台系统来说,需要以什么样的颗粒度来拆分系统。这一直是基于微服务架构的系统中绕不过去的问题。

+

已H项目来说,当初考量是这么划分微服务的,其中

+
    +
  • 基础服务: 作用主要是负责用户,角色权限,登陆鉴权,以及一些基础主档数据的日常维护功能
  • +
  • 商品服务: 物料,销售商品,款式,平台商品等和实际商品相关的主档数据
  • +
  • 库存服务:负责管理各个库存层级库存(总库存,冻结库存,在途库存等),以及展示各种出入库单据,并向其他微服务提供出入库服务接口。
  • +
  • 订单服务: 负责接收各个公域平台的ToC订单,售后单(含发货前退款,退货,换货单等),并根据配置策略进行单据路由(分仓,分物流,Hold单,加送赠品,通知仓库发货,通知平台发货等)
  • +
  • 接口服务: 主要承揽中台服务和第三方平台间的交互处理(主要是第三方平台发起的主调通知以及回调)
  • +
+

上述五个服务为核心服务,是保证整个中台系统基础业务功能运行的重要组成部分。除此之外还有,

+
    +
  • 采购服务:负责ToB业务线的处理
  • +
  • 财务服务:满足一些企业的财务对账以及开具电子发票的功能
  • +
  • 报表服务: 每天,每周定期从其他服务(主要是订单)
  • +
  • 会员服务:管理各类会员
  • +
+

等7,8个非核心服务,组成非核心业务。来构成一个完整的中台系统。

+

拆分策略以及缺点

当时主要考虑到整个中台系统是可以拆分出售,客户可以只选择购买自身需要的服务,而不需要的服务则无需购买服务,所以当初在拆分服务上在业务领域层面上需要更加低颗粒度;同时考虑到再业务高峰期(下午到晚上,就是各位疯狂刷淘宝,京东的时候)各个服务的负载是各不相同的,如果在高峰期对于特定服务需要进行精准扩容的话,拆分的颗粒度也需要更加细致,所以结果上导致整个系统满血运行起来要至少要十几个服务,外加一些没有云平台的中间件。整个Kubernetes环境中,至少要有十五六个Deployment,考虑到系统稳定性,一个Deployment也至少要有2到3个Pod,那整个系统至少就是50个Pod。

+

对于一个中大型的电商企业再服务器成本上可以负担这样一个能支持50个Pod的Kubernentes集群(考虑到需要一些中间件的云产品,当时估算最小的服务器成本都要到70K/年,一个中大型企业能面对双十一流量的服务器成本都要到300K/年以上)。所以整个系统对于中小型企业来说前期的投入负担相当大(尤其是在后疫情时代的2022年,2023年),以至于早起的好几个客户都是需要我们自己贴补服务器费用的。

+

复盘

现在重新复盘当初服务的拆分策略,个人觉得对于核心业务来说,相似领域的一些服务应该可以适当的聚合。比如上述的五大核心服务上中的商品服务,订单服务,库存服务三个核心服务应该可以合并在一起。首先这3个核心服务是H项目的最重要的竞争力,几乎不可能出现把这3个服务拆分出售的可能性。

+

而且在业务高峰期有订单服务有新订单处理时,势必会产生库存的变化,因此会调用到库存服务。三个服务之间的调用是相当频繁的,尤其是负责处理商品主档数据的商品服务,无论是通过HTTP或是其他方式的远程调用的话,都会有许多浪费的带宽。如果把三个服务整合,那么之间的调用基本上就是线程间的交互,无论是效率和可靠性上都会有更加显著的提升。

+

结尾

怎么拆分服务一直是个难题。虽然事后复盘的时候我的很多想法也不一定是正确,但是什么样的规模的客户需要使用什么样的拆分颗粒度,都是需要仔细考虑的。绝对不是一尘不变的。

+

H项目心路历程记 (二)

(书接上文)

+

前文已经吐槽了产品设计以及中间件上的相关问题。接下来讨论一下技术上的一些问题。

+

关于配置

对于一个Spring的应用来说,应用配置怎么存取其实并不能说是一个技术问题。更应该算是设计或是架构问题。哪些配置应该进入配置文件,通过微服务配置中心(例如Nacos, Apollo,Kubernetes ConfigMap等)来进行在线配置,哪些配置应该通过其他方式来配置,亦或者哪些配置项目看似是配置项其实本质上就是常量。实际上截止现在H项目的确在有些微服务上是完全没有厘清三者之间的区别,以及界限。以至于现在堆积在配置文件中的内容实在太多。多个运行环境需要统一维护的难度实在太大。

+

对于配置来说,理想的状态应该是下面这样的。

+
    +
  1. 通过微服务配置中心来影响Spring的配置文件来实现动态配置的方案,通常会伴随Spring容器的重启(有些情况下甚至需要整个Docker容器),所以存在这里的配置项应该是随着运行环境改变才会需要改动的配置。例如:

    +
      +
    • 外部系统的链接信息(比如MySQL, Redis的连接信息)
    • +
    • 外部系统的认证凭证(数据库用户密码,第三方系统的密钥等)
    • +
    • 因为运行环境不同导致的不同变量 (例如消息队列的Topic,Group需要根据运行环境使用不同的名称)
    • +
    +
  2. +
  3. 通过数据库或是Redis存储的配置信息,这类配置主要是应该以业务级别的配置,可以通过页面进行修改,瞬间或是几分钟内生效,且不会造成微服务重启(无论是Spring或是Docker容器的重启)。对于这类配置有几个问题还是需要注意的。

    +
      +
    • 配置是否需要集中管理,每个微服务,都有自己独特的业务配置,这些业务配置是否需要集中到一个微服务统一管理,还是分散到各自的微服务进行单独管理。
    • +
    • 全局性统一管理的业务配置对外保留接口应该利用缓存有效的减少数据库访问的频次。甚至可以只使用内存而不是Redis来缓存这部分内容。如果使用内存的话则需要考虑失效时效和重新装载的机制。
    • +
    +
  4. +
  5. 无效配置,实际上很多配置说是可以修改,其实自从开始使用的那天开始就没有修改过。比如用来做分布式锁的键值。这部分需要梳理出来直接以常量的形式做在代码里。

    +
  6. +
+

数据清理以及归档

对于电商行业的零售业务来说每天产生的单据比如订单少做200,300条,多的是上千条。加上明细数据。几个月以后这些数据就会给MySQL的库表增加检索压力。整个H项目在这块是相当薄弱的。只依靠云平台的能力,对一些不需要留档的数据进行清理。远远没有能定期归档。

+

对于数据清理这块大致可以分为这几类

+
    +
  • 定期可以无脑物理删除的数据,比如一些日志类数据。
  • +
  • 定期直接归档的数据,归档后即可清理的数据。
  • +
  • 业务必须处理完成后才能归档并清理的数据。例如订单数据等。
  • +
+

对于数据归档的时候,还需要考虑一些相关联单据数据一并归档。比如订单归档时,同时需要归档相关联的退换货单,发货单,以及操作日志等等。 归档格式基本上就是考虑单条数据以JSON格式存储,然后一个时间段的数据做成压缩包存储在OSS上,然后提供给客户下载。

+

而对于归档数据的检索功能,客户选择需要归档的数据,我们可以将归档的数据(每个压缩包内的所有数据)导入到一个主数据库以外的,临时文档数据库(例如MongoDB),然后针对这个临时的文档数据库进行检索。当然提供的检索项目也会远少于热数据的检索项目。这个临时文档数据库基本上保持一两天数据,定期就全部回收。

+

未完待续

+

H项目心路历程记 (一)

前言

2021年年中从前一家公司离职,经过朋友介绍到了现在公司参与了Halfling项目(以下简称H项目),并从零开始的设计开发到第一个客户交付的全部流程。

+

H项目是一个面向零售行业在电商时代的智能一体化中台产品。项目最早是从2021年中开始立项投入,期间经过数个版本迭代,以及2022年初的魔都封城等诸多困难。终于在2023年Q2季度完成了针对首个客户的上线。

+

作为从头开始全程参与项目的技术人员,经过2年多的产品研发期间有不少想吐槽和想说的。

+

业务设计

最初H项目是以打造一个面向零售行业业务中台为目的进行项目立项的,其中一个很重要的点就是希望除核心组件以外都可以独立售卖。所以在架构设计的使用当下最流行的微服务架构(基于Spring Boot + Kubernetes集群的解决方案), 再做业务拆分到服务的时候,进行了更细颗粒度的拆分。所以前前后后一共拆分了近20个微服务。

+

实际上到了2022年Q2前后,两个核心服务订单管理和库存服务实现以后发现,各个业务服务中间的耦合程度比想象中的还要精密,单个服务拆开单独售卖几乎不可能(应该说是完全不可能)。 然而由于服务拆分的过细,导致一个服务需要频繁访问其他服务获取大量数据。尤其类似订单关联数据比如商品的主档数据,会穿越好几个服务。而每个服务都会重复去获取主档数据,造成了大量的额外请求。 虽然最后在所有服务底部兜底使用同一个Redis作为缓存来减少额外的请求调用。

+

随着时间来到2023年产品组负责人离职后,整个H项目的方向有从行业的业务中台降级成中小企业的订单管理系统(Order Management System, OMS)。前期做的很多设计比如用户权限体系(当初需求是要精确到每个页面的每个按钮,如果下拉列表框有外调请求也需要做权限控制)等就显的很重,导致用户在使用以及配置都很不方便。而有些设计就显得稍显欠缺。 所以整个2022年Q4到2023年都是在对业务层上反复横跳,比如库存模型和商品模型的调整。这点是相当影响团队士气,同时也加剧了研发组和产品组之间的矛盾。

+

然后还有一个很重要的问题是多租户的态度上。早起项目是以服务中大体量客户为目的,所以设计之初并没有考虑多租户的问题。但实际项的进展过程中发现部分中大体量的客户也需要类似多租户的功能。这类客户需求与其说是需求多租户,不如说是希望有一个多租户级别的数据隔离,每个业务部门只能操作自己的业务部门的数据(数据包含商品,品牌,属性,平台店铺,订单,售后单据等),而总部依据可以查看一些汇总信息(例如销售对账单据等)。而随着2022年疫情状况严峻,很多中大客户对于内部平台升级和替换都变的更加谨慎和保守。所以当项目方向变成中小企业OMS时,多租户SaaS化的需求就更加迫切了。所以后期如何在原有系统上在扩展一套多租户SaaS化功能也是个课题。

+

中间件

H项目使用的中间件中比较特别的有分布式任务管理系统xxl-job,消息队列RocketMQ,这两个中间件都是第一次使用。关于这两个中间件也有不是可以谈谈的。

+

分布式任务管理系统 XXL-JOB

作为一个开源的分布式任务管理系统,xxl-job足够简单且相当容易上手使用,无论是基于Spring,还是原生Java做开发都是相当简单的。

+

而不足之处也很明显,最近1年版本迭代速度明显变慢,有些功能和BUG都有待处理的。比如想实动态创建定时任务这类功能就必须自己动手去对xxl-job-admin(调度中心)进行修改。

+

对于任务执行日志这块,现在xxl-job也是文件格式在执行器所在服务器上进行输出的,当执行器容器化或是运行在Kubernetes集群中的时候,当容器或是Pod重启后,就会丢失日志。这点在后期运维以及调试上还是有点麻烦的。

+

消息队列 RocketMQ

阿里出品的消息队列如果基于云产品来使用的话问题不大(阿里云卖的价钱也是有点黑的),但是在运维上还是有点问题的比如Topic,Group这些东西都只能人工去添加,阿里云并没有提供导入服务。全系统如果使用消息队列的场景多的话,维护这些Topic和Group还是很酸爽的。还有一点RocketMQ重复消息这一点是无法保证的。用阿里云的原话

+
+

绝大多数情况下,消息是不重复的。作为一款分布式消息中间件,在网络抖动、应用处理超时等异常情况下,无法保证消息不重复,但是能保证消息不丢失。

+
+

实际运用上,在大数据量并发的情况下,我们实际上是遇到了消息重复消费的问题。这点还是需要通过代码干预(例如增加一个唯一MsgId,对MsgId做分布式锁来解决)。

+

还有一点就是在旧版本SDK的默认配置下消费者的负载是有点小问题,总是有一个节点会比其他节点更容易消费到大量消息。据阿里云自己说明在新版本SDK的默认负载配置也已经修复这个问题了。

+

(未完待续)

+

Spring Cloud Kubernetes环境下使用Jasypt

前言

最近半年着手开始做了基于微服务的中台项目,整个项目的技术栈采用的是Java + Spring Cloud + Kubernetes + Istio

+

业务开放上还是相当顺利的。但是在安全审核上,运维组提出了一个简易。现在项目一些敏感配置,例如MySQL用户的密码,Redis的密码等现在都是明文保存在Kubernetes的ConfigMap中的(是的,我们并没有Nacos作为微服务的配置中心)。这样可能存在安全隐患。

+

首次尝试

既然有问题,那就解决问题。要给配置文件中的属性项目加密很简单,稍微Google一下,就有现成的方案了。

+

现在比较常用的解决方案就是集成Jasypt,然后通过jasypt-spring-boot-starter来融合进Spring。

+

POM包加入jasypt-spring-boot-starter

1
2
3
4
5
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
+ +

Dockerfile中增加java参数

1
2
...
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar app.jar --jasypt.encryptor.password=helloworld $PARAMS"]
+ +

在ConfigMap中添加加密属性

1
2
3
4
5
6
7
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
data:
application.yaml: |-
test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
+ +

利用actuator接口测试

management.endpoints.web.exposure.include属性中增加env,这样我们就可以通过调用/actuator/env来查看一下env接口返回的整个Spring 容器中所有的PropertySource。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
...
"propertySources": [
{
"name": "bootstrapProperties-configmap.demo.default",
"properties": {
"test2": {
"value": "Hello,world"
}
}
}
...
]
}
+ +

OK, 这下配置项已经加密了。问题解决了。

+

但是…

+

新的问题

自从项目集成了Jayspt以后,出现了一个奇怪的问题。每次项目试图通过修改ConfigMap的配置文件,然后试图通过spring-cloud-starter-kubernetes-fabric8-config来做自动Reload,都失败了。然而查阅应用日志,并没有出现任何异常。无奈只能打开spring-cloudjasypt-spring-bootDEBUG日志。

+

进过几天对日志和两边源代码的分析。终于找到了原因

+

原因

在Spring Boot启动时jasypt-spring-boot会将下面6种配置(并不仅限与这6种配置文件)

+
    +
  • Classpath下的application.yaml
  • +
  • Classpath下的bootstrap.yaml
  • +
  • 集群里名称为${spring.cloud.kubernetes.config.name}的ConfigMap
  • +
  • 集群里名称为${spring.cloud.kubernetes.config.name}-kubernetes的ConfigMap
  • +
  • Java启动参数
  • +
  • 环境变量
  • +
+

转换成jasypt-spring-boot自己的PropertySource实现类EncryptableMapPropertySourceWrapper

+

但是如果使用Kubernetes的ConfigMap来作微服务配置中心的时候,Spring Cloud会在ConfigurationChangeDetector中查找配置类org.springframework.cloud.bootstrap.config.BootstrapPropertySource, 并依据BootstrapPropertySource的类型来判断容器内的配置与集群中ConfigMap里的配置是否有差异,来触发配置reload。

+

由于jasypt-spring-boot已经将所有的配置文件转型成了EncryptableMapPropertySourceWrapper, 所以ConfigurationChangeDetector无法找到BootstrapPropertySource所以会一直任务ConfigMap的里的配置没有变化,导致整个Reload失效(无论是使用polling还是event方式)

+

解决问题

为了保证ConfigMap变化后自动Reload的功能,所以jasypt-spring-boot不能把BootstrapPropertySource转换成EncryptableMapPropertySourceWrapper

+

所以我们需要设置jasypt.encryptor.skip-property-sources配置项, Classpath中的application.yaml需要增加配置

+
1
2
3
jasypt:
encryptor:
skip-property-sources: org.springframework.cloud.bootstrap.config.BootstrapPropertySource
+ +

skip-property-sources配置项配置后,加密项目就不能配置在ConfigMap里了,毕竟已经被我们忽略了。那么我们只能另外找一个PropertySource来存放加密项目了。

+

Classpath中的两个Yaml由于编译时会被Maven打包进Jar文件,会牵涉多个CI/CD多个流程显然不合适,启动参数配置项的也要影响到Docker镜像制作这个流程。所以判断下来最适合的PropertySource就是环境变量了。

+

环境变量增加加密项

在Kubernetes的部署Yaml中,添加加密数据项application.test.str

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: demo
name: demo
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- env:
- name: TZ
value: Asia/Shanghai
- name: application.test.str
value: >-
ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
....
+ +

如果需要更加严密的加密方针的话,我们可以把环境变量的内容放进Kubernetes的Secrets中。

+

在ConfigMap中引用application.test.str

1
2
3
4
5
6
7
8
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
data:
application.yaml: |-
test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
test3: ${application.test.str}
+ +

通过actuator接口来测试

通过actuator\env接口来测试一下

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
...
"propertySources": [
{
"name": "bootstrapProperties-configmap.demo.default",
"properties": {
"test2": {
"value": "ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)"
},
"test3": {
"value": "Hello,world"
}
}
}
...
]
}
+ +

这样ConfigMap中的配置项test3就可以通过环境变量引用并使用加密配置项了。同时修改ConfigMap依然可以触发auto reload了。这下终于算是解决了。

+

Largrange项目架构与设计回顾 (二)

Largrange项目架构与设计回顾 (一) 里面我讲了一下项目开始架构和技术选型的一些内容。这一章来聊聊业务设计上的这点事。

+

总体来说项目设计的时候,我们对业务模块的划分和拆解总体上来说都是遵循着高内聚,低耦合的原则来进行划分的。大部分和业务相关的服务,都还是能很好的进行功能划分的。但是有一些和具体业务关联性并不是很高的服务在界定与实现时出现了一些问题。

+

任务调度服务

由于平台方面会有一些控制命令下发给Android设备,而且下发的命令并不一定都是实时的,大部分都是指定一个时间来下发,所以在设计的时候就考虑到需要一个任务调度服务(以下略称cron),来处理这些下发指令以及未来可能会有的定时批处理任务的业务需求。技术选型的时候考虑到整个服务是构建在Kubernetes上的一个分布式的微服务架构,所以就没有选择Spring Scheduler,而是使用了Quarter来支撑整个cron

+

从技术选型上来说cron没有什么问题,但是在设计如何使用cron上还是有点问题的, 我们先来看看已推送服务为例,整个平台中cron的处理流程是怎样的。

+
    +
  1. Platform 调用 cron的创建定时任务Job的接口(接口的参数为推送时使用的相关参数)
  2. +
  3. cron 创建Quartz的Job和Trigger,并将Job和Trigger的Name(Quartz中Job和Trigger的唯一标识符)返回个Platform
  4. +
  5. cron 在指定时间触发推送的Job,即调用Push服务的推送接口
  6. +
  7. Push的推送接口调用第三方服务商的推送服务,并将第三方推送服务的调用结果返回给cron
  8. +
  9. cron通过调用Platform预留的推送服务回调地址将第三方推送服务调用接口返还给Platform
  10. +
  11. Platform 将第三方推送服务的调用结果留档保存,并继续业务处理
  12. +
+

设计之初考虑到不想在cron中牵扯到具体的业务, 所以设计了一个Platform的回调接口来处理推送后的具体业务处理。虽然保证了cron尽量减少了和业务逻辑接触与数据库的访问。但是前前后后要访问集群内部的其他微服务2次,额外增加了网络开销,以及因为网络通信造成额外的通信失败风险。而且针对每种不同的定时任务,都需要额外开发一个QuartzJobBean,很难形成统一的QuartzJobBean来进行处理。今后如果还有相似项目还是需要重新考量一下如何设计一个更加完善的定时任务。

+

总结

暂时就想到了这些东西,今后想到啥还会继续在这里补存。

+

Largrange项目架构与设计回顾 (一)

+

项目背景

+
+

从去年年底开始一个老客户希望在他们的一个传统的机械设备(后面略称 E机关 )上外装一个Android设备。 Android设备和 E机关 之间通过串口或是RJ45接口进行数据交互,主要是Android设备获取 E机关 内部的数据,并不会通过接口来控制 E机关 。 Android设备则通过4G 来和平台交互,上报Android设备的状态数据,同时接受平台的控制。以此来实现让传统机械设备也能拥抱物联网的概念。

+

最后围绕着客户的需求分成了3个项目来并行推进

+
    +
  1. Android设备的硬件设备的设计、选材、样机制作与量产规划
  2. +
  3. 在Android设备上,进行与E机关以及平台进行交互的APP开发
  4. +
  5. 用于Android设备交互的平台的架构、设计与开发
  6. +
+

我们平台Team就负责 3.用于Android设备交互的平台 并命名Lagrange (拉格朗日) 。

+

设计&架构

整个平台这块不仅需要向Android设备的APP提供数据交互接口,还需要有一个供相关运营人员使用的前端Web应用,以及与云平台(主要是Aliyun) 交互的功能。所以考虑到多方面使用微服务的架构来实现整个平台端。
由于E机关的工作工况不能保证长期较的稳定的连接到4G网络,所以我们并不考虑使用Socket长连接的方式来做APP和平台之间的数据交互,要实现平台对Android设备进行反控的话,只能实现推送方式来把控制命令下发给Android设备,所以还需要有一个第三方推送服务商交互的服务。同时控制推送也有实时和非实时以及定期推送的需求,所以可能还需要一个任务调度的服务。

+

根据对上述业务进行梳理,我们将项目分成几个服务

+
    +
  • 提供Android设备的交互接口的 App Service
  • +
  • 提供运营人员使用前端Web应用 Platform Web Service
  • +
  • 前端Web应用使用到的一些接口 Platform Service
  • +
  • 负责第三方推送服务商交互的 Push Service
  • +
  • 提供云平台鉴权用的 Auth Service
  • +
  • 用来管理任务调度的 Cron Service
  • +
+

由于前一个项目实施的时候没有使用容器部署,每当访问量峰值的时候,我们这些码农兼运维就各种加班,所以这次项目决定直接将服务容器化,同时选择了Kubernetes来管理容器。由于客观原因线上的Kubernetes直接购买了Aliyun的托管版Kubernetes服务。 内部的开发测试环境则使用了 Rancher 2.0 来构建Kubernetes集群。

+

技术选型

a. 后端服务选型

由于不考虑长连接的原因,所以在后端服务在技术选型上基本就不考虑Netty了,直接上Spring大礼包。

+
    +
  • Spring Boot 开发接口
  • +
  • Spring Data 配合 JPA 来进行数据的持久化
  • +
  • Spring Cloud Kubenetes 来做数据的Config的autoreload
  • +
  • Quartz 负责处理任务调度
  • +
+

服务之间调用都使用HTTP服务, 服务发现也有Kubernetes的DNS机制支持。服务网格则选择了比较成熟的Istio,主要还是Aliyun的Kubernetes可以集成Istio,部署和使用都相当方便。

+

b. 前端Web应用选型

因为前端Web应用主要是给运营人员使用,所以我们考虑使用Single Page Application来做个前后端分离的Web应用。框架这块由于团队成员基本上没有什么前端开发经验,基本都是后台写Java的码农,所以框架选择有点随性,直接就点名了vue.js。前端控件库则用的是阿里系的Antd。

+

c. 数据持久层

主数据库选择了mysql,缓存用的redis。这些都是团队比较熟悉的。由于一些特殊的业务需求和使用场景,我们还加了一个mongodb来做为一个副数据库,主要存放一些特殊业务使用的数据。

+

d. DevOps

用了相当传统的GitLab CE 加上Jenkins的组合,实现前后端代码的自动编译,推送到私有的镜像仓库(使用Aliyun的镜像仓库服务)

+

最后

以上基本是项目最早做设计时候的各种考量。暂时先写这么多,过两天再回顾一下当初设计上有哪些觉得不足的地方。

+

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/tags/Kubernetes/index.html b/tags/Kubernetes/index.html new file mode 100644 index 0000000..861b253 --- /dev/null +++ b/tags/Kubernetes/index.html @@ -0,0 +1,251 @@ + +标签: Kubernetes - 西门闲话

H项目心路历程记(三)

现状

对于一个基于微服务的中台系统来说,需要以什么样的颗粒度来拆分系统。这一直是基于微服务架构的系统中绕不过去的问题。

+

已H项目来说,当初考量是这么划分微服务的,其中

+
    +
  • 基础服务: 作用主要是负责用户,角色权限,登陆鉴权,以及一些基础主档数据的日常维护功能
  • +
  • 商品服务: 物料,销售商品,款式,平台商品等和实际商品相关的主档数据
  • +
  • 库存服务:负责管理各个库存层级库存(总库存,冻结库存,在途库存等),以及展示各种出入库单据,并向其他微服务提供出入库服务接口。
  • +
  • 订单服务: 负责接收各个公域平台的ToC订单,售后单(含发货前退款,退货,换货单等),并根据配置策略进行单据路由(分仓,分物流,Hold单,加送赠品,通知仓库发货,通知平台发货等)
  • +
  • 接口服务: 主要承揽中台服务和第三方平台间的交互处理(主要是第三方平台发起的主调通知以及回调)
  • +
+

上述五个服务为核心服务,是保证整个中台系统基础业务功能运行的重要组成部分。除此之外还有,

+
    +
  • 采购服务:负责ToB业务线的处理
  • +
  • 财务服务:满足一些企业的财务对账以及开具电子发票的功能
  • +
  • 报表服务: 每天,每周定期从其他服务(主要是订单)
  • +
  • 会员服务:管理各类会员
  • +
+

等7,8个非核心服务,组成非核心业务。来构成一个完整的中台系统。

+

拆分策略以及缺点

当时主要考虑到整个中台系统是可以拆分出售,客户可以只选择购买自身需要的服务,而不需要的服务则无需购买服务,所以当初在拆分服务上在业务领域层面上需要更加低颗粒度;同时考虑到再业务高峰期(下午到晚上,就是各位疯狂刷淘宝,京东的时候)各个服务的负载是各不相同的,如果在高峰期对于特定服务需要进行精准扩容的话,拆分的颗粒度也需要更加细致,所以结果上导致整个系统满血运行起来要至少要十几个服务,外加一些没有云平台的中间件。整个Kubernetes环境中,至少要有十五六个Deployment,考虑到系统稳定性,一个Deployment也至少要有2到3个Pod,那整个系统至少就是50个Pod。

+

对于一个中大型的电商企业再服务器成本上可以负担这样一个能支持50个Pod的Kubernentes集群(考虑到需要一些中间件的云产品,当时估算最小的服务器成本都要到70K/年,一个中大型企业能面对双十一流量的服务器成本都要到300K/年以上)。所以整个系统对于中小型企业来说前期的投入负担相当大(尤其是在后疫情时代的2022年,2023年),以至于早起的好几个客户都是需要我们自己贴补服务器费用的。

+

复盘

现在重新复盘当初服务的拆分策略,个人觉得对于核心业务来说,相似领域的一些服务应该可以适当的聚合。比如上述的五大核心服务上中的商品服务,订单服务,库存服务三个核心服务应该可以合并在一起。首先这3个核心服务是H项目的最重要的竞争力,几乎不可能出现把这3个服务拆分出售的可能性。

+

而且在业务高峰期有订单服务有新订单处理时,势必会产生库存的变化,因此会调用到库存服务。三个服务之间的调用是相当频繁的,尤其是负责处理商品主档数据的商品服务,无论是通过HTTP或是其他方式的远程调用的话,都会有许多浪费的带宽。如果把三个服务整合,那么之间的调用基本上就是线程间的交互,无论是效率和可靠性上都会有更加显著的提升。

+

结尾

怎么拆分服务一直是个难题。虽然事后复盘的时候我的很多想法也不一定是正确,但是什么样的规模的客户需要使用什么样的拆分颗粒度,都是需要仔细考虑的。绝对不是一尘不变的。

+

H项目心路历程记 (二)

(书接上文)

+

前文已经吐槽了产品设计以及中间件上的相关问题。接下来讨论一下技术上的一些问题。

+

关于配置

对于一个Spring的应用来说,应用配置怎么存取其实并不能说是一个技术问题。更应该算是设计或是架构问题。哪些配置应该进入配置文件,通过微服务配置中心(例如Nacos, Apollo,Kubernetes ConfigMap等)来进行在线配置,哪些配置应该通过其他方式来配置,亦或者哪些配置项目看似是配置项其实本质上就是常量。实际上截止现在H项目的确在有些微服务上是完全没有厘清三者之间的区别,以及界限。以至于现在堆积在配置文件中的内容实在太多。多个运行环境需要统一维护的难度实在太大。

+

对于配置来说,理想的状态应该是下面这样的。

+
    +
  1. 通过微服务配置中心来影响Spring的配置文件来实现动态配置的方案,通常会伴随Spring容器的重启(有些情况下甚至需要整个Docker容器),所以存在这里的配置项应该是随着运行环境改变才会需要改动的配置。例如:

    +
      +
    • 外部系统的链接信息(比如MySQL, Redis的连接信息)
    • +
    • 外部系统的认证凭证(数据库用户密码,第三方系统的密钥等)
    • +
    • 因为运行环境不同导致的不同变量 (例如消息队列的Topic,Group需要根据运行环境使用不同的名称)
    • +
    +
  2. +
  3. 通过数据库或是Redis存储的配置信息,这类配置主要是应该以业务级别的配置,可以通过页面进行修改,瞬间或是几分钟内生效,且不会造成微服务重启(无论是Spring或是Docker容器的重启)。对于这类配置有几个问题还是需要注意的。

    +
      +
    • 配置是否需要集中管理,每个微服务,都有自己独特的业务配置,这些业务配置是否需要集中到一个微服务统一管理,还是分散到各自的微服务进行单独管理。
    • +
    • 全局性统一管理的业务配置对外保留接口应该利用缓存有效的减少数据库访问的频次。甚至可以只使用内存而不是Redis来缓存这部分内容。如果使用内存的话则需要考虑失效时效和重新装载的机制。
    • +
    +
  4. +
  5. 无效配置,实际上很多配置说是可以修改,其实自从开始使用的那天开始就没有修改过。比如用来做分布式锁的键值。这部分需要梳理出来直接以常量的形式做在代码里。

    +
  6. +
+

数据清理以及归档

对于电商行业的零售业务来说每天产生的单据比如订单少做200,300条,多的是上千条。加上明细数据。几个月以后这些数据就会给MySQL的库表增加检索压力。整个H项目在这块是相当薄弱的。只依靠云平台的能力,对一些不需要留档的数据进行清理。远远没有能定期归档。

+

对于数据清理这块大致可以分为这几类

+
    +
  • 定期可以无脑物理删除的数据,比如一些日志类数据。
  • +
  • 定期直接归档的数据,归档后即可清理的数据。
  • +
  • 业务必须处理完成后才能归档并清理的数据。例如订单数据等。
  • +
+

对于数据归档的时候,还需要考虑一些相关联单据数据一并归档。比如订单归档时,同时需要归档相关联的退换货单,发货单,以及操作日志等等。 归档格式基本上就是考虑单条数据以JSON格式存储,然后一个时间段的数据做成压缩包存储在OSS上,然后提供给客户下载。

+

而对于归档数据的检索功能,客户选择需要归档的数据,我们可以将归档的数据(每个压缩包内的所有数据)导入到一个主数据库以外的,临时文档数据库(例如MongoDB),然后针对这个临时的文档数据库进行检索。当然提供的检索项目也会远少于热数据的检索项目。这个临时文档数据库基本上保持一两天数据,定期就全部回收。

+

未完待续

+

H项目心路历程记 (一)

前言

2021年年中从前一家公司离职,经过朋友介绍到了现在公司参与了Halfling项目(以下简称H项目),并从零开始的设计开发到第一个客户交付的全部流程。

+

H项目是一个面向零售行业在电商时代的智能一体化中台产品。项目最早是从2021年中开始立项投入,期间经过数个版本迭代,以及2022年初的魔都封城等诸多困难。终于在2023年Q2季度完成了针对首个客户的上线。

+

作为从头开始全程参与项目的技术人员,经过2年多的产品研发期间有不少想吐槽和想说的。

+

业务设计

最初H项目是以打造一个面向零售行业业务中台为目的进行项目立项的,其中一个很重要的点就是希望除核心组件以外都可以独立售卖。所以在架构设计的使用当下最流行的微服务架构(基于Spring Boot + Kubernetes集群的解决方案), 再做业务拆分到服务的时候,进行了更细颗粒度的拆分。所以前前后后一共拆分了近20个微服务。

+

实际上到了2022年Q2前后,两个核心服务订单管理和库存服务实现以后发现,各个业务服务中间的耦合程度比想象中的还要精密,单个服务拆开单独售卖几乎不可能(应该说是完全不可能)。 然而由于服务拆分的过细,导致一个服务需要频繁访问其他服务获取大量数据。尤其类似订单关联数据比如商品的主档数据,会穿越好几个服务。而每个服务都会重复去获取主档数据,造成了大量的额外请求。 虽然最后在所有服务底部兜底使用同一个Redis作为缓存来减少额外的请求调用。

+

随着时间来到2023年产品组负责人离职后,整个H项目的方向有从行业的业务中台降级成中小企业的订单管理系统(Order Management System, OMS)。前期做的很多设计比如用户权限体系(当初需求是要精确到每个页面的每个按钮,如果下拉列表框有外调请求也需要做权限控制)等就显的很重,导致用户在使用以及配置都很不方便。而有些设计就显得稍显欠缺。 所以整个2022年Q4到2023年都是在对业务层上反复横跳,比如库存模型和商品模型的调整。这点是相当影响团队士气,同时也加剧了研发组和产品组之间的矛盾。

+

然后还有一个很重要的问题是多租户的态度上。早起项目是以服务中大体量客户为目的,所以设计之初并没有考虑多租户的问题。但实际项的进展过程中发现部分中大体量的客户也需要类似多租户的功能。这类客户需求与其说是需求多租户,不如说是希望有一个多租户级别的数据隔离,每个业务部门只能操作自己的业务部门的数据(数据包含商品,品牌,属性,平台店铺,订单,售后单据等),而总部依据可以查看一些汇总信息(例如销售对账单据等)。而随着2022年疫情状况严峻,很多中大客户对于内部平台升级和替换都变的更加谨慎和保守。所以当项目方向变成中小企业OMS时,多租户SaaS化的需求就更加迫切了。所以后期如何在原有系统上在扩展一套多租户SaaS化功能也是个课题。

+

中间件

H项目使用的中间件中比较特别的有分布式任务管理系统xxl-job,消息队列RocketMQ,这两个中间件都是第一次使用。关于这两个中间件也有不是可以谈谈的。

+

分布式任务管理系统 XXL-JOB

作为一个开源的分布式任务管理系统,xxl-job足够简单且相当容易上手使用,无论是基于Spring,还是原生Java做开发都是相当简单的。

+

而不足之处也很明显,最近1年版本迭代速度明显变慢,有些功能和BUG都有待处理的。比如想实动态创建定时任务这类功能就必须自己动手去对xxl-job-admin(调度中心)进行修改。

+

对于任务执行日志这块,现在xxl-job也是文件格式在执行器所在服务器上进行输出的,当执行器容器化或是运行在Kubernetes集群中的时候,当容器或是Pod重启后,就会丢失日志。这点在后期运维以及调试上还是有点麻烦的。

+

消息队列 RocketMQ

阿里出品的消息队列如果基于云产品来使用的话问题不大(阿里云卖的价钱也是有点黑的),但是在运维上还是有点问题的比如Topic,Group这些东西都只能人工去添加,阿里云并没有提供导入服务。全系统如果使用消息队列的场景多的话,维护这些Topic和Group还是很酸爽的。还有一点RocketMQ重复消息这一点是无法保证的。用阿里云的原话

+
+

绝大多数情况下,消息是不重复的。作为一款分布式消息中间件,在网络抖动、应用处理超时等异常情况下,无法保证消息不重复,但是能保证消息不丢失。

+
+

实际运用上,在大数据量并发的情况下,我们实际上是遇到了消息重复消费的问题。这点还是需要通过代码干预(例如增加一个唯一MsgId,对MsgId做分布式锁来解决)。

+

还有一点就是在旧版本SDK的默认配置下消费者的负载是有点小问题,总是有一个节点会比其他节点更容易消费到大量消息。据阿里云自己说明在新版本SDK的默认负载配置也已经修复这个问题了。

+

(未完待续)

+

AKS中重写规则踩坑小记录

前言

最近在做标准产品在不同云平台中的部署验证,有幸体验了一下微软的Azure。负责采购的运维部门这次采用了Application Gateway来搭配AKS(Azure Kubernetes Service)对外暴露服务,正好借着这个机会来体验一下Application Gateway

+

应用场景

    +
  1. 域名api.demo.com指向Application Gateway的IP地址
  2. +
  3. AKS内部2个Service, gateway-servicebackend-service分别需要通过Application Gateway对外暴露。
  4. +
  5. /gateway/指向gateway-service, 然后/backend/指向backend-service。而且两个Service都没有context-path,所以需要做一个Rewrite重写URI到Service的根目录上。
  6. +
+

定义重写集

打开AKS对应的应用程序网关设置 > 重写。选择添加重写集。在1. 名称和关联这个Tab上只需要填写名称这项即可(名称后面在做ingress时需要使用), 关联的传递规则不需要选择。2. 重写规则配置里添加一个重写规则,然后填上重写规则的名称,并添加条件(默认新建重写规则时,只会生成操作,不会生成条件)

+

条件做如下设置

    +
  • 要检查的变量类型 : 服务器变量
  • +
  • 服务器变量: request_uri
  • +
  • 区分大小写:
  • +
  • 运算符: 等号(=)
  • +
  • 要匹配的模式: /(gateway|backend)/?(.*)
  • +
+

操作做如下设置

    +
  • 重写类型: URL
  • +
  • 操作类型: 设置
  • +
  • 组件: URL路径和URL查询字符串
  • +
  • URL路径值: /{var_request_uri_2}
  • +
  • 重新计算路径映射: 不选中
  • +
  • URL查询字符串值: 留空不设值
  • +
+

特殊说明

操作里的URL路径值不能使用正则表达式GROUP替换组,例如$1$2之类的。Azure自己定义了一套对应的替换组命名规则。具体可以参考这个网页使用应用程序网关重写 HTTP 标头和 URL

+

另外一个需要注意一点,如果在条件里选择了服务器变量request_uri的时候,注意这个request_uri是完整的原始请求URI(携带了查询参数)。例如: 在请求http://api.demo.com/gateway/search?foo=bar&hello=world中,request_uri的值将为/gateway/search?foo=bar&hello=world。由于request_uri里包含了查询参数,所以在操作组件中建议勾选URL路径和URL查询字符串。如果只选择URL路径的情况下可能出现无法预期的错误。以我们上述的配置来说明。

+

对象URL: http://api.demo.com/gateway/search?foo=bar&hello=world

+ + + + + + + + + + + + + +
组件URL路径和URL查询字符串URL路径
结果/search?foo=bar&hello=world/search?foo=bar&hello=world?foo=bar&hello=world
+

ACK的Ingress设置

当选择了Application Gateway作为对外暴露Service的方式时,Kubernetes集群里(kube-system命名空间里)多一个Application Gateway Ingress Controller(Azure工单时通常会简称为agic)的Deployment,所以对外暴露服务时可以像传统nginx ingress controller一样添加一个Ingress对象即可(甚至配置也和ngic大致相同,只是多了2个annotations)

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
# 这里指定重写规则集(不是重写规则的名字)
appgw.ingress.kubernetes.io/rewrite-rule-set: rule-backend
# 指定说明你这里ingress的类型是agic
kubernetes.io/ingress.class: azure/application-gateway
name: backend-ingress
namespace: default
spec:
rules:
- host: api.demo.com
http:
paths:
- backend:
service:
name: gateway-service
port:
number: 8080
path: /gateway/
pathType: Prefix
- backend:
service:
name: backend-service
port:
number: 8080
path: /backend/
pathType: Prefix
+ +

总结

由于微软云这块文档有部分缺失,导致在配置这块花了一点时间去排查,甚至开了工单。总结下来Ingress的配置主要是根据请求路径路由到对应的Service,重写规则集才是实际负责根据正则来进行匹配重写。

+

Largrange项目架构与设计回顾 (二)

Largrange项目架构与设计回顾 (一) 里面我讲了一下项目开始架构和技术选型的一些内容。这一章来聊聊业务设计上的这点事。

+

总体来说项目设计的时候,我们对业务模块的划分和拆解总体上来说都是遵循着高内聚,低耦合的原则来进行划分的。大部分和业务相关的服务,都还是能很好的进行功能划分的。但是有一些和具体业务关联性并不是很高的服务在界定与实现时出现了一些问题。

+

任务调度服务

由于平台方面会有一些控制命令下发给Android设备,而且下发的命令并不一定都是实时的,大部分都是指定一个时间来下发,所以在设计的时候就考虑到需要一个任务调度服务(以下略称cron),来处理这些下发指令以及未来可能会有的定时批处理任务的业务需求。技术选型的时候考虑到整个服务是构建在Kubernetes上的一个分布式的微服务架构,所以就没有选择Spring Scheduler,而是使用了Quarter来支撑整个cron

+

从技术选型上来说cron没有什么问题,但是在设计如何使用cron上还是有点问题的, 我们先来看看已推送服务为例,整个平台中cron的处理流程是怎样的。

+
    +
  1. Platform 调用 cron的创建定时任务Job的接口(接口的参数为推送时使用的相关参数)
  2. +
  3. cron 创建Quartz的Job和Trigger,并将Job和Trigger的Name(Quartz中Job和Trigger的唯一标识符)返回个Platform
  4. +
  5. cron 在指定时间触发推送的Job,即调用Push服务的推送接口
  6. +
  7. Push的推送接口调用第三方服务商的推送服务,并将第三方推送服务的调用结果返回给cron
  8. +
  9. cron通过调用Platform预留的推送服务回调地址将第三方推送服务调用接口返还给Platform
  10. +
  11. Platform 将第三方推送服务的调用结果留档保存,并继续业务处理
  12. +
+

设计之初考虑到不想在cron中牵扯到具体的业务, 所以设计了一个Platform的回调接口来处理推送后的具体业务处理。虽然保证了cron尽量减少了和业务逻辑接触与数据库的访问。但是前前后后要访问集群内部的其他微服务2次,额外增加了网络开销,以及因为网络通信造成额外的通信失败风险。而且针对每种不同的定时任务,都需要额外开发一个QuartzJobBean,很难形成统一的QuartzJobBean来进行处理。今后如果还有相似项目还是需要重新考量一下如何设计一个更加完善的定时任务。

+

总结

暂时就想到了这些东西,今后想到啥还会继续在这里补存。

+

Largrange项目架构与设计回顾 (一)

+

项目背景

+
+

从去年年底开始一个老客户希望在他们的一个传统的机械设备(后面略称 E机关 )上外装一个Android设备。 Android设备和 E机关 之间通过串口或是RJ45接口进行数据交互,主要是Android设备获取 E机关 内部的数据,并不会通过接口来控制 E机关 。 Android设备则通过4G 来和平台交互,上报Android设备的状态数据,同时接受平台的控制。以此来实现让传统机械设备也能拥抱物联网的概念。

+

最后围绕着客户的需求分成了3个项目来并行推进

+
    +
  1. Android设备的硬件设备的设计、选材、样机制作与量产规划
  2. +
  3. 在Android设备上,进行与E机关以及平台进行交互的APP开发
  4. +
  5. 用于Android设备交互的平台的架构、设计与开发
  6. +
+

我们平台Team就负责 3.用于Android设备交互的平台 并命名Lagrange (拉格朗日) 。

+

设计&架构

整个平台这块不仅需要向Android设备的APP提供数据交互接口,还需要有一个供相关运营人员使用的前端Web应用,以及与云平台(主要是Aliyun) 交互的功能。所以考虑到多方面使用微服务的架构来实现整个平台端。
由于E机关的工作工况不能保证长期较的稳定的连接到4G网络,所以我们并不考虑使用Socket长连接的方式来做APP和平台之间的数据交互,要实现平台对Android设备进行反控的话,只能实现推送方式来把控制命令下发给Android设备,所以还需要有一个第三方推送服务商交互的服务。同时控制推送也有实时和非实时以及定期推送的需求,所以可能还需要一个任务调度的服务。

+

根据对上述业务进行梳理,我们将项目分成几个服务

+
    +
  • 提供Android设备的交互接口的 App Service
  • +
  • 提供运营人员使用前端Web应用 Platform Web Service
  • +
  • 前端Web应用使用到的一些接口 Platform Service
  • +
  • 负责第三方推送服务商交互的 Push Service
  • +
  • 提供云平台鉴权用的 Auth Service
  • +
  • 用来管理任务调度的 Cron Service
  • +
+

由于前一个项目实施的时候没有使用容器部署,每当访问量峰值的时候,我们这些码农兼运维就各种加班,所以这次项目决定直接将服务容器化,同时选择了Kubernetes来管理容器。由于客观原因线上的Kubernetes直接购买了Aliyun的托管版Kubernetes服务。 内部的开发测试环境则使用了 Rancher 2.0 来构建Kubernetes集群。

+

技术选型

a. 后端服务选型

由于不考虑长连接的原因,所以在后端服务在技术选型上基本就不考虑Netty了,直接上Spring大礼包。

+
    +
  • Spring Boot 开发接口
  • +
  • Spring Data 配合 JPA 来进行数据的持久化
  • +
  • Spring Cloud Kubenetes 来做数据的Config的autoreload
  • +
  • Quartz 负责处理任务调度
  • +
+

服务之间调用都使用HTTP服务, 服务发现也有Kubernetes的DNS机制支持。服务网格则选择了比较成熟的Istio,主要还是Aliyun的Kubernetes可以集成Istio,部署和使用都相当方便。

+

b. 前端Web应用选型

因为前端Web应用主要是给运营人员使用,所以我们考虑使用Single Page Application来做个前后端分离的Web应用。框架这块由于团队成员基本上没有什么前端开发经验,基本都是后台写Java的码农,所以框架选择有点随性,直接就点名了vue.js。前端控件库则用的是阿里系的Antd。

+

c. 数据持久层

主数据库选择了mysql,缓存用的redis。这些都是团队比较熟悉的。由于一些特殊的业务需求和使用场景,我们还加了一个mongodb来做为一个副数据库,主要存放一些特殊业务使用的数据。

+

d. DevOps

用了相当传统的GitLab CE 加上Jenkins的组合,实现前后端代码的自动编译,推送到私有的镜像仓库(使用Aliyun的镜像仓库服务)

+

最后

以上基本是项目最早做设计时候的各种考量。暂时先写这么多,过两天再回顾一下当初设计上有哪些觉得不足的地方。

+

阿里云Kubernetes上线踩坑记

1
2
Update:
2020-04-08 增加istio-ingressgateway高可用的设置
+ +

最近公司因为项目需要,在阿里云上部署了一个Kubernetes集群。虽然阿里云的文档说的还算细致,但是还是有些没有明确说明的细节。

+

1. 购买篇

申请项目预算的时候,只考虑到Worker节点,1个SLB节点以及域名和证书的预算。但是实际购买的时候发现还有许多额外的开销。

+

1.1 SNAT

这个和EIP一并购买,可以方便通过公网使用kubectl访问集群。关于SNAT网关至今不是很明白需要购买这个服务的意义何在,只是为了一个EIP来访问集群吗?

+

1.2 Ingress

这个选上了后,阿里云会给你买个SLB而且还是带公网访问的,如果你后期考虑使用Istio的话,建议你集群创建后,直接停止这个SLB,以免产生额外的费用。

+

1.3 日志服务

通过阿里云的日志服务来收集应用的的日志,挺好用的。但是另外收费,如果有能力的自建日志服务的可不购买。

+

2. Istio

阿里云的Kubernetes集群完美集成了Istio,根据向导就能很简单的部署成功。

+

2.1 额外的SLB

Istio的Gateway 需要绑定一个新的SLB,和Ingress的SLB不能是同一个,又是一笔额外的开销

+

2.2 集群外访问

这个在阿里云的Istio FAQ中有提到,按照指导很容易解决

+

2.2 SLB的443监听

为了方便443端口的证书绑定,我们直接删除了SLB上原有的443监听(TCP协议), 重新建了一个443监听(HTTPS协议),指向和80端口同样的虚拟服务器组。但是设置健康检查时一直出错,经过排查发现SLB健康检查发送的请求协议是HTTP 1.0的,Istio的envoy直接反悔了426(Upgrade Required)这个状态码,所以我们无奈只能把健康检查的检查返回状态改为http_4xx,这样就能通过SLB的健康检查了。

+

2.3 istio-ingressgateway的高可用

istio-ingressgateway要达成高可用,只需要增加通过伸缩POD就可以实现,于istio-ingressgateway对应的SLB中的虚拟服务器组也会自动增加,完全不需要进行额外的手动设定。

+

由于istio-ingressgateway中挂载了HPAHorizontalPodAutoscaler(简称HPA),通常三节点的集群中最小POD数只有1台,在3节点的集群中,要实现高可用,需要手动修改HPA,增加最小POD数。

+
+

基本上现在遇到了这些坑,再有在总结吧。

+

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/tags/Spring/index.html b/tags/Spring/index.html new file mode 100644 index 0000000..8acbd17 --- /dev/null +++ b/tags/Spring/index.html @@ -0,0 +1,119 @@ + +标签: Spring - 西门闲话

Spring Cloud Kubernetes环境下使用Jasypt

前言

最近半年着手开始做了基于微服务的中台项目,整个项目的技术栈采用的是Java + Spring Cloud + Kubernetes + Istio

+

业务开放上还是相当顺利的。但是在安全审核上,运维组提出了一个简易。现在项目一些敏感配置,例如MySQL用户的密码,Redis的密码等现在都是明文保存在Kubernetes的ConfigMap中的(是的,我们并没有Nacos作为微服务的配置中心)。这样可能存在安全隐患。

+

首次尝试

既然有问题,那就解决问题。要给配置文件中的属性项目加密很简单,稍微Google一下,就有现成的方案了。

+

现在比较常用的解决方案就是集成Jasypt,然后通过jasypt-spring-boot-starter来融合进Spring。

+

POM包加入jasypt-spring-boot-starter

1
2
3
4
5
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
+ +

Dockerfile中增加java参数

1
2
...
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar app.jar --jasypt.encryptor.password=helloworld $PARAMS"]
+ +

在ConfigMap中添加加密属性

1
2
3
4
5
6
7
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
data:
application.yaml: |-
test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
+ +

利用actuator接口测试

management.endpoints.web.exposure.include属性中增加env,这样我们就可以通过调用/actuator/env来查看一下env接口返回的整个Spring 容器中所有的PropertySource。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
...
"propertySources": [
{
"name": "bootstrapProperties-configmap.demo.default",
"properties": {
"test2": {
"value": "Hello,world"
}
}
}
...
]
}
+ +

OK, 这下配置项已经加密了。问题解决了。

+

但是…

+

新的问题

自从项目集成了Jayspt以后,出现了一个奇怪的问题。每次项目试图通过修改ConfigMap的配置文件,然后试图通过spring-cloud-starter-kubernetes-fabric8-config来做自动Reload,都失败了。然而查阅应用日志,并没有出现任何异常。无奈只能打开spring-cloudjasypt-spring-bootDEBUG日志。

+

进过几天对日志和两边源代码的分析。终于找到了原因

+

原因

在Spring Boot启动时jasypt-spring-boot会将下面6种配置(并不仅限与这6种配置文件)

+
    +
  • Classpath下的application.yaml
  • +
  • Classpath下的bootstrap.yaml
  • +
  • 集群里名称为${spring.cloud.kubernetes.config.name}的ConfigMap
  • +
  • 集群里名称为${spring.cloud.kubernetes.config.name}-kubernetes的ConfigMap
  • +
  • Java启动参数
  • +
  • 环境变量
  • +
+

转换成jasypt-spring-boot自己的PropertySource实现类EncryptableMapPropertySourceWrapper

+

但是如果使用Kubernetes的ConfigMap来作微服务配置中心的时候,Spring Cloud会在ConfigurationChangeDetector中查找配置类org.springframework.cloud.bootstrap.config.BootstrapPropertySource, 并依据BootstrapPropertySource的类型来判断容器内的配置与集群中ConfigMap里的配置是否有差异,来触发配置reload。

+

由于jasypt-spring-boot已经将所有的配置文件转型成了EncryptableMapPropertySourceWrapper, 所以ConfigurationChangeDetector无法找到BootstrapPropertySource所以会一直任务ConfigMap的里的配置没有变化,导致整个Reload失效(无论是使用polling还是event方式)

+

解决问题

为了保证ConfigMap变化后自动Reload的功能,所以jasypt-spring-boot不能把BootstrapPropertySource转换成EncryptableMapPropertySourceWrapper

+

所以我们需要设置jasypt.encryptor.skip-property-sources配置项, Classpath中的application.yaml需要增加配置

+
1
2
3
jasypt:
encryptor:
skip-property-sources: org.springframework.cloud.bootstrap.config.BootstrapPropertySource
+ +

skip-property-sources配置项配置后,加密项目就不能配置在ConfigMap里了,毕竟已经被我们忽略了。那么我们只能另外找一个PropertySource来存放加密项目了。

+

Classpath中的两个Yaml由于编译时会被Maven打包进Jar文件,会牵涉多个CI/CD多个流程显然不合适,启动参数配置项的也要影响到Docker镜像制作这个流程。所以判断下来最适合的PropertySource就是环境变量了。

+

环境变量增加加密项

在Kubernetes的部署Yaml中,添加加密数据项application.test.str

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: demo
name: demo
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- env:
- name: TZ
value: Asia/Shanghai
- name: application.test.str
value: >-
ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
....
+ +

如果需要更加严密的加密方针的话,我们可以把环境变量的内容放进Kubernetes的Secrets中。

+

在ConfigMap中引用application.test.str

1
2
3
4
5
6
7
8
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
data:
application.yaml: |-
test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)
test3: ${application.test.str}
+ +

通过actuator接口来测试

通过actuator\env接口来测试一下

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
...
"propertySources": [
{
"name": "bootstrapProperties-configmap.demo.default",
"properties": {
"test2": {
"value": "ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)"
},
"test3": {
"value": "Hello,world"
}
}
}
...
]
}
+ +

这样ConfigMap中的配置项test3就可以通过环境变量引用并使用加密配置项了。同时修改ConfigMap依然可以触发auto reload了。这下终于算是解决了。

+

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/tags/generic/index.html b/tags/generic/index.html new file mode 100644 index 0000000..3186d31 --- /dev/null +++ b/tags/generic/index.html @@ -0,0 +1,121 @@ + +标签: generic - 西门闲话

重学Java (一) 泛型

1. 前言

泛型编程自从 Java 5.0 中引入后已经超过15个年头了。对于现在的 Java 码农来说熟练使用泛型编程已经是家常便饭的事情了。所以本文就在不对泛型的基础使用在做说明了。 如果你还不会使用泛型的话,可以参考下面两个链接

+ +

这篇文章就简答聊一下,我实际在开发工作中很少用的到泛型方法这个知识点,以及在实际项目中有哪些东西会使用到泛型。

+

2. 泛型方法

在阅读代码的时候我们经常会看到下面这样的方法 (这段代码摘自 java.util.AbstractCollection)

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 public <T> T[] toArray(T[] a) {
// Estimate size of array; be prepared to see more or fewer elements
int size = size();
T[] r = a.length >= size ? a :
(T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
Iterator<E> it = iterator();

for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) { // fewer elements than expected
if (a == r) {
r[i] = null; // null-terminate
} else if (a.length < i) {
return Arrays.copyOf(r, i);
} else {
System.arraycopy(r, 0, a, 0, i);
if (a.length > i) {
a[i] = null;
}
}
return a;
}
r[i] = (T)it.next();
}
// more elements than expected
return it.hasNext() ? finishToArray(r, it) : r;
}
+ +

那么 pulic 关键字后面的那个 <T> 就是用来标记这个方法是一个泛型方法。 那什么是泛型方法呢。

+

官方的解释是这样的

+
1
Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.
+ +

通俗点来将就是将一个方法泛型化,让一个普通的类的某一个方法具有泛型功能。 如果在一个泛型类中增加一个泛型方法,那这个泛型方法就可以有一套独立于这个类的泛型类型。

+

通过一个简单的例子, 我们来看看

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* GenericClass 这个泛型类是一个简单的套皮的 HashMap
*/
public class GenericClass<K, V> {

private Map<K, V> map = new HashMap<>();

public V put(K key, V value) {
return map.put(key, value);
}

public V get(K key) {
return map.get(key);
}

// 泛型方法 genericMethod 可以接受一个全新的、作用域只限本函数的泛型类型T
public <T> T genericMethod(T t) {
return t;
}

}
+ +

实际使用起来

+
1
2
3
4
5
6
GenericClass<String, Integer> map = new GenericClass<>();
// put 和 get 方法的参数必须使用定义时指定的 String 和 Integer
System.out.println(map.put("One", 1));
System.out.println(map.get("One"));
// 泛型方法 genericMethod 就可以接受一个 String 和 Integer 以外的类型
System.out.println(map.genericMethod(new Double(1.0)).getClass());
+ +

我们再来看看 JDK 中使用到泛型方法的例子。我们最常使用的泛型容器 ArrayList 中有个 toArray 方法。JDK 在它的实现中就提供了两个版本,其中一个就是泛型方法的版本

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
// 这是一个普通版本,返回一个Object的数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}

// 这是一个泛型方法的版本,将容器里存储的元素输出到 T[] 数组中。 其中 T 必须是 E 的父类,否则 System.arraycopy 会抛出 ArrayStoreException 异常
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
}
+ +

泛型方法总体上来说就是可以给与现有的方法实现上,增加一个更加灵活的实现可能。

+

3. 实战应用

在实际的项目中,对于泛型的使用,除了像倾倒垃圾一样往泛型容易里塞各种 java bean 和其他泛型对象。还能怎么使用泛型呢?

+

我们在实际的一些项目中,会对数据库中的一些表(多数时候是全部)先实现 CRUD (Create, Read, Update, Delete)的操作,并从这些操作中延伸出一些简单的 REST 风格的 WebAPI 接口,然后才会根据实际业务需要实现一些更复杂的业务接口。

+

大体上会是下面这个样子。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

// 这是一个简单的 Entity 对象
// 通常现在的 Java 应用都会使用到 Lombok 和 Spring Boot
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Entity
@Table(name = "user")
public class User {
@Id
private Long id;
private String username;
private String password;
}

// 然后这个是 DAO 接口继承自 spring-data 的 JpaRepository
public interface UserDao extends JpaRepository<User, Long> {
}

// 在来是一个访问 User 资源的 Service 和他的实现
public interface UserService {
List<User> findAll();
Optional<User> findById(Long id);
User save (User user)
void deleteById(Long id);
}

@Service
public class UserSerivceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}

@Override
public List<User> findAll() {
return this.dao.findAll();
}

@Override
public Optional<User> findById(Long id) {
return this.dao.findById(id);
}

@Override
public User save(User user) {
return this.dao.save(user);
}

@Override
public void deleteById(Long id) {
this.dao.deleteById(id);
}
}

// 最后就是 WebAPI 的接口了
@RestController
@RequestMapping("/user/")
public class UserController{
private UserService userService;
public UserController(userService userService) {
this.userService = userService;
}

@GetMapping
@ResponseBody
public List<User> fetch() {
return this.userService.findAll();
}

@GetMapping("{id}")
@ResponseBody
public User get(@PathVariable("id") Long id) {
// 由于是示例这里就不考虑没有数据的情况了
return this.userService.findById(id).get();
}

@PostMapping
@ResponseBody
public User create(@RequestBody User user) {
return this.userService.save(user);
}

@PutMapping("{id}")
@ResponseBody
public User update(@RequestBody User user) {
return this.userService.save(user);
}

@DeleteMapping("{id}")
@ResponseBody
public User delete(@PathVariable("id") Long id) {
User user = this.userService.findById(id);
this.userService.deleteById(id);
return user;
}
}
+ +

大致一个表的一套相关接口就是这个样子的。如果你的数据库中有大量表的话,而且每个表都需要提供 REST 风格的 WebAPI 接口的话,那么这将是一个相当枯燥的而又及其容易出错的工作。

+

为了不让这项枯燥而又容易犯错的工作占去我们宝贵的私人时间,我们可以通过泛型和继承的技巧来重构从 Service 层到 Controller 的这段代码(感谢 spring-data 提供了 JpaRepository, 让我们不至于从 DAO 层重构)

+

3.1 Service 层的重构

首先是 Service 接口的重构,我们 Service 层接口就是定义了一组 CRUD 的操作,我们可以将这组 CRUD 操作抽象到一个父接口,然后所有 Service 层的接口都将继承自这个父接口。而接口中出现的 Entity 和主键的类型(上例中 User 的主键 id 的类型是 Long)就可以用泛型来展现。

+
1
2
3
4
5
6
7
8
9
10
11
// 这里泛型表示 E 来指代 Entity, ID 用来指代 Entity 主键的类型
public interface ICrudService<E, ID> {
List<E> findAll();
Optional<E> findById(ID id);
E save(E e);
void deleteById(ID id);
}

// 然后 Service 层的接口,就可以简化成这样
public interface UserService extends ICrudService<User, Long> {
}
+ +

同样 Service 层的实现也可以使用相似的方法具体实现可以抽象到一个基类中。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 相比 ICrudService 这里有多了一个泛型 T 来代表 Entity 对应的 DAO, 我们的每一个 DAO 都继承自
// spring-data 的 JpaRepository 所以,这里可以使用到泛型的边界
public abstract class AbstractCrudService<T extends JpaRepository<E, ID>, E, ID> {
private T dao;
public AbstractCrudService(T dao) {
this.dao = dao;
}

public List<E> findAll() {
return this.dao.findAll();
}

public Optional<E> findById(ID id) {
return this.dao.findById(id);
}

public E save(E e) {
return this.dao.save(e);
}

public void deleteById(ID id) {
this.dao.deleteById(id);
}
}

// 那 Service 的实现类可以简化成这样
@Service
public class UserServiceImpl extends AbstractCrudService<UserDao, User, Long> implements UserService {
public UserServiceImpl(UserDao dao) {
supper(dao);
}
}
+ +

同样我们可以通过相同的方法来对 Controller 层进行重构

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Controller 层的基类
public abstract class AbstractCrudController<T extends ICrudService<E, ID>, E, ID> {
private T service;
public AbstractCrudController(T service) {
this.service = service;
}

@GetMapping
@ResponseBody
public List<E> fetch() {
return this.service.findAll();
}

@GetMapping("{id}")
@ResponseBody
public E get(@PathVariable("id") ID id) {
// 由于是示例这里就不考虑没有数据的情况了
return this.service.findById(id).get();
}

@PostMapping
@ResponseBody
public E create(@RequestBody E e) {
return this.service.save(e);
}

@PutMapping("{id}")
@ResponseBody
public E update(@RequestBody E e) {
return this.service.save(e);
}

@DeleteMapping("{id}")
@ResponseBody
public E delete(@PathVariable("id") ID id) {
E e = this.service.findById(id).get();
this.service.deleteById(id);
return e;
}
}

// 具体的 WebAPI
@RestController
@RequestMapping("/user/")
public class UserController extends AbstractCrudController<UserService, User, Long> {
public UserController(UserService service) {
super(service);
}
}
+ +

经过重构可以消减掉 Servcie 和 Controller 中的大量重复代码,使代码更容易维护了。

+

4. 结尾

关于泛型就简单的说这些了,泛型作为 Java 日常开发中一个常用的知识点,其实还有很多知识点可以供我们挖掘,奈何本人才疏学浅,这么多年工作下来,只积累出来这么点内容。

+

文末放上示例代码的代码库:

+ +

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/tags/index.html b/tags/index.html new file mode 100644 index 0000000..f6a2aca --- /dev/null +++ b/tags/index.html @@ -0,0 +1,74 @@ + +标签 - 西门闲话

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/tags/java/index.html b/tags/java/index.html new file mode 100644 index 0000000..dd6797d --- /dev/null +++ b/tags/java/index.html @@ -0,0 +1,121 @@ + +标签: java - 西门闲话

重学Java (一) 泛型

1. 前言

泛型编程自从 Java 5.0 中引入后已经超过15个年头了。对于现在的 Java 码农来说熟练使用泛型编程已经是家常便饭的事情了。所以本文就在不对泛型的基础使用在做说明了。 如果你还不会使用泛型的话,可以参考下面两个链接

+ +

这篇文章就简答聊一下,我实际在开发工作中很少用的到泛型方法这个知识点,以及在实际项目中有哪些东西会使用到泛型。

+

2. 泛型方法

在阅读代码的时候我们经常会看到下面这样的方法 (这段代码摘自 java.util.AbstractCollection)

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 public <T> T[] toArray(T[] a) {
// Estimate size of array; be prepared to see more or fewer elements
int size = size();
T[] r = a.length >= size ? a :
(T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
Iterator<E> it = iterator();

for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) { // fewer elements than expected
if (a == r) {
r[i] = null; // null-terminate
} else if (a.length < i) {
return Arrays.copyOf(r, i);
} else {
System.arraycopy(r, 0, a, 0, i);
if (a.length > i) {
a[i] = null;
}
}
return a;
}
r[i] = (T)it.next();
}
// more elements than expected
return it.hasNext() ? finishToArray(r, it) : r;
}
+ +

那么 pulic 关键字后面的那个 <T> 就是用来标记这个方法是一个泛型方法。 那什么是泛型方法呢。

+

官方的解释是这样的

+
1
Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.
+ +

通俗点来将就是将一个方法泛型化,让一个普通的类的某一个方法具有泛型功能。 如果在一个泛型类中增加一个泛型方法,那这个泛型方法就可以有一套独立于这个类的泛型类型。

+

通过一个简单的例子, 我们来看看

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* GenericClass 这个泛型类是一个简单的套皮的 HashMap
*/
public class GenericClass<K, V> {

private Map<K, V> map = new HashMap<>();

public V put(K key, V value) {
return map.put(key, value);
}

public V get(K key) {
return map.get(key);
}

// 泛型方法 genericMethod 可以接受一个全新的、作用域只限本函数的泛型类型T
public <T> T genericMethod(T t) {
return t;
}

}
+ +

实际使用起来

+
1
2
3
4
5
6
GenericClass<String, Integer> map = new GenericClass<>();
// put 和 get 方法的参数必须使用定义时指定的 String 和 Integer
System.out.println(map.put("One", 1));
System.out.println(map.get("One"));
// 泛型方法 genericMethod 就可以接受一个 String 和 Integer 以外的类型
System.out.println(map.genericMethod(new Double(1.0)).getClass());
+ +

我们再来看看 JDK 中使用到泛型方法的例子。我们最常使用的泛型容器 ArrayList 中有个 toArray 方法。JDK 在它的实现中就提供了两个版本,其中一个就是泛型方法的版本

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
// 这是一个普通版本,返回一个Object的数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}

// 这是一个泛型方法的版本,将容器里存储的元素输出到 T[] 数组中。 其中 T 必须是 E 的父类,否则 System.arraycopy 会抛出 ArrayStoreException 异常
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
}
+ +

泛型方法总体上来说就是可以给与现有的方法实现上,增加一个更加灵活的实现可能。

+

3. 实战应用

在实际的项目中,对于泛型的使用,除了像倾倒垃圾一样往泛型容易里塞各种 java bean 和其他泛型对象。还能怎么使用泛型呢?

+

我们在实际的一些项目中,会对数据库中的一些表(多数时候是全部)先实现 CRUD (Create, Read, Update, Delete)的操作,并从这些操作中延伸出一些简单的 REST 风格的 WebAPI 接口,然后才会根据实际业务需要实现一些更复杂的业务接口。

+

大体上会是下面这个样子。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

// 这是一个简单的 Entity 对象
// 通常现在的 Java 应用都会使用到 Lombok 和 Spring Boot
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Entity
@Table(name = "user")
public class User {
@Id
private Long id;
private String username;
private String password;
}

// 然后这个是 DAO 接口继承自 spring-data 的 JpaRepository
public interface UserDao extends JpaRepository<User, Long> {
}

// 在来是一个访问 User 资源的 Service 和他的实现
public interface UserService {
List<User> findAll();
Optional<User> findById(Long id);
User save (User user)
void deleteById(Long id);
}

@Service
public class UserSerivceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}

@Override
public List<User> findAll() {
return this.dao.findAll();
}

@Override
public Optional<User> findById(Long id) {
return this.dao.findById(id);
}

@Override
public User save(User user) {
return this.dao.save(user);
}

@Override
public void deleteById(Long id) {
this.dao.deleteById(id);
}
}

// 最后就是 WebAPI 的接口了
@RestController
@RequestMapping("/user/")
public class UserController{
private UserService userService;
public UserController(userService userService) {
this.userService = userService;
}

@GetMapping
@ResponseBody
public List<User> fetch() {
return this.userService.findAll();
}

@GetMapping("{id}")
@ResponseBody
public User get(@PathVariable("id") Long id) {
// 由于是示例这里就不考虑没有数据的情况了
return this.userService.findById(id).get();
}

@PostMapping
@ResponseBody
public User create(@RequestBody User user) {
return this.userService.save(user);
}

@PutMapping("{id}")
@ResponseBody
public User update(@RequestBody User user) {
return this.userService.save(user);
}

@DeleteMapping("{id}")
@ResponseBody
public User delete(@PathVariable("id") Long id) {
User user = this.userService.findById(id);
this.userService.deleteById(id);
return user;
}
}
+ +

大致一个表的一套相关接口就是这个样子的。如果你的数据库中有大量表的话,而且每个表都需要提供 REST 风格的 WebAPI 接口的话,那么这将是一个相当枯燥的而又及其容易出错的工作。

+

为了不让这项枯燥而又容易犯错的工作占去我们宝贵的私人时间,我们可以通过泛型和继承的技巧来重构从 Service 层到 Controller 的这段代码(感谢 spring-data 提供了 JpaRepository, 让我们不至于从 DAO 层重构)

+

3.1 Service 层的重构

首先是 Service 接口的重构,我们 Service 层接口就是定义了一组 CRUD 的操作,我们可以将这组 CRUD 操作抽象到一个父接口,然后所有 Service 层的接口都将继承自这个父接口。而接口中出现的 Entity 和主键的类型(上例中 User 的主键 id 的类型是 Long)就可以用泛型来展现。

+
1
2
3
4
5
6
7
8
9
10
11
// 这里泛型表示 E 来指代 Entity, ID 用来指代 Entity 主键的类型
public interface ICrudService<E, ID> {
List<E> findAll();
Optional<E> findById(ID id);
E save(E e);
void deleteById(ID id);
}

// 然后 Service 层的接口,就可以简化成这样
public interface UserService extends ICrudService<User, Long> {
}
+ +

同样 Service 层的实现也可以使用相似的方法具体实现可以抽象到一个基类中。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 相比 ICrudService 这里有多了一个泛型 T 来代表 Entity 对应的 DAO, 我们的每一个 DAO 都继承自
// spring-data 的 JpaRepository 所以,这里可以使用到泛型的边界
public abstract class AbstractCrudService<T extends JpaRepository<E, ID>, E, ID> {
private T dao;
public AbstractCrudService(T dao) {
this.dao = dao;
}

public List<E> findAll() {
return this.dao.findAll();
}

public Optional<E> findById(ID id) {
return this.dao.findById(id);
}

public E save(E e) {
return this.dao.save(e);
}

public void deleteById(ID id) {
this.dao.deleteById(id);
}
}

// 那 Service 的实现类可以简化成这样
@Service
public class UserServiceImpl extends AbstractCrudService<UserDao, User, Long> implements UserService {
public UserServiceImpl(UserDao dao) {
supper(dao);
}
}
+ +

同样我们可以通过相同的方法来对 Controller 层进行重构

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Controller 层的基类
public abstract class AbstractCrudController<T extends ICrudService<E, ID>, E, ID> {
private T service;
public AbstractCrudController(T service) {
this.service = service;
}

@GetMapping
@ResponseBody
public List<E> fetch() {
return this.service.findAll();
}

@GetMapping("{id}")
@ResponseBody
public E get(@PathVariable("id") ID id) {
// 由于是示例这里就不考虑没有数据的情况了
return this.service.findById(id).get();
}

@PostMapping
@ResponseBody
public E create(@RequestBody E e) {
return this.service.save(e);
}

@PutMapping("{id}")
@ResponseBody
public E update(@RequestBody E e) {
return this.service.save(e);
}

@DeleteMapping("{id}")
@ResponseBody
public E delete(@PathVariable("id") ID id) {
E e = this.service.findById(id).get();
this.service.deleteById(id);
return e;
}
}

// 具体的 WebAPI
@RestController
@RequestMapping("/user/")
public class UserController extends AbstractCrudController<UserService, User, Long> {
public UserController(UserService service) {
super(service);
}
}
+ +

经过重构可以消减掉 Servcie 和 Controller 中的大量重复代码,使代码更容易维护了。

+

4. 结尾

关于泛型就简单的说这些了,泛型作为 Java 日常开发中一个常用的知识点,其实还有很多知识点可以供我们挖掘,奈何本人才疏学浅,这么多年工作下来,只积累出来这么点内容。

+

文末放上示例代码的代码库:

+ +

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/tags/mongodb/index.html b/tags/mongodb/index.html new file mode 100644 index 0000000..d1cb506 --- /dev/null +++ b/tags/mongodb/index.html @@ -0,0 +1,99 @@ + +标签: mongodb - 西门闲话

在ubuntu上从零搭建node.js + nginx + mongodb环境

说到后端开发环境,最有名的莫过于LAMP和LNMP,最近由于node.js的强势崛起,越来越多的后端开发也开始试水node.js了。我最近也因为各种原因,前前后后总够构建了好几台node.js + nginx + mongodb的Linux服务器。

+

首先关于Linux服务器,比起CentOS来说,我更加喜欢ubuntu一点。所以无论是阿里云还是一些海外的vps服务器上,我也倾向选用ubuntu服务器,本贴也是基于ubuntu服务器里说明的。

+

1.开始前的一些准备

首先还是需要刷新一下ubuntu的包索引并安装build-essential和libssl-dev这2个包以及curl这个工具。

+
1
2
3
sudo apt-get update
sudo apt-get install build-essential libssl-dev
sudo apt-get isntall curl
+ +

2.安装node.js

关于安装node.js这一点,我不是很推荐使用apt-get 来安装node.js的环境。主要是因为node.js和io.js合并以后,版本迭代速度相当频繁(主要还是因为更多ES6的特性得到了支持)。今后很有可能会有在一台服务器上使用不同版本的node.js的需求。

+

这里推荐一个管理不同版本node.js的工具:nvm,官网: https://github.com/creationix/nvm  。安装nvm,如果前面你安装了curl的话可以

+
1
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
+

如果没有按照curl的话,也可以使用wget来进行安装

+
1
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
+ +

然后nvm就会自动安装到home目录下面的.nvm目录里,并会在.bashrc里自动添加nvm的环境变量。为了让环境变量生效,最简单的方法就是通过ssh或是telnet重新连接你的服务器。

+

安装完nvm后,就可以通过nvm来安装指定版本的node.js了。

+
1
2
3
4
5
# 列出可以安装的node版本号
nvm ls-remote

# 安装指定版本的node (当前最新版本为v5.7.1, LTS版是v4.3.2)
nvm install v4.3.2
+ +

3.安装nginx

由于ubuntu源(尤其是阿里云的源)上的nginx经常不是最新的,如果需要安装最新版本nginx的时候需要手动添加nginx的源。

+
1
2
3
4
5
6
7
8
9
10
11
# 添加nginx的mainline仓库
cd /tmp/ && wget http://nginx.org/keys/nginx_signing.key
sudo apt-key add nginx_signing.key

# 编辑/etc/apt/sources.list.d/nginx.list 添加下面2行内容,井号不需要
# deb http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
# deb-src http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
sudo vi /etc/apt/sources.list.d/nginx.list

# 更新源,并安装nginx
sudo apt-get update && sudo apt-get install nginx
+ +

在编辑/etc/apt/sources.list.d/nginx.list的时候需要注意,“ubuntu代号”需要根据ubuntu服务器的版本不同手动调整的,比如14.04是trusty。通过下面的命令可以获取ubuntu的代号。

+
1
lsb_release -cs
+ +

4.安装mongodb

同样和nginx有同样的问题,要安装最新3.2版本的mongodb也需要手动添加ubuntu的源。

+
1
2
3
4
5
6
7
8
9
10
11
# 导入mongodb的public key
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927

# 生成mongodb的源list
echo "deb http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list

# 更新源
sudo apt-get update

# 安装最新版本的mongodb
sudo apt-get install -y mongodb-org
+ +

以上一台node.js + nginx + mongodb的ubuntu服务器就完成了。

+

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/tags/nginx/index.html b/tags/nginx/index.html new file mode 100644 index 0000000..85a60d1 --- /dev/null +++ b/tags/nginx/index.html @@ -0,0 +1,99 @@ + +标签: nginx - 西门闲话

在ubuntu上从零搭建node.js + nginx + mongodb环境

说到后端开发环境,最有名的莫过于LAMP和LNMP,最近由于node.js的强势崛起,越来越多的后端开发也开始试水node.js了。我最近也因为各种原因,前前后后总够构建了好几台node.js + nginx + mongodb的Linux服务器。

+

首先关于Linux服务器,比起CentOS来说,我更加喜欢ubuntu一点。所以无论是阿里云还是一些海外的vps服务器上,我也倾向选用ubuntu服务器,本贴也是基于ubuntu服务器里说明的。

+

1.开始前的一些准备

首先还是需要刷新一下ubuntu的包索引并安装build-essential和libssl-dev这2个包以及curl这个工具。

+
1
2
3
sudo apt-get update
sudo apt-get install build-essential libssl-dev
sudo apt-get isntall curl
+ +

2.安装node.js

关于安装node.js这一点,我不是很推荐使用apt-get 来安装node.js的环境。主要是因为node.js和io.js合并以后,版本迭代速度相当频繁(主要还是因为更多ES6的特性得到了支持)。今后很有可能会有在一台服务器上使用不同版本的node.js的需求。

+

这里推荐一个管理不同版本node.js的工具:nvm,官网: https://github.com/creationix/nvm  。安装nvm,如果前面你安装了curl的话可以

+
1
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
+

如果没有按照curl的话,也可以使用wget来进行安装

+
1
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
+ +

然后nvm就会自动安装到home目录下面的.nvm目录里,并会在.bashrc里自动添加nvm的环境变量。为了让环境变量生效,最简单的方法就是通过ssh或是telnet重新连接你的服务器。

+

安装完nvm后,就可以通过nvm来安装指定版本的node.js了。

+
1
2
3
4
5
# 列出可以安装的node版本号
nvm ls-remote

# 安装指定版本的node (当前最新版本为v5.7.1, LTS版是v4.3.2)
nvm install v4.3.2
+ +

3.安装nginx

由于ubuntu源(尤其是阿里云的源)上的nginx经常不是最新的,如果需要安装最新版本nginx的时候需要手动添加nginx的源。

+
1
2
3
4
5
6
7
8
9
10
11
# 添加nginx的mainline仓库
cd /tmp/ && wget http://nginx.org/keys/nginx_signing.key
sudo apt-key add nginx_signing.key

# 编辑/etc/apt/sources.list.d/nginx.list 添加下面2行内容,井号不需要
# deb http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
# deb-src http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
sudo vi /etc/apt/sources.list.d/nginx.list

# 更新源,并安装nginx
sudo apt-get update && sudo apt-get install nginx
+ +

在编辑/etc/apt/sources.list.d/nginx.list的时候需要注意,“ubuntu代号”需要根据ubuntu服务器的版本不同手动调整的,比如14.04是trusty。通过下面的命令可以获取ubuntu的代号。

+
1
lsb_release -cs
+ +

4.安装mongodb

同样和nginx有同样的问题,要安装最新3.2版本的mongodb也需要手动添加ubuntu的源。

+
1
2
3
4
5
6
7
8
9
10
11
# 导入mongodb的public key
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927

# 生成mongodb的源list
echo "deb http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list

# 更新源
sudo apt-get update

# 安装最新版本的mongodb
sudo apt-get install -y mongodb-org
+ +

以上一台node.js + nginx + mongodb的ubuntu服务器就完成了。

+

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/tags/node-js/index.html b/tags/node-js/index.html new file mode 100644 index 0000000..0354f1f --- /dev/null +++ b/tags/node-js/index.html @@ -0,0 +1,99 @@ + +标签: node.js - 西门闲话

在ubuntu上从零搭建node.js + nginx + mongodb环境

说到后端开发环境,最有名的莫过于LAMP和LNMP,最近由于node.js的强势崛起,越来越多的后端开发也开始试水node.js了。我最近也因为各种原因,前前后后总够构建了好几台node.js + nginx + mongodb的Linux服务器。

+

首先关于Linux服务器,比起CentOS来说,我更加喜欢ubuntu一点。所以无论是阿里云还是一些海外的vps服务器上,我也倾向选用ubuntu服务器,本贴也是基于ubuntu服务器里说明的。

+

1.开始前的一些准备

首先还是需要刷新一下ubuntu的包索引并安装build-essential和libssl-dev这2个包以及curl这个工具。

+
1
2
3
sudo apt-get update
sudo apt-get install build-essential libssl-dev
sudo apt-get isntall curl
+ +

2.安装node.js

关于安装node.js这一点,我不是很推荐使用apt-get 来安装node.js的环境。主要是因为node.js和io.js合并以后,版本迭代速度相当频繁(主要还是因为更多ES6的特性得到了支持)。今后很有可能会有在一台服务器上使用不同版本的node.js的需求。

+

这里推荐一个管理不同版本node.js的工具:nvm,官网: https://github.com/creationix/nvm  。安装nvm,如果前面你安装了curl的话可以

+
1
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
+

如果没有按照curl的话,也可以使用wget来进行安装

+
1
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
+ +

然后nvm就会自动安装到home目录下面的.nvm目录里,并会在.bashrc里自动添加nvm的环境变量。为了让环境变量生效,最简单的方法就是通过ssh或是telnet重新连接你的服务器。

+

安装完nvm后,就可以通过nvm来安装指定版本的node.js了。

+
1
2
3
4
5
# 列出可以安装的node版本号
nvm ls-remote

# 安装指定版本的node (当前最新版本为v5.7.1, LTS版是v4.3.2)
nvm install v4.3.2
+ +

3.安装nginx

由于ubuntu源(尤其是阿里云的源)上的nginx经常不是最新的,如果需要安装最新版本nginx的时候需要手动添加nginx的源。

+
1
2
3
4
5
6
7
8
9
10
11
# 添加nginx的mainline仓库
cd /tmp/ && wget http://nginx.org/keys/nginx_signing.key
sudo apt-key add nginx_signing.key

# 编辑/etc/apt/sources.list.d/nginx.list 添加下面2行内容,井号不需要
# deb http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
# deb-src http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
sudo vi /etc/apt/sources.list.d/nginx.list

# 更新源,并安装nginx
sudo apt-get update && sudo apt-get install nginx
+ +

在编辑/etc/apt/sources.list.d/nginx.list的时候需要注意,“ubuntu代号”需要根据ubuntu服务器的版本不同手动调整的,比如14.04是trusty。通过下面的命令可以获取ubuntu的代号。

+
1
lsb_release -cs
+ +

4.安装mongodb

同样和nginx有同样的问题,要安装最新3.2版本的mongodb也需要手动添加ubuntu的源。

+
1
2
3
4
5
6
7
8
9
10
11
# 导入mongodb的public key
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927

# 生成mongodb的源list
echo "deb http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list

# 更新源
sudo apt-get update

# 安装最新版本的mongodb
sudo apt-get install -y mongodb-org
+ +

以上一台node.js + nginx + mongodb的ubuntu服务器就完成了。

+

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git a/tags/ubuntu/index.html b/tags/ubuntu/index.html new file mode 100644 index 0000000..8dc97af --- /dev/null +++ b/tags/ubuntu/index.html @@ -0,0 +1,99 @@ + +标签: ubuntu - 西门闲话

在ubuntu上从零搭建node.js + nginx + mongodb环境

说到后端开发环境,最有名的莫过于LAMP和LNMP,最近由于node.js的强势崛起,越来越多的后端开发也开始试水node.js了。我最近也因为各种原因,前前后后总够构建了好几台node.js + nginx + mongodb的Linux服务器。

+

首先关于Linux服务器,比起CentOS来说,我更加喜欢ubuntu一点。所以无论是阿里云还是一些海外的vps服务器上,我也倾向选用ubuntu服务器,本贴也是基于ubuntu服务器里说明的。

+

1.开始前的一些准备

首先还是需要刷新一下ubuntu的包索引并安装build-essential和libssl-dev这2个包以及curl这个工具。

+
1
2
3
sudo apt-get update
sudo apt-get install build-essential libssl-dev
sudo apt-get isntall curl
+ +

2.安装node.js

关于安装node.js这一点,我不是很推荐使用apt-get 来安装node.js的环境。主要是因为node.js和io.js合并以后,版本迭代速度相当频繁(主要还是因为更多ES6的特性得到了支持)。今后很有可能会有在一台服务器上使用不同版本的node.js的需求。

+

这里推荐一个管理不同版本node.js的工具:nvm,官网: https://github.com/creationix/nvm  。安装nvm,如果前面你安装了curl的话可以

+
1
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
+

如果没有按照curl的话,也可以使用wget来进行安装

+
1
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
+ +

然后nvm就会自动安装到home目录下面的.nvm目录里,并会在.bashrc里自动添加nvm的环境变量。为了让环境变量生效,最简单的方法就是通过ssh或是telnet重新连接你的服务器。

+

安装完nvm后,就可以通过nvm来安装指定版本的node.js了。

+
1
2
3
4
5
# 列出可以安装的node版本号
nvm ls-remote

# 安装指定版本的node (当前最新版本为v5.7.1, LTS版是v4.3.2)
nvm install v4.3.2
+ +

3.安装nginx

由于ubuntu源(尤其是阿里云的源)上的nginx经常不是最新的,如果需要安装最新版本nginx的时候需要手动添加nginx的源。

+
1
2
3
4
5
6
7
8
9
10
11
# 添加nginx的mainline仓库
cd /tmp/ && wget http://nginx.org/keys/nginx_signing.key
sudo apt-key add nginx_signing.key

# 编辑/etc/apt/sources.list.d/nginx.list 添加下面2行内容,井号不需要
# deb http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
# deb-src http://nginx.org/packages/mainline/ubuntu/ ubuntu代号 nginx
sudo vi /etc/apt/sources.list.d/nginx.list

# 更新源,并安装nginx
sudo apt-get update && sudo apt-get install nginx
+ +

在编辑/etc/apt/sources.list.d/nginx.list的时候需要注意,“ubuntu代号”需要根据ubuntu服务器的版本不同手动调整的,比如14.04是trusty。通过下面的命令可以获取ubuntu的代号。

+
1
lsb_release -cs
+ +

4.安装mongodb

同样和nginx有同样的问题,要安装最新3.2版本的mongodb也需要手动添加ubuntu的源。

+
1
2
3
4
5
6
7
8
9
10
11
# 导入mongodb的public key
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927

# 生成mongodb的源list
echo "deb http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list

# 更新源
sudo apt-get update

# 安装最新版本的mongodb
sudo apt-get install -y mongodb-org
+ +

以上一台node.js + nginx + mongodb的ubuntu服务器就完成了。

+

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git "a/tags/\346\236\266\346\236\204/index.html" "b/tags/\346\236\266\346\236\204/index.html" new file mode 100644 index 0000000..211d4b7 --- /dev/null +++ "b/tags/\346\236\266\346\236\204/index.html" @@ -0,0 +1,193 @@ + +标签: 架构 - 西门闲话

H项目心路历程记(三)

现状

对于一个基于微服务的中台系统来说,需要以什么样的颗粒度来拆分系统。这一直是基于微服务架构的系统中绕不过去的问题。

+

已H项目来说,当初考量是这么划分微服务的,其中

+
    +
  • 基础服务: 作用主要是负责用户,角色权限,登陆鉴权,以及一些基础主档数据的日常维护功能
  • +
  • 商品服务: 物料,销售商品,款式,平台商品等和实际商品相关的主档数据
  • +
  • 库存服务:负责管理各个库存层级库存(总库存,冻结库存,在途库存等),以及展示各种出入库单据,并向其他微服务提供出入库服务接口。
  • +
  • 订单服务: 负责接收各个公域平台的ToC订单,售后单(含发货前退款,退货,换货单等),并根据配置策略进行单据路由(分仓,分物流,Hold单,加送赠品,通知仓库发货,通知平台发货等)
  • +
  • 接口服务: 主要承揽中台服务和第三方平台间的交互处理(主要是第三方平台发起的主调通知以及回调)
  • +
+

上述五个服务为核心服务,是保证整个中台系统基础业务功能运行的重要组成部分。除此之外还有,

+
    +
  • 采购服务:负责ToB业务线的处理
  • +
  • 财务服务:满足一些企业的财务对账以及开具电子发票的功能
  • +
  • 报表服务: 每天,每周定期从其他服务(主要是订单)
  • +
  • 会员服务:管理各类会员
  • +
+

等7,8个非核心服务,组成非核心业务。来构成一个完整的中台系统。

+

拆分策略以及缺点

当时主要考虑到整个中台系统是可以拆分出售,客户可以只选择购买自身需要的服务,而不需要的服务则无需购买服务,所以当初在拆分服务上在业务领域层面上需要更加低颗粒度;同时考虑到再业务高峰期(下午到晚上,就是各位疯狂刷淘宝,京东的时候)各个服务的负载是各不相同的,如果在高峰期对于特定服务需要进行精准扩容的话,拆分的颗粒度也需要更加细致,所以结果上导致整个系统满血运行起来要至少要十几个服务,外加一些没有云平台的中间件。整个Kubernetes环境中,至少要有十五六个Deployment,考虑到系统稳定性,一个Deployment也至少要有2到3个Pod,那整个系统至少就是50个Pod。

+

对于一个中大型的电商企业再服务器成本上可以负担这样一个能支持50个Pod的Kubernentes集群(考虑到需要一些中间件的云产品,当时估算最小的服务器成本都要到70K/年,一个中大型企业能面对双十一流量的服务器成本都要到300K/年以上)。所以整个系统对于中小型企业来说前期的投入负担相当大(尤其是在后疫情时代的2022年,2023年),以至于早起的好几个客户都是需要我们自己贴补服务器费用的。

+

复盘

现在重新复盘当初服务的拆分策略,个人觉得对于核心业务来说,相似领域的一些服务应该可以适当的聚合。比如上述的五大核心服务上中的商品服务,订单服务,库存服务三个核心服务应该可以合并在一起。首先这3个核心服务是H项目的最重要的竞争力,几乎不可能出现把这3个服务拆分出售的可能性。

+

而且在业务高峰期有订单服务有新订单处理时,势必会产生库存的变化,因此会调用到库存服务。三个服务之间的调用是相当频繁的,尤其是负责处理商品主档数据的商品服务,无论是通过HTTP或是其他方式的远程调用的话,都会有许多浪费的带宽。如果把三个服务整合,那么之间的调用基本上就是线程间的交互,无论是效率和可靠性上都会有更加显著的提升。

+

结尾

怎么拆分服务一直是个难题。虽然事后复盘的时候我的很多想法也不一定是正确,但是什么样的规模的客户需要使用什么样的拆分颗粒度,都是需要仔细考虑的。绝对不是一尘不变的。

+

H项目心路历程记 (二)

(书接上文)

+

前文已经吐槽了产品设计以及中间件上的相关问题。接下来讨论一下技术上的一些问题。

+

关于配置

对于一个Spring的应用来说,应用配置怎么存取其实并不能说是一个技术问题。更应该算是设计或是架构问题。哪些配置应该进入配置文件,通过微服务配置中心(例如Nacos, Apollo,Kubernetes ConfigMap等)来进行在线配置,哪些配置应该通过其他方式来配置,亦或者哪些配置项目看似是配置项其实本质上就是常量。实际上截止现在H项目的确在有些微服务上是完全没有厘清三者之间的区别,以及界限。以至于现在堆积在配置文件中的内容实在太多。多个运行环境需要统一维护的难度实在太大。

+

对于配置来说,理想的状态应该是下面这样的。

+
    +
  1. 通过微服务配置中心来影响Spring的配置文件来实现动态配置的方案,通常会伴随Spring容器的重启(有些情况下甚至需要整个Docker容器),所以存在这里的配置项应该是随着运行环境改变才会需要改动的配置。例如:

    +
      +
    • 外部系统的链接信息(比如MySQL, Redis的连接信息)
    • +
    • 外部系统的认证凭证(数据库用户密码,第三方系统的密钥等)
    • +
    • 因为运行环境不同导致的不同变量 (例如消息队列的Topic,Group需要根据运行环境使用不同的名称)
    • +
    +
  2. +
  3. 通过数据库或是Redis存储的配置信息,这类配置主要是应该以业务级别的配置,可以通过页面进行修改,瞬间或是几分钟内生效,且不会造成微服务重启(无论是Spring或是Docker容器的重启)。对于这类配置有几个问题还是需要注意的。

    +
      +
    • 配置是否需要集中管理,每个微服务,都有自己独特的业务配置,这些业务配置是否需要集中到一个微服务统一管理,还是分散到各自的微服务进行单独管理。
    • +
    • 全局性统一管理的业务配置对外保留接口应该利用缓存有效的减少数据库访问的频次。甚至可以只使用内存而不是Redis来缓存这部分内容。如果使用内存的话则需要考虑失效时效和重新装载的机制。
    • +
    +
  4. +
  5. 无效配置,实际上很多配置说是可以修改,其实自从开始使用的那天开始就没有修改过。比如用来做分布式锁的键值。这部分需要梳理出来直接以常量的形式做在代码里。

    +
  6. +
+

数据清理以及归档

对于电商行业的零售业务来说每天产生的单据比如订单少做200,300条,多的是上千条。加上明细数据。几个月以后这些数据就会给MySQL的库表增加检索压力。整个H项目在这块是相当薄弱的。只依靠云平台的能力,对一些不需要留档的数据进行清理。远远没有能定期归档。

+

对于数据清理这块大致可以分为这几类

+
    +
  • 定期可以无脑物理删除的数据,比如一些日志类数据。
  • +
  • 定期直接归档的数据,归档后即可清理的数据。
  • +
  • 业务必须处理完成后才能归档并清理的数据。例如订单数据等。
  • +
+

对于数据归档的时候,还需要考虑一些相关联单据数据一并归档。比如订单归档时,同时需要归档相关联的退换货单,发货单,以及操作日志等等。 归档格式基本上就是考虑单条数据以JSON格式存储,然后一个时间段的数据做成压缩包存储在OSS上,然后提供给客户下载。

+

而对于归档数据的检索功能,客户选择需要归档的数据,我们可以将归档的数据(每个压缩包内的所有数据)导入到一个主数据库以外的,临时文档数据库(例如MongoDB),然后针对这个临时的文档数据库进行检索。当然提供的检索项目也会远少于热数据的检索项目。这个临时文档数据库基本上保持一两天数据,定期就全部回收。

+

未完待续

+

H项目心路历程记 (一)

前言

2021年年中从前一家公司离职,经过朋友介绍到了现在公司参与了Halfling项目(以下简称H项目),并从零开始的设计开发到第一个客户交付的全部流程。

+

H项目是一个面向零售行业在电商时代的智能一体化中台产品。项目最早是从2021年中开始立项投入,期间经过数个版本迭代,以及2022年初的魔都封城等诸多困难。终于在2023年Q2季度完成了针对首个客户的上线。

+

作为从头开始全程参与项目的技术人员,经过2年多的产品研发期间有不少想吐槽和想说的。

+

业务设计

最初H项目是以打造一个面向零售行业业务中台为目的进行项目立项的,其中一个很重要的点就是希望除核心组件以外都可以独立售卖。所以在架构设计的使用当下最流行的微服务架构(基于Spring Boot + Kubernetes集群的解决方案), 再做业务拆分到服务的时候,进行了更细颗粒度的拆分。所以前前后后一共拆分了近20个微服务。

+

实际上到了2022年Q2前后,两个核心服务订单管理和库存服务实现以后发现,各个业务服务中间的耦合程度比想象中的还要精密,单个服务拆开单独售卖几乎不可能(应该说是完全不可能)。 然而由于服务拆分的过细,导致一个服务需要频繁访问其他服务获取大量数据。尤其类似订单关联数据比如商品的主档数据,会穿越好几个服务。而每个服务都会重复去获取主档数据,造成了大量的额外请求。 虽然最后在所有服务底部兜底使用同一个Redis作为缓存来减少额外的请求调用。

+

随着时间来到2023年产品组负责人离职后,整个H项目的方向有从行业的业务中台降级成中小企业的订单管理系统(Order Management System, OMS)。前期做的很多设计比如用户权限体系(当初需求是要精确到每个页面的每个按钮,如果下拉列表框有外调请求也需要做权限控制)等就显的很重,导致用户在使用以及配置都很不方便。而有些设计就显得稍显欠缺。 所以整个2022年Q4到2023年都是在对业务层上反复横跳,比如库存模型和商品模型的调整。这点是相当影响团队士气,同时也加剧了研发组和产品组之间的矛盾。

+

然后还有一个很重要的问题是多租户的态度上。早起项目是以服务中大体量客户为目的,所以设计之初并没有考虑多租户的问题。但实际项的进展过程中发现部分中大体量的客户也需要类似多租户的功能。这类客户需求与其说是需求多租户,不如说是希望有一个多租户级别的数据隔离,每个业务部门只能操作自己的业务部门的数据(数据包含商品,品牌,属性,平台店铺,订单,售后单据等),而总部依据可以查看一些汇总信息(例如销售对账单据等)。而随着2022年疫情状况严峻,很多中大客户对于内部平台升级和替换都变的更加谨慎和保守。所以当项目方向变成中小企业OMS时,多租户SaaS化的需求就更加迫切了。所以后期如何在原有系统上在扩展一套多租户SaaS化功能也是个课题。

+

中间件

H项目使用的中间件中比较特别的有分布式任务管理系统xxl-job,消息队列RocketMQ,这两个中间件都是第一次使用。关于这两个中间件也有不是可以谈谈的。

+

分布式任务管理系统 XXL-JOB

作为一个开源的分布式任务管理系统,xxl-job足够简单且相当容易上手使用,无论是基于Spring,还是原生Java做开发都是相当简单的。

+

而不足之处也很明显,最近1年版本迭代速度明显变慢,有些功能和BUG都有待处理的。比如想实动态创建定时任务这类功能就必须自己动手去对xxl-job-admin(调度中心)进行修改。

+

对于任务执行日志这块,现在xxl-job也是文件格式在执行器所在服务器上进行输出的,当执行器容器化或是运行在Kubernetes集群中的时候,当容器或是Pod重启后,就会丢失日志。这点在后期运维以及调试上还是有点麻烦的。

+

消息队列 RocketMQ

阿里出品的消息队列如果基于云产品来使用的话问题不大(阿里云卖的价钱也是有点黑的),但是在运维上还是有点问题的比如Topic,Group这些东西都只能人工去添加,阿里云并没有提供导入服务。全系统如果使用消息队列的场景多的话,维护这些Topic和Group还是很酸爽的。还有一点RocketMQ重复消息这一点是无法保证的。用阿里云的原话

+
+

绝大多数情况下,消息是不重复的。作为一款分布式消息中间件,在网络抖动、应用处理超时等异常情况下,无法保证消息不重复,但是能保证消息不丢失。

+
+

实际运用上,在大数据量并发的情况下,我们实际上是遇到了消息重复消费的问题。这点还是需要通过代码干预(例如增加一个唯一MsgId,对MsgId做分布式锁来解决)。

+

还有一点就是在旧版本SDK的默认配置下消费者的负载是有点小问题,总是有一个节点会比其他节点更容易消费到大量消息。据阿里云自己说明在新版本SDK的默认负载配置也已经修复这个问题了。

+

(未完待续)

+

Largrange项目架构与设计回顾 (二)

Largrange项目架构与设计回顾 (一) 里面我讲了一下项目开始架构和技术选型的一些内容。这一章来聊聊业务设计上的这点事。

+

总体来说项目设计的时候,我们对业务模块的划分和拆解总体上来说都是遵循着高内聚,低耦合的原则来进行划分的。大部分和业务相关的服务,都还是能很好的进行功能划分的。但是有一些和具体业务关联性并不是很高的服务在界定与实现时出现了一些问题。

+

任务调度服务

由于平台方面会有一些控制命令下发给Android设备,而且下发的命令并不一定都是实时的,大部分都是指定一个时间来下发,所以在设计的时候就考虑到需要一个任务调度服务(以下略称cron),来处理这些下发指令以及未来可能会有的定时批处理任务的业务需求。技术选型的时候考虑到整个服务是构建在Kubernetes上的一个分布式的微服务架构,所以就没有选择Spring Scheduler,而是使用了Quarter来支撑整个cron

+

从技术选型上来说cron没有什么问题,但是在设计如何使用cron上还是有点问题的, 我们先来看看已推送服务为例,整个平台中cron的处理流程是怎样的。

+
    +
  1. Platform 调用 cron的创建定时任务Job的接口(接口的参数为推送时使用的相关参数)
  2. +
  3. cron 创建Quartz的Job和Trigger,并将Job和Trigger的Name(Quartz中Job和Trigger的唯一标识符)返回个Platform
  4. +
  5. cron 在指定时间触发推送的Job,即调用Push服务的推送接口
  6. +
  7. Push的推送接口调用第三方服务商的推送服务,并将第三方推送服务的调用结果返回给cron
  8. +
  9. cron通过调用Platform预留的推送服务回调地址将第三方推送服务调用接口返还给Platform
  10. +
  11. Platform 将第三方推送服务的调用结果留档保存,并继续业务处理
  12. +
+

设计之初考虑到不想在cron中牵扯到具体的业务, 所以设计了一个Platform的回调接口来处理推送后的具体业务处理。虽然保证了cron尽量减少了和业务逻辑接触与数据库的访问。但是前前后后要访问集群内部的其他微服务2次,额外增加了网络开销,以及因为网络通信造成额外的通信失败风险。而且针对每种不同的定时任务,都需要额外开发一个QuartzJobBean,很难形成统一的QuartzJobBean来进行处理。今后如果还有相似项目还是需要重新考量一下如何设计一个更加完善的定时任务。

+

总结

暂时就想到了这些东西,今后想到啥还会继续在这里补存。

+

Largrange项目架构与设计回顾 (一)

+

项目背景

+
+

从去年年底开始一个老客户希望在他们的一个传统的机械设备(后面略称 E机关 )上外装一个Android设备。 Android设备和 E机关 之间通过串口或是RJ45接口进行数据交互,主要是Android设备获取 E机关 内部的数据,并不会通过接口来控制 E机关 。 Android设备则通过4G 来和平台交互,上报Android设备的状态数据,同时接受平台的控制。以此来实现让传统机械设备也能拥抱物联网的概念。

+

最后围绕着客户的需求分成了3个项目来并行推进

+
    +
  1. Android设备的硬件设备的设计、选材、样机制作与量产规划
  2. +
  3. 在Android设备上,进行与E机关以及平台进行交互的APP开发
  4. +
  5. 用于Android设备交互的平台的架构、设计与开发
  6. +
+

我们平台Team就负责 3.用于Android设备交互的平台 并命名Lagrange (拉格朗日) 。

+

设计&架构

整个平台这块不仅需要向Android设备的APP提供数据交互接口,还需要有一个供相关运营人员使用的前端Web应用,以及与云平台(主要是Aliyun) 交互的功能。所以考虑到多方面使用微服务的架构来实现整个平台端。
由于E机关的工作工况不能保证长期较的稳定的连接到4G网络,所以我们并不考虑使用Socket长连接的方式来做APP和平台之间的数据交互,要实现平台对Android设备进行反控的话,只能实现推送方式来把控制命令下发给Android设备,所以还需要有一个第三方推送服务商交互的服务。同时控制推送也有实时和非实时以及定期推送的需求,所以可能还需要一个任务调度的服务。

+

根据对上述业务进行梳理,我们将项目分成几个服务

+
    +
  • 提供Android设备的交互接口的 App Service
  • +
  • 提供运营人员使用前端Web应用 Platform Web Service
  • +
  • 前端Web应用使用到的一些接口 Platform Service
  • +
  • 负责第三方推送服务商交互的 Push Service
  • +
  • 提供云平台鉴权用的 Auth Service
  • +
  • 用来管理任务调度的 Cron Service
  • +
+

由于前一个项目实施的时候没有使用容器部署,每当访问量峰值的时候,我们这些码农兼运维就各种加班,所以这次项目决定直接将服务容器化,同时选择了Kubernetes来管理容器。由于客观原因线上的Kubernetes直接购买了Aliyun的托管版Kubernetes服务。 内部的开发测试环境则使用了 Rancher 2.0 来构建Kubernetes集群。

+

技术选型

a. 后端服务选型

由于不考虑长连接的原因,所以在后端服务在技术选型上基本就不考虑Netty了,直接上Spring大礼包。

+
    +
  • Spring Boot 开发接口
  • +
  • Spring Data 配合 JPA 来进行数据的持久化
  • +
  • Spring Cloud Kubenetes 来做数据的Config的autoreload
  • +
  • Quartz 负责处理任务调度
  • +
+

服务之间调用都使用HTTP服务, 服务发现也有Kubernetes的DNS机制支持。服务网格则选择了比较成熟的Istio,主要还是Aliyun的Kubernetes可以集成Istio,部署和使用都相当方便。

+

b. 前端Web应用选型

因为前端Web应用主要是给运营人员使用,所以我们考虑使用Single Page Application来做个前后端分离的Web应用。框架这块由于团队成员基本上没有什么前端开发经验,基本都是后台写Java的码农,所以框架选择有点随性,直接就点名了vue.js。前端控件库则用的是阿里系的Antd。

+

c. 数据持久层

主数据库选择了mysql,缓存用的redis。这些都是团队比较熟悉的。由于一些特殊的业务需求和使用场景,我们还加了一个mongodb来做为一个副数据库,主要存放一些特殊业务使用的数据。

+

d. DevOps

用了相当传统的GitLab CE 加上Jenkins的组合,实现前后端代码的自动编译,推送到私有的镜像仓库(使用Aliyun的镜像仓库服务)

+

最后

以上基本是项目最早做设计时候的各种考量。暂时先写这么多,过两天再回顾一下当初设计上有哪些觉得不足的地方。

+

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file diff --git "a/tags/\351\230\205\350\257\273/index.html" "b/tags/\351\230\205\350\257\273/index.html" new file mode 100644 index 0000000..f0da1aa --- /dev/null +++ "b/tags/\351\230\205\350\257\273/index.html" @@ -0,0 +1,111 @@ + +标签: 阅读 - 西门闲话

2020年阅读总结

多灾多难的2020年已经没有几天了,整个2020年要说还真是像一记刹车,去年很多想法,计划都有因为疫情原因搁置了。平时的生活更是从宅家变成了重度宅家的状态,自然就多出了许多大段大段的连续时间,可以用来好好玩玩大作,看看小说。仔细盘点一下今年总共看了10本小说外加2本科普书籍( 2020年阅读书单
),算是自2018年以来读书最多的一个年份了。其中不乏拖了近3年的那本《西方文化中的数学》。接着就来絮叨絮叨今年看的这12本书吧。

+
+
+

《莱博维茨的赞歌》

+
+

第一次被安利《莱博维茨的赞歌》的机核的电台节目辐射-视觉、音乐以及文学, 当时听完节目就相当好奇,可以被一部载入史册的CRPG游戏誉为精神文本的小说到底是个什么样子的。但是当时新星版的译本几乎就是绝本了,某鱼的二手价也要已经飙到了3位数。直到今年年初在核市节目上听到了中信版译本的消息,才有机会买来拜读。

+

全书通过3个故事讲述了核战灭世后莱博维茨修道院从保卫文明,到重建,然后看着文明再度毁灭的故事。各种无不透露着辐射那股 War, War never changed的味道。

+
+

《占星术杀人事件》《屋顶上的小丑》《亿男》

+
+

这3本书都是2019年上海书展上买的。前两本都是岛田庄司的推理名篇,其中《占星术杀人事件》在本格派推理史上有着非凡的地位,各种被抄袭从未被超越过。《屋顶上的小丑》要是最吸引人的话就是多线叙事,每条线都掐在恰如其分的地方。《亿男》则是挂着悬疑羊头卖着人生哲理。

+
+

《神经漫游者》

+
+

威廉吉布森的蔓生三部曲之一,开创了赛博朋克的科幻流派,说实话这本书我看的很痛苦,很多地方实在读不懂,到现在我还是不明白那些无法读懂的地方到底是原著如此,还是翻译的不行,亦或者是Kindle版排版太烂了。

+
+

《神们自己》,《神的九十亿个名字》

+
+

阿瑟克拉克的中篇小说和短篇小说集,这两本书阅读体验奇佳,尤其是在读完《神经漫游者》后,虽然三本书被Amazon打在同一个包里来卖的,但是光从排版上就比《神经漫游者》高出不少,所以才会有我对后者排版问题的质疑。

+

《神们自己》三线叙事,讲述了2个平行宇宙中的2个物种在能源,种族存续,家庭伦理上的各种讨论和故事。 《神的九十亿个名字》作为短篇小说集收录了好几篇风格题材迥异的小说,花上十几分钟读上一个故事是相当惬意的。

+
+

《佐伊的战争》 《人类决裂》 《万物的终结》

+
+

约翰斯卡尔齐的《老人的战争》系列的后3部,前三部大概是3,4年前读的吧,在这个宇宙观里最吸引人的设定就绿皮的人类防卫军战士,以及跨种族间的政治斗争。系列的六本书相当于在这个大设定下讲的六个故事。每个故事虽说都是独立的故事,但是前后皆有关联,而且最重要的就是每本的长度都算不上很长,相当适合于一口气读完。

+
+

《西方文化中的数学》

+
+

这本书大概是从2017年开始读的,当时意气奋发觉得对于理科生出身的我,书中内容应该很好理解,没想到最后不光是文化还是数学读的都是一知半解。只能说自大了。

+
+

《一想到还有95%的问题留给人类,我就放心了》

+
+

大概是18年还是19年前后对量子物理和宇宙物理开始有了兴趣,虽然各中理论并不是很了解,但是仍然很好奇的买了这本书,全书插画相当有趣,同时使用浅显易懂的语言来解释各种复杂的物理现象,以及当今人类物理知识的边界。强烈推荐给想假装成学霸的朋友。

+
+
+

2021年读什么

+
+

具体读啥还没想好基本上还是以科幻和悬疑为主,但是肯定会读的应该有海明威的《丧钟为谁而鸣》和小林泰三的《醉步男》吧。

+

© 2023 Sebastian Qu  Powered by Hexo & Icarus

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×

\ No newline at end of file