diff --git "a/2020/07/30/\346\200\273\347\273\223\346\212\200\346\234\257\346\226\207\347\253\240\345\206\231\344\275\234\346\212\200\345\267\247/index.html" "b/2020/07/30/\346\200\273\347\273\223\346\212\200\346\234\257\346\226\207\347\253\240\345\206\231\344\275\234\346\212\200\345\267\247/index.html" new file mode 100644 index 0000000..a0e489b --- /dev/null +++ "b/2020/07/30/\346\200\273\347\273\223\346\212\200\346\234\257\346\226\207\347\253\240\345\206\231\344\275\234\346\212\200\345\267\247/index.html" @@ -0,0 +1,731 @@ + + + + + + + + + + + + 总结技术文章写作技巧 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+ +
+ + + +
+ +
+ 总结技术文章写作技巧 +
+ + + +
+ +
+ +
+ +
+
+ linshukai + + Lv2 + +
+
+ + + + +
+
+
+ + +
+ + +

1、端正心态

  对于写作不要产生恐惧,起点期望放低点,没有什么事情能一蹴而就,同时可以通过阅读其他的技术文章,模仿行文方式,不断重复练习,对比吸取相关长处,补足自己的缺点。

+

2、明确定位

 对于这篇文章需要给什么类型的读者看,需要怎样的技术背景,需要怎样的技术栈,以及相关的项目背景。明确了定位方便切入不同读者的阅读需求。

+

3、标题切入

  对于文章的标题需要提供给读者一个很好的切入角度,整篇文章围绕着标题,起到文章的中心作用。

+

4、简介与大纲

  针对不同的文章,如果对于一些小项目经验或者工具使用的分享,列提纲和简介能够给读者一目了然,在第一时间的情况下获取到需要的知识信息,简介中我一般喜欢写背景,即项目背景,或者技术背景,通过这些背景能够很好的把读者带到一个阅读环境。提纲的话就因人而异,如果仅仅是比较小型的分享,对于列提纲显得没那么重要。
  而针对大型的技术的分享,对于一些写到比较全的技术分享,就需要通过列提纲对自己的内容进行很好的归纳总结成每个点,同时提纲好处就是能够让读者随时从某个地方切入,省去一些自己知道的环节。

+

5、正文写作

  通过提问和问答模式:能够让读者目的明确地进行阅读,同时让读者带着问题去阅读,就能更好的抓住文章的重点和逻辑,同时读者的阅读效果和阅读感受就会更加好。
  通过讲故事的方法:讲故事的话,一般是可以设定某个情节,让读者代入到该场景,通过叙事的模式和节奏,让读者更加容易接受专业知识。

+

6、额外

    +
  • 通过合适的段落和句子,减轻读者阅读的压力;
  • +
  • 对于需要结合代码讲清楚专业知识,则需要将代码贴出来,并且在一些关键的代码中需要有清晰的注释;
  • +
  • 对于文章出现的Demo可以附加上demo的链接;
  • +
  • 如果对于一些比较专业的术语或者细节,可以通过类比形式解释清楚问题,如果文字描述不清楚可以选择配图的方式;
  • +
+

总结

    +
  • 取好标题,醒目突出中心
  • +
  • 图文并茂,适当配图说明
  • +
  • 篇幅适宜,不宜过短也避免冗长
  • +
  • 格式统一,基本排版规则需要遵守
  • +
  • 细节处理,错别字标点处理正确。
  • +
+

参考

+

参考文章:http://news.51cto.com/art/201904/594736.htm

+
+ +
+ + + +
+ + +
+ + + +
+
+ + + +
+
+ +
+
+ + + + +
+ +
+ +
+ + +
+ +
+ +
+ + + + + + + + + + + +
+
+ + +
+
+
+
+
+ +
+ +
+
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + +
+
+
    + + +
  • + +
  • + + + + +
+
+ +
+ + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + +
  • + +
  • + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+
+
+
+
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + diff --git "a/2020/07/30/\346\230\216\346\234\235\351\202\243\344\272\233\344\272\213/index.html" "b/2020/07/30/\346\230\216\346\234\235\351\202\243\344\272\233\344\272\213/index.html" new file mode 100644 index 0000000..4445039 --- /dev/null +++ "b/2020/07/30/\346\230\216\346\234\235\351\202\243\344\272\233\344\272\213/index.html" @@ -0,0 +1,700 @@ + + + + + + + + + + + + 明朝那些事儿 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+ +
+ + + +
+ +
+ 明朝那些事儿 +
+ + + +
+ +
+ +
+ +
+
+ linshukai + + Lv2 + +
+
+ + + + +
+
+
+ + +
+ + +
+

关于这个系列的丛书,掰掰手指头估计读了有三次遍了,都是在人生不同的阶段去阅读,或者懵懂,或者无聊,或者失意,每次隔段时间再次捧起这些书的时候,心中各种人物仿佛唤醒你脑中最深处的回忆。唤起那份第一次阅读时带来的惊喜,各种人物形形色色也顿时鲜明起来。
其实每每一口气读完整套书籍,胸中总有种不吐不快的感觉,就像如鲠在喉,慢慢顺着明朝建立起来时候缓缓地回忆那时候地一切,顿时起了一身的“鸡皮疙瘩”,历史总是默默无语,总是安静的看着这一切事情的发生,大浪淘进了无数的英雄豪杰,他们的归宿总是惊人的相似,之所以我想些这篇文章,真的是有感于里面的许多人物给我带来不同的感受,他们或狡诈,或阴险,或偏执、或聪明、或愚蠢、或可爱、或勇敢、或自负等等等,我只是想把我已经看完的书,看完的一段历史,重新在我的视角里 ,说说自己的看法。

+
+

朱元璋

要说自己最开始知道明朝有关的历史,第一个毋庸置疑的是明朝的开国皇帝朱元璋,对于我来说,在没了解这段历史时,单纯的感觉认为这是一个很”励志“的人,为什么呢?因为他出生卑微,却靠着自己独有的战略眼光以及用人智慧,一步步走向属于他的巅峰。属实”励志“,但当我对这位人物有了一定的历史了解之后,我又开始发觉这是一个极其复杂的人。比如经常有人说他天生英雄,其实他是实在走投无路,被人告密之后,还让自己朋友周德兴占了一卦,才觉得跟着造反的。造反的过程中刚开始也十分不顺利,但是你在过程中慢慢的就会发现到朱元璋的过人之处,过人的胸怀,对自己的战略眼光视角的自信,对事物本质的洞悉能力。
在他打败陈友谅、张士诚的时候,都能为他每次出色战略决定感慨,当然这些也少不了他手下那些骁勇善战的将士,以及足智多谋的谋士。在推翻元朝统治,建立大明之后,慢慢随着岁月的逝去,越来越多的老朋友离开了朱元璋的身边,这里面有很多都是为了自己后代能够统治安稳而解决掉。当他的马皇后最终离开了他的身边之后,他或许真的流泪了,那个在他坎坷流离的生命岁月中一直不放弃不抛弃的跟着他的女人走了,渐渐的也只剩给他一个人了。

+

朱棣

朱棣可能是我在明朝中印象最深刻的一个皇帝,为什么呢?因为他可以说是一个双面皇帝,对自己敌人不择手段,宁杀错不放过,对自己的朋友,百姓都是关怀备至,一副仁君的模样。然后他本身就在军队中长大,练就了一身的指挥打仗的本领,在实战中也是经常出奇制胜。在最开始的时候,朱棣也是不太像造反的,毕竟这种一旦失败就要人头落地的买卖实在不划算,而身边的和尚姚广孝一直在劝说他造反,最后因为朱允炆听从黄子澄、齐泰的建议削藩,最终导致朱棣起兵造反。而让我铭记朱棣的名字,是因为朱棣一手建立了永乐盛世,万邦来朝的繁华景象。这背后是他数次北伐亲征,兢兢业业。以及派遣郑和下西洋传播和平理想的信念。他不是一个好人,但是一个好皇帝。

+

未完。。。

可能因为胸中的那团气突然散了,感觉有许多东西其实没有讲出来,但是仿佛这其中犹如一团乱麻,始终牵绕着我。朱元璋,李善忠,刘伯温,徐达,常遇春,汤和,马皇后,朱棣,朱允炆,黄子澄,齐泰,方孝孺,姚广孝,郑和,朱高煦,朱瞻基,杨士奇,杨荣,杨溥,于谦,朱祁镇…..,这些人仿佛是历史长河中一粒粒沙子,他们任何一个人只是历史选中的角色,但是任何一个却无法阻挡历史的脚步。

+ +
+ + + +
+ + +
+ + + +
+
+ + + +
+
+ +
+
+ + + + +
+ +
+ +
+ + +
+ + + + + + + + + + + +
+
+ + +
+
+
+ +
+
+ +
+ +
+
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + +
+
+
    + + +
  • + +
  • + + + + +
+
+ +
+ + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + +
  • + +
  • + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+
+
+
+ +
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + diff --git "a/2020/09/12/Python\345\221\275\344\273\244\350\241\214\346\250\241\345\235\227\357\274\210sys.argv,argparse,click\357\274\211/index.html" "b/2020/09/12/Python\345\221\275\344\273\244\350\241\214\346\250\241\345\235\227\357\274\210sys.argv,argparse,click\357\274\211/index.html" new file mode 100644 index 0000000..0aee17b --- /dev/null +++ "b/2020/09/12/Python\345\221\275\344\273\244\350\241\214\346\250\241\345\235\227\357\274\210sys.argv,argparse,click\357\274\211/index.html" @@ -0,0 +1,764 @@ + + + + + + + + + + + + Python命令行模块(sys.argv,argparse,click) | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+ +
+ + + +
+ +
+ Python命令行模块(sys.argv,argparse,click) +
+ + + +
+ +
+ +
+ +
+
+ linshukai + + Lv2 + +
+
+ + + + +
+
+
+ + +
+ + +
+

Python作为一门脚本语言,经常作为脚本接受命令行传入参数,Python接受命令行参数大概有三种方式。因为在日常工作场景会经常使用到,这里对这几种方式进行总结。

+
+

命令行参数模块

这里命令行参数模块平时工作中用到最多就是这三种模块:sys.argv,argparse,click。sys.argv和argparse都是内置模块,click则是第三方模块。

+

sys.argv模块(内置模块)

先看一个简单的示例:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/python
import sys

def hello(name, age, sex, *args):
print("Hello, My name is {name}.".format(name=name))
print("I'm {age} years old.".format(age=age))
print("I'm a {sex}".format(sex=sex))

print("Other word:\n{args}".format(args="\n".join(args)))


if __name__ == "__main__":
file_name = sys.argv[0]
name = sys.argv[1]
age = sys.argv[2]
sex = sys.argv[3]
other = sys.argv[4:]
hello(name, age, sex, *other)
+

调用脚本:

+
1
python test_sysargv.py zhangsan 13 man nibi ss
+

脚本输出:

+
1
2
3
4
5
6
Hello, My name is zhangsan.
I'm 13 years old.
I'm a man
Other word:
nibi
ss
+
+

sys.argv模块不难理解,命令参数作为列表传入Python脚本中,argv[0]是脚本的名字,argv[1]则是第一个参数,后面以此类推。所以在脚本中只需要提取列表中的参数即可使用。上面演示的是正确调用Python脚本的情况,下面则是调用失败的情况。

+
+

错误调用脚本:

+
1
python test_sysargv.py zhangsan 13
+

错误输出:

+
1
2
3
4
Traceback (most recent call last):
File "test_sysargv.py", line 16, in <module>
sex = sys.argv[3]
IndexError: list index out of range
+
+

关于错误也很好理解,经典的列表索引超出范围,之所列表索引超出范围,没有传入足够的参数。当然你可以使用try…except捕获错误。但是这种做法太过死板,因为在命令行中必须按照脚本规定的参数顺序输入参数,所以这种模块使用一般是针对一些需要的参数比较少并且固定的脚本。

+
+

argparse模块(内置模块)

同样的先看一个简单的示例:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/python
import argparse

parser = argparse.ArgumentParser(description='Test for argparse module.') # 构建命令参数实例
parser.add_argument("--name", "-n", help="name attribute: 非必要属性")
parser.add_argument("--age", "-a", help="age attribute: 非必要属性")
parser.add_argument("--sex", "-s", help="sex attribute: 非必要属性")
parser.add_argument("--type", "-t", help="type attribute: 非必要属性", required=True)
args = parser.parse_args()


def hello(name, age, sex, *args):
print("Hello, My name is {name}.".format(name=name))
print("I'm {age} years old.".format(age=age))
print("I'm a {sex}".format(sex=sex))

print("Other word:\n{args}".format(args="\n".join(args)))


if __name__ == "__main__":
print("Format of transfer file: {type}".format(type=args.type))
if args.name and args.age and args.sex:
hello(args.name, args.age, args.sex)
+

执行脚本:

+
1
python3 test_argparse.py -t json -n zhangsan -a 13 -s man
+

脚本成功输出:

+
1
2
3
4
5
6
Format of transfer file: json
Hello, My name is zhangsan.
I'm 13 years old.
I'm a man
Other word:

+
+

关于argparse模块的使用,首先需要生成一个命令行参数的实例,然后通过对这个对象添加属性,添加需要从命令行获取的参数,包括哪些是必要参数(required=True),哪些非必要参数,同时也可以对每个参数进行帮助提示(help=””)。

+
+
+

而上面示例中分别添加了四个属性,–name和-n同时可以再命令行中使用,都表示了参数name。ArgumentParser通过parse_ags()方法解析参数,检查命令行,将每个参数转换为适当的类型,所以在脚本中同时也可以使用args.n和args.name获取到参数,相对应的如果没有传入该参数,脚本中则获取到None。

+
+

查看命令行参数之后脚本的帮助提示:

+
1
2
3
4
5
6
7
8
9
10
11
python3 test_argparse.py -h
usage: test_argparse.py [-h] [--name NAME] [--age AGE] [--sex SEX] --type TYPE

Test for argparse module.

optional arguments:
-h, --help show this help message and exit
--name NAME, -n NAME name attribute: 非必要属性
--age AGE, -a AGE age attribute: 非必要属性
--sex SEX, -s SEX sex attribute: 非必要属性
--type TYPE, -t TYPE type attribute: 非必要属性
+
+

另外在添加命令行参数的属性中,还有更多的设置,多余的可以参考Python官方文档,里面都有详细的标注,这里就不展开来讲,总结的就是关于argparse模块的使用非常简便,同时十分人性化,也很符合日常工作的需要。

+
+

click模块

先开一个简单的使用示例:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/python
import click

@click.command()
@click.option("--name", default="zhangsan", help="name attribute: 非必要属性")
@click.option("--age", help="age attribute", type=int)
@click.option("--sex", help="sex attribute")
@click.option("-t", help="type attribute: 必要属性", required=True)
def hello(t, name, age, sex, *args):
print("Format of transfer file: {type}".format(type=t))
print("Hello, My name is {name}.".format(name=name))
print("I'm {age} years old.".format(age=age))
print("I'm a {sex}".format(sex=sex))

print("Other word:\n{args}".format(args="\n".join(args)))


if __name__ == "__main__":
hello()

+

执行脚本:

+
1
python3 test_click.py -t 1 --age 13 --sex man
+

脚本输出:

+
1
2
3
4
5
6
Format of transfer file: 1
Hello, My name is zhangsan.
I'm 13 years old.
I'm a man
Other word:

+
+

click模块是Flask团队优秀的开源项目,使用方法和argparse模块很相似,同样为命令行封装了大量的方法,使用者只需要专注代码功能的实现。
click模块和argparse模块不同的地方就是,click模块使用装饰器的方式给函数添加命令行属性,关于装饰器简单来讲就是能够在不修改原有函数的基础上添加功能。虽然使用装饰器但是添加命令行属性的方式和argparse模块很相似,包括options中常用的参数含义也有很多类似的地方。值得注意的就是一开始需要通过command()将函数成为命令行的接口。
关于的click模块的就大致讲到这里,其余有兴趣的可以再去了解一下。

+
+

总结

关于这三个模块值得注意的是,尽量贴近自己应用场景去选择,真正的做到自己能够方便使用才是你去使用这些模块的原因。

+ +
+ + + +
+ + +
+ + + +
+
+ + + +
+
+ +
+
+ + + + +
+ +
+ +
+ + +
+ +
+ +
+ + + + + + + + + + + +
+
+ + +
+
+
+ +
+
+ +
+ +
+
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + +
+
+
    + + +
  • + +
  • + + + + +
+
+ +
+ + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + +
  • + +
  • + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+
+
+
+ +
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + diff --git "a/2020/12/07/Python\345\256\232\346\227\266\344\273\273\345\212\241\345\210\251\345\231\250\342\200\224Apscheduler/index.html" "b/2020/12/07/Python\345\256\232\346\227\266\344\273\273\345\212\241\345\210\251\345\231\250\342\200\224Apscheduler/index.html" new file mode 100644 index 0000000..1d16370 --- /dev/null +++ "b/2020/12/07/Python\345\256\232\346\227\266\344\273\273\345\212\241\345\210\251\345\231\250\342\200\224Apscheduler/index.html" @@ -0,0 +1,752 @@ + + + + + + + + + + + + Python定时任务利器—Apscheduler | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+ +
+ + + +
+ +
+ Python定时任务利器—Apscheduler +
+ + + +
+ +
+ +
+ +
+
+ linshukai + + Lv2 + +
+
+ + + + +
+
+
+ + +
+ + +

导语

在工作场景遇到了这么一个场景,就是需要定期去执行一个缓存接口,用于同步设备配置。首先想到的就是Linux上的crontab,可以定期,或者间隔一段时间去执行任务。但是如果你想要把这个定时任务作为一个模块集成到Python项目中,或者想持久化任务,显然crontab不太适用。Python的APScheduler模块能够很好的解决此类问题,所以专门写这篇文章,从简单入门开始记录关于APScheduler最基础的使用场景,以及解决持久化任务的问题,最后结合其他框架深层次定制定时任务模块这几个点入手。

+

简单介绍

先简单介绍一下Apscheduler模块包含的四种组件:

+
    +
  • Trigger触发器
  • +
  • Job作业
  • +
  • Excutor执行器
  • +
  • Scheduler调度器
  • +
+

大概了解了Apscheduler包含的几种概念,现在先来看一下一个简单的示例:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# -*- coding: utf-8 -*-

from apscheduler.schedulers.blocking import BlockingScheduler
import time


def hello():
print(time.strftime("%c"))


if __name__ == "__main__":
scheduler = BlockingScheduler()
scheduler.add_job(hello, 'interval', seconds=5)
scheduler.start()
+

示例的输出:

+
1
2
3
4
5
6
Thu Dec  3 16:01:20 2020
Thu Dec 3 16:01:25 2020
Thu Dec 3 16:01:30 2020
Thu Dec 3 16:01:35 2020
Thu Dec 3 16:01:40 2020
..........
+

这个简单的示例,我们用上面提到几种组件分析一下运行逻辑:

+
    +
  • 首先是Scheduler调度器,这个示例使用的BlockingScheduler调度器,在官方文档中的解释是,BlockingScheduler适合当你的这个定时任务程序是唯一运行的程序;换言之,则是BlockingScheduler调度器是一个阻塞调度器,当程序运行这种调度器,进程则会阻塞,无法执行其他操作;
  • +
  • 其次是Job作业和触发器,这两个放在一起讲是因为,在定义作业的时候,你就需要选择一个触发器,这里选择的是interval触发器,这种触发器会以固定时间间隔运行作业。换言之,为调度器添加一个hello的工作,并以每5秒的时间间隔执行任务。
  • +
  • 最后就是执行器,默认是ThreadPoolExcutor执行器,他们将任务中可调用对象交给线程池执行操作,等完成操作后,执行器会通知调度程序。
  • +
+

内置的三种Trigger触发器类型:

+
    +
  • date:特定时间仅运行一次作业
  • +
  • interval: 固定的时间间隔内运行一次作业
  • +
  • cron: 在一天内特定的时间定期运行作业
  • +
+

常见的Scheduler调度器:

+
    +
  • BlockingScheduler: 调度程序是流程中唯一运行的东西
  • +
  • BackgroundScheduler: 调度程序在应用程序内部的后台运行时使用
  • +
  • AsyncIOScheduler: 应用程序使用asyncio模块
  • +
  • GeventScheduler: 应用程序使用gevent模块
  • +
  • TornadoScheduler:构建Tornado应用程序时使用
  • +
  • TwistedScheduler: 构建Tornado应用程序时使用
  • +
  • QtScheduler: 在构建QT应用程序时使用
  • +
+

常见的JobStore:

+
    +
  • MemoryJobStore
  • +
  • MongoDBJobStore
  • +
  • SQLAlchemyJobStore
  • +
  • RedisJobStore
  • +
+

进阶使用

通过上面一个简单的示例了解大概的工作流程,以及各个组件在整个流程中的作用,以下的示例是Flask Web框架结合使用Apscheduler定时器,定时执行任务。

+
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
# -*- coding: utf-8 -*-

from flask import Flask, Blueprint, request
from apscheduler.executors.pool import ThreadPoolExecutor
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.redis import RedisJobStore
import time

app = Flask(__name__)
executors = {"default": ThreadPoolExecutor(5)}
default_redis_jobstore = RedisJobStore(db=2,
jobs_key="apschedulers.default_jobs",
run_times_key="apschedulers.default_run_times",
host = '127.0.0.1',
port = 6379
)

scheduler = BackgroundScheduler(executors=executors)
scheduler.add_jobstore(default_redis_jobstore)
scheduler.start()

def say_hello():
print(time.strftime("%c"))


@app.route("/get_job", methods=['GET'])
def get_job():
if scheduler.get_job("say_hello_test"):
return "YES"
else:
return "NO"

@app.route("/start_job", methods=["GET"])
def start_job():
if not scheduler.get_job("say_hello_test"):
scheduler.add_job(say_hello, "interval", seconds=5, id="say_hello_test")
return "Start Scuessfully!"
else:
return "Started Failed"

@app.route("/remove_job", methods=["GET"])
def remove_job():
if scheduler.get_job("say_hello_test"):
scheduler.remove_job("say_hello_test")
return "Delete Successfully!"
else:
return "Delete Failed"


if __name__ == "__main__":
app.run(host="127.0.0.1", port=8787, debug=True)
+
    +
  • 先分析Jobstore,这里使用的是RedisJobstore,将任务序列化存入到Redis数据库中。这里顺便提一下,为什么需要设置作业存储器,原因是当调度器程序崩溃时,仍然能够保留作业,当然选择什么作业存储器,可以根据具体的工作场景,目前主流的mysql,mongodb,redis,SQLite基本都支持;
  • +
  • 然后再看看Scheduler,这里使用的时BackgroundScheduler,因为这里要求调度程序不能阻塞flask程序的正常接收请求,所以选在BackgrounScheduler让它在开始执行任务时是在后台运行的,不会阻塞主线程;
  • +
  • 最后看看工作的逻辑,这里get_job获取作业的状态,查看作业是否存在,start_job则是先判断作业是否启动,然后再决定启动操作,remove_job则是停止作业。而这里的作业定义则是通过interval触发器,每五秒执行一次say_hello任务;
  • +
+

总结

最后总结一下,首先你要设置一个作业存储器用于在调度程序崩溃重新恢复时,还能够在作业存储器中获取到作业继续执行;然后你需要设置一个执行器,这个根据作业的类型,比如时一个CPU密集型的任务,那就可以用进程池执行器,默认是用线程池执行器;最后创建配置调度器,启动调度,可以在启动前添加作业,也可以在启动后添加,删除,获取作业。(在这里需要明白的一点就是应用程序不会直接去操作作业存储器,作业或者执行器,而是调度器提供适当的接口来处理这些接口。)

ApScheduler是一个不错的定时任务库,能够动态的添加删除,同时也支持不同的触发器类型,这也是它的优势,相反一些如果是静态任务,其实可以用如linux的crontab工具去做定时任务。有关这方面的记录还会持续更新,如果有什么问题,可以提出来,大家一起探讨。

+ +
+ + + +
+ + +
+ + + +
+
+ + + +
+
+ +
+
+ + + + +
+ +
+ +
+ + +
+ +
+ +
+ + + + + + + + + + + +
+
+ + +
+
+
+ +
+
+ +
+ +
+
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + +
+
+
    + + +
  • + +
  • + + + + +
+
+ +
+ + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + +
  • + +
  • + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+
+
+
+ +
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + diff --git "a/2020/12/15/\346\265\205\346\236\220Python\351\241\271\347\233\256\351\203\250\347\275\262/index.html" "b/2020/12/15/\346\265\205\346\236\220Python\351\241\271\347\233\256\351\203\250\347\275\262/index.html" new file mode 100644 index 0000000..624c4a0 --- /dev/null +++ "b/2020/12/15/\346\265\205\346\236\220Python\351\241\271\347\233\256\351\203\250\347\275\262/index.html" @@ -0,0 +1,798 @@ + + + + + + + + + + + + 浅析Python项目部署 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+ +
+ + + +
+ +
+ 浅析Python项目部署 +
+ + + +
+ +
+ +
+ +
+
+ linshukai + + Lv2 + +
+
+ + + + +
+
+
+ + +
+ + +

基础理论

关于Web服务器和应用服务器

基本概念:

+
    +
  • Web服务器主要功能就是存储、处理、传递网页,客户端和服务器之间基于HTTP协议进行通信。
  • +
  • 应用服务器主要是处理动态请求,调用相应的对象完成对请求的处理,返回响应的结果。
  • +
+

两者之间的区别:

+
    +
  • Web服务器主要是以HTTP为核心,WEB UI为向导的应用,处理一些静态请求。
  • +
  • 应用服务器虽然也支持HTTP协议,但应用服务器可以通过很多协议为应用程序提供商业逻辑。
  • +
+

Python项目部署架构

在我们平时搭建一个Python Web项目时,比如Django框架的项目,这时候常见的服务端架构:

+
    +
  1. Nginx服务器作为代理服务器,代理处理静态资源(js,css,图片)请求,以及动态请求(增删改查)转发以及返回处理结果。
  2. +
  3. uWSGI服务器负责接受Nginx服务器,处理过后转发给Django应用,接受Django应用处理返回的信息,在转发给nginx
  4. +
  5. Django应用从uWSGI服务器接收请求,调用相应的业务逻辑,处理数据渲染相应的页面并且返回给uWSGI服务器。
  6. +
+

关于cgi、wsgi、uwsgi、http协议

接下来针对上文提到Django项目部署架构,聊聊这几个协议在这过程中的作用:

+
    +
  • http协议:客户端程序和Nginx服务器通信就是基于http协议,而Nginx服务器作为一个代理服务器,会根据HTTP请求返回静态资源还是转发动态请求。
  • +
  • cgi协议:cgi协议是外部应用程序和Web服务器之间的接口标准,简而言之,就是规定如何让一个程序与Web服务器程序之间通信。
  • +
  • wsgi协议:基于现存的CGI标准设计,一个Python web框架编写的应用程序和Web服务器之间通信的规范。
  • +
  • uwsgi:uWSGI服务器独有的协议,用于uWSGI服务器和其他Web服务器之间的数据通信
  • +
+

关于cgi、fastcgi、php-fpm、FastCGI(额外)

    +
  • CGI:是 Web Server 与 Web Application 之间数据交换的一种协议。
  • +
  • FastCGI:同 CGI,是一种通信协议,但比 CGI 在效率上做了一些优化。同样,SCGI 协议与 FastCGI 类似。
  • +
  • PHP-CGI:是 PHP (Web Application)对 Web Server 提供的 CGI 协议的接口程序。
  • +
  • PHP-FPM:是 PHP(Web Application)对 Web Server 提供的 FastCGI 协议的接口程序,额外还提供了相对智能一些任务管理。
  • +
+

关于uWSGI、Nginx服务器

简单来讲,uWSGI也是一个Web服务器,他虽然同时实现了http、uwsgi、wsgi协议,但是更多被作为应用服务器和应用程序之间进行通信。
那么这里就有个疑问了,为什么uWSGI都能直接处理http请求了,还需要Nginx服务器?这样处理有几点原因:

+
    +
  • 首先Nginx服务器在这过程中属于代理服务器的角色,每当一个http请求进来,就需要经过Nginx服务器,而Nginx服务器的优势就在于异步非阻塞的网络模型,它能够在单线程的情况下处理大量的请求,针对处理静态资源请求;而对于动态请求,能够通过缓存的功能,并且配合CDN进行优化,这样能够极大降低系统的负载,降低客户端响应时间。
  • +
  • 其次,Nginx服务器能够进行负载均衡,启用多个后端服务器,通过Nginx对HTTP请求进行分配,这样能够极大优化架构的效率,提高处理的性能。
  • +
  • 最后,Nginx有很多模块支持一些比如白名单,黑名单功能,并且配合keepalive实现高可用的架构.
    总而言之,Nginx服务器在处理http请求上,都有着uWSGI没有的优势,所以在日常部署环境中,经常会使用到Nginx+uWSGI。
  • +
+

uWSGI

安装部署

uWSGI有两种安装方式,一种是通过pip安装,一种是通过源码安装。这里就简单介绍pip安装方式,源码安装有兴趣可以私下自己了解。

+
1
pip install uwsgi
+ +

相关文件

uWSGI服务器在启动的过程中主要涉及到以下这几个文件,其中uwsgi.sock还会涉及到Nginx相关部署的问题,在Nginx配置的时候回继续展开讲。

+
1
2
3
4
5
6
7
(venv) [root@mbb-48 uwsgi]# tree .
.
|-- uwsgi.ini # uwsgi配置文件
|-- uwsgi.log # uwsgi日志文件
|-- uwsgi.pid # uwsgi运行进程pid
|-- uwsgi.sock # uwsgi socket
`-- uwsgi.status # uwsgi状态文件
+ +

配置示例

以下配置是自己一个项目中用到的,其中的配置参数都是比较常见通用。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[uwsgi]
chdir=/data/Novel/novel_test
module=novel_test.wsgi:application
home=/data/Novel/venv
static-map=/static=/data/Novel/novel_test/static
threads=8
http=0.0.0.0:23606
master=true
vacuum=true
thunder-lock=true
uid=root
gid=root
harakiri=30
post-buffering=4096
socket=%(chdir)/uwsgi/uwsgi.sock
stats=%(chdir)/uwsgi/uwsgi.status
pidfile=%(chdir)/uwsgi/uwsgi.pid
daemonize=%(chdir)/uwsgi/uwsgi.log
+

配置解析

    +
  • chdir: 定义项目的目录
  • +
  • module: 要使用的 WSGI 模块,不同的Python web框架中使用方式也不太一样。
  • +
  • home: 指定Python执行环境,这个参数针对有不同Python运行环境,比如virtualenv创建独立Python环境的情况。
  • +
  • static-map: 映射静态目录
  • +
  • threads: 线程数量
  • +
  • http: 指定启动地址和端口
  • +
  • master: 启用主进程
  • +
  • vacuum: 退出时,清理产生的中间文件(sock,pid,stats)
  • +
  • thunder-lock: 序列化接收的内容
  • +
  • uid: 指定启动用户
  • +
  • gid: 指定启动组
  • +
  • harakiri: 设置服务器响应时间
  • +
  • post-buffering: 启用缓冲
  • +
  • socket: socket文件存放路径
  • +
  • stats: stats文件存放路径
  • +
  • pidfile: pid文件存放路径
  • +
  • daemonize: 日志文件输出文件路径
  • +
+

额外配置

    +
  • processes: 进程数量
  • +
  • buffer-size: 设置缓冲大小
  • +
  • listen: 设置监听队列大小(默认100)
  • +
  • max-requests: 每个工作进程请求书的最高上限
  • +
  • procname-prefix-spaced: 工作进程的前缀名
  • +
  • wsgi-file: 指定加载WSGI文件
  • +
+

常用命令

上面介绍了相关常用的配置参数,当设置uWSGI参数后,需要启动指定配置文件,以及有关暂停,重启的动作。

+
1
2
3
uwsgi --ini uwsgi.ini   # 启动uWSGI
uwsgi --stop uwsgi.pid # 暂停uWSGI
uwsgi --reload uwsgi.pid # 重启uWSGI
+ +

服务监控

uWSGI之一Stats服务器机制,它能将uWSGI状态作为一个JSON对象导出一个socket中,只需要像我们前面配置uWSGI配置文件一样,添加stats选项,跟着一个有效的socket地址接口。

当你配置完毕后,就可以通过客户端连接到指定的socket地址,将会获得一个包含uWSGI内部统计数据的JSON对象。

+
1
uwsgi --connect-and-read uwsgi.status
+

执行完这个命令后,读取的返回的结果是一个json字符串,字符串中包括每个线程的状态,整个应用的负载,版本,监听队列等详细的信息。

+

uwsgitop查看实时状态

uwsgitop是一个开源的实时监控uWSGI服务器状态的工具,安装也十分简单:

+
1
pip install uwsgitop
+

具体uwsgitop像一个top命令,监听应用程序并且使用socket地址进行调用,查看进程运行状态,运行详细信息:

+
1
2
3
4
5
6
7
8
9
10
uwsgitop uwsgi/uwsgi.status
uwsgi-2.0.19.1 - Sun Sep 20 15:58:48 2020 - req: 6 - RPS: 0 - lq: 0 - tx: 67.6K
node: mbb-48 - cwd: /data/Novel/novel_test - uid: 0 - gid: 0 - masterpid: 15109
WID % PID REQ RPS EXC SIG STATUS AVG RSS VSZ TX ReSpwn HC RunT LastSpwn
1 100.0 15144 6 0 0 0 idle 15ms 0 0 67.6K 1 0 125.449 15:48:08
:2 16.7 - 1 0 - - idle - - - - -
:3 16.7 - 1 0 - - idle - - - - -
:0 33.3 - 2 0 - - idle - - - - -
:1 33.3 - 2 0 - - idle - - - - -

+

具体有兴趣的同学可以参考github地址:

+
+

https://github.com/xrmx/uwsgitop

+
+

总结

关于uWSGI服务器相关使用就总结到这里,因为在平时工作中会经常接触到,但是由于没有应用到一些高并发的场景,对于uwsgi调优方面没有多少经验,等待以后遇到此类问题再继续总结性能调优方面的问题。同时有关Nginx方面的配置留到下一篇文章继续讲,未完待续。。。。。

+

参考文章:

+

https://uwsgi-docs-zh.readthedocs.io/zh_CN/latest/StatsServer.html

https://uwsgi-docs.readthedocs.io/en/latest/Options.html#harakiri

https://www.jianshu.com/p/c3b13b5ad3d7

https://docs.djangoproject.com/zh-hans/2.2/howto/deployment/wsgi/uwsgi/

https://blog.51cto.com/mbb97/2151933

+
+ +
+ + + +
+ + +
+ + + +
+
+ + + +
+
+ +
+
+ + + + +
+ +
+ +
+ + +
+ +
+ +
+ + + + + + + + + + + +
+
+ + +
+
+
+ +
+
+ +
+ +
+
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + +
+
+
    + + +
  • + +
  • + + + + +
+
+ +
+ + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + +
  • + +
  • + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+
+
+
+ +
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + diff --git "a/2021/04/25/Python Socket\347\274\226\347\250\213/index.html" "b/2021/04/25/Python Socket\347\274\226\347\250\213/index.html" new file mode 100644 index 0000000..9a91706 --- /dev/null +++ "b/2021/04/25/Python Socket\347\274\226\347\250\213/index.html" @@ -0,0 +1,787 @@ + + + + + + + + + + + + Python Socket编程 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+ +
+ + + +
+ +
+ Python Socket编程 +
+ + + +
+ +
+ +
+ +
+
+ linshukai + + Lv2 + +
+
+ + + + +
+
+
+ + +
+ + +

背景

关于Python Socket编程,首先需要了解几个计算机网络的知识,通过以下的几个问题,有助于更好的理解Socket编程的意义,以及整个框架方面的知识:

+
    +
  • TCP和UDP协议本质上的区别?

    +
    +

    TCP协议,面向连接,可靠,基于字节流的传输层通信协议;UDP协议无连接,不可靠,基于数据包的传输层协议。

    TCP协议在建立连接的过程需要经历三次握手,断开连接则需要经历四次挥手,而这建立连接的过程增加了传输过程中的安全性。
    而建立连接的过程则会消耗系统的资源,消耗更多的时间,而相比较UDP协议传输过程则不会出现这种问题。

    总结来讲,基于TCP协议传输,需要不断的确认对方是否收到信息,从而建立连接(确认过程次数有限制,即三次握手),UDP协议传输则
    不需要确认接收端是否收到信息,只需要将信息发给对方。

    +
    +
  • +
  • TCP/IP协议栈、HTTP协议、Socket之间的区别和联系?

    +
    +

    TCP/IP协议栈就是一系列网络协议,可以分为四层模型来分析:应用层、传输层、网络层、链路层;

    HTTP协议(超文本传输协议)就是在这一协议栈中的应用层协议;HTTP协议简单来说,它的作用就是规范数据的格式,让程序能够方便的识别,并且收发双方都需要遵循同样的协议格式进行数据传输。(应用层的协议也和HTTP协议的作用类似,不一样的是定义不同的数据格式。)

    Socket可以理解为TCP/IP协议栈提供的对外的操作接口,即应用层通过网络协议进行通信的接口。Socket可以使用不同的网络协议进行端对端的通信;

    +
    +
  • +
  • TCP Socket服务器的通信过程?

    +
    +

    Server端:

    建立连接(socket()函数创建socket描述符、bind()函数绑定特定的监听地址(ip+port)、listen()函数监听socket、accept()阻塞等待客户端连接)

    数据交互(read()函数阻塞等待客户端发送数据、write()函数发送给客户端数据)

    Client端:

    建立连接(socket()函数创建socket描述符、connect()函数向指定的监听地址发送连接请求)

    数据交互(wirte()函数发送服务端数据、read()函数足阻塞等待接受服务端发送的数据)

    +
    +
  • +
  • socket和websocket之间的联系?

    +
    +

    webosocket是一种通信协议,不同于HTTP请求,客户端请求服务端资源,服务端响应的通信过程;websocket允许服务端主动
    向客户端推送消息,同时做到客户端和服务端双向通讯的协议。(具体底层原理有待后面实践,暂时未接触)

    +
    +
  • +
  • HTTP,WSGI协议的联系和区别?

    +
    +

    HTTP协议(超文本传输协议),属于TCP/IP协议栈中应用层的协议。用于规范传输数据的格式,是一种客户端和服务端传输的规则。

    WSGI协议则是Python定义的Web服务器和框架程序通信的接口规则。两者联系不大,强行说的话,Python框架程序主要处理的是HTTP请求。

    (后期可以实现一个WSGI协议的Python框架,用于处理HTTP请求的实验。)

    +
    +
  • +
  • 主流Web框架,异步Web框架?

    +
    +

    主流Web框架:Django、Flask

    异步Web框架:Tornado(内置异步模块)、Snaic(Python自带asyncio)、FastAPI(基于Starlette库) 、aiohttp(基于asyncio)

    +
    +
  • +
  • asyncio,aiohttp之间的联系?(异步编程)

    +
    +

    asyncio是一个异步IO库,aiohttp就是基于asyncio的异步HTTP框架(支持客户端/服务端)

    +
    +
  • +
+

代码设计

Python提供了基本的socket模块:

+
    +
  1. socket模块;提供了标准的BSD Sockets API;
  2. +
  3. socketserver模块:提供了服务器中心类,简化服务器的开发;
  4. +
+

TCP Socket服务端

socket模块:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# -*- coding: utf-8 -*-
from socket import socket, AF_INET, SOCK_STREAM

def echo_handler(sock ,address):
print("Get Connection from address:", address)

while True:
response = sock.recv(8192)
if not response:
break
print(f"Got {response}")
sock.sendall(response)

def echo_server(address, back_log=5):
sock = socket(AF_INET, SOCK_STREAM)
sock.bind(address)
sock.listen(back_log)

while True:
sock_client, address = sock.accept()
echo_handler(sock_client, address)

if __name__ == "__main__":
echo_server(('localhost', 5000))
+ +

代码详解:

+
    +
  • 创建一个基于IPV4和TCP协议的Socket,这里AF_INET指的是使用IPV4协议,SOCK_STREAM指定使用面向流的TCP协议,绑定监听端口,设置等待连接的最大数量
  • +
  • 创建一个永久循环,获取客户端请求的连接,accept()会等待并返回一个客户端的连接;
  • +
  • 连接建立后,等待客户端数据,接受完客户端数据,然后返回数据给客户端,最后关闭连接
  • +
+

存在的问题:当出现多个客户端请求时,由于是单个线程会发生阻塞的情况,所以如果需要多线程处理多个客户端请求,可以这样改;

+
1
2
3
4
5
6
from threading import Thread

while True:
client_sock, address = sock.accept()
thread = Thread(target=echo_handler, args=(client_sock, address))
thread.start()
+

这样的话,就会在每个客户端请求的时候,生成一个子线程然后处理请求;
(但是存在一个问题:当突然大量请求连接,消耗系统资源达到上限后,很可能造成程序无法处理后续请求。)

+

socketserver模块:

+
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
from socketserver import BaseRequestHandler, TCPServer

class EchoHandler(BaseRequestHandler):
def handle(self):
print("Got Connection From: %s" % str(self.client_address))
while True:
msg = self.request.recv(8192)
if not msg:
break
self.request.send(msg)

if __name__ == "__main__":
server = TCPServer(("", 5000), EchoHandler)
server.serve_forever()
```

```python
from socketserver import StreamRequestHandler, TCPServer, ThreadingTCPServer
import time

class EchoHandler(StreamRequestHandler):
def handle(self):
print("Got Connection Address: %s" % str(self.client_address))
for line in self.rfile:
print(line)
self.wfile.write(bytes("hello {}".format(line.decode('utf-8')).encode('utf-8')))

if __name__ == "__main__":
serv = ThreadingTCPServer(("", 5000), EchoHandler)
serv.serve_forever()
+ +

代码详解:

+
    +
  • 处理多个客户端,初始化一个ThreadingTCPServer实例,ThreadingTCPServer处理客户端的连接,会为每个客户端创建一个线程进行交互。
  • +
  • 设置绑定的IP地址和端口,以及处理类;
  • +
  • 使用StreamRequestHandler(使用流的请求处理程序类,类似file-like对象,提供标准文件接口简化通信过程),重写里面的handle方法,获取请求数据,返回数据给客户端;
  • +
+

TCP Socket客户端

socket模块:

+
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
# -*- coding: utf-8 -*-
from socket import socket, AF_INET, SOCK_STREAM
import time

def request_handler():
start_time = time.time()
sock_client = socket(AF_INET, SOCK_STREAM)
sock_client.connect(('localhost', 5000))

book_content = ""
with open("send_books.txt", "r") as f:
book_content = f.read()

content_list = book_content.split("\n")
for content in content_list:
if content:
sock_client.send((content).encode())
time.sleep(2)
response = sock_client.recv(8192)
print(response)

end_time = time.time()
print("总共耗时:", end_time-start_time)



if __name__ == "__main__":
request_handler()
+ + +

UDP Socket

Socket模块:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from socket import socket, AF_INET, SOCK_DGRAM
import time

def time_server(address):
sock = socket(AF_INET, SOCK_DGRAM)
sock.bind(address)

while True:
msg, addr = sock.recvfrom(8192)
print('Get message from', addr)
resp = time.ctime()
sock.sendto(resp.encode('ascii'), addr)

if __name__ == "__main__":
time_server(('', 5000))
+
+

代码不详解,和之前的差不多,注意不同的协议就完事了

+
+

客户端测试:

+
1
2
3
4
5
6
7
from socket import socket, AF_INET, SOCK_DGRAM

if __name__ == "__main__":
s = socket(AF_INET, SOCK_DGRAM)
s.sendto(b'hello', ('localhost', 5000))
text = s.recvfrom(8192)
print(text)
+ +

socketserver模块:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from socketserver import BaseRequestHandler, UDPServer
import time


class TimeHandler(BaseRequestHandler):
def handle(self):
print("Got Connection %s".format(str(self.client_address)))
data = self.request[0]
print(data)
msg, sock = self.request
print(msg)
data = time.ctime()
sock.sendto(data.encode('ascii'), self.client_address)

if __name__ == "__main__":
u = UDPServer(("localhost", 9999), TimeHandler)
u.serve_forever()
+
+

代码不在赘述,如果需要多线程处理并发操作可以使用ThreadingUDPServer

+
+

总结

+

关于本篇介绍Python Socket编程,大都是皮毛,只是谈到了Python实际处理socket的几个模块,
关于socket底层方面的知识并未提及,先了解个大概,从实际使用方面出发,在实际使用过程中结合
计算机网络知识,能够理解socket在整个TCP/IP协议栈中的作用。

socket和socketserver模块都可以用来编写网络程序,不同的是socketserver省事很多,你可以专注
业务逻辑,不用去理会socket的各种细节,包括不限于多线程/多进程,接收数据,发送数据,通信过程。

+
+ +
+ + + +
+ + +
+ + + +
+
+ + + +
+
+ +
+
+ + + + +
+ +
+ +
+ + +
+ +
+ +
+ + + + + + + + + + + +
+
+ + +
+
+
+ +
+
+ +
+ +
+
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + +
+
+
    + + +
  • + +
  • + + + + +
+
+ +
+ + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + +
  • + +
  • + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+
+
+
+ +
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + diff --git "a/2023/06/24/Gorm\346\241\206\346\236\266-CRUD\346\223\215\344\275\234/index.html" "b/2023/06/24/Gorm\346\241\206\346\236\266-CRUD\346\223\215\344\275\234/index.html" new file mode 100644 index 0000000..58aee84 --- /dev/null +++ "b/2023/06/24/Gorm\346\241\206\346\236\266-CRUD\346\223\215\344\275\234/index.html" @@ -0,0 +1,824 @@ + + + + + + + + + + + + Gorm框架-CRUD操作 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+ +
+ + + +
+ +
+ Gorm框架-CRUD操作 +
+ + + +
+ +
+ +
+ +
+
+ linshukai + + Lv2 + +
+
+ + + + +
+
+
+ + +
+ + +

简介

关于ORM(Object-Relational Mapping)

ORM其实指的是将关系型数据库中的数据和面向对象程序中对象模型进行映射的技术;
ORM可以用来自动化处理SQL语句的生成和执行,程序员可以更专注于业务逻辑的实现而不是数据的细节。

+

为什么需要ORM?(参考gpt,结合自身使用过程)

+
    +
  • 提高开发效率(自动化生成SQL,减少手动编写SQL时间)
  • +
  • 只需要定义好模型,可以自动处理不同数据库之间的差异;(如果传统编写,换数据库相当于需要吧原来的逻辑重写一遍)
  • +
  • 易于维护,方便拓展(传统编写SQL如果多起来,排查问题和重构时很痛苦);
  • +
  • 安全,参数化查询避免SQL注入
  • +
+

关于Gorm

Gorm是一个全功能的ORM框架,主要针对Go语言而开发,支持处理主流的关系型数据库(MySQL、PostgreSQL、SQL Server)
以及一些NoSQL数据库。由于这边是直接使用的是Gorm 2,关于Gorm的重要特性可以以gorm官网为准,可以直接参考:

+
+

https://gorm.io/zh_CN/docs/index.html#%E5%AE%89%E8%A3%85

+
+

安装和设置

快速安装Gorm2,Mysql驱动

1
2
go get gorm.io/gorm
go get gorm.io/driver/mysql
+ +

初始化Mysql连接(参考官网)

1
2
3
4
5
6
7
8
9
10
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)

func main() {
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
}
+

备注:

+
    +
  • 定义DSN(Data Source Name)连接数据库字符串dsn;
  • +
  • 传入MySQL驱动,初始化gorm数据库连接;
  • +
+

这是我自己定义初始化连接,读取对应配置文件

+
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
type Config struct {
MysqlConfig DBConfig `json:"mysql"`
}

type DBConfig struct {
Username string `json:"username"`
Password string `json:"password"`
Host string `json:"host"`
Port int `json:"port"`
DBName string `json:"prefix"`
}

func initDB() (db *gorm.DB, err error) {
confPath := "conf.json"
if _, err = os.Stat(confPath); err != nil {
return
}

var config Config
if err = configor.Load(&config, confPath); err != nil {
return
}

// 新建Database Gorm连接
mysqlConfig := &config.MysqlConfig
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4",
mysqlConfig.Username, mysqlConfig.Password, mysqlConfig.Host,
mysqlConfig.Port, mysqlConfig.DBName)
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})

return
}

func main() {

db, err := initDB()

if err != nil {
log.Fatalf(err.Error())
return
}

db = db.Debug()
execSQL(db)
}
+

备注:

+
    +
  • 这边我在项目中定义了关于数据库连接的结构体,通过读取预设好的config.json文件,加载好预设的配置信息;
  • +
  • 另外这边的db = db.Debug(),可以开启db的DEBUG模式,能够打印出执行的SQL语句以及对应的参数,以方便调试;
  • +
+

config.json参考

+
1
2
3
4
5
6
7
8
9
{
"mysql": {
"username": "root",
"password": "root",
"host": "127.0.0.1",
"prefix": "GormDemo",
"port": 3306
}
}
+ + +

定义模型

由于Gorm模型的结构体名的蛇形复数作为表名,字段名的蛇形作为列名,如果Mysql表的设计遵循了GORM约定,则可少写很多代码,
但是实际情况,往往不是这样:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type User struct {
ID string `gorm:"column:id" faker:"-"`
Email string `gorm:"column:email" faker:"email"`
Password string `gorm:"column:password" faker:"password"`
PhoneNumber string `gorm:"column:phone_number" faker:"phone_number"`
UserName string `gorm:"column:username" faker:"username"`
FirstName string `gorm:"first_name" faker:"first_name"`
LastName string `gorm:"last_name" faker:"last_name"`
Century string `gorm:"century" faker:"century"`
Date string `gorm:"date" faker:"date"`
}

func (u User) TableName() string {
return "user"
}
+

备注:

+
    +
  • 这是我定义的结构体User,通过实现Tabler接口来更改默认表名,TableName()方法会将Music表名重写为user(如果不修改则会默认为users);
  • +
  • 修改对应字段对应数据的列名,修改标签column;
  • +
  • 模型中的faker的标签可以忽略,这边主要是生成假数据才会使用到的;
  • +
+

CRUD

创建

单挑记录插入

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// insertOneRow 单挑记录插入
func insertOneRow(db *gorm.DB){
var tmpUser *User

// 生成随机数据
err := faker.FakeData(&tmpUser)
if err != nil {
log.Fatalf(err.Error())
}

result := db.Create(tmpUser)
if result.Error != nil {
log.Fatalf(result.Error.Error())
}

fmt.Println("result RowsAffected: ", result.RowsAffected)
fmt.Printf("%+v\n", tmpUser)

}
+

备注:

+
    +
  • 由于想生成写测试数据,所以使用了github.com/go-faker/faker生成假数据,有兴趣可以了解,像前面的结构体定义Tag之后就能生成对应的假数据;
  • +
  • 基本创建单条记录没啥好说的,只要定义模型的结构体,补充数据,然后插入即可;
  • +
+

批量记录插入

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// insertRows 批量插入
func insertRows(db *gorm.DB) {
var users []*User
for i := 0; i < 10; i++ {
tmpUser := User{}
err := faker.FakeData(&tmpUser)
if err != nil {
log.Fatal(err.Error())
}
users = append(users, &tmpUser)
}

result := db.Create(users)

if result.Error != nil {
log.Fatalf(result.Error.Error())
}

fmt.Println("RowsAffected: ", result.RowsAffected)

for _, m := range users {
fmt.Printf("%+v\n", m)
}
}
+ +

定义钩子函数

+
1
2
3
4
func (u *User) BeforeCreate(tx *gorm.DB) (err error){
u.ID = uuid.New()
return nil
}
+

备注:

+
    +
  • 在插入记录之前,生成uuid(具体根据实际业务场景补充处理的业务逻辑,包括检验等)
  • +
  • 官方还提供提供很多额外的钩子函数(BeforeSave,AfterSave等)具体参考官方提供的文档
  • +
+

查询

简单查询

+
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
func printRecord(u *User, result *gorm.DB){
fmt.Printf("%v\n", u)
fmt.Println(result.Error, result.RowsAffected)
}

func printRecords(u []*User, result *gorm.DB){

for _, u := range u {
fmt.Println(u)
}
fmt.Println(result.Error, result.RowsAffected)

}

func simpleQueryRow(db *gorm.DB){

// 查询第一条记录(主键升序)
var firstUser *User
result := db.First(&firstUser)
printRecord(firstUser, result)

// 仅当有一个ID主键时,可直接定义User时把ID初始化
firstIDUser2 := &User{ID: "e8efff22-a497-4a88-be1e-5123eb23ff75"}
result = db.First(&firstIDUser2)
printRecord(firstIDUser2, result)

// 查询表中第一条记录(没有指定排序字段)
var firstUser2 *User
result = db.Take(&firstUser2)
printRecord(firstUser2, result)

// 查询表中最后一条记录(主键排序)
var lastUser *User
result = db.Last(&lastUser)
printRecord(lastUser, result)

// 查询当前所有记录
var users []*User
result = db.Find(&users)
printRecords(users, result)

}
+

备注:

+
    +
  • 这几个都是按照官方给出的示例,整理了一下日常比较常用到的查询操作
  • +
+

条件查询

+
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
func condQueryRow(db *gorm.DB){

// 查询当前username为condQueryRow的第一条记录(Struct方式)
var tmpUser1 *User
result := db.Where(&User{UserName: "qNptxqb"}).First(&tmpUser1)
printRecord(tmpUser1, result)

// 查询当前username为condQueryRow的第一条记录(Map方式)
var tmpUser2 *User
result = db.Where(map[string]interface{}{"username": "qNptxqb"}).First(&tmpUser2)
printRecord(tmpUser2, result)

// 指定Century查询字段查询记录
var tmpUser3 []User
result = db.Where(&User{Century: "VII", UserName: "jaQlaFs"}, "Century").Find(&tmpUser3)
printRecords(tmpUser3, result)

// String 条件,直接写表达式
var tmpUser4 *User
result = db.Where("username = ?", "qNptxqb").First(&tmpUser4)
printRecord(tmpUser4, result)

var users []User
result = db.Where("date > ?", "2010-10-1").Find(&users)
printRecords(users, result)

// Order排序(默认升序)
var users2 []User
result = db.Where("date > ?", "2010-10-1").Order("date").Find(&users2)
printRecords(users2, result)

// 查询特定的字段,不返回所有字段
var tmpUser5 *User
result = db.Select("username", "date").Where("username = ?", "qNptxqb").First(&tmpUser5)
printRecord(tmpUser5, result)
}
+

备注:

+
    +
  • Gorm提供了很多条件查询的场景,目前对我而言基本的查询业务逻辑都能支持。具体可能出现一些复杂的sql,后面会介绍怎么直接使用sql查询;
  • +
+

高级查询

+
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
type APIUser struct {
ID string `gorm:"primaryKey,column:id"`
UserName string `gorm:"column:username"`
FirstName string `gorm:"first_name"`
LastName string `gorm:"last_name"`
}

func advancedQueryRow(db *gorm.DB){

// 智能选择字段,如果经常只需要查询某些字段,可以重新定义小结构体
var apiUser []APIUser
result := db.Model(&User{}).Find(&apiUser)
for _, user := range apiUser{
fmt.Println(user)
}
fmt.Println(result.Error, result.RowsAffected)

// 扫描结果绑定值map[string]interface{} 或者 []map[string]interface{}
var users []map[string]interface{}
result = db.Model(&User{}).Find(&users)
for _, user := range users{
fmt.Println(user)
}
fmt.Println(result.Error, result.RowsAffected)

// Pluck查询单个列,并将结果扫描到切片
var emails []string
result = db.Model(&User{}).Pluck("email",&emails)
fmt.Println(emails)
fmt.Println(result.Error, result.RowsAffected)

// Count查询
var count int64
result = db.Model(&User{}).Where("date > ?", "2012-10-22").Count(&count)
fmt.Println(count)
fmt.Println(result.Error, result.RowsAffected)
}
+

备注:

+
    +
  • 定义小结构体可以实现在调用API时自动选择特定字段
  • +
  • Pluck适合查询单个列,如果需要查询多个常用字段可以通过Select和Scan
  • +
+

更新

常用更新操作

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func updateRow(db *gorm.DB){
// Save会保存所有字段,即使字段是零值,如果保存的值没有主键,就会创建,否则则是更新指定记录
result := db.Save(&User{ID: "e8efff22-a497-4a88-be1e-5123eb23ff75", UserName: "zhangsan", Date: "2023-12-12"})
fmt.Println(result.Error, result.RowsAffected)

// 更新单个列
result = db.Model(&User{}).Where("username = ?", "jaQlaFs").Update("first_name", "zhangsan")
fmt.Println(result.Error, result.RowsAffected)

// 更新多个列
result = db.Model(&User{}).Where("username = ?", "zhangsan").Updates(User{FirstName: "zhangsan2", LastName: "zhangsan3"})
fmt.Println(result.Error, result.RowsAffected)

// 更新指定列(Select指定last_name)
result = db.Model(&User{}).Where("username = ?", "zhangsan").Select("last_name").Updates(User{FirstName: "zhangsan2", LastName: "zhangsan4"})
fmt.Println(result.Error, result.RowsAffected)
}
+

备注:

+
    +
  • 具体一些更新的操作和查询类似,具体区分即可;
  • +
+

删除

常用删除操作

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func deleteRows(db *gorm.DB){

// 指定匹配字段删除数据
result := db.Delete(&User{}, map[string]interface{}{"username": "NJrauTj"})
fmt.Println(result.Error, result.RowsAffected)

result = db.Delete(&User{}, "username = ?", "NJrauTj")
fmt.Println(result.Error, result.RowsAffected)

// Where指定字段匹配删除数据
result = db.Where("username = ? and phone_number = ?", "jXQKmPv", "574-821-9631").Delete(&User{})
fmt.Println(result.Error, result.RowsAffected)

// 批量删除的两种方式
result = db.Where("email like ?", "%.com%").Delete(&User{})
fmt.Println(result.Error, result.RowsAffected)

result = db.Delete(&User{}, "email like ?", "%.com%")
fmt.Println(result.Error, result.RowsAffected)
}
+

备注:

+
    +
  • 普通删除常用场景匹配指定单挑数据删除以及批量删除,语法和更新类似;
  • +
  • Gorm文档中有涉及禁用全局删除,即当执行不带任何条件的批量删除时就会返回错误;以及关于删除的钩子函数有实用
    场景的同学,可以看官方文档,这里不再赘述;
  • +
+

原生SQL和SQL生成器

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
// execSQL 执行原生SQL语句
func execSQL(db *gorm.DB){

// 将查询SQL的结果映射到指定的单个变量中
var oneUser User
result := db.Raw("SELECT * FROM user LIMIT 1").Scan(&oneUser)
fmt.Println(oneUser)
fmt.Println(result.Error, result.RowsAffected)

// 将查询SQL的批量结果映射到列表中
var users []User
result = db.Raw("SELECT * FROM user").Scan(&users)
for _, user := range users {
fmt.Println(user)
}
fmt.Println(result.Error, result.RowsAffected)

var updateUser User
result = db.Raw("UPDATE users SET username = ? where id = ?", "toms jobs", "ab6f089b-3272-49b5-858f-a93ed5a43b4f").Scan(&updateUser)
fmt.Println(updateUser)
fmt.Println(result.Error, result.RowsAffected)

// 直接通过Exec函数执行Update操作,不返回任何查询结果?
result = db.Exec("UPDATE user SET username = ? where id = ?", "toms jobs", "ab6f089b-3272-49b5-858f-a93ed5a43b4f")
fmt.Println(result.Error, result.RowsAffected)

// DryRun模式,在不执行的情况下生成SQL及其参数,可以用于准备或测试的SQL
var tmpUsers []APIUser
stmt := db.Session(&gorm.Session{DryRun: true}).Model(&User{}).Find(&tmpUsers).Statement
fmt.Println(stmt.SQL.String())
fmt.Println(stmt.Vars)
}
+

备注:

+
    +
  • Scan函数是会将查询SQL的结果映射到定义的变量,如果不需要返回查询结果可以直接使用Exec函数执行原生SQL;
  • +
  • DryRun模式,可以直接生成SQL机器参数,但是不会直接执行;
  • +
+

总结

关于Gorm的CURD日常使用就介绍到这里,如果同学对Gorm感兴趣,可以接去Gorm官网,我这里只是简单介绍一下在日常业务环境经常使用的操作,具体Gorm中文文档地址。

+

至于Gorm后面可能还会出几篇文章介绍Gorm模型之间的关联关系,以及关联模式下的CRUD;还有关于Gorm的性能优化,具体能搭配一些常用的插件。

+

上述文章的源码:

+
+

https://github.com/libuliduobuqiuqiu/GoDemo/blob/master/GormDemo/gorm_demo.go

+
+ +
+ + + +
+ + +
+ + + +
+
+ + + +
+
+ +
+
+ + + + +
+ +
+ +
+ + +
+ +
+ +
+ + + + + + + + + + + +
+
+ + +
+
+
+ +
+
+ +
+ +
+
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + +
+
+
    + + +
  • + +
  • + + + + +
+
+ +
+ + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + +
  • + +
  • + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+
+
+
+ +
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + diff --git "a/2023/12/03/Flask\344\270\200\344\270\252g\345\274\225\345\217\221\347\232\204\346\200\235\350\200\203/index.html" "b/2023/12/03/Flask\344\270\200\344\270\252g\345\274\225\345\217\221\347\232\204\346\200\235\350\200\203/index.html" new file mode 100644 index 0000000..0e7f732 --- /dev/null +++ "b/2023/12/03/Flask\344\270\200\344\270\252g\345\274\225\345\217\221\347\232\204\346\200\235\350\200\203/index.html" @@ -0,0 +1,776 @@ + + + + + + + + + + + + Flask一个g引发的思考 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+ +
+ + + +
+ +
+ Flask一个g引发的思考 +
+ + + +
+ +
+ +
+ +
+
+ linshukai + + Lv2 + +
+
+ + + + +
+
+
+ + +
+ + +

背景

+

最近有面试一家公司,感觉准备的不是很充分,感觉很多东西都答的挺菜的,自己写的文章里面的问题都没答上来更是汗流浃背。
所以大概列了一下其中的问题,进行了一番总结补充,重新制定一下复习的计划。
其中里面我觉得有个问题我觉得挺有意思的,我记得大概是问了一下Flask的g有使用过?具体实现的原理是啥?
我想了一下 ,这不就是平时用来存储一下数据的全局变量的么?有啥原理的,后面大概翻了一下Flask的源码,具体从头捋了一遍,所以就有这篇文章。

+
+

源码阅读

g

首先我们从平时导入的”from flask import g”入手,定位到flask框架代码中g,作为起点继续往下探索;

+
1
2
3
4
5
6
7
8
9
10
11
_cv_app: ContextVar[AppContext] = ContextVar("flask.app_ctx")
app_ctx: AppContext = LocalProxy( # type: ignore[assignment]
_cv_app, unbound_message=_no_app_msg
)
current_app: Flask = LocalProxy( # type: ignore[assignment]
_cv_app, "app", unbound_message=_no_app_msg
)
g: _AppCtxGlobals = LocalProxy( # type: ignore[assignment]
_cv_app, "g", unbound_message=_no_app_msg
)

+

这里面重要的就两块地方,_cv_app,g:

+
    +
  • _cv_app这个对象实际上就是初始化一个上下文变量ContexVar,根据这个类型注解,这个上下文变量中的值是AppContext对象;
  • +
  • g实质就是_AppCtxGlobals对象,但是这里是通过LocalProxy代理对象进行访问;
  • +
+

LocalProxy.__init__

接下来继续分析一下LocalProxy这个代理对象是怎么代理访问_AppCtxGlobals,也就是应用上下文中的对象:(代码过长,我截取初始化中最重要的几部分)

+
1
2
3
4
5
6
7
def __init__(
self,
local: ContextVar[T] | Local | LocalStack[T] | t.Callable[[], T],
name: str | None = None,
*,
unbound_message: str | None = None,
) -> None:
+
    +
  • 定义传入的参数,这里local就是_cv_app上下文变量,name就是”g”;
  • +
+
1
2
3
4
if name is None:
get_name = _identity
else:
get_name = attrgetter(name)
+
    +
  • 生成一个可调用对象,用于提取对象中的属性值。后续get_name(obj)相当于直接调用obj.name属性;
  • +
+
1
2
3
4
5
6
7
8
9
elif isinstance(local, ContextVar):

def _get_current_object() -> T:
try:
obj = local.get()
except LookupError:
raise RuntimeError(unbound_message) from None

return get_name(obj)
+

这部分有两块地方比较模糊,local.get(),get_name(obj)

+
    +
  • 由于传入local是ContexVar类型,定位到if匹配的代码,这部分是定义_get_current_object方法
  • +
  • local.get()其实返回的就是set进去的AppContext对象,所以obj就是AppContext类型;
  • +
  • get_name方法前面定义好了,所以可以理解为AppContext.g;
  • +
+
1
object.__setattr__(self, "_get_current_object", _get_current_object)
+

LocalProxy代理器绑定_get_current_object方法

+

LocalProxy.__setattr__、__getattr__

众所周知,当给一个类绑定属性时候,会调用类的__setattr__方法,当读取一个不存在的属性时,会调用__getattr__方法,调用一个已存在属性时,会调用__getattribute__方法;

+
1
2
3
__getattr__ = _ProxyLookup(getattr)
# __getattribute__ triggered through __getattr__
__setattr__ = _ProxyLookup(setattr) # type: ignore
+
    +
  • 这部分有个地方挺有意思的,就是__getattribute__方法源代码是注释的了,并且配上通过__getattr__触发,这里可能需要绕个弯,本质上这里是代理器对象,目标对象并不是直接绑定到代理的。所以其实每次通过g去获取存储的对象是,这个代理器的__getattr__就会被触发了;
  • +
  • 设置__setattr__方法;可以理解为后续LocalProxy的实例对象设置属性时,_ProxyLookup(setattr)(self, key, value),这个self指的是LocalProxy的实例对象
  • +
+

_PorxyLookup.__init__

接下来,我们接到看这个_PorxyLookup对象,这个本质上就是个描述器,用于查找对象的。

+

描述器开始有点熟悉了,上面提到_ProxyLookup的实例当成方法一样调用的时候,应该先定位到_PorxyLookup对象__call__方法,先看看_PorxyLookup的初始化__init__:

+
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
def __init__(
self,
f: t.Callable | None = None,
fallback: t.Callable | None = None,
class_value: t.Any | None = None,
is_attr: bool = False,
) -> None:
bind_f: t.Callable[[LocalProxy, t.Any], t.Callable] | None

if hasattr(f, "__get__"):
# A Python function, can be turned into a bound method.

def bind_f(instance: LocalProxy, obj: t.Any) -> t.Callable:
return f.__get__(obj, type(obj)) # type: ignore

elif f is not None:
# A C function, use partial to bind the first argument.

def bind_f(instance: LocalProxy, obj: t.Any) -> t.Callable:
return partial(f, obj)

else:
# Use getattr, which will produce a bound method.
bind_f = None

self.bind_f = bind_f
self.fallback = fallback
self.class_value = class_value
self.is_attr = is_attr
+
    +
  • 判断传入的f是否为描述器,很显然传入的setattr为否,且f不为None:
  • +
  • 声明一个bind_f函数,函数用于返回partial对象,partial函数是用于固定了obj参数;
  • +
  • 前面提到传入的是setattr,setattr的函数定义setattr(x,y,z),正常使用需要传入三个参数,才能达到x.y = z的效果;
  • +
  • 现在就是bind_f直接返回的是固定了第一个参数为obj的setattr函数,后续只需要调用只需要传入后两个参数即可;
    (注意这个self.bind_f,后面会用到)
  • +
+

_PorxyLookup.__call__

接着继续看_PorxyLookup的__call__方法:

+
1
2
3
4
5
6
7
def __call__(self, instance: LocalProxy, *args: t.Any, **kwargs: t.Any) -> t.Any:
"""Support calling unbound methods from the class. For example,
this happens with ``copy.copy``, which does
``type(x).__copy__(x)``. ``type(x)`` can't be proxied, so it
returns the proxy type and descriptor.
"""
return self.__get__(instance, type(instance))(*args, **kwargs)
+
    +
  • 这里其实就是当实例当做函数一样调用时会触发的__call__方法,这里可以看到instance对应着我们传入的LocalProxy实例对象,*args就是我们传入设置的参数了;
  • +
  • 后面就是调用描述器的__get__方法了,直接传入实例对象,实例类型;
  • +
+

_PorxyLookup.__get__

这部分算我们寻找设置g这个全局上下文变量的重点,把前面做的所有事情进行一次“回收”使用:

+
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
def __get__(self, instance: LocalProxy, owner: type | None = None) -> t.Any:
if instance is None:
if self.class_value is not None:
return self.class_value

return self

try:
obj = instance._get_current_object()
except RuntimeError:
if self.fallback is None:
raise

fallback = self.fallback.__get__(instance, owner)

if self.is_attr:
# __class__ and __doc__ are attributes, not methods.
# Call the fallback to get the value.
return fallback()

return fallback

if self.bind_f is not None:
return self.bind_f(instance, obj)

return getattr(obj, self.name)
+
    +
  1. _get_current_object方法,前面在LocalProxy代理器对象绑定好的方法,方法里面其实最终返回的是就是一个AppContext.g对象;
  2. +
  3. self.bind_f前面初始化刚绑定好的,这里其实就是返回一个固定参数的setattr函数,setattr的第一个参数固定成obj了;
  4. +
  5. 结合上面__call__方法,再把剩余需要设置的参数传进来,假设在flask框架设置g全局变量是g.name = “flask”,其实在这里执行的就是setattr(AppContext.g, “name”, “flask”);
  6. +
  7. 所以就是调用目标对象AppContext中g属性的__setattr__方法;
  8. +
+

整体总结

整体看下来就是,废了老大劲,其实就是调用AppContext的方法。而里面代码架构的核心就是代理模式,使用者就是通过g这个代理器去访问AppContext里面的g属性。而这个AppContext对象则是,存放在_cv_app这个上下文变量。而这个g实质就是在存储数据的应用上下文。

+

下面展示一下AppContext类:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class AppContext:
"""The app context contains application-specific information. An app
context is created and pushed at the beginning of each request if
one is not already active. An app context is also pushed when
running CLI commands.
"""

def __init__(self, app: Flask) -> None:
self.app = app
self.url_adapter = app.create_url_adapter(None)
self.g: _AppCtxGlobals = app.app_ctx_globals_class()
self._cv_tokens: list[contextvars.Token] = []

def push(self) -> None:
"""Binds the app context to the current context."""
self._cv_tokens.append(_cv_app.set(self))
appcontext_pushed.send(self.app, _async_wrapper=self.app.ensure_sync)
+
    +
  • 这里面有趣的点就是什么时候触发这个push方法,即需要把当前的AppContext保存到_cv_app这个上下文变量中,不然你直接在flask程序启动的前调用这个g,会发现抛出一个RuntimeError的异常,因为_cv_app里面是空的;
  • +
  • 这里不详细列了,有兴趣的再去仔细看看,答案是Flask对象中的wsgi_app方法中,当WSGI服务器调用Falsk对象作为应用程序时就会调用wsgi_app方法,wsgi_app就会推送应用程序上下文;
  • +
+

最后一试

上面提到很关键的代理模式,众所周知,一般来说代理模式目的就是防止调用者和执行者发生关系,所以需要一个代理对象。如果上面这么绕的代码看不懂,其实可以简洁成以下的代码,
或许你直接就豁然开朗了 。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from werkzeug.local import LocalProxy
from contextvars import ContextVar


class Info:
pass


class Person:
def __init__(self):
self.info = Info()


if __name__ == "__main__":
flask_var = ContextVar("flask.context")
p = Person()
flask_var.set(p)

proxy = LocalProxy(flask_var, "info", unbound_message="Error bound msg")
proxy.name = "zhangsan"
proxy.age = 12
print(p.info.name, p.info.age)
+ +

所以梳理完整遍代码,好像大概知道了一点原理。。。

+

参考链接:

+
+

https://www.cnblogs.com/cwp-bg/p/10084480.html

+
+
+

https://flask.palletsprojects.com/en/3.0.x/appcontext/

+
+ +
+ + + +
+ + +
+ + + +
+
+ + + +
+
+ +
+
+ + + + +
+ + +
+ +
+ +
+ + + + + + + + + + + +
+
+ + +
+
+
+ +
+
+ +
+ +
+
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + +
+
+
    + + +
  • + +
  • + + + + +
+
+ +
+ + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + +
  • + +
  • + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+
+
+
+ +
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..5832de2 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +linshukai.cn \ No newline at end of file diff --git a/archives/2020/07/index.html b/archives/2020/07/index.html new file mode 100644 index 0000000..e70e9af --- /dev/null +++ b/archives/2020/07/index.html @@ -0,0 +1,440 @@ + + + + + + + + + + + + 归档: 2020/7 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+
+ +
+ +
+
+ 2020 +
+ +
+ +
+ +
+
+ + + + +
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + diff --git a/archives/2020/09/index.html b/archives/2020/09/index.html new file mode 100644 index 0000000..a693272 --- /dev/null +++ b/archives/2020/09/index.html @@ -0,0 +1,433 @@ + + + + + + + + + + + + 归档: 2020/9 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+
+ +
+ +
+
+ 2020 +
+ +
+ +
+ +
+
+ + + + +
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + diff --git a/archives/2020/12/index.html b/archives/2020/12/index.html new file mode 100644 index 0000000..5604c51 --- /dev/null +++ b/archives/2020/12/index.html @@ -0,0 +1,440 @@ + + + + + + + + + + + + 归档: 2020/12 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+
+ +
+ +
+
+ 2020 +
+ +
+ +
+ +
+
+ + + + +
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + diff --git a/archives/2020/index.html b/archives/2020/index.html new file mode 100644 index 0000000..2ab3344 --- /dev/null +++ b/archives/2020/index.html @@ -0,0 +1,461 @@ + + + + + + + + + + + + 归档: 2020 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+
+ +
+ +
+
+ 2020 +
+ +
+ +
+ +
+
+ + + + +
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + diff --git a/archives/2021/04/index.html b/archives/2021/04/index.html new file mode 100644 index 0000000..38be480 --- /dev/null +++ b/archives/2021/04/index.html @@ -0,0 +1,433 @@ + + + + + + + + + + + + 归档: 2021/4 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+
+ +
+ +
+
+ 2021 +
+ +
+ +
+ +
+
+ + + + +
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + diff --git a/archives/2021/index.html b/archives/2021/index.html new file mode 100644 index 0000000..e579c37 --- /dev/null +++ b/archives/2021/index.html @@ -0,0 +1,433 @@ + + + + + + + + + + + + 归档: 2021 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+
+ +
+ +
+
+ 2021 +
+ +
+ +
+ +
+
+ + + + +
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + diff --git a/archives/2023/06/index.html b/archives/2023/06/index.html new file mode 100644 index 0000000..53f0bb7 --- /dev/null +++ b/archives/2023/06/index.html @@ -0,0 +1,433 @@ + + + + + + + + + + + + 归档: 2023/6 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+
+ +
+ +
+
+ 2023 +
+ +
+ +
+ +
+
+ + + + +
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + diff --git a/archives/2023/12/index.html b/archives/2023/12/index.html new file mode 100644 index 0000000..37cb9b6 --- /dev/null +++ b/archives/2023/12/index.html @@ -0,0 +1,433 @@ + + + + + + + + + + + + 归档: 2023/12 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+
+ +
+ +
+
+ 2023 +
+ +
+ +
+ +
+
+ + + + +
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + diff --git a/archives/2023/index.html b/archives/2023/index.html new file mode 100644 index 0000000..d4934db --- /dev/null +++ b/archives/2023/index.html @@ -0,0 +1,440 @@ + + + + + + + + + + + + 归档: 2023 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+
+ +
+ +
+
+ 2023 +
+ +
+ +
+ +
+
+ + + + +
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + diff --git a/archives/index.html b/archives/index.html new file mode 100644 index 0000000..14cefdc --- /dev/null +++ b/archives/index.html @@ -0,0 +1,500 @@ + + + + + + + + + + + + 归档 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+
+ +
+ +
+
+ 2023 +
+ +
+ +
+
+ 2021 +
+ +
+ +
+
+ 2020 +
+ +
+ +
+ +
+
+ + + + +
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + diff --git a/categories/Go/index.html b/categories/Go/index.html new file mode 100644 index 0000000..48f41a5 --- /dev/null +++ b/categories/Go/index.html @@ -0,0 +1,436 @@ + + + + + + + + + + + + 分类: Go | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+
+  Go +
+
+ +
+ +
+
+ 2023 +
+ +
+ +
+ +
+
+ + + + +
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + diff --git a/categories/Python/index.html b/categories/Python/index.html new file mode 100644 index 0000000..293dd82 --- /dev/null +++ b/categories/Python/index.html @@ -0,0 +1,482 @@ + + + + + + + + + + + + 分类: Python | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+
+  Python +
+
+ +
+ +
+
+ 2023 +
+ +
+ +
+
+ 2021 +
+ +
+ +
+
+ 2020 +
+ +
+ +
+ +
+
+ + + + +
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + diff --git a/categories/index.html b/categories/index.html new file mode 100644 index 0000000..c27d8cf --- /dev/null +++ b/categories/index.html @@ -0,0 +1,416 @@ + + + + + + + + + + + + 分类 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+ + + + +
+
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + diff --git "a/categories/\347\254\224\350\256\260/index.html" "b/categories/\347\254\224\350\256\260/index.html" new file mode 100644 index 0000000..3cd32a4 --- /dev/null +++ "b/categories/\347\254\224\350\256\260/index.html" @@ -0,0 +1,443 @@ + + + + + + + + + + + + 分类: 笔记 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
+
+  笔记 +
+
+ +
+ +
+
+ 2020 +
+ +
+ +
+ +
+
+ + + + +
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + diff --git a/css/common/animated.css b/css/common/animated.css new file mode 100644 index 0000000..a1ac846 --- /dev/null +++ b/css/common/animated.css @@ -0,0 +1,180 @@ +.fade-in-down-animation { + animation-name: fade-in-down; + animation-duration: 1s; + animation-fill-mode: both; +} +.title-hover-animation { + position: relative; + display: inline-block; + color: var(--text-color-2); + line-height: 1.3; + vertical-align: top; + border-bottom: none; +} +.title-hover-animation::before { + position: absolute; + bottom: -4px; + left: 0; + width: 100%; + height: 2px; + background-color: var(--text-color-2); + transform: scaleX(0); + visibility: hidden; + content: ""; + transition-delay: 0s, 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease-in-out, ease-in-out; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.2s, 0.2s; + transition-property: color, background, box-shadow, border-color, visibility, transform; +} +.title-hover-animation:hover::before { + transform: scaleX(1); + visibility: visible; +} +@-moz-keyframes fade-in-down { + 0% { + transform: translateY(-50px); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } +} +@-webkit-keyframes fade-in-down { + 0% { + transform: translateY(-50px); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } +} +@-o-keyframes fade-in-down { + 0% { + transform: translateY(-50px); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } +} +@keyframes fade-in-down { + 0% { + transform: translateY(-50px); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } +} +@-moz-keyframes heartbeat-animate { + 0%, 100% { + transform: scale(1); + } + 10%, 30% { + transform: scale(0.88); + } + 20%, 40%, 60%, 80% { + transform: scale(1.08); + } + 50%, 70% { + transform: scale(1.08); + } +} +@-webkit-keyframes heartbeat-animate { + 0%, 100% { + transform: scale(1); + } + 10%, 30% { + transform: scale(0.88); + } + 20%, 40%, 60%, 80% { + transform: scale(1.08); + } + 50%, 70% { + transform: scale(1.08); + } +} +@-o-keyframes heartbeat-animate { + 0%, 100% { + transform: scale(1); + } + 10%, 30% { + transform: scale(0.88); + } + 20%, 40%, 60%, 80% { + transform: scale(1.08); + } + 50%, 70% { + transform: scale(1.08); + } +} +@keyframes heartbeat-animate { + 0%, 100% { + transform: scale(1); + } + 10%, 30% { + transform: scale(0.88); + } + 20%, 40%, 60%, 80% { + transform: scale(1.08); + } + 50%, 70% { + transform: scale(1.08); + } +} +@-moz-keyframes img-loading-animation { + to { + transform: rotate(1turn); + } +} +@-webkit-keyframes img-loading-animation { + to { + transform: rotate(1turn); + } +} +@-o-keyframes img-loading-animation { + to { + transform: rotate(1turn); + } +} +@keyframes img-loading-animation { + to { + transform: rotate(1turn); + } +} +@-moz-keyframes blink-caret { + from, to { + opacity: 0; + } + 50% { + opacity: 1; + } +} +@-webkit-keyframes blink-caret { + from, to { + opacity: 0; + } + 50% { + opacity: 1; + } +} +@-o-keyframes blink-caret { + from, to { + opacity: 0; + } + 50% { + opacity: 1; + } +} +@keyframes blink-caret { + from, to { + opacity: 0; + } + 50% { + opacity: 1; + } +} diff --git a/css/common/basic.css b/css/common/basic.css new file mode 100644 index 0000000..88e3672 --- /dev/null +++ b/css/common/basic.css @@ -0,0 +1,720 @@ +:root { + --base-font-size: 15.2px; + --base-line-height: 22px; + --base-font-weight: 400; + --base-font-family: Optima-Regular, Optima, PingFang SC, Microsoft YaHei, sans-serif; + --page-content-width: 80%; + --page-content-width-tablet: 88%; + --page-content-width-mobile: 90%; + --page-content-max-width: 928px; + --page-content-max-width-2: calc(var(--page-content-max-width) * 1.2); + --components-spacing: 36px; + --box-border-radius: 8px; + --header-height: 70px; + --header-shrink-height: calc(var(--header-height) * 0.72); + --header-backdrop-filter-blur: 4px; + --header-scroll-progress-bar-height: 2PX; + --first-screen-font-size: 2rem; + --first-screen-icon-size: 1.8rem; + --first-screen-font-color-light: #50505c; + --first-screen-font-color-dark: #adbac5; + --home-post-hover-scale: 1; + --post-author-avatar: block; + --post-create-datetime: flex; + --post-update-datetime: flex; + --post-img-align: 0 auto 0 0; +} +.fade-in-down-animation { + animation-name: fade-in-down; + animation-duration: 1s; + animation-fill-mode: both; +} +.title-hover-animation { + position: relative; + display: inline-block; + color: var(--text-color-2); + line-height: 1.3; + vertical-align: top; + border-bottom: none; +} +.title-hover-animation::before { + position: absolute; + bottom: -4px; + left: 0; + width: 100%; + height: 2px; + background-color: var(--text-color-2); + transform: scaleX(0); + visibility: hidden; + content: ""; + transition-delay: 0s, 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease-in-out, ease-in-out; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.2s, 0.2s; + transition-property: color, background, box-shadow, border-color, visibility, transform; +} +.title-hover-animation:hover::before { + transform: scaleX(1); + visibility: visible; +} +@-moz-keyframes fade-in-down { + 0% { + transform: translateY(-50px); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } +} +@-webkit-keyframes fade-in-down { + 0% { + transform: translateY(-50px); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } +} +@-o-keyframes fade-in-down { + 0% { + transform: translateY(-50px); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } +} +@keyframes fade-in-down { + 0% { + transform: translateY(-50px); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } +} +@-moz-keyframes heartbeat-animate { + 0%, 100% { + transform: scale(1); + } + 10%, 30% { + transform: scale(0.88); + } + 20%, 40%, 60%, 80% { + transform: scale(1.08); + } + 50%, 70% { + transform: scale(1.08); + } +} +@-webkit-keyframes heartbeat-animate { + 0%, 100% { + transform: scale(1); + } + 10%, 30% { + transform: scale(0.88); + } + 20%, 40%, 60%, 80% { + transform: scale(1.08); + } + 50%, 70% { + transform: scale(1.08); + } +} +@-o-keyframes heartbeat-animate { + 0%, 100% { + transform: scale(1); + } + 10%, 30% { + transform: scale(0.88); + } + 20%, 40%, 60%, 80% { + transform: scale(1.08); + } + 50%, 70% { + transform: scale(1.08); + } +} +@keyframes heartbeat-animate { + 0%, 100% { + transform: scale(1); + } + 10%, 30% { + transform: scale(0.88); + } + 20%, 40%, 60%, 80% { + transform: scale(1.08); + } + 50%, 70% { + transform: scale(1.08); + } +} +@-moz-keyframes img-loading-animation { + to { + transform: rotate(1turn); + } +} +@-webkit-keyframes img-loading-animation { + to { + transform: rotate(1turn); + } +} +@-o-keyframes img-loading-animation { + to { + transform: rotate(1turn); + } +} +@keyframes img-loading-animation { + to { + transform: rotate(1turn); + } +} +@-moz-keyframes blink-caret { + from, to { + opacity: 0; + } + 50% { + opacity: 1; + } +} +@-webkit-keyframes blink-caret { + from, to { + opacity: 0; + } + 50% { + opacity: 1; + } +} +@-o-keyframes blink-caret { + from, to { + opacity: 0; + } + 50% { + opacity: 1; + } +} +@keyframes blink-caret { + from, to { + opacity: 0; + } + 50% { + opacity: 1; + } +} +:root { + --primary-color: #06c; + --primary-color-light-1: #0075eb; + --primary-color-light-2: #0a85ff; + --primary-color-dark-1: #005cb8; + --primary-color-dark-2: #0052a3; + --background-color-1: #fff; + --background-color-1-transparent: rgba(255,255,255,0.6); + --background-color-2: #f7f7f7; + --background-color-3: #f0f0f0; + --content-background-color: #fff; + --text-color-1: #484853; + --text-color-2: #4c4c57; + --text-color-3: #50505c; + --text-color-4: #808091; + --text-color-5: #b7b7c0; + --text-color-6: #ededef; + --toc-text-color: #6f6f80; + --badge-color: #f0f0f0; + --badge-background-color: #9393a1; + --border-color: #b3b3b3; + --selection-color: #0075eb; + --shadow-color: rgba(0,0,0,0.16); + --shadow-hover-color: rgba(0,0,0,0.22); + --scrollbar-color: #60606e; + --scrollbar-background-color: #e6e6e6; + --toc-scrollbar-color: rgba(80,80,92,0.1); + --copyright-icon-bg-color: rgba(80,80,92,0.12); + --avatar-background-color: #005cb8; + --header-transparent-background-1: rgba(255,255,255,0.28); + --header-transparent-background-2: rgba(255,255,255,0.4); + --pjax-progress-bar-color: linear-gradient(45deg, #f10006, #ef5b00, #e59c01, #19ca05, #00cab5, #0264c8, #c303c3); + --post-h-bottom-border-color: rgba(80,80,92,0.15); + --article-aging-tips-color: $article-aging-tips-color; + --article-aging-tips-background-color: $article-aging-tips-background-color; + --article-aging-tips-border-color: $article-aging-tips-border-color; + --keep-info-color: rgba(117,117,122,0.8); + --keep-info-background-color: rgba(117,117,122,0.1); + --keep-info-border-color: rgba(117,117,122,0.6); + --keep-primary-color: #0458ab; + --keep-primary-background-color: rgba(4,88,171,0.1); + --keep-primary-border-color: rgba(4,88,171,0.6); + --keep-warning-color: #b78d0f; + --keep-warning-background-color: rgba(183,141,15,0.1); + --keep-warning-border-color: rgba(183,141,15,0.6); + --keep-success-color: #10b981; + --keep-success-background-color: rgba(16,185,129,0.1); + --keep-success-border-color: rgba(16,185,129,0.6); + --keep-danger-color: #f43f5e; + --keep-danger-background-color: rgba(244,63,94,0.1); + --keep-danger-border-color: rgba(244,63,94,0.6); +} +@media (prefers-color-scheme: light) { + :root { + --primary-color: #06c; + --primary-color-light-1: #0075eb; + --primary-color-light-2: #0a85ff; + --primary-color-dark-1: #005cb8; + --primary-color-dark-2: #0052a3; + --background-color-1: #fff; + --background-color-1-transparent: rgba(255,255,255,0.6); + --background-color-2: #f7f7f7; + --background-color-3: #f0f0f0; + --content-background-color: #fff; + --text-color-1: #484853; + --text-color-2: #4c4c57; + --text-color-3: #50505c; + --text-color-4: #808091; + --text-color-5: #b7b7c0; + --text-color-6: #ededef; + --toc-text-color: #6f6f80; + --badge-color: #f0f0f0; + --badge-background-color: #9393a1; + --border-color: #b3b3b3; + --selection-color: #0075eb; + --shadow-color: rgba(0,0,0,0.16); + --shadow-hover-color: rgba(0,0,0,0.22); + --scrollbar-color: #60606e; + --scrollbar-background-color: #e6e6e6; + --toc-scrollbar-color: rgba(80,80,92,0.1); + --copyright-icon-bg-color: rgba(80,80,92,0.12); + --avatar-background-color: #005cb8; + --header-transparent-background-1: rgba(255,255,255,0.28); + --header-transparent-background-2: rgba(255,255,255,0.4); + --pjax-progress-bar-color: linear-gradient(45deg, #f10006, #ef5b00, #e59c01, #19ca05, #00cab5, #0264c8, #c303c3); + --post-h-bottom-border-color: rgba(80,80,92,0.15); + --article-aging-tips-color: $article-aging-tips-color; + --article-aging-tips-background-color: $article-aging-tips-background-color; + --article-aging-tips-border-color: $article-aging-tips-border-color; + --keep-info-color: rgba(117,117,122,0.8); + --keep-info-background-color: rgba(117,117,122,0.1); + --keep-info-border-color: rgba(117,117,122,0.6); + --keep-primary-color: #0458ab; + --keep-primary-background-color: rgba(4,88,171,0.1); + --keep-primary-border-color: rgba(4,88,171,0.6); + --keep-warning-color: #b78d0f; + --keep-warning-background-color: rgba(183,141,15,0.1); + --keep-warning-border-color: rgba(183,141,15,0.6); + --keep-success-color: #10b981; + --keep-success-background-color: rgba(16,185,129,0.1); + --keep-success-border-color: rgba(16,185,129,0.6); + --keep-danger-color: #f43f5e; + --keep-danger-background-color: rgba(244,63,94,0.1); + --keep-danger-border-color: rgba(244,63,94,0.6); + } +} +@media (prefers-color-scheme: dark) { + :root { + --primary-color: #06c; + --primary-color-light-1: #0075eb; + --primary-color-light-2: #0a85ff; + --primary-color-dark-1: #005cb8; + --primary-color-dark-2: #0052a3; + --background-color-1: #22272e; + --background-color-1-transparent: rgba(34,39,46,0.6); + --background-color-2: #292f38; + --background-color-3: #313842; + --content-background-color: #21262d; + --text-color-1: #b7c2cc; + --text-color-2: #b2bec8; + --text-color-3: #adbac5; + --text-color-4: #6c8397; + --text-color-5: #4d5e6c; + --text-color-6: #2e3841; + --toc-text-color: #8296a6; + --badge-color: #343c47; + --badge-background-color: #ced6dc; + --border-color: #596678; + --selection-color: #005ebc; + --shadow-color: rgba(120,120,120,0.18); + --shadow-hover-color: rgba(120,120,120,0.24); + --scrollbar-color: #1f2329; + --scrollbar-background-color: #47515f; + --toc-scrollbar-color: rgba(173,186,197,0.1); + --copyright-icon-bg-color: rgba(173,186,197,0.12); + --avatar-background-color: #004a93; + --header-transparent-background-1: rgba(34,39,46,0.28); + --header-transparent-background-2: rgba(34,39,46,0.4); + --pjax-progress-bar-color: linear-gradient(45deg, #ea404a, #ea722f, #e9a71f, #67e559, #18ecec, #1b85f1, #ee1dee); + --post-h-bottom-border-color: rgba(173,186,197,0.15); + --article-aging-tips-color: $dark-article-aging-tips-color; + --article-aging-tips-background-color: $dark-article-aging-tips-background-color; + --article-aging-tips-border-color: $dark-article-aging-tips-border-color; + --keep-info-color: #9999a2; + --keep-info-background-color: rgba(153,153,162,0.1); + --keep-info-border-color: rgba(153,153,162,0.5); + --keep-primary-color: #268bef; + --keep-primary-background-color: rgba(38,139,239,0.1); + --keep-primary-border-color: rgba(38,139,239,0.6); + --keep-warning-color: #ecc34d; + --keep-warning-background-color: rgba(236,195,77,0.1); + --keep-warning-border-color: rgba(236,195,77,0.6); + --keep-success-color: #10b981; + --keep-success-background-color: rgba(16,185,129,0.1); + --keep-success-border-color: rgba(16,185,129,0.6); + --keep-danger-color: #f43f5e; + --keep-danger-background-color: rgba(244,63,94,0.1); + --keep-danger-border-color: rgba(244,63,94,0.6); + } +} +.light-mode { + --primary-color: #06c; + --primary-color-light-1: #0075eb; + --primary-color-light-2: #0a85ff; + --primary-color-dark-1: #005cb8; + --primary-color-dark-2: #0052a3; + --background-color-1: #fff; + --background-color-1-transparent: rgba(255,255,255,0.6); + --background-color-2: #f7f7f7; + --background-color-3: #f0f0f0; + --content-background-color: #fff; + --text-color-1: #484853; + --text-color-2: #4c4c57; + --text-color-3: #50505c; + --text-color-4: #808091; + --text-color-5: #b7b7c0; + --text-color-6: #ededef; + --toc-text-color: #6f6f80; + --badge-color: #f0f0f0; + --badge-background-color: #9393a1; + --border-color: #b3b3b3; + --selection-color: #0075eb; + --shadow-color: rgba(0,0,0,0.16); + --shadow-hover-color: rgba(0,0,0,0.22); + --scrollbar-color: #60606e; + --scrollbar-background-color: #e6e6e6; + --toc-scrollbar-color: rgba(80,80,92,0.1); + --copyright-icon-bg-color: rgba(80,80,92,0.12); + --avatar-background-color: #005cb8; + --header-transparent-background-1: rgba(255,255,255,0.28); + --header-transparent-background-2: rgba(255,255,255,0.4); + --pjax-progress-bar-color: linear-gradient(45deg, #f10006, #ef5b00, #e59c01, #19ca05, #00cab5, #0264c8, #c303c3); + --post-h-bottom-border-color: rgba(80,80,92,0.15); + --article-aging-tips-color: $article-aging-tips-color; + --article-aging-tips-background-color: $article-aging-tips-background-color; + --article-aging-tips-border-color: $article-aging-tips-border-color; + --keep-info-color: rgba(117,117,122,0.8); + --keep-info-background-color: rgba(117,117,122,0.1); + --keep-info-border-color: rgba(117,117,122,0.6); + --keep-primary-color: #0458ab; + --keep-primary-background-color: rgba(4,88,171,0.1); + --keep-primary-border-color: rgba(4,88,171,0.6); + --keep-warning-color: #b78d0f; + --keep-warning-background-color: rgba(183,141,15,0.1); + --keep-warning-border-color: rgba(183,141,15,0.6); + --keep-success-color: #10b981; + --keep-success-background-color: rgba(16,185,129,0.1); + --keep-success-border-color: rgba(16,185,129,0.6); + --keep-danger-color: #f43f5e; + --keep-danger-background-color: rgba(244,63,94,0.1); + --keep-danger-border-color: rgba(244,63,94,0.6); +} +.dark-mode { + --primary-color: #06c; + --primary-color-light-1: #0075eb; + --primary-color-light-2: #0a85ff; + --primary-color-dark-1: #005cb8; + --primary-color-dark-2: #0052a3; + --background-color-1: #22272e; + --background-color-1-transparent: rgba(34,39,46,0.6); + --background-color-2: #292f38; + --background-color-3: #313842; + --content-background-color: #21262d; + --text-color-1: #b7c2cc; + --text-color-2: #b2bec8; + --text-color-3: #adbac5; + --text-color-4: #6c8397; + --text-color-5: #4d5e6c; + --text-color-6: #2e3841; + --toc-text-color: #8296a6; + --badge-color: #343c47; + --badge-background-color: #ced6dc; + --border-color: #596678; + --selection-color: #005ebc; + --shadow-color: rgba(120,120,120,0.18); + --shadow-hover-color: rgba(120,120,120,0.24); + --scrollbar-color: #1f2329; + --scrollbar-background-color: #47515f; + --toc-scrollbar-color: rgba(173,186,197,0.1); + --copyright-icon-bg-color: rgba(173,186,197,0.12); + --avatar-background-color: #004a93; + --header-transparent-background-1: rgba(34,39,46,0.28); + --header-transparent-background-2: rgba(34,39,46,0.4); + --pjax-progress-bar-color: linear-gradient(45deg, #ea404a, #ea722f, #e9a71f, #67e559, #18ecec, #1b85f1, #ee1dee); + --post-h-bottom-border-color: rgba(173,186,197,0.15); + --article-aging-tips-color: $dark-article-aging-tips-color; + --article-aging-tips-background-color: $dark-article-aging-tips-background-color; + --article-aging-tips-border-color: $dark-article-aging-tips-border-color; + --keep-info-color: #9999a2; + --keep-info-background-color: rgba(153,153,162,0.1); + --keep-info-border-color: rgba(153,153,162,0.5); + --keep-primary-color: #268bef; + --keep-primary-background-color: rgba(38,139,239,0.1); + --keep-primary-border-color: rgba(38,139,239,0.6); + --keep-warning-color: #ecc34d; + --keep-warning-background-color: rgba(236,195,77,0.1); + --keep-warning-border-color: rgba(236,195,77,0.6); + --keep-success-color: #10b981; + --keep-success-background-color: rgba(16,185,129,0.1); + --keep-success-border-color: rgba(16,185,129,0.6); + --keep-danger-color: #f43f5e; + --keep-danger-background-color: rgba(244,63,94,0.1); + --keep-danger-border-color: rgba(244,63,94,0.6); +} +* { + transition-delay: 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s; + transition-property: color, background, box-shadow, border-color; +} +*::-webkit-scrollbar { + width: 0.4rem; + height: 0.4rem; + transition: all 0.2s ease; +} +*::-webkit-scrollbar-thumb { + background: var(--scrollbar-color); + border-radius: 0.1rem; +} +*::-webkit-scrollbar-track { + background: var(--scrollbar-background-color); +} +html, +body { + position: relative; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + color: var(--text-color-3); + font-weight: var(--base-font-weight); + font-size: var(--base-font-size); + font-family: var(--base-font-family); + line-height: var(--base-line-height); + letter-spacing: 0.2px; + background: var(--background-color-1); +} +html::-webkit-scrollbar, +body::-webkit-scrollbar { + width: 0.6rem; + height: 0.6rem; +} +@media (max-width: 800px) { + html::-webkit-scrollbar, + body::-webkit-scrollbar { + width: 0.5rem; + height: 0.5rem; + } +} +@media (max-width: 500px) { + html::-webkit-scrollbar, + body::-webkit-scrollbar { + width: 0.4rem; + height: 0.4rem; + } +} +@media (max-width: 800px) { + html, + body { + font-size: calc(var(--base-font-size) * 0.95) !important; + line-height: calc(var(--base-line-height) * 0.95) !important; + } +} +@media (max-width: 500px) { + html, + body { + font-size: calc(var(--base-font-size) * 0.9) !important; + line-height: calc(var(--base-line-height) * 0.9) !important; + } +} +::selection { + color: #fff; + background: var(--selection-color); +} +ul, +ol, +li { + margin: 0; + padding: 0; + list-style: none; +} +a { + color: var(--text-color-3); + text-decoration: none; +} +a i, +a span { + color: var(--text-color-3); +} +a:hover, +a:active { + color: var(--primary-color); + text-decoration: none !important; +} +a:hover i, +a:active i, +a:hover span, +a:active span { + color: var(--primary-color); +} +.dark-mode img { + filter: brightness(0.9); +} +.dark-mode img:hover { + filter: brightness(1); +} +img[lazyload] { + position: relative; + box-sizing: border-box; + width: 8rem; + height: 8rem; + box-shadow: none !important; + cursor: not-allowed; + pointer-events: none; +} +img[lazyload]::before { + position: absolute; + top: 0; + left: 0; + display: block; + width: 100%; + height: 100%; + background: var(--background-color-1); + content: ''; + transition-delay: 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.2s; + transition-property: color, background, box-shadow, border-color, background; +} +img[lazyload]::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: block; + width: 2rem; + height: 2rem; + margin: auto; + border: 2px solid var(--text-color-6); + border-top-color: var(--selection-color); + border-left-color: var(--selection-color); + border-radius: 50%; + animation: img-loading-animation 750ms infinite linear; + content: ''; + transition-delay: 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.2s; + transition-property: color, background, box-shadow, border-color, border; +} +.flex-center { + display: flex; + align-items: center; + justify-content: center; +} +.border-box { + position: relative; + box-sizing: border-box; +} +.text-ellipsis { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.clear::after { + display: block; + clear: both; + height: 0; + overflow: hidden; + visibility: hidden; + content: ''; +} +.tooltip { + position: relative; + box-sizing: border-box; +} +.tooltip:hover .tooltip-content { + display: inline-block; +} +.tooltip.show-img .tooltip-content { + display: none !important; +} +.tooltip .tooltip-content { + position: absolute; + top: -0.4rem; + left: 50%; + z-index: 1010; + display: none; + box-sizing: border-box; + padding: 0.2rem 0.6rem; + color: var(--text-color-6); + font-size: 0.8rem; + letter-spacing: 0.8px; + white-space: nowrap; + background: var(--text-color-1); + border-radius: 0.3rem; + transform: translateX(-50%) translateY(-100%); + transition-delay: 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.2s; + transition-property: color, background, box-shadow, border-color, display; + -moz-user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; +} +.tooltip-img { + position: relative; + box-sizing: border-box; +} +.tooltip-img.show-img .tooltip-img-box { + display: flex; +} +.tooltip-img .tooltip-img-box { + position: absolute; + top: -0.4rem; + left: 50%; + z-index: 1011; + display: none; + align-items: center; + justify-content: center; + box-sizing: border-box; + min-height: 6rem; + overflow: hidden; + background: var(--background-color-3); + border: 0.2rem solid var(--text-color-4); + border-radius: 0.3rem; + transform: translateX(-50%) translateY(-100%); + transition-delay: 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.2s; + transition-property: color, background, box-shadow, border-color, display; + -moz-user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; +} +.tooltip-img .tooltip-img-box.has-tip { + flex-direction: column; + justify-content: space-between; +} +.tooltip-img .tooltip-img-box img { + display: block; + max-height: 10rem; +} +.tooltip-img .tooltip-img-box .tip { + position: relative; + box-sizing: border-box; + width: 100%; + padding: 0.3rem 0; + color: #555; + font-size: 0.9rem; + text-align: center; +} diff --git a/css/common/code-block/code-block.css b/css/common/code-block/code-block.css new file mode 100644 index 0000000..01b7b80 --- /dev/null +++ b/css/common/code-block/code-block.css @@ -0,0 +1,100 @@ +.highlight-container { + position: relative; + box-sizing: border-box; + margin: 1.4rem 0; +} +.highlight-container.mac { + margin: 1.4rem 0 1.8rem 0; + box-shadow: 0 0.8rem 2rem 0 rgba(0,0,0,0.4); +} +.highlight-container.mac:hover .code-tools-box .copy { + opacity: 1; +} +.highlight-container.mac .code-tools-box { + justify-content: flex-end; + padding: 0.4rem 0.6rem 0.7rem 0.4rem; + background: var(--mac-toolbar-background-color); +} +.highlight-container.mac .code-tools-box::before { + position: absolute; + left: 0.8rem; + width: 0.76rem; + height: 0.76rem; + background: #fc625d; + border-radius: 50%; + box-shadow: 1.3rem 0 #fdbc40, 2.6rem 0 #35cd4b; + content: ''; +} +.highlight-container.mac .code-tools-box.folded { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.highlight-container.mac .code-tools-box.folded .copy { + display: none; +} +.highlight-container.mac .code-tools-box .code-lang { + order: 1; + color: #bbb; +} +.highlight-container.mac .code-tools-box .fold { + order: 2; + padding: 0 0.1rem 0 0.6rem; +} +.highlight-container.mac .code-tools-box .fold i { + color: #ccc; +} +.highlight-container.mac .code-tools-box .copy { + position: absolute; + top: 3rem; + right: 0.5rem; + box-sizing: border-box; + padding: 0 0.1rem; + opacity: 0; +} +.highlight-container.mac .code-tools-box .copy i { + font-size: 1rem; +} +.highlight-container .code-tools-box { + position: relative; + z-index: $z-index-1; + display: flex; + align-items: center; + justify-content: space-between; + box-sizing: border-box; + width: 100%; + padding: 0.3rem 0.4rem; + color: var(--toolbar-foreground); + background: var(--toolbar-background); + border-top-left-radius: 0.3rem; + border-top-right-radius: 0.3rem; +} +.highlight-container .code-tools-box.folded { + border-bottom-right-radius: 0.3rem; + border-bottom-left-radius: 0.3rem; +} +.highlight-container .code-tools-box .code-lang { + justify-content: flex-start; + margin-left: 0.2rem; + font-weight: 600; + font-size: 0.9rem; + font-family: "Source Code Pro", consolas, Menlo; + color: var(--text-color-3); +} +.highlight-container .code-tools-box .tool { + cursor: pointer; +} +.highlight-container .code-tools-box .tool i { + font-size: 0.8rem; + color: var(--text-color-3); +} +.highlight-container .code-tools-box .fold { + padding: 0 0.4rem 0 0.2rem; +} +.highlight-container figure.highlight { + position: relative; + box-sizing: border-box; + margin: 0; +} +.highlight-container figure.highlight.folded { + height: 0; +} diff --git a/css/common/code-block/code-theme.css b/css/common/code-block/code-theme.css new file mode 100644 index 0000000..00289c0 --- /dev/null +++ b/css/common/code-block/code-theme.css @@ -0,0 +1,99 @@ +:root { + --toolbar-foreground: #50505c; + --toolbar-background: #f0f0f0; + --code-foreground: #535360; + --code-background: #f2f2f2; + --highlight-background: #fafafa; + --highlight-foreground: #535360; + --highlight-comment: #8e908c; + --highlight-red: #c82829; + --highlight-orange: #f5871f; + --highlight-yellow: #eab700; + --highlight-green: #718c00; + --highlight-aqua: #3e999f; + --highlight-blue: #4271ae; + --highlight-purple: #8959a8; + --highlight-gutter-color: #585865; + --highlight-gutter-bg-color: #f2f2f2; + --mac-toolbar-background-color: #21252b; +} +@media (prefers-color-scheme: light) { + :root { + --toolbar-foreground: #50505c; + --toolbar-background: #f0f0f0; + --code-foreground: #535360; + --code-background: #f2f2f2; + --highlight-background: #fafafa; + --highlight-foreground: #535360; + --highlight-comment: #8e908c; + --highlight-red: #c82829; + --highlight-orange: #f5871f; + --highlight-yellow: #eab700; + --highlight-green: #718c00; + --highlight-aqua: #3e999f; + --highlight-blue: #4271ae; + --highlight-purple: #8959a8; + --highlight-gutter-color: #585865; + --highlight-gutter-bg-color: #f2f2f2; + --mac-toolbar-background-color: #21252b; + } +} +@media (prefers-color-scheme: dark) { + :root { + --toolbar-foreground: #adbac5; + --toolbar-background: #313842; + --code-foreground: #afbbc6; + --code-background: #1f2329; + --highlight-background: #1f242a; + --highlight-foreground: #afbbc6; + --highlight-comment: #969896; + --highlight-red: #c66; + --highlight-orange: #de935f; + --highlight-yellow: #f0c674; + --highlight-green: #b5bd68; + --highlight-aqua: #8abeb7; + --highlight-blue: #81a2be; + --highlight-purple: #b294bb; + --highlight-gutter-color: #b1bdc8; + --highlight-gutter-bg-color: #1f2329; + --mac-toolbar-background-color: #1f2021; + } +} +.light-mode { + --toolbar-foreground: #50505c; + --toolbar-background: #f0f0f0; + --code-foreground: #535360; + --code-background: #f2f2f2; + --highlight-background: #fafafa; + --highlight-foreground: #535360; + --highlight-comment: #8e908c; + --highlight-red: #c82829; + --highlight-orange: #f5871f; + --highlight-yellow: #eab700; + --highlight-green: #718c00; + --highlight-aqua: #3e999f; + --highlight-blue: #4271ae; + --highlight-purple: #8959a8; + --highlight-gutter-color: #585865; + --highlight-gutter-bg-color: #f2f2f2; + --mac-toolbar-background-color: #21252b; +} +.dark-mode { + --toolbar-foreground: #adbac5; + --toolbar-background: #313842; + --code-foreground: #afbbc6; + --code-background: #1f2329; + --highlight-background: #1f242a; + --highlight-foreground: #afbbc6; + --highlight-comment: #969896; + --highlight-red: #c66; + --highlight-orange: #de935f; + --highlight-yellow: #f0c674; + --highlight-green: #b5bd68; + --highlight-aqua: #8abeb7; + --highlight-blue: #81a2be; + --highlight-purple: #b294bb; + --highlight-gutter-color: #b1bdc8; + --highlight-gutter-bg-color: #1f2329; + --mac-toolbar-background-color: #1f2021; +} diff --git a/css/common/code-block/highlight.css b/css/common/code-block/highlight.css new file mode 100644 index 0000000..4e099e3 --- /dev/null +++ b/css/common/code-block/highlight.css @@ -0,0 +1,273 @@ +:root { + --toolbar-foreground: #50505c; + --toolbar-background: #f0f0f0; + --code-foreground: #535360; + --code-background: #f2f2f2; + --highlight-background: #fafafa; + --highlight-foreground: #535360; + --highlight-comment: #8e908c; + --highlight-red: #c82829; + --highlight-orange: #f5871f; + --highlight-yellow: #eab700; + --highlight-green: #718c00; + --highlight-aqua: #3e999f; + --highlight-blue: #4271ae; + --highlight-purple: #8959a8; + --highlight-gutter-color: #585865; + --highlight-gutter-bg-color: #f2f2f2; + --mac-toolbar-background-color: #21252b; +} +@media (prefers-color-scheme: light) { + :root { + --toolbar-foreground: #50505c; + --toolbar-background: #f0f0f0; + --code-foreground: #535360; + --code-background: #f2f2f2; + --highlight-background: #fafafa; + --highlight-foreground: #535360; + --highlight-comment: #8e908c; + --highlight-red: #c82829; + --highlight-orange: #f5871f; + --highlight-yellow: #eab700; + --highlight-green: #718c00; + --highlight-aqua: #3e999f; + --highlight-blue: #4271ae; + --highlight-purple: #8959a8; + --highlight-gutter-color: #585865; + --highlight-gutter-bg-color: #f2f2f2; + --mac-toolbar-background-color: #21252b; + } +} +@media (prefers-color-scheme: dark) { + :root { + --toolbar-foreground: #adbac5; + --toolbar-background: #313842; + --code-foreground: #afbbc6; + --code-background: #1f2329; + --highlight-background: #1f242a; + --highlight-foreground: #afbbc6; + --highlight-comment: #969896; + --highlight-red: #c66; + --highlight-orange: #de935f; + --highlight-yellow: #f0c674; + --highlight-green: #b5bd68; + --highlight-aqua: #8abeb7; + --highlight-blue: #81a2be; + --highlight-purple: #b294bb; + --highlight-gutter-color: #b1bdc8; + --highlight-gutter-bg-color: #1f2329; + --mac-toolbar-background-color: #1f2021; + } +} +.light-mode { + --toolbar-foreground: #50505c; + --toolbar-background: #f0f0f0; + --code-foreground: #535360; + --code-background: #f2f2f2; + --highlight-background: #fafafa; + --highlight-foreground: #535360; + --highlight-comment: #8e908c; + --highlight-red: #c82829; + --highlight-orange: #f5871f; + --highlight-yellow: #eab700; + --highlight-green: #718c00; + --highlight-aqua: #3e999f; + --highlight-blue: #4271ae; + --highlight-purple: #8959a8; + --highlight-gutter-color: #585865; + --highlight-gutter-bg-color: #f2f2f2; + --mac-toolbar-background-color: #21252b; +} +.dark-mode { + --toolbar-foreground: #adbac5; + --toolbar-background: #313842; + --code-foreground: #afbbc6; + --code-background: #1f2329; + --highlight-background: #1f242a; + --highlight-foreground: #afbbc6; + --highlight-comment: #969896; + --highlight-red: #c66; + --highlight-orange: #de935f; + --highlight-yellow: #f0c674; + --highlight-green: #b5bd68; + --highlight-aqua: #8abeb7; + --highlight-blue: #81a2be; + --highlight-purple: #b294bb; + --highlight-gutter-color: #b1bdc8; + --highlight-gutter-bg-color: #1f2329; + --mac-toolbar-background-color: #1f2021; +} +.code-block, +pre, +.highlight { + margin: 1.5rem 0; + padding: 0; + overflow: auto; + color: var(--highlight-foreground); + font-size: 0.96rem; + line-height: 1.5rem; + background: var(--highlight-background); +} +pre, +code { + font-family: "Source Code Pro", consolas, Menlo; +} +code { + padding: 0.4rem; + color: var(--code-foreground); + font-size: 0.96rem; + word-wrap: break-word; + background: var(--code-background); + border-radius: 0.2rem; +} +pre { + padding: 0.6rem; +} +pre code { + padding: 0; + color: var(--highlight-foreground); + text-shadow: none; + background: none; +} +.highlight { + border-bottom-right-radius: 0.1rem; + border-bottom-left-radius: 0.1rem; +} +.highlight pre { + margin: 0; + padding: 0.6rem 0; + border: none; +} +.highlight table { + width: auto; + margin: 0; + border: none; + border-spacing: unset; +} +.highlight td { + padding: 0; + border: none; +} +.highlight figcaption { + box-sizing: border-box; + padding: 0.5rem; + color: var(--highlight-foreground); + font-size: 1rem; + line-height: 1rem; +} +.highlight figcaption a { + float: right; + color: var(--highlight-foreground); + font-size: 0.9rem; +} +.highlight figcaption a:hover { + border-bottom-color: var(--highlight-foreground); +} +.highlight .gutter pre { + padding-right: 0.6rem; + padding-left: 0.6rem; + color: var(--highlight-gutter-color); + text-align: center; + background-color: var(--highlight-gutter-bg-color); +} +.highlight .code pre { + width: 100%; + padding-right: 0.6rem; + padding-left: 0.6rem; + background-color: var(--highlight-background); +} +.highlight .line { + height: 1.5rem; + color: var(--highlight-foreground); +} +.highlight .line .language-javascript { + color: var(--highlight-foreground); +} +.highlight .line .attr { + color: var(--highlight-foreground); +} +.highlight .line .string { + color: var(--highlight-foreground); +} +.gutter { + color: var(--highlight-gutter-color); + background: var(--highlight-gutter-bg-color); +} +.gutter .line { + color: var(--highlight-gutter-color); +} +.gist table { + width: auto; +} +.gist table td { + border: none; +} +pre .deletion { + background: var(--highlight-deletion); +} +pre .addition { + background: var(--highlight-addition); +} +pre .meta { + color: var(--highlight-purple); +} +pre .comment { + color: var(--highlight-comment); +} +pre .variable, +pre .attribute, +pre .tag, +pre .regexp, +pre .ruby .constant, +pre .xml .tag .title, +pre .xml .pi, +pre .xml .doctype, +pre .html .doctype, +pre .css .id, +pre .css .class, +pre .css .pseudo { + color: var(--highlight-red); +} +pre .property { + color: var(--highlight-blue); +} +pre .number, +pre .preprocessor, +pre .built_in, +pre .literal, +pre .params, +pre .constant, +pre .command { + color: var(--highlight-orange); +} +pre .ruby .class .title, +pre .css .rules .attribute, +pre .string, +pre .value, +pre .inheritance, +pre .header, +pre .ruby .symbol, +pre .xml .cdata, +pre .special, +pre .number, +pre .formula { + color: var(--highlight-green); +} +pre .title, +pre .css .hexcolor { + color: var(--highlight-aqua); +} +pre .function, +pre .python .decorator, +pre .python .title, +pre .ruby .function .title, +pre .ruby .title .keyword, +pre .perl .sub, +pre .javascript .title, +pre .coffeescript .title { + color: var(--highlight-blue); +} +pre .keyword, +pre .javascript .function { + color: var(--highlight-purple); +} diff --git a/css/common/css-variables.css b/css/common/css-variables.css new file mode 100644 index 0000000..8939f15 --- /dev/null +++ b/css/common/css-variables.css @@ -0,0 +1,26 @@ +:root { + --base-font-size: 15.2px; + --base-line-height: 22px; + --base-font-weight: 400; + --base-font-family: Optima-Regular, Optima, PingFang SC, Microsoft YaHei, sans-serif; + --page-content-width: 80%; + --page-content-width-tablet: 88%; + --page-content-width-mobile: 90%; + --page-content-max-width: 928px; + --page-content-max-width-2: calc(var(--page-content-max-width) * 1.2); + --components-spacing: 36px; + --box-border-radius: 8px; + --header-height: 70px; + --header-shrink-height: calc(var(--header-height) * 0.72); + --header-backdrop-filter-blur: 4px; + --header-scroll-progress-bar-height: 2PX; + --first-screen-font-size: 2rem; + --first-screen-icon-size: 1.8rem; + --first-screen-font-color-light: #50505c; + --first-screen-font-color-dark: #adbac5; + --home-post-hover-scale: 1; + --post-author-avatar: block; + --post-create-datetime: flex; + --post-update-datetime: flex; + --post-img-align: 0 auto 0 0; +} diff --git a/css/common/keep-style.css b/css/common/keep-style.css new file mode 100644 index 0000000..92f64dd --- /dev/null +++ b/css/common/keep-style.css @@ -0,0 +1,264 @@ +:root { + --primary-color: #06c; + --primary-color-light-1: #0075eb; + --primary-color-light-2: #0a85ff; + --primary-color-dark-1: #005cb8; + --primary-color-dark-2: #0052a3; + --background-color-1: #fff; + --background-color-1-transparent: rgba(255,255,255,0.6); + --background-color-2: #f7f7f7; + --background-color-3: #f0f0f0; + --content-background-color: #fff; + --text-color-1: #484853; + --text-color-2: #4c4c57; + --text-color-3: #50505c; + --text-color-4: #808091; + --text-color-5: #b7b7c0; + --text-color-6: #ededef; + --toc-text-color: #6f6f80; + --badge-color: #f0f0f0; + --badge-background-color: #9393a1; + --border-color: #b3b3b3; + --selection-color: #0075eb; + --shadow-color: rgba(0,0,0,0.16); + --shadow-hover-color: rgba(0,0,0,0.22); + --scrollbar-color: #60606e; + --scrollbar-background-color: #e6e6e6; + --toc-scrollbar-color: rgba(80,80,92,0.1); + --copyright-icon-bg-color: rgba(80,80,92,0.12); + --avatar-background-color: #005cb8; + --header-transparent-background-1: rgba(255,255,255,0.28); + --header-transparent-background-2: rgba(255,255,255,0.4); + --pjax-progress-bar-color: linear-gradient(45deg, #f10006, #ef5b00, #e59c01, #19ca05, #00cab5, #0264c8, #c303c3); + --post-h-bottom-border-color: rgba(80,80,92,0.15); + --article-aging-tips-color: $article-aging-tips-color; + --article-aging-tips-background-color: $article-aging-tips-background-color; + --article-aging-tips-border-color: $article-aging-tips-border-color; + --keep-info-color: rgba(117,117,122,0.8); + --keep-info-background-color: rgba(117,117,122,0.1); + --keep-info-border-color: rgba(117,117,122,0.6); + --keep-primary-color: #0458ab; + --keep-primary-background-color: rgba(4,88,171,0.1); + --keep-primary-border-color: rgba(4,88,171,0.6); + --keep-warning-color: #b78d0f; + --keep-warning-background-color: rgba(183,141,15,0.1); + --keep-warning-border-color: rgba(183,141,15,0.6); + --keep-success-color: #10b981; + --keep-success-background-color: rgba(16,185,129,0.1); + --keep-success-border-color: rgba(16,185,129,0.6); + --keep-danger-color: #f43f5e; + --keep-danger-background-color: rgba(244,63,94,0.1); + --keep-danger-border-color: rgba(244,63,94,0.6); +} +@media (prefers-color-scheme: light) { + :root { + --primary-color: #06c; + --primary-color-light-1: #0075eb; + --primary-color-light-2: #0a85ff; + --primary-color-dark-1: #005cb8; + --primary-color-dark-2: #0052a3; + --background-color-1: #fff; + --background-color-1-transparent: rgba(255,255,255,0.6); + --background-color-2: #f7f7f7; + --background-color-3: #f0f0f0; + --content-background-color: #fff; + --text-color-1: #484853; + --text-color-2: #4c4c57; + --text-color-3: #50505c; + --text-color-4: #808091; + --text-color-5: #b7b7c0; + --text-color-6: #ededef; + --toc-text-color: #6f6f80; + --badge-color: #f0f0f0; + --badge-background-color: #9393a1; + --border-color: #b3b3b3; + --selection-color: #0075eb; + --shadow-color: rgba(0,0,0,0.16); + --shadow-hover-color: rgba(0,0,0,0.22); + --scrollbar-color: #60606e; + --scrollbar-background-color: #e6e6e6; + --toc-scrollbar-color: rgba(80,80,92,0.1); + --copyright-icon-bg-color: rgba(80,80,92,0.12); + --avatar-background-color: #005cb8; + --header-transparent-background-1: rgba(255,255,255,0.28); + --header-transparent-background-2: rgba(255,255,255,0.4); + --pjax-progress-bar-color: linear-gradient(45deg, #f10006, #ef5b00, #e59c01, #19ca05, #00cab5, #0264c8, #c303c3); + --post-h-bottom-border-color: rgba(80,80,92,0.15); + --article-aging-tips-color: $article-aging-tips-color; + --article-aging-tips-background-color: $article-aging-tips-background-color; + --article-aging-tips-border-color: $article-aging-tips-border-color; + --keep-info-color: rgba(117,117,122,0.8); + --keep-info-background-color: rgba(117,117,122,0.1); + --keep-info-border-color: rgba(117,117,122,0.6); + --keep-primary-color: #0458ab; + --keep-primary-background-color: rgba(4,88,171,0.1); + --keep-primary-border-color: rgba(4,88,171,0.6); + --keep-warning-color: #b78d0f; + --keep-warning-background-color: rgba(183,141,15,0.1); + --keep-warning-border-color: rgba(183,141,15,0.6); + --keep-success-color: #10b981; + --keep-success-background-color: rgba(16,185,129,0.1); + --keep-success-border-color: rgba(16,185,129,0.6); + --keep-danger-color: #f43f5e; + --keep-danger-background-color: rgba(244,63,94,0.1); + --keep-danger-border-color: rgba(244,63,94,0.6); + } +} +@media (prefers-color-scheme: dark) { + :root { + --primary-color: #06c; + --primary-color-light-1: #0075eb; + --primary-color-light-2: #0a85ff; + --primary-color-dark-1: #005cb8; + --primary-color-dark-2: #0052a3; + --background-color-1: #22272e; + --background-color-1-transparent: rgba(34,39,46,0.6); + --background-color-2: #292f38; + --background-color-3: #313842; + --content-background-color: #21262d; + --text-color-1: #b7c2cc; + --text-color-2: #b2bec8; + --text-color-3: #adbac5; + --text-color-4: #6c8397; + --text-color-5: #4d5e6c; + --text-color-6: #2e3841; + --toc-text-color: #8296a6; + --badge-color: #343c47; + --badge-background-color: #ced6dc; + --border-color: #596678; + --selection-color: #005ebc; + --shadow-color: rgba(120,120,120,0.18); + --shadow-hover-color: rgba(120,120,120,0.24); + --scrollbar-color: #1f2329; + --scrollbar-background-color: #47515f; + --toc-scrollbar-color: rgba(173,186,197,0.1); + --copyright-icon-bg-color: rgba(173,186,197,0.12); + --avatar-background-color: #004a93; + --header-transparent-background-1: rgba(34,39,46,0.28); + --header-transparent-background-2: rgba(34,39,46,0.4); + --pjax-progress-bar-color: linear-gradient(45deg, #ea404a, #ea722f, #e9a71f, #67e559, #18ecec, #1b85f1, #ee1dee); + --post-h-bottom-border-color: rgba(173,186,197,0.15); + --article-aging-tips-color: $dark-article-aging-tips-color; + --article-aging-tips-background-color: $dark-article-aging-tips-background-color; + --article-aging-tips-border-color: $dark-article-aging-tips-border-color; + --keep-info-color: #9999a2; + --keep-info-background-color: rgba(153,153,162,0.1); + --keep-info-border-color: rgba(153,153,162,0.5); + --keep-primary-color: #268bef; + --keep-primary-background-color: rgba(38,139,239,0.1); + --keep-primary-border-color: rgba(38,139,239,0.6); + --keep-warning-color: #ecc34d; + --keep-warning-background-color: rgba(236,195,77,0.1); + --keep-warning-border-color: rgba(236,195,77,0.6); + --keep-success-color: #10b981; + --keep-success-background-color: rgba(16,185,129,0.1); + --keep-success-border-color: rgba(16,185,129,0.6); + --keep-danger-color: #f43f5e; + --keep-danger-background-color: rgba(244,63,94,0.1); + --keep-danger-border-color: rgba(244,63,94,0.6); + } +} +.light-mode { + --primary-color: #06c; + --primary-color-light-1: #0075eb; + --primary-color-light-2: #0a85ff; + --primary-color-dark-1: #005cb8; + --primary-color-dark-2: #0052a3; + --background-color-1: #fff; + --background-color-1-transparent: rgba(255,255,255,0.6); + --background-color-2: #f7f7f7; + --background-color-3: #f0f0f0; + --content-background-color: #fff; + --text-color-1: #484853; + --text-color-2: #4c4c57; + --text-color-3: #50505c; + --text-color-4: #808091; + --text-color-5: #b7b7c0; + --text-color-6: #ededef; + --toc-text-color: #6f6f80; + --badge-color: #f0f0f0; + --badge-background-color: #9393a1; + --border-color: #b3b3b3; + --selection-color: #0075eb; + --shadow-color: rgba(0,0,0,0.16); + --shadow-hover-color: rgba(0,0,0,0.22); + --scrollbar-color: #60606e; + --scrollbar-background-color: #e6e6e6; + --toc-scrollbar-color: rgba(80,80,92,0.1); + --copyright-icon-bg-color: rgba(80,80,92,0.12); + --avatar-background-color: #005cb8; + --header-transparent-background-1: rgba(255,255,255,0.28); + --header-transparent-background-2: rgba(255,255,255,0.4); + --pjax-progress-bar-color: linear-gradient(45deg, #f10006, #ef5b00, #e59c01, #19ca05, #00cab5, #0264c8, #c303c3); + --post-h-bottom-border-color: rgba(80,80,92,0.15); + --article-aging-tips-color: $article-aging-tips-color; + --article-aging-tips-background-color: $article-aging-tips-background-color; + --article-aging-tips-border-color: $article-aging-tips-border-color; + --keep-info-color: rgba(117,117,122,0.8); + --keep-info-background-color: rgba(117,117,122,0.1); + --keep-info-border-color: rgba(117,117,122,0.6); + --keep-primary-color: #0458ab; + --keep-primary-background-color: rgba(4,88,171,0.1); + --keep-primary-border-color: rgba(4,88,171,0.6); + --keep-warning-color: #b78d0f; + --keep-warning-background-color: rgba(183,141,15,0.1); + --keep-warning-border-color: rgba(183,141,15,0.6); + --keep-success-color: #10b981; + --keep-success-background-color: rgba(16,185,129,0.1); + --keep-success-border-color: rgba(16,185,129,0.6); + --keep-danger-color: #f43f5e; + --keep-danger-background-color: rgba(244,63,94,0.1); + --keep-danger-border-color: rgba(244,63,94,0.6); +} +.dark-mode { + --primary-color: #06c; + --primary-color-light-1: #0075eb; + --primary-color-light-2: #0a85ff; + --primary-color-dark-1: #005cb8; + --primary-color-dark-2: #0052a3; + --background-color-1: #22272e; + --background-color-1-transparent: rgba(34,39,46,0.6); + --background-color-2: #292f38; + --background-color-3: #313842; + --content-background-color: #21262d; + --text-color-1: #b7c2cc; + --text-color-2: #b2bec8; + --text-color-3: #adbac5; + --text-color-4: #6c8397; + --text-color-5: #4d5e6c; + --text-color-6: #2e3841; + --toc-text-color: #8296a6; + --badge-color: #343c47; + --badge-background-color: #ced6dc; + --border-color: #596678; + --selection-color: #005ebc; + --shadow-color: rgba(120,120,120,0.18); + --shadow-hover-color: rgba(120,120,120,0.24); + --scrollbar-color: #1f2329; + --scrollbar-background-color: #47515f; + --toc-scrollbar-color: rgba(173,186,197,0.1); + --copyright-icon-bg-color: rgba(173,186,197,0.12); + --avatar-background-color: #004a93; + --header-transparent-background-1: rgba(34,39,46,0.28); + --header-transparent-background-2: rgba(34,39,46,0.4); + --pjax-progress-bar-color: linear-gradient(45deg, #ea404a, #ea722f, #e9a71f, #67e559, #18ecec, #1b85f1, #ee1dee); + --post-h-bottom-border-color: rgba(173,186,197,0.15); + --article-aging-tips-color: $dark-article-aging-tips-color; + --article-aging-tips-background-color: $dark-article-aging-tips-background-color; + --article-aging-tips-border-color: $dark-article-aging-tips-border-color; + --keep-info-color: #9999a2; + --keep-info-background-color: rgba(153,153,162,0.1); + --keep-info-border-color: rgba(153,153,162,0.5); + --keep-primary-color: #268bef; + --keep-primary-background-color: rgba(38,139,239,0.1); + --keep-primary-border-color: rgba(38,139,239,0.6); + --keep-warning-color: #ecc34d; + --keep-warning-background-color: rgba(236,195,77,0.1); + --keep-warning-border-color: rgba(236,195,77,0.6); + --keep-success-color: #10b981; + --keep-success-background-color: rgba(16,185,129,0.1); + --keep-success-border-color: rgba(16,185,129,0.6); + --keep-danger-color: #f43f5e; + --keep-danger-background-color: rgba(244,63,94,0.1); + --keep-danger-border-color: rgba(244,63,94,0.6); +} diff --git a/css/common/markdown.css b/css/common/markdown.css new file mode 100644 index 0000000..7f4b1ca --- /dev/null +++ b/css/common/markdown.css @@ -0,0 +1,237 @@ +.keep-markdown-body { + font-size: 1rem; +} +.keep-markdown-body blockquote { + position: relative; + display: flex; + flex-direction: column; + justify-content: flex-start; + box-sizing: border-box; + margin: 1.4rem 0; + color: var(--text-color-3); + background: var(--background-color-2); + border-left: 0.4rem solid var(--text-color-4); + border-top-left-radius: 0.2rem; + border-bottom-left-radius: 0.2rem; +} +.keep-markdown-body blockquote p, +.keep-markdown-body blockquote ul, +.keep-markdown-body blockquote ol, +.keep-markdown-body blockquote footer { + position: relative; + box-sizing: border-box; + padding: 0.4rem 0.4rem 0.4rem 0.8rem !important; +} +.keep-markdown-body blockquote footer { + margin-bottom: 0.6rem; + font-style: italic; +} +.keep-markdown-body blockquote cite { + position: relative; + box-sizing: border-box; + margin-left: 1rem; + color: var(--text-color-4); +} +.keep-markdown-body blockquote cite::before { + content: '— '; +} +.keep-markdown-body p { + margin: 0.6rem 0; + color: var(--text-color-3); + line-height: 2; +} +.keep-markdown-body a { + position: relative; + box-sizing: border-box; + padding-bottom: 0.2rem; + text-decoration: none; + overflow-wrap: break-word; + border-bottom: 0.1rem solid var(--text-color-4); + outline: 0; + cursor: pointer; +} +.keep-markdown-body a .fas, +.keep-markdown-body a .far, +.keep-markdown-body a .fab { + position: relative; + margin: 0 0.2rem 0 0.4rem; + color: var(--text-color-4); + font-size: 0.88rem; +} +.keep-markdown-body a:hover { + text-decoration: underline; +} +.keep-markdown-body a:hover::after { + background: var(--primary-color); +} +.keep-markdown-body strong { + color: var(--text-color-3); +} +.keep-markdown-body em { + color: var(--text-color-3); +} +.keep-markdown-body ul li, +.keep-markdown-body ol li { + margin-left: 1rem; + line-height: 2rem; +} +.keep-markdown-body ul li { + list-style: disc; +} +.keep-markdown-body ul li ul li { + list-style: circle; +} +.keep-markdown-body ul li ul li ul li { + list-style: square; +} +.keep-markdown-body ol li { + list-style: decimal; +} +.keep-markdown-body ol li ol li { + list-style: upper-alpha; +} +.keep-markdown-body ol li ol li ol li { + list-style: upper-roman; +} +.keep-markdown-body li { + color: var(--text-color-3); +} +.keep-markdown-body h1, +.keep-markdown-body h2, +.keep-markdown-body h3, +.keep-markdown-body h4, +.keep-markdown-body h5, +.keep-markdown-body h6 { + position: relative; + box-sizing: border-box; + padding-top: 0.4rem; + padding-bottom: 0.2rem; + overflow: hidden; + color: var(--text-color-2); + line-height: 1.5; + white-space: nowrap; + text-overflow: ellipsis; + border-bottom: 1px solid var(--post-h-bottom-border-color); +keep-tablet() +} +.keep-markdown-body h1:hover a.headerlink, +.keep-markdown-body h2:hover a.headerlink, +.keep-markdown-body h3:hover a.headerlink, +.keep-markdown-body h4:hover a.headerlink, +.keep-markdown-body h5:hover a.headerlink, +.keep-markdown-body h6:hover a.headerlink { + visibility: visible; +} +.keep-markdown-body h1 a.headerlink, +.keep-markdown-body h2 a.headerlink, +.keep-markdown-body h3 a.headerlink, +.keep-markdown-body h4 a.headerlink, +.keep-markdown-body h5 a.headerlink, +.keep-markdown-body h6 a.headerlink { + position: relative; + float: right; + box-sizing: border-box; + margin-left: 0.32rem; + padding-top: 0.25rem; + color: var(--text-color-4); + font-size: 1.2rem; + text-decoration: none; + border: none; + visibility: hidden; +} +.keep-markdown-body h1 a.headerlink:hover, +.keep-markdown-body h2 a.headerlink:hover, +.keep-markdown-body h3 a.headerlink:hover, +.keep-markdown-body h4 a.headerlink:hover, +.keep-markdown-body h5 a.headerlink:hover, +.keep-markdown-body h6 a.headerlink:hover { + color: var(--text-color-3); +} +.keep-markdown-body h1 a.headerlink::before, +.keep-markdown-body h2 a.headerlink::before, +.keep-markdown-body h3 a.headerlink::before, +.keep-markdown-body h4 a.headerlink::before, +.keep-markdown-body h5 a.headerlink::before, +.keep-markdown-body h6 a.headerlink::before { + font-weight: 600; + font-family: 'Font Awesome 6 Free'; + content: '\f0c1'; +} +.keep-markdown-body h1 { + font-weight: 600; + font-size: 1.8rem; +keep-tablet() +} +.keep-markdown-body h2 { + font-weight: 600; + font-size: 1.7rem; +keep-tablet() +} +.keep-markdown-body h3 { + font-weight: 550; + font-size: 1.6rem; +keep-tablet() +} +.keep-markdown-body h4 { + font-weight: 550; + font-size: 1.5rem; +keep-tablet() +} +.keep-markdown-body h5 { + font-weight: 500; + font-size: 1.28rem; +keep-tablet() +} +.keep-markdown-body h6 { + font-weight: 500; + font-size: 1.2rem; + line-height: 1.2; +keep-tablet() +} +.keep-markdown-body img { + position: relative; + display: block; + box-sizing: border-box; + max-width: 100%; + margin: var(--post-img-align); + margin-bottom: 2rem; + box-shadow: 0 0 0.2rem var(--shadow-color); + cursor: zoom-in; + opacity: 1; +} +.keep-markdown-body img.hide { + opacity: 0; +} +.keep-markdown-body img[lazyload] { + margin: 0.8rem auto 0.2rem; +} +.keep-markdown-body > table { + width: 100%; + overflow: auto; + border-collapse: collapse; + border-spacing: 0; +keep-mobile() +} +.keep-markdown-body > table td, +.keep-markdown-body > table th { + padding: 0; +} +.keep-markdown-body > table th { + font-weight: 600; +} +.keep-markdown-body > table td, +.keep-markdown-body > table th { + padding: 0.4rem 1rem; + border: 0.1rem solid var(--border-color); +} +.keep-markdown-body > table tr { + background-color: var(--background-color-1); + border: 0.1rem solid var(--text-color-6); +} +.keep-markdown-body > table tr:nth-child(2n) { + background-color: var(--background-color-2); +} +.keep-markdown-body .mermaid { + background: #f5f5f5 !important; + border-radius: 0.3rem; +} diff --git a/css/common/stylus-variables.css b/css/common/stylus-variables.css new file mode 100644 index 0000000..e69de29 diff --git a/css/common/tags/keep-button.css b/css/common/tags/keep-button.css new file mode 100644 index 0000000..38e8f46 --- /dev/null +++ b/css/common/tags/keep-button.css @@ -0,0 +1,78 @@ +.keep-button { + position: relative; + box-sizing: border-box; + margin: auto 0.6rem; + padding: 0.6rem 1rem; + color: var(--text-color-3); + font-size: 1rem; + background: var(--background-color-1); + border: none; + border-radius: 0.4rem; + box-shadow: 0.1rem 0.2rem 0.4rem var(--shadow-color); + cursor: pointer; +} +.keep-button i { + color: var(--text-color-3); +} +.keep-button:hover { + color: var(--background-color-1); + background: var(--primary-color); +} +.keep-button:hover i { + color: var(--background-color-1); +} +.keep-button.size-small { + margin: auto 0.3rem; + padding: 0.5rem 0.8rem; + font-size: 0.8rem; + border-radius: 0.3rem; + box-shadow: 0.1rem 0.1rem 0.3rem var(--shadow-color); +} +.keep-button.size-large { + width: 100%; + margin: 0.6rem 0; + padding: 1rem; + font-size: 1.1rem; + border-radius: 0.5rem; + box-shadow: 0.1rem 0.2rem 0.5rem var(--shadow-color); +} +.keep-button.color-info { + color: var(--text-color-3); + background-color: var(--background-color-1); +} +.keep-button.color-info:hover { + color: var(--background-color-1); + background: var(--primary-color); +} +.keep-button.color-primary { + color: var(--keep-primary-color); + background-color: var(--keep-primary-background-color); +} +.keep-button.color-primary:hover { + color: #fff; + background-color: var(--keep-primary-color); +} +.keep-button.color-success { + color: var(--keep-success-color); + background-color: var(--keep-success-background-color); +} +.keep-button.color-success:hover { + color: #fff; + background-color: var(--keep-success-color); +} +.keep-button.color-warning { + color: var(--keep-warning-color); + background-color: var(--keep-warning-background-color); +} +.keep-button.color-warning:hover { + color: #fff; + background-color: var(--keep-warning-color); +} +.keep-button.color-danger { + color: var(--keep-danger-color); + background-color: var(--keep-danger-background-color); +} +.keep-button.color-danger:hover { + color: #fff; + background-color: var(--keep-danger-color); +} diff --git a/css/common/tags/keep-note.css b/css/common/tags/keep-note.css new file mode 100644 index 0000000..143a63c --- /dev/null +++ b/css/common/tags/keep-note.css @@ -0,0 +1,46 @@ +.keep-note { + position: relative; + box-sizing: border-box; + width: 100%; + margin-bottom: 2rem; + padding: 0.8rem 1rem; + font-size: 0.9rem; + border-style: solid; + border-width: 0.1rem; + border-radius: 0.4rem; +} +.keep-note.info { + color: var(--keep-info-color); + background-color: var(--keep-info-background-color); + border-color: var(--keep-info-border-color); +} +.keep-note.primary { + color: var(--keep-primary-color); + background-color: var(--keep-primary-background-color); + border-color: var(--keep-primary-border-color); +} +.keep-note.success { + color: var(--keep-success-color); + background-color: var(--keep-success-background-color); + border-color: var(--keep-success-border-color); +} +.keep-note.warning { + color: var(--keep-warning-color); + background-color: var(--keep-warning-background-color); + border-color: var(--keep-warning-border-color); +} +.keep-note.danger { + color: var(--keep-danger-color); + background-color: var(--keep-danger-background-color); + border-color: var(--keep-danger-border-color); +} +.keep-note .keep-note-title { + box-sizing: border-box; + padding: 0.6rem 0 0 0; + color: inherit; + font-weight: 600; + font-size: 1rem; +} +.keep-note p { + color: inherit; +} diff --git a/css/common/tags/keep-tabs.css b/css/common/tags/keep-tabs.css new file mode 100644 index 0000000..71a09a6 --- /dev/null +++ b/css/common/tags/keep-tabs.css @@ -0,0 +1,65 @@ +.keep-tabs { + position: relative; + box-sizing: border-box; + width: 100%; + height: auto; + background: var(--background-color); + border-radius: 0.4rem; + box-shadow: 0.1rem 0.1rem 0.5rem var(--shadow-color); +} +.keep-tabs .tabs-nav { + position: relative; + display: flex; + justify-content: flex-start; + box-sizing: border-box; + list-style: none; +} +.keep-tabs .tabs-nav::before { + position: absolute; + bottom: 0; + left: 0; + box-sizing: border-box; + width: 100%; + height: 2px; + background: var(--border-color); + content: ''; +} +.keep-tabs .tabs-nav .tab { + position: relative; + box-sizing: border-box; + margin-right: 0.8rem; + padding: 1rem 0.6rem; + overflow: hidden; + color: var(--text-color-3); + cursor: pointer; +} +.keep-tabs .tabs-nav .tab.active { + font-weight: 600; +} +.keep-tabs .tabs-nav .tab.active::before { + position: absolute; + bottom: 0; + left: 50%; + box-sizing: border-box; + width: 100%; + height: 2px; + background: var(--primary-color); + border-radius: 0.2rem; + transform: translateX(-50%); + content: ''; +} +.keep-tabs .tabs-content { + position: relative; + box-sizing: border-box; +} +.keep-tabs .tabs-content .tab-pane { + position: relative; + box-sizing: border-box; + width: 100%; + height: auto; + min-height: 10rem; + padding: 0.6rem 0.8rem; +} +.keep-tabs .tabs-content .tab-pane:not(.active) { + display: none; +} diff --git a/css/layout/archive-content.css b/css/layout/archive-content.css new file mode 100644 index 0000000..e69de29 diff --git a/css/layout/article-content.css b/css/layout/article-content.css new file mode 100644 index 0000000..c088eda --- /dev/null +++ b/css/layout/article-content.css @@ -0,0 +1,272 @@ +.post-page-container { + display: flex; + justify-content: space-between; + width: 100%; + height: 100%; +keep-tablet() +} +.post-page-container.show-toc .pc-post-toc { + display: block; +} +.post-page-container.show-toc .article-content-container { + width: calc(100% - 15rem); +} +.post-page-container .article-content-container { + order: 0; + width: 100%; + height: 100%; +keep-mobile() +} +.post-page-container .article-content-container .article-content-top { + display: flex; + align-items: flex-end; + width: 100%; + padding-top: 2rem; + padding-right: 2rem; + overflow: hidden; + background-color: var(--background-color-2); + border-top-left-radius: var(--box-border-radius); + border-top-right-radius: var(--box-border-radius); +keep-tablet() +keep-mobile() +} +.post-page-container .article-content-container .article-content-top .cover-article-title { + position: relative; + z-index: 1001; + box-sizing: border-box; + max-width: 100%; + max-height: 100%; + padding: 1rem 2rem; + overflow-y: auto; + color: var(--text-color-2); + font-weight: 600; + font-size: 1.76rem; + line-height: 1.6; + background: var(--background-color-1-transparent); + border-top-right-radius: var(--box-border-radius); + backdrop-filter: blur(2px); +} +.post-page-container .article-content-container .article-content-top .post-cover { + position: absolute; + top: 0; + left: 0; + box-sizing: border-box; + width: 100%; + height: 100%; + object-fit: cover; +} +.post-page-container .article-content-container .article-content-bottom { + padding: 2rem; +keep-mobile() +} +.post-page-container .article-content-container .article-content-bottom.has-cover { + padding-top: 1rem; +} +.post-page-container .article-content-container .article-content-bottom .article-title { + color: var(--text-color-2); + font-weight: 600; + font-size: 1.6rem; + line-height: 1.6; +keep-tablet() +keep-mobile() +} +.post-page-container .article-content-container .article-content-bottom .article-header { + display: flex; + justify-content: flex-start; + width: 100%; +} +.post-page-container .article-content-container .article-content-bottom .article-header .avatar-box { + display: var(--post-author-avatar); + flex-shrink: 0; + width: 3.2rem; + height: 3.2rem; + margin-right: 0.8rem; + padding: 0.1rem; + border: 1px solid var(--border-color); + border-radius: 50%; +} +.post-page-container .article-content-container .article-content-bottom .article-header .avatar-box img { + width: 100%; + height: 100%; + background: var(--avatar-background-color); + border-radius: 50%; +} +.post-page-container .article-content-container .article-content-bottom .article-header .info-box { + display: flex; + flex-direction: column; + justify-content: space-between; + box-sizing: border-box; + width: 100%; + height: 100%; + padding: 0.2rem 0; +} +.post-page-container .article-content-container .article-content-bottom .article-header .info-box .author { + display: flex; + align-items: center; + font-weight: 600; + font-size: 1.18rem; +} +.post-page-container .article-content-container .article-content-bottom .article-header .info-box .author .name { + color: var(--text-color-3); +} +.post-page-container .article-content-container .article-content-bottom .article-header .info-box .author .author-label { + margin-left: 0.8rem; + padding: 0 0.4rem; + color: #fff; + font-weight: 500; + font-size: 0.8rem; + background: var(--selection-color); + border-radius: 0.4rem; +} +.post-page-container .article-content-container .article-content-bottom .article-header .info-box .meta-info { + margin-top: 0.1rem; +} +.post-page-container .article-content-container .article-content-bottom .article-header, +.post-page-container .article-content-container .article-content-bottom .article-header-meta-info { + margin-top: 1rem; +keep-tablet() +} +.post-page-container .article-content-container .article-content-bottom .article-content { + margin-top: calc(var(--components-spacing) * 0.8); + padding-bottom: calc(var(--components-spacing) * 0.8); + color: var(--text-color-3); + text-align: justify; + word-wrap: break-word; +keep-tablet() + border-bottom: 2px dashed var(--border-color); +} +.post-page-container .article-content-container .article-content-bottom .article-content .article-aging-tips { + position: relative; + display: none; + box-sizing: border-box; + margin-bottom: 1.8rem; + padding: 1rem; + color: var(--keep-warning-color); + line-height: 1.6; + background: var(--keep-warning-background-color); + border: 0.1rem solid var(--keep-warning-border-color); + border-radius: 0.4rem; +} +.post-page-container .article-content-container .article-content-bottom .article-content .article-aging-tips i { + margin-right: 0.4rem; + color: var(--keep-warning-color); +} +.post-page-container .article-content-container .article-content-bottom .article-content .article-aging-tips .days { + color: var(--keep-warning-color); +} +.post-page-container .article-content-container .article-content-bottom .post-bottom-tags-and-share { + display: flex; + justify-content: space-between; + width: 100%; + margin-top: 1rem; +} +.post-page-container .article-content-container .article-content-bottom .post-bottom-tags-and-share .post-tags-box { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: flex-start; + width: 100%; + font-size: 0.96rem; +} +.post-page-container .article-content-container .article-content-bottom .post-bottom-tags-and-share .post-tags-box .tag-item { + margin-right: 0.4rem; + line-height: 2; +} +.post-page-container .article-content-container .article-content-bottom .post-bottom-tags-and-share .post-tags-box .tag-item .icon { + color: var(--text-color-3); + font-size: 0.88rem; +} +.post-page-container .article-content-container .article-content-bottom .article-nav { + height: 2.8rem; + margin-top: var(--components-spacing); +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-prev, +.post-page-container .article-content-container .article-content-bottom .article-nav .article-next { + box-sizing: border-box; + max-width: 14rem; + height: 100%; + border-radius: 0.32rem; +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-prev a, +.post-page-container .article-content-container .article-content-bottom .article-nav .article-next a { + position: relative; + display: block; + box-sizing: border-box; + width: 100%; + height: 100%; +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-prev a.prev, +.post-page-container .article-content-container .article-content-bottom .article-nav .article-next a.prev { + padding-left: 1rem; +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-prev a.next, +.post-page-container .article-content-container .article-content-bottom .article-nav .article-next a.next { + padding-right: 1rem; +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-prev a .arrow-icon, +.post-page-container .article-content-container .article-content-bottom .article-nav .article-next a .arrow-icon { + position: absolute; + top: 0; + width: 1rem; + height: 100%; +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-prev a .arrow-icon.left, +.post-page-container .article-content-container .article-content-bottom .article-nav .article-next a .arrow-icon.left { + left: 0; +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-prev a .arrow-icon.right, +.post-page-container .article-content-container .article-content-bottom .article-nav .article-next a .arrow-icon.right { + right: 0; +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-prev a .title, +.post-page-container .article-content-container .article-content-bottom .article-nav .article-next a .title { + width: 100%; + height: 100%; +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-prev { + float: left; +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-next { + float: right; +} +.post-page-container .article-content-container .article-content-bottom .article-nav .post-nav-item { + display: none; +keep-tablet() +} +.post-page-container .pc-post-toc { + position: sticky; + top: calc(var(--header-height) + var(--components-spacing)); + display: none; + flex-shrink: 0; + box-sizing: border-box; + width: 15rem; + max-height: calc(100vh - calc(var(--header-height) + var(--components-spacing))); +} +.post-page-container .pc-post-toc.right-toc { + order: 1; + padding: 2rem 0 2rem 1.8rem; +} +.post-page-container .pc-post-toc.left-toc { + order: -1; + padding: 2rem 1.8rem 2rem 0; +} +.header-shrink .post-page-container .pc-post-toc { + top: calc(var(--header-shrink-height) + var(--components-spacing)); + max-height: calc(100vh - calc(var(--header-shrink-height) + var(--components-spacing))); +} +.post-page-container .hbe-container .hbe-input-field { + color: var(--text-color-3) !important; + background: var(--background-color-3) !important; +} +.post-page-container .hbe-container .hbe-input-label::before { + display: none !important; +} +.post-page-container .hbe-container .hbe-input-label::after { + background: var(--text-color-4) !important; +} +.post-page-container .hbe-container .hbe-input-label .hbe-input-label-content { + color: var(--text-color-4) !important; +} +.post-page-container .hbe-container .hbe-button { + margin-top: 2rem; +} diff --git a/css/layout/category-content.css b/css/layout/category-content.css new file mode 100644 index 0000000..bf48bb3 --- /dev/null +++ b/css/layout/category-content.css @@ -0,0 +1,11 @@ +.category-container .category-name { + margin-bottom: var(--components-spacing); + color: var(--text-color-2); + font-weight: 600; + font-size: 1.5rem; +keep-tablet() +keep-mobile() +} +.category-container .category-name i { + color: var(--text-color-2); +} diff --git a/css/layout/category-list.css b/css/layout/category-list.css new file mode 100644 index 0000000..5cec3d2 --- /dev/null +++ b/css/layout/category-list.css @@ -0,0 +1,41 @@ +.category-list-container .category-list-content ul.site-all-category-list { + user-select: none; +} +.category-list-container .category-list-content ul.site-all-category-list > li.site-all-category-list-item { + position: relative; + box-sizing: border-box; + margin-bottom: 0.8rem; + font-size: 1rem; +} +.category-list-container .category-list-content ul.site-all-category-list > li.site-all-category-list-item:last-child { + margin-bottom: 0; +} +.category-list-container .category-list-content ul.site-all-category-list > li.site-all-category-list-item .self-category-info { + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + box-sizing: border-box; + padding: 0.5rem 0; +} +.category-list-container .category-list-content ul.site-all-category-list > li.site-all-category-list-item .self-category-info .left { + position: relative; + box-sizing: border-box; +} +.category-list-container .category-list-content ul.site-all-category-list > li.site-all-category-list-item .self-category-info .left .icon { + padding-right: 0.4rem; + color: var(--text-color-3); + font-size: 0.8rem; + cursor: pointer; +} +.category-list-container .category-list-content ul.site-all-category-list > li.site-all-category-list-item .self-category-info .right .site-all-category-list-count { + color: var(--text-color-3); +} +.category-list-container .category-list-content ul.site-all-category-list > li.site-all-category-list-item .site-all-category-list-child { + position: relative; + box-sizing: border-box; + height: 0; + margin-left: 1.5rem; + overflow: hidden; + visibility: hidden; +} diff --git a/css/layout/home-content.css b/css/layout/home-content.css new file mode 100644 index 0000000..ba42dda --- /dev/null +++ b/css/layout/home-content.css @@ -0,0 +1,74 @@ +.home-content-container { + background: var(--background-color-1); +} +.home-content-container .home-article-list .home-article-item { + position: relative; +} +.home-content-container .home-article-list .home-article-item .post-sticky-box { + position: absolute; + z-index: $z-index-1; + display: flex; + align-items: center; + box-sizing: border-box; + padding: 0.1rem 0.4rem; + color: var(--text-color-4); + font-size: 12px; + letter-spacing: 1px; + background: var(--background-color-3); + border-radius: 0.3rem; + transform: scale(0.86); + cursor: default; +keep-mobile() +} +.home-content-container .home-article-list .home-article-item .post-sticky-box.cover { + top: 0.6rem; + right: 0.6rem; +} +.home-content-container .home-article-list .home-article-item .post-sticky-box.card { + top: 0.8rem; + right: 0.8rem; +} +.home-content-container .home-article-list .home-article-item .home-article-item-top { + width: 100%; + overflow: hidden; + background-color: var(--background-color-2); + border-top-left-radius: var(--box-border-radius); + border-top-right-radius: var(--box-border-radius); +keep-tablet() +keep-mobile() +} +.home-content-container .home-article-list .home-article-item .home-article-item-top .home-cover { + width: 100%; + height: 100%; + object-fit: cover; + transform-origin: center center; +} +.home-content-container .home-article-list .home-article-item .home-article-item-top .home-cover:hover { + transform: scale(1.03); +} +.home-content-container .home-article-list .home-article-item .home-article-item-bottom { + padding: 2rem; +} +.home-content-container .home-article-list .home-article-item .home-article-item-bottom .sticky-icon { + position: absolute; + top: 1.2rem; + right: 1.2rem; + color: var(--text-color-4); + font-size: 1.2rem; + transform: rotate(45deg); +} +.home-content-container .home-article-list .home-article-item .home-article-item-bottom .home-article-title { + margin: 0; + color: var(--text-color-2); + font-weight: 600; + font-size: 1.4rem; + line-height: 1.5; +keep-tablet() +keep-mobile() +} +.home-content-container .home-article-list .home-article-item .home-article-item-bottom .home-article-content { + margin: 1.8rem 0; + color: var(--text-color-3); + text-align: justify; + word-wrap: break-word; +} diff --git a/css/layout/page.css b/css/layout/page.css new file mode 100644 index 0000000..f86e824 --- /dev/null +++ b/css/layout/page.css @@ -0,0 +1,118 @@ +.page-container { + width: 100%; + height: auto; + background: var(--background-color-1); +} +.page-container .page-main-content { + display: flex; + flex-direction: column; + justify-content: space-between; + min-height: 100vh; + padding-top: var(--header-height); +keep-tablet() +keep-mobile() +} +.page-container .page-main-content.is-home .transparent-1 { + background: var(--header-transparent-background-1); + backdrop-filter: blur(var(--header-backdrop-filter-blur)); +} +.page-container .page-main-content.is-home .transparent-2 { + background: var(--header-transparent-background-2); + backdrop-filter: blur(calc(var(--header-backdrop-filter-blur) * 1.25)); +} +.header-shrink .page-container .page-main-content { + padding-top: var(--header-shrink-height); +keep-tablet() +keep-mobile() +} +.page-container .page-main-content .page-main-content-top { + position: fixed; + top: 0; + right: 0; + z-index: $z-index-5; + box-sizing: border-box; + width: 100%; + height: var(--header-height); +keep-tablet() +keep-mobile() +} +.page-container .page-main-content .page-main-content-top.hide { + transform: translateY(-105%); +} +.header-shrink .page-container .page-main-content .page-main-content-top { + height: var(--header-shrink-height); +keep-tablet() +keep-mobile() +} +.page-container .page-main-content .page-main-content-middle { + display: flex; + justify-content: center; + width: 100%; + padding: var(--components-spacing) 0; +keep-tablet() +keep-mobile() +} +.page-container .page-main-content .page-main-content-middle .main-content { + width: var(--page-content-width); + max-width: var(--page-content-max-width); + height: 100%; +keep-tablet() +keep-mobile() +} +.has-toc .page-container .page-main-content .page-main-content-middle .main-content { + max-width: var(--page-content-max-width-2); +} +.page-container .page-main-content .page-main-content-bottom { + width: 100%; +} +.page-container .post-tools { + position: fixed; + top: calc(var(--header-height) + var(--components-spacing)); + box-sizing: border-box; + opacity: 0; +keep-tablet() +} +.page-container .post-tools.right-toc { + left: calc((100vw - var(--page-content-max-width) / 2) - 5rem); +} +.page-container .post-tools.left-toc { + right: calc((100vw - var(--page-content-max-width) / 2) - 5rem); +} +.header-shrink .page-container .post-tools { + top: calc(var(--header-shrink-height) + var(--components-spacing)); +} +.page-container .side-tools { + position: fixed; + right: 0; + bottom: 1.6rem; + box-sizing: border-box; + padding-right: 1rem; +keep-tablet() +} +.page-container .side-tools:hover .side-tools-container { + transform: translateX(0); + opacity: 1; +} +.page-container .tablet-post-toc-mask { + position: fixed; + top: 0; + right: 0; + z-index: $z-index-10; + box-sizing: border-box; + width: 100%; + height: 100%; + background: rgba(0,0,0,0); + visibility: hidden; +} +.page-container .tablet-post-toc-mask .tablet-post-toc { + position: absolute; + top: 0; + left: 0; + box-sizing: border-box; + width: 22rem; + height: 100%; + padding: 1.2rem 1rem; + background: var(--background-color-1); + transform: translateX(-100%); +keep-mobile() +} diff --git a/css/layout/tag-content.css b/css/layout/tag-content.css new file mode 100644 index 0000000..95c03a5 --- /dev/null +++ b/css/layout/tag-content.css @@ -0,0 +1,11 @@ +.tag-container .tag-name { + margin-bottom: var(--components-spacing); + color: var(--text-color-2); + font-weight: 600; + font-size: 1.5rem; +keep-tablet() +keep-mobile() +} +.tag-container .tag-name i { + color: var(--text-color-2); +} diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..1d1887e --- /dev/null +++ b/css/style.css @@ -0,0 +1,3989 @@ +:root { + --base-font-size: 15.2px; + --base-line-height: 22px; + --base-font-weight: 400; + --base-font-family: Optima-Regular, Optima, PingFang SC, Microsoft YaHei, sans-serif; + --page-content-width: 80%; + --page-content-width-tablet: 88%; + --page-content-width-mobile: 90%; + --page-content-max-width: 928px; + --page-content-max-width-2: calc(var(--page-content-max-width) * 1.2); + --components-spacing: 36px; + --box-border-radius: 8px; + --header-height: 70px; + --header-shrink-height: calc(var(--header-height) * 0.72); + --header-backdrop-filter-blur: 4px; + --header-scroll-progress-bar-height: 2PX; + --first-screen-font-size: 2rem; + --first-screen-icon-size: 1.8rem; + --first-screen-font-color-light: #50505c; + --first-screen-font-color-dark: #adbac5; + --home-post-hover-scale: 1; + --post-author-avatar: block; + --post-create-datetime: flex; + --post-update-datetime: flex; + --post-img-align: 0 auto 0 0; +} +.fade-in-down-animation { + animation-name: fade-in-down; + animation-duration: 1s; + animation-fill-mode: both; +} +.title-hover-animation { + position: relative; + display: inline-block; + color: var(--text-color-2); + line-height: 1.3; + vertical-align: top; + border-bottom: none; +} +.title-hover-animation::before { + position: absolute; + bottom: -4px; + left: 0; + width: 100%; + height: 2px; + background-color: var(--text-color-2); + transform: scaleX(0); + visibility: hidden; + content: ""; + transition-delay: 0s, 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease-in-out, ease-in-out; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.2s, 0.2s; + transition-property: color, background, box-shadow, border-color, visibility, transform; +} +.title-hover-animation:hover::before { + transform: scaleX(1); + visibility: visible; +} +@-moz-keyframes fade-in-down { + 0% { + transform: translateY(-50px); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } +} +@-webkit-keyframes fade-in-down { + 0% { + transform: translateY(-50px); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } +} +@-o-keyframes fade-in-down { + 0% { + transform: translateY(-50px); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } +} +@keyframes fade-in-down { + 0% { + transform: translateY(-50px); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } +} +@-moz-keyframes heartbeat-animate { + 0%, 100% { + transform: scale(1); + } + 10%, 30% { + transform: scale(0.88); + } + 20%, 40%, 60%, 80% { + transform: scale(1.08); + } + 50%, 70% { + transform: scale(1.08); + } +} +@-webkit-keyframes heartbeat-animate { + 0%, 100% { + transform: scale(1); + } + 10%, 30% { + transform: scale(0.88); + } + 20%, 40%, 60%, 80% { + transform: scale(1.08); + } + 50%, 70% { + transform: scale(1.08); + } +} +@-o-keyframes heartbeat-animate { + 0%, 100% { + transform: scale(1); + } + 10%, 30% { + transform: scale(0.88); + } + 20%, 40%, 60%, 80% { + transform: scale(1.08); + } + 50%, 70% { + transform: scale(1.08); + } +} +@keyframes heartbeat-animate { + 0%, 100% { + transform: scale(1); + } + 10%, 30% { + transform: scale(0.88); + } + 20%, 40%, 60%, 80% { + transform: scale(1.08); + } + 50%, 70% { + transform: scale(1.08); + } +} +@-moz-keyframes img-loading-animation { + to { + transform: rotate(1turn); + } +} +@-webkit-keyframes img-loading-animation { + to { + transform: rotate(1turn); + } +} +@-o-keyframes img-loading-animation { + to { + transform: rotate(1turn); + } +} +@keyframes img-loading-animation { + to { + transform: rotate(1turn); + } +} +@-moz-keyframes blink-caret { + from, to { + opacity: 0; + } + 50% { + opacity: 1; + } +} +@-webkit-keyframes blink-caret { + from, to { + opacity: 0; + } + 50% { + opacity: 1; + } +} +@-o-keyframes blink-caret { + from, to { + opacity: 0; + } + 50% { + opacity: 1; + } +} +@keyframes blink-caret { + from, to { + opacity: 0; + } + 50% { + opacity: 1; + } +} +:root { + --primary-color: #06c; + --primary-color-light-1: #0075eb; + --primary-color-light-2: #0a85ff; + --primary-color-dark-1: #005cb8; + --primary-color-dark-2: #0052a3; + --background-color-1: #fff; + --background-color-1-transparent: rgba(255,255,255,0.6); + --background-color-2: #f7f7f7; + --background-color-3: #f0f0f0; + --content-background-color: #fff; + --text-color-1: #484853; + --text-color-2: #4c4c57; + --text-color-3: #50505c; + --text-color-4: #808091; + --text-color-5: #b7b7c0; + --text-color-6: #ededef; + --toc-text-color: #6f6f80; + --badge-color: #f0f0f0; + --badge-background-color: #9393a1; + --border-color: #b3b3b3; + --selection-color: #0075eb; + --shadow-color: rgba(0,0,0,0.16); + --shadow-hover-color: rgba(0,0,0,0.22); + --scrollbar-color: #60606e; + --scrollbar-background-color: #e6e6e6; + --toc-scrollbar-color: rgba(80,80,92,0.1); + --copyright-icon-bg-color: rgba(80,80,92,0.12); + --avatar-background-color: #005cb8; + --header-transparent-background-1: rgba(255,255,255,0.28); + --header-transparent-background-2: rgba(255,255,255,0.4); + --pjax-progress-bar-color: linear-gradient(45deg, #f10006, #ef5b00, #e59c01, #19ca05, #00cab5, #0264c8, #c303c3); + --post-h-bottom-border-color: rgba(80,80,92,0.15); + --article-aging-tips-color: $article-aging-tips-color; + --article-aging-tips-background-color: $article-aging-tips-background-color; + --article-aging-tips-border-color: $article-aging-tips-border-color; + --keep-info-color: rgba(117,117,122,0.8); + --keep-info-background-color: rgba(117,117,122,0.1); + --keep-info-border-color: rgba(117,117,122,0.6); + --keep-primary-color: #0458ab; + --keep-primary-background-color: rgba(4,88,171,0.1); + --keep-primary-border-color: rgba(4,88,171,0.6); + --keep-warning-color: #b78d0f; + --keep-warning-background-color: rgba(183,141,15,0.1); + --keep-warning-border-color: rgba(183,141,15,0.6); + --keep-success-color: #10b981; + --keep-success-background-color: rgba(16,185,129,0.1); + --keep-success-border-color: rgba(16,185,129,0.6); + --keep-danger-color: #f43f5e; + --keep-danger-background-color: rgba(244,63,94,0.1); + --keep-danger-border-color: rgba(244,63,94,0.6); +} +@media (prefers-color-scheme: light) { + :root { + --primary-color: #06c; + --primary-color-light-1: #0075eb; + --primary-color-light-2: #0a85ff; + --primary-color-dark-1: #005cb8; + --primary-color-dark-2: #0052a3; + --background-color-1: #fff; + --background-color-1-transparent: rgba(255,255,255,0.6); + --background-color-2: #f7f7f7; + --background-color-3: #f0f0f0; + --content-background-color: #fff; + --text-color-1: #484853; + --text-color-2: #4c4c57; + --text-color-3: #50505c; + --text-color-4: #808091; + --text-color-5: #b7b7c0; + --text-color-6: #ededef; + --toc-text-color: #6f6f80; + --badge-color: #f0f0f0; + --badge-background-color: #9393a1; + --border-color: #b3b3b3; + --selection-color: #0075eb; + --shadow-color: rgba(0,0,0,0.16); + --shadow-hover-color: rgba(0,0,0,0.22); + --scrollbar-color: #60606e; + --scrollbar-background-color: #e6e6e6; + --toc-scrollbar-color: rgba(80,80,92,0.1); + --copyright-icon-bg-color: rgba(80,80,92,0.12); + --avatar-background-color: #005cb8; + --header-transparent-background-1: rgba(255,255,255,0.28); + --header-transparent-background-2: rgba(255,255,255,0.4); + --pjax-progress-bar-color: linear-gradient(45deg, #f10006, #ef5b00, #e59c01, #19ca05, #00cab5, #0264c8, #c303c3); + --post-h-bottom-border-color: rgba(80,80,92,0.15); + --article-aging-tips-color: $article-aging-tips-color; + --article-aging-tips-background-color: $article-aging-tips-background-color; + --article-aging-tips-border-color: $article-aging-tips-border-color; + --keep-info-color: rgba(117,117,122,0.8); + --keep-info-background-color: rgba(117,117,122,0.1); + --keep-info-border-color: rgba(117,117,122,0.6); + --keep-primary-color: #0458ab; + --keep-primary-background-color: rgba(4,88,171,0.1); + --keep-primary-border-color: rgba(4,88,171,0.6); + --keep-warning-color: #b78d0f; + --keep-warning-background-color: rgba(183,141,15,0.1); + --keep-warning-border-color: rgba(183,141,15,0.6); + --keep-success-color: #10b981; + --keep-success-background-color: rgba(16,185,129,0.1); + --keep-success-border-color: rgba(16,185,129,0.6); + --keep-danger-color: #f43f5e; + --keep-danger-background-color: rgba(244,63,94,0.1); + --keep-danger-border-color: rgba(244,63,94,0.6); + } +} +@media (prefers-color-scheme: dark) { + :root { + --primary-color: #06c; + --primary-color-light-1: #0075eb; + --primary-color-light-2: #0a85ff; + --primary-color-dark-1: #005cb8; + --primary-color-dark-2: #0052a3; + --background-color-1: #22272e; + --background-color-1-transparent: rgba(34,39,46,0.6); + --background-color-2: #292f38; + --background-color-3: #313842; + --content-background-color: #21262d; + --text-color-1: #b7c2cc; + --text-color-2: #b2bec8; + --text-color-3: #adbac5; + --text-color-4: #6c8397; + --text-color-5: #4d5e6c; + --text-color-6: #2e3841; + --toc-text-color: #8296a6; + --badge-color: #343c47; + --badge-background-color: #ced6dc; + --border-color: #596678; + --selection-color: #005ebc; + --shadow-color: rgba(120,120,120,0.18); + --shadow-hover-color: rgba(120,120,120,0.24); + --scrollbar-color: #1f2329; + --scrollbar-background-color: #47515f; + --toc-scrollbar-color: rgba(173,186,197,0.1); + --copyright-icon-bg-color: rgba(173,186,197,0.12); + --avatar-background-color: #004a93; + --header-transparent-background-1: rgba(34,39,46,0.28); + --header-transparent-background-2: rgba(34,39,46,0.4); + --pjax-progress-bar-color: linear-gradient(45deg, #ea404a, #ea722f, #e9a71f, #67e559, #18ecec, #1b85f1, #ee1dee); + --post-h-bottom-border-color: rgba(173,186,197,0.15); + --article-aging-tips-color: $dark-article-aging-tips-color; + --article-aging-tips-background-color: $dark-article-aging-tips-background-color; + --article-aging-tips-border-color: $dark-article-aging-tips-border-color; + --keep-info-color: #9999a2; + --keep-info-background-color: rgba(153,153,162,0.1); + --keep-info-border-color: rgba(153,153,162,0.5); + --keep-primary-color: #268bef; + --keep-primary-background-color: rgba(38,139,239,0.1); + --keep-primary-border-color: rgba(38,139,239,0.6); + --keep-warning-color: #ecc34d; + --keep-warning-background-color: rgba(236,195,77,0.1); + --keep-warning-border-color: rgba(236,195,77,0.6); + --keep-success-color: #10b981; + --keep-success-background-color: rgba(16,185,129,0.1); + --keep-success-border-color: rgba(16,185,129,0.6); + --keep-danger-color: #f43f5e; + --keep-danger-background-color: rgba(244,63,94,0.1); + --keep-danger-border-color: rgba(244,63,94,0.6); + } +} +.light-mode { + --primary-color: #06c; + --primary-color-light-1: #0075eb; + --primary-color-light-2: #0a85ff; + --primary-color-dark-1: #005cb8; + --primary-color-dark-2: #0052a3; + --background-color-1: #fff; + --background-color-1-transparent: rgba(255,255,255,0.6); + --background-color-2: #f7f7f7; + --background-color-3: #f0f0f0; + --content-background-color: #fff; + --text-color-1: #484853; + --text-color-2: #4c4c57; + --text-color-3: #50505c; + --text-color-4: #808091; + --text-color-5: #b7b7c0; + --text-color-6: #ededef; + --toc-text-color: #6f6f80; + --badge-color: #f0f0f0; + --badge-background-color: #9393a1; + --border-color: #b3b3b3; + --selection-color: #0075eb; + --shadow-color: rgba(0,0,0,0.16); + --shadow-hover-color: rgba(0,0,0,0.22); + --scrollbar-color: #60606e; + --scrollbar-background-color: #e6e6e6; + --toc-scrollbar-color: rgba(80,80,92,0.1); + --copyright-icon-bg-color: rgba(80,80,92,0.12); + --avatar-background-color: #005cb8; + --header-transparent-background-1: rgba(255,255,255,0.28); + --header-transparent-background-2: rgba(255,255,255,0.4); + --pjax-progress-bar-color: linear-gradient(45deg, #f10006, #ef5b00, #e59c01, #19ca05, #00cab5, #0264c8, #c303c3); + --post-h-bottom-border-color: rgba(80,80,92,0.15); + --article-aging-tips-color: $article-aging-tips-color; + --article-aging-tips-background-color: $article-aging-tips-background-color; + --article-aging-tips-border-color: $article-aging-tips-border-color; + --keep-info-color: rgba(117,117,122,0.8); + --keep-info-background-color: rgba(117,117,122,0.1); + --keep-info-border-color: rgba(117,117,122,0.6); + --keep-primary-color: #0458ab; + --keep-primary-background-color: rgba(4,88,171,0.1); + --keep-primary-border-color: rgba(4,88,171,0.6); + --keep-warning-color: #b78d0f; + --keep-warning-background-color: rgba(183,141,15,0.1); + --keep-warning-border-color: rgba(183,141,15,0.6); + --keep-success-color: #10b981; + --keep-success-background-color: rgba(16,185,129,0.1); + --keep-success-border-color: rgba(16,185,129,0.6); + --keep-danger-color: #f43f5e; + --keep-danger-background-color: rgba(244,63,94,0.1); + --keep-danger-border-color: rgba(244,63,94,0.6); +} +.dark-mode { + --primary-color: #06c; + --primary-color-light-1: #0075eb; + --primary-color-light-2: #0a85ff; + --primary-color-dark-1: #005cb8; + --primary-color-dark-2: #0052a3; + --background-color-1: #22272e; + --background-color-1-transparent: rgba(34,39,46,0.6); + --background-color-2: #292f38; + --background-color-3: #313842; + --content-background-color: #21262d; + --text-color-1: #b7c2cc; + --text-color-2: #b2bec8; + --text-color-3: #adbac5; + --text-color-4: #6c8397; + --text-color-5: #4d5e6c; + --text-color-6: #2e3841; + --toc-text-color: #8296a6; + --badge-color: #343c47; + --badge-background-color: #ced6dc; + --border-color: #596678; + --selection-color: #005ebc; + --shadow-color: rgba(120,120,120,0.18); + --shadow-hover-color: rgba(120,120,120,0.24); + --scrollbar-color: #1f2329; + --scrollbar-background-color: #47515f; + --toc-scrollbar-color: rgba(173,186,197,0.1); + --copyright-icon-bg-color: rgba(173,186,197,0.12); + --avatar-background-color: #004a93; + --header-transparent-background-1: rgba(34,39,46,0.28); + --header-transparent-background-2: rgba(34,39,46,0.4); + --pjax-progress-bar-color: linear-gradient(45deg, #ea404a, #ea722f, #e9a71f, #67e559, #18ecec, #1b85f1, #ee1dee); + --post-h-bottom-border-color: rgba(173,186,197,0.15); + --article-aging-tips-color: $dark-article-aging-tips-color; + --article-aging-tips-background-color: $dark-article-aging-tips-background-color; + --article-aging-tips-border-color: $dark-article-aging-tips-border-color; + --keep-info-color: #9999a2; + --keep-info-background-color: rgba(153,153,162,0.1); + --keep-info-border-color: rgba(153,153,162,0.5); + --keep-primary-color: #268bef; + --keep-primary-background-color: rgba(38,139,239,0.1); + --keep-primary-border-color: rgba(38,139,239,0.6); + --keep-warning-color: #ecc34d; + --keep-warning-background-color: rgba(236,195,77,0.1); + --keep-warning-border-color: rgba(236,195,77,0.6); + --keep-success-color: #10b981; + --keep-success-background-color: rgba(16,185,129,0.1); + --keep-success-border-color: rgba(16,185,129,0.6); + --keep-danger-color: #f43f5e; + --keep-danger-background-color: rgba(244,63,94,0.1); + --keep-danger-border-color: rgba(244,63,94,0.6); +} +* { + transition-delay: 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s; + transition-property: color, background, box-shadow, border-color; +} +*::-webkit-scrollbar { + width: 0.4rem; + height: 0.4rem; + transition: all 0.2s ease; +} +*::-webkit-scrollbar-thumb { + background: var(--scrollbar-color); + border-radius: 0.1rem; +} +*::-webkit-scrollbar-track { + background: var(--scrollbar-background-color); +} +html, +body { + position: relative; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + color: var(--text-color-3); + font-weight: var(--base-font-weight); + font-size: var(--base-font-size); + font-family: var(--base-font-family); + line-height: var(--base-line-height); + letter-spacing: 0.2px; + background: var(--background-color-1); +} +html::-webkit-scrollbar, +body::-webkit-scrollbar { + width: 0.6rem; + height: 0.6rem; +} +@media (max-width: 800px) { + html::-webkit-scrollbar, + body::-webkit-scrollbar { + width: 0.5rem; + height: 0.5rem; + } +} +@media (max-width: 500px) { + html::-webkit-scrollbar, + body::-webkit-scrollbar { + width: 0.4rem; + height: 0.4rem; + } +} +@media (max-width: 800px) { + html, + body { + font-size: calc(var(--base-font-size) * 0.95) !important; + line-height: calc(var(--base-line-height) * 0.95) !important; + } +} +@media (max-width: 500px) { + html, + body { + font-size: calc(var(--base-font-size) * 0.9) !important; + line-height: calc(var(--base-line-height) * 0.9) !important; + } +} +::selection { + color: #fff; + background: var(--selection-color); +} +ul, +ol, +li { + margin: 0; + padding: 0; + list-style: none; +} +a { + color: var(--text-color-3); + text-decoration: none; +} +a i, +a span { + color: var(--text-color-3); +} +a:hover, +a:active { + color: var(--primary-color); + text-decoration: none !important; +} +a:hover i, +a:active i, +a:hover span, +a:active span { + color: var(--primary-color); +} +.dark-mode img { + filter: brightness(0.9); +} +.dark-mode img:hover { + filter: brightness(1); +} +img[lazyload] { + position: relative; + box-sizing: border-box; + width: 8rem; + height: 8rem; + box-shadow: none !important; + cursor: not-allowed; + pointer-events: none; +} +img[lazyload]::before { + position: absolute; + top: 0; + left: 0; + display: block; + width: 100%; + height: 100%; + background: var(--background-color-1); + content: ''; + transition-delay: 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.2s; + transition-property: color, background, box-shadow, border-color, background; +} +img[lazyload]::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: block; + width: 2rem; + height: 2rem; + margin: auto; + border: 2px solid var(--text-color-6); + border-top-color: var(--selection-color); + border-left-color: var(--selection-color); + border-radius: 50%; + animation: img-loading-animation 750ms infinite linear; + content: ''; + transition-delay: 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.2s; + transition-property: color, background, box-shadow, border-color, border; +} +.flex-center { + display: flex; + align-items: center; + justify-content: center; +} +.border-box { + position: relative; + box-sizing: border-box; +} +.text-ellipsis { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.clear::after { + display: block; + clear: both; + height: 0; + overflow: hidden; + visibility: hidden; + content: ''; +} +.tooltip { + position: relative; + box-sizing: border-box; +} +.tooltip:hover .tooltip-content { + display: inline-block; +} +.tooltip.show-img .tooltip-content { + display: none !important; +} +.tooltip .tooltip-content { + position: absolute; + top: -0.4rem; + left: 50%; + z-index: 1010; + display: none; + box-sizing: border-box; + padding: 0.2rem 0.6rem; + color: var(--text-color-6); + font-size: 0.8rem; + letter-spacing: 0.8px; + white-space: nowrap; + background: var(--text-color-1); + border-radius: 0.3rem; + transform: translateX(-50%) translateY(-100%); + transition-delay: 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.2s; + transition-property: color, background, box-shadow, border-color, display; + -moz-user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; +} +.tooltip-img { + position: relative; + box-sizing: border-box; +} +.tooltip-img.show-img .tooltip-img-box { + display: flex; +} +.tooltip-img .tooltip-img-box { + position: absolute; + top: -0.4rem; + left: 50%; + z-index: 1011; + display: none; + align-items: center; + justify-content: center; + box-sizing: border-box; + min-height: 6rem; + overflow: hidden; + background: var(--background-color-3); + border: 0.2rem solid var(--text-color-4); + border-radius: 0.3rem; + transform: translateX(-50%) translateY(-100%); + transition-delay: 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.2s; + transition-property: color, background, box-shadow, border-color, display; + -moz-user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; +} +.tooltip-img .tooltip-img-box.has-tip { + flex-direction: column; + justify-content: space-between; +} +.tooltip-img .tooltip-img-box img { + display: block; + max-height: 10rem; +} +.tooltip-img .tooltip-img-box .tip { + position: relative; + box-sizing: border-box; + width: 100%; + padding: 0.3rem 0; + color: #555; + font-size: 0.9rem; + text-align: center; +} +.keep-markdown-body { + font-size: 1rem; +} +.keep-markdown-body blockquote { + position: relative; + display: flex; + flex-direction: column; + justify-content: flex-start; + box-sizing: border-box; + margin: 1.4rem 0; + color: var(--text-color-3); + background: var(--background-color-2); + border-left: 0.4rem solid var(--text-color-4); + border-top-left-radius: 0.2rem; + border-bottom-left-radius: 0.2rem; +} +.keep-markdown-body blockquote p, +.keep-markdown-body blockquote ul, +.keep-markdown-body blockquote ol, +.keep-markdown-body blockquote footer { + position: relative; + box-sizing: border-box; + padding: 0.4rem 0.4rem 0.4rem 0.8rem !important; +} +.keep-markdown-body blockquote footer { + margin-bottom: 0.6rem; + font-style: italic; +} +.keep-markdown-body blockquote cite { + position: relative; + box-sizing: border-box; + margin-left: 1rem; + color: var(--text-color-4); +} +.keep-markdown-body blockquote cite::before { + content: '— '; +} +.keep-markdown-body p { + margin: 0.6rem 0; + color: var(--text-color-3); + line-height: 2; +} +.keep-markdown-body a { + position: relative; + box-sizing: border-box; + padding-bottom: 0.2rem; + text-decoration: none; + overflow-wrap: break-word; + border-bottom: 0.1rem solid var(--text-color-4); + outline: 0; + cursor: pointer; +} +.keep-markdown-body a .fas, +.keep-markdown-body a .far, +.keep-markdown-body a .fab { + position: relative; + margin: 0 0.2rem 0 0.4rem; + color: var(--text-color-4); + font-size: 0.88rem; +} +.keep-markdown-body a:hover { + text-decoration: underline; +} +.keep-markdown-body a:hover::after { + background: var(--primary-color); +} +.keep-markdown-body strong { + color: var(--text-color-3); +} +.keep-markdown-body em { + color: var(--text-color-3); +} +.keep-markdown-body ul li, +.keep-markdown-body ol li { + margin-left: 1rem; + line-height: 2rem; +} +.keep-markdown-body ul li { + list-style: disc; +} +.keep-markdown-body ul li ul li { + list-style: circle; +} +.keep-markdown-body ul li ul li ul li { + list-style: square; +} +.keep-markdown-body ol li { + list-style: decimal; +} +.keep-markdown-body ol li ol li { + list-style: upper-alpha; +} +.keep-markdown-body ol li ol li ol li { + list-style: upper-roman; +} +.keep-markdown-body li { + color: var(--text-color-3); +} +.keep-markdown-body h1, +.keep-markdown-body h2, +.keep-markdown-body h3, +.keep-markdown-body h4, +.keep-markdown-body h5, +.keep-markdown-body h6 { + position: relative; + box-sizing: border-box; + padding-top: 0.4rem; + padding-bottom: 0.2rem; + overflow: hidden; + color: var(--text-color-2); + line-height: 1.5; + white-space: nowrap; + text-overflow: ellipsis; + border-bottom: 1px solid var(--post-h-bottom-border-color); +} +@media (max-width: 800px) { + .keep-markdown-body h1, + .keep-markdown-body h2, + .keep-markdown-body h3, + .keep-markdown-body h4, + .keep-markdown-body h5, + .keep-markdown-body h6 { + line-height: 1.25; + } +} +.keep-markdown-body h1:hover a.headerlink, +.keep-markdown-body h2:hover a.headerlink, +.keep-markdown-body h3:hover a.headerlink, +.keep-markdown-body h4:hover a.headerlink, +.keep-markdown-body h5:hover a.headerlink, +.keep-markdown-body h6:hover a.headerlink { + visibility: visible; +} +.keep-markdown-body h1 a.headerlink, +.keep-markdown-body h2 a.headerlink, +.keep-markdown-body h3 a.headerlink, +.keep-markdown-body h4 a.headerlink, +.keep-markdown-body h5 a.headerlink, +.keep-markdown-body h6 a.headerlink { + position: relative; + float: right; + box-sizing: border-box; + margin-left: 0.32rem; + padding-top: 0.25rem; + color: var(--text-color-4); + font-size: 1.2rem; + text-decoration: none; + border: none; + visibility: hidden; +} +.keep-markdown-body h1 a.headerlink:hover, +.keep-markdown-body h2 a.headerlink:hover, +.keep-markdown-body h3 a.headerlink:hover, +.keep-markdown-body h4 a.headerlink:hover, +.keep-markdown-body h5 a.headerlink:hover, +.keep-markdown-body h6 a.headerlink:hover { + color: var(--text-color-3); +} +.keep-markdown-body h1 a.headerlink::before, +.keep-markdown-body h2 a.headerlink::before, +.keep-markdown-body h3 a.headerlink::before, +.keep-markdown-body h4 a.headerlink::before, +.keep-markdown-body h5 a.headerlink::before, +.keep-markdown-body h6 a.headerlink::before { + font-weight: 600; + font-family: 'Font Awesome 6 Free'; + content: '\f0c1'; +} +.keep-markdown-body h1 { + font-weight: 600; + font-size: 1.8rem; +} +@media (max-width: 800px) { + .keep-markdown-body h1 { + font-size: 1.7rem; + } +} +.keep-markdown-body h2 { + font-weight: 600; + font-size: 1.7rem; +} +@media (max-width: 800px) { + .keep-markdown-body h2 { + font-size: 1.6rem; + } +} +.keep-markdown-body h3 { + font-weight: 550; + font-size: 1.6rem; +} +@media (max-width: 800px) { + .keep-markdown-body h3 { + font-size: 1.5rem; + } +} +.keep-markdown-body h4 { + font-weight: 550; + font-size: 1.5rem; +} +@media (max-width: 800px) { + .keep-markdown-body h4 { + font-size: 1.4rem; + } +} +.keep-markdown-body h5 { + font-weight: 500; + font-size: 1.28rem; +} +@media (max-width: 800px) { + .keep-markdown-body h5 { + font-size: 1.18rem; + } +} +.keep-markdown-body h6 { + font-weight: 500; + font-size: 1.2rem; + line-height: 1.2; +} +@media (max-width: 800px) { + .keep-markdown-body h6 { + font-size: 1.1rem; + line-height: 1.1; + } +} +.keep-markdown-body img { + position: relative; + display: block; + box-sizing: border-box; + max-width: 100%; + margin: var(--post-img-align); + margin-bottom: 2rem; + box-shadow: 0 0 0.2rem var(--shadow-color); + cursor: zoom-in; + opacity: 1; +} +.keep-markdown-body img.hide { + opacity: 0; +} +.keep-markdown-body img[lazyload] { + margin: 0.8rem auto 0.2rem; +} +.keep-markdown-body > table { + width: 100%; + overflow: auto; + border-collapse: collapse; + border-spacing: 0; +} +@media (max-width: 500px) { + .keep-markdown-body > table { + table-layout: fixed; + } +} +.keep-markdown-body > table td, +.keep-markdown-body > table th { + padding: 0; +} +.keep-markdown-body > table th { + font-weight: 600; +} +.keep-markdown-body > table td, +.keep-markdown-body > table th { + padding: 0.4rem 1rem; + border: 0.1rem solid var(--border-color); +} +.keep-markdown-body > table tr { + background-color: var(--background-color-1); + border: 0.1rem solid var(--text-color-6); +} +.keep-markdown-body > table tr:nth-child(2n) { + background-color: var(--background-color-2); +} +.keep-markdown-body .mermaid { + background: #f5f5f5 !important; + border-radius: 0.3rem; +} +:root { + --toolbar-foreground: #50505c; + --toolbar-background: #f0f0f0; + --code-foreground: #535360; + --code-background: #f2f2f2; + --highlight-background: #fafafa; + --highlight-foreground: #535360; + --highlight-comment: #8e908c; + --highlight-red: #c82829; + --highlight-orange: #f5871f; + --highlight-yellow: #eab700; + --highlight-green: #718c00; + --highlight-aqua: #3e999f; + --highlight-blue: #4271ae; + --highlight-purple: #8959a8; + --highlight-gutter-color: #585865; + --highlight-gutter-bg-color: #f2f2f2; + --mac-toolbar-background-color: #21252b; +} +@media (prefers-color-scheme: light) { + :root { + --toolbar-foreground: #50505c; + --toolbar-background: #f0f0f0; + --code-foreground: #535360; + --code-background: #f2f2f2; + --highlight-background: #fafafa; + --highlight-foreground: #535360; + --highlight-comment: #8e908c; + --highlight-red: #c82829; + --highlight-orange: #f5871f; + --highlight-yellow: #eab700; + --highlight-green: #718c00; + --highlight-aqua: #3e999f; + --highlight-blue: #4271ae; + --highlight-purple: #8959a8; + --highlight-gutter-color: #585865; + --highlight-gutter-bg-color: #f2f2f2; + --mac-toolbar-background-color: #21252b; + } +} +@media (prefers-color-scheme: dark) { + :root { + --toolbar-foreground: #adbac5; + --toolbar-background: #313842; + --code-foreground: #afbbc6; + --code-background: #1f2329; + --highlight-background: #1f242a; + --highlight-foreground: #afbbc6; + --highlight-comment: #969896; + --highlight-red: #c66; + --highlight-orange: #de935f; + --highlight-yellow: #f0c674; + --highlight-green: #b5bd68; + --highlight-aqua: #8abeb7; + --highlight-blue: #81a2be; + --highlight-purple: #b294bb; + --highlight-gutter-color: #b1bdc8; + --highlight-gutter-bg-color: #1f2329; + --mac-toolbar-background-color: #1f2021; + } +} +.light-mode { + --toolbar-foreground: #50505c; + --toolbar-background: #f0f0f0; + --code-foreground: #535360; + --code-background: #f2f2f2; + --highlight-background: #fafafa; + --highlight-foreground: #535360; + --highlight-comment: #8e908c; + --highlight-red: #c82829; + --highlight-orange: #f5871f; + --highlight-yellow: #eab700; + --highlight-green: #718c00; + --highlight-aqua: #3e999f; + --highlight-blue: #4271ae; + --highlight-purple: #8959a8; + --highlight-gutter-color: #585865; + --highlight-gutter-bg-color: #f2f2f2; + --mac-toolbar-background-color: #21252b; +} +.dark-mode { + --toolbar-foreground: #adbac5; + --toolbar-background: #313842; + --code-foreground: #afbbc6; + --code-background: #1f2329; + --highlight-background: #1f242a; + --highlight-foreground: #afbbc6; + --highlight-comment: #969896; + --highlight-red: #c66; + --highlight-orange: #de935f; + --highlight-yellow: #f0c674; + --highlight-green: #b5bd68; + --highlight-aqua: #8abeb7; + --highlight-blue: #81a2be; + --highlight-purple: #b294bb; + --highlight-gutter-color: #b1bdc8; + --highlight-gutter-bg-color: #1f2329; + --mac-toolbar-background-color: #1f2021; +} +.code-block, +pre, +.highlight { + margin: 1.5rem 0; + padding: 0; + overflow: auto; + color: var(--highlight-foreground); + font-size: 0.96rem; + line-height: 1.5rem; + background: var(--highlight-background); + -ms-text-size-adjust: none; + -moz-text-size-adjust: none; + -webkit-text-size-adjust: none; +} +pre, +code { + font-family: "Source Code Pro", consolas, Menlo; +} +code { + padding: 0.4rem; + color: var(--code-foreground); + font-size: 0.96rem; + word-wrap: break-word; + background: var(--code-background); + border-radius: 0.2rem; +} +pre { + padding: 0.6rem; +} +pre code { + padding: 0; + color: var(--highlight-foreground); + text-shadow: none; + background: none; +} +.highlight { + border-bottom-right-radius: 0.1rem; + border-bottom-left-radius: 0.1rem; +} +.highlight pre { + margin: 0; + padding: 0.6rem 0; + border: none; +} +.highlight table { + width: auto; + margin: 0; + border: none; + border-spacing: unset; +} +.highlight td { + padding: 0; + border: none; +} +.highlight figcaption { + box-sizing: border-box; + padding: 0.5rem; + color: var(--highlight-foreground); + font-size: 1rem; + line-height: 1rem; +} +.highlight figcaption a { + float: right; + color: var(--highlight-foreground); + font-size: 0.9rem; +} +.highlight figcaption a:hover { + border-bottom-color: var(--highlight-foreground); +} +.highlight .gutter pre { + padding-right: 0.6rem; + padding-left: 0.6rem; + color: var(--highlight-gutter-color); + text-align: center; + background-color: var(--highlight-gutter-bg-color); +} +.highlight .code pre { + width: 100%; + padding-right: 0.6rem; + padding-left: 0.6rem; + background-color: var(--highlight-background); +} +.highlight .line { + height: 1.5rem; + color: var(--highlight-foreground); +} +.highlight .line .language-javascript { + color: var(--highlight-foreground); +} +.highlight .line .attr { + color: var(--highlight-foreground); +} +.highlight .line .string { + color: var(--highlight-foreground); +} +.gutter { + -moz-user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; + color: var(--highlight-gutter-color); + background: var(--highlight-gutter-bg-color); +} +.gutter .line { + color: var(--highlight-gutter-color); +} +.gist table { + width: auto; +} +.gist table td { + border: none; +} +pre .deletion { + background: var(--highlight-deletion); +} +pre .addition { + background: var(--highlight-addition); +} +pre .meta { + color: var(--highlight-purple); +} +pre .comment { + color: var(--highlight-comment); +} +pre .variable, +pre .attribute, +pre .tag, +pre .regexp, +pre .ruby .constant, +pre .xml .tag .title, +pre .xml .pi, +pre .xml .doctype, +pre .html .doctype, +pre .css .id, +pre .css .class, +pre .css .pseudo { + color: var(--highlight-red); +} +pre .property { + color: var(--highlight-blue); +} +pre .number, +pre .preprocessor, +pre .built_in, +pre .literal, +pre .params, +pre .constant, +pre .command { + color: var(--highlight-orange); +} +pre .ruby .class .title, +pre .css .rules .attribute, +pre .string, +pre .value, +pre .inheritance, +pre .header, +pre .ruby .symbol, +pre .xml .cdata, +pre .special, +pre .number, +pre .formula { + color: var(--highlight-green); +} +pre .title, +pre .css .hexcolor { + color: var(--highlight-aqua); +} +pre .function, +pre .python .decorator, +pre .python .title, +pre .ruby .function .title, +pre .ruby .title .keyword, +pre .perl .sub, +pre .javascript .title, +pre .coffeescript .title { + color: var(--highlight-blue); +} +pre .keyword, +pre .javascript .function { + color: var(--highlight-purple); +} +.highlight-container { + position: relative; + box-sizing: border-box; + margin: 1.4rem 0; +} +.highlight-container.mac { + margin: 1.4rem 0 1.8rem 0; + box-shadow: 0 0.8rem 2rem 0 rgba(0,0,0,0.4); +} +.highlight-container.mac:hover .code-tools-box .copy { + opacity: 1; +} +.highlight-container.mac .code-tools-box { + justify-content: flex-end; + padding: 0.4rem 0.6rem 0.7rem 0.4rem; + background: var(--mac-toolbar-background-color); +} +.highlight-container.mac .code-tools-box::before { + position: absolute; + left: 0.8rem; + width: 0.76rem; + height: 0.76rem; + background: #fc625d; + border-radius: 50%; + box-shadow: 1.3rem 0 #fdbc40, 2.6rem 0 #35cd4b; + content: ''; +} +.highlight-container.mac .code-tools-box.folded { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.highlight-container.mac .code-tools-box.folded .copy { + display: none; +} +.highlight-container.mac .code-tools-box .code-lang { + order: 1; + color: #bbb; +} +.highlight-container.mac .code-tools-box .fold { + order: 2; + padding: 0 0.1rem 0 0.6rem; +} +.highlight-container.mac .code-tools-box .fold i { + color: #ccc; +} +.highlight-container.mac .code-tools-box .copy { + position: absolute; + top: 3rem; + right: 0.5rem; + box-sizing: border-box; + padding: 0 0.1rem; + opacity: 0; + transition-delay: 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease-in-out; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.2s; + transition-property: color, background, box-shadow, border-color, opacity; +} +.highlight-container.mac .code-tools-box .copy i { + font-size: 1rem; +} +.highlight-container .code-tools-box { + position: relative; + z-index: 1001; + display: flex; + align-items: center; + justify-content: space-between; + box-sizing: border-box; + width: 100%; + padding: 0.3rem 0.4rem; + color: var(--toolbar-foreground); + background: var(--toolbar-background); + border-top-left-radius: 0.3rem; + border-top-right-radius: 0.3rem; +} +.highlight-container .code-tools-box.folded { + border-bottom-right-radius: 0.3rem; + border-bottom-left-radius: 0.3rem; +} +.highlight-container .code-tools-box .code-lang { + justify-content: flex-start; + margin-left: 0.2rem; + font-weight: 600; + font-size: 0.9rem; + font-family: "Source Code Pro", consolas, Menlo; + color: var(--text-color-3); +} +.highlight-container .code-tools-box .tool { + -moz-user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; + cursor: pointer; +} +.highlight-container .code-tools-box .tool i { + font-size: 0.8rem; + color: var(--text-color-3); +} +.highlight-container .code-tools-box .fold { + padding: 0 0.4rem 0 0.2rem; +} +.highlight-container figure.highlight { + position: relative; + box-sizing: border-box; + margin: 0; +} +.highlight-container figure.highlight.folded { + height: 0; +} +:root { + --toolbar-foreground: #50505c; + --toolbar-background: #f0f0f0; + --code-foreground: #535360; + --code-background: #f2f2f2; + --highlight-background: #fafafa; + --highlight-foreground: #535360; + --highlight-comment: #8e908c; + --highlight-red: #c82829; + --highlight-orange: #f5871f; + --highlight-yellow: #eab700; + --highlight-green: #718c00; + --highlight-aqua: #3e999f; + --highlight-blue: #4271ae; + --highlight-purple: #8959a8; + --highlight-gutter-color: #585865; + --highlight-gutter-bg-color: #f2f2f2; + --mac-toolbar-background-color: #21252b; +} +@media (prefers-color-scheme: light) { + :root { + --toolbar-foreground: #50505c; + --toolbar-background: #f0f0f0; + --code-foreground: #535360; + --code-background: #f2f2f2; + --highlight-background: #fafafa; + --highlight-foreground: #535360; + --highlight-comment: #8e908c; + --highlight-red: #c82829; + --highlight-orange: #f5871f; + --highlight-yellow: #eab700; + --highlight-green: #718c00; + --highlight-aqua: #3e999f; + --highlight-blue: #4271ae; + --highlight-purple: #8959a8; + --highlight-gutter-color: #585865; + --highlight-gutter-bg-color: #f2f2f2; + --mac-toolbar-background-color: #21252b; + } +} +@media (prefers-color-scheme: dark) { + :root { + --toolbar-foreground: #adbac5; + --toolbar-background: #313842; + --code-foreground: #afbbc6; + --code-background: #1f2329; + --highlight-background: #1f242a; + --highlight-foreground: #afbbc6; + --highlight-comment: #969896; + --highlight-red: #c66; + --highlight-orange: #de935f; + --highlight-yellow: #f0c674; + --highlight-green: #b5bd68; + --highlight-aqua: #8abeb7; + --highlight-blue: #81a2be; + --highlight-purple: #b294bb; + --highlight-gutter-color: #b1bdc8; + --highlight-gutter-bg-color: #1f2329; + --mac-toolbar-background-color: #1f2021; + } +} +.light-mode { + --toolbar-foreground: #50505c; + --toolbar-background: #f0f0f0; + --code-foreground: #535360; + --code-background: #f2f2f2; + --highlight-background: #fafafa; + --highlight-foreground: #535360; + --highlight-comment: #8e908c; + --highlight-red: #c82829; + --highlight-orange: #f5871f; + --highlight-yellow: #eab700; + --highlight-green: #718c00; + --highlight-aqua: #3e999f; + --highlight-blue: #4271ae; + --highlight-purple: #8959a8; + --highlight-gutter-color: #585865; + --highlight-gutter-bg-color: #f2f2f2; + --mac-toolbar-background-color: #21252b; +} +.dark-mode { + --toolbar-foreground: #adbac5; + --toolbar-background: #313842; + --code-foreground: #afbbc6; + --code-background: #1f2329; + --highlight-background: #1f242a; + --highlight-foreground: #afbbc6; + --highlight-comment: #969896; + --highlight-red: #c66; + --highlight-orange: #de935f; + --highlight-yellow: #f0c674; + --highlight-green: #b5bd68; + --highlight-aqua: #8abeb7; + --highlight-blue: #81a2be; + --highlight-purple: #b294bb; + --highlight-gutter-color: #b1bdc8; + --highlight-gutter-bg-color: #1f2329; + --mac-toolbar-background-color: #1f2021; +} +.keep-note { + position: relative; + box-sizing: border-box; + width: 100%; + margin-bottom: 2rem; + padding: 0.8rem 1rem; + font-size: 0.9rem; + border-style: solid; + border-width: 0.1rem; + border-radius: 0.4rem; +} +.keep-note.info { + color: var(--keep-info-color); + background-color: var(--keep-info-background-color); + border-color: var(--keep-info-border-color); +} +.keep-note.primary { + color: var(--keep-primary-color); + background-color: var(--keep-primary-background-color); + border-color: var(--keep-primary-border-color); +} +.keep-note.success { + color: var(--keep-success-color); + background-color: var(--keep-success-background-color); + border-color: var(--keep-success-border-color); +} +.keep-note.warning { + color: var(--keep-warning-color); + background-color: var(--keep-warning-background-color); + border-color: var(--keep-warning-border-color); +} +.keep-note.danger { + color: var(--keep-danger-color); + background-color: var(--keep-danger-background-color); + border-color: var(--keep-danger-border-color); +} +.keep-note .keep-note-title { + box-sizing: border-box; + padding: 0.6rem 0 0 0; + color: inherit; + font-weight: 600; + font-size: 1rem; +} +.keep-note p { + color: inherit; +} +.keep-button { + position: relative; + box-sizing: border-box; + margin: auto 0.6rem; + padding: 0.6rem 1rem; + color: var(--text-color-3); + font-size: 1rem; + background: var(--background-color-1); + border: none; + border-radius: 0.4rem; + box-shadow: 0.1rem 0.2rem 0.4rem var(--shadow-color); + cursor: pointer; +} +.keep-button i { + color: var(--text-color-3); +} +.keep-button:hover { + color: var(--background-color-1); + background: var(--primary-color); +} +.keep-button:hover i { + color: var(--background-color-1); +} +.keep-button.size-small { + margin: auto 0.3rem; + padding: 0.5rem 0.8rem; + font-size: 0.8rem; + border-radius: 0.3rem; + box-shadow: 0.1rem 0.1rem 0.3rem var(--shadow-color); +} +.keep-button.size-large { + width: 100%; + margin: 0.6rem 0; + padding: 1rem; + font-size: 1.1rem; + border-radius: 0.5rem; + box-shadow: 0.1rem 0.2rem 0.5rem var(--shadow-color); +} +.keep-button.color-info { + color: var(--text-color-3); + background-color: var(--background-color-1); +} +.keep-button.color-info:hover { + color: var(--background-color-1); + background: var(--primary-color); +} +.keep-button.color-primary { + color: var(--keep-primary-color); + background-color: var(--keep-primary-background-color); +} +.keep-button.color-primary:hover { + color: #fff; + background-color: var(--keep-primary-color); +} +.keep-button.color-success { + color: var(--keep-success-color); + background-color: var(--keep-success-background-color); +} +.keep-button.color-success:hover { + color: #fff; + background-color: var(--keep-success-color); +} +.keep-button.color-warning { + color: var(--keep-warning-color); + background-color: var(--keep-warning-background-color); +} +.keep-button.color-warning:hover { + color: #fff; + background-color: var(--keep-warning-color); +} +.keep-button.color-danger { + color: var(--keep-danger-color); + background-color: var(--keep-danger-background-color); +} +.keep-button.color-danger:hover { + color: #fff; + background-color: var(--keep-danger-color); +} +.keep-tabs { + position: relative; + box-sizing: border-box; + width: 100%; + height: auto; + background: var(--background-color); + border-radius: 0.4rem; + box-shadow: 0.1rem 0.1rem 0.5rem var(--shadow-color); +} +.keep-tabs .tabs-nav { + position: relative; + display: flex; + justify-content: flex-start; + box-sizing: border-box; + list-style: none; +} +.keep-tabs .tabs-nav::before { + position: absolute; + bottom: 0; + left: 0; + box-sizing: border-box; + width: 100%; + height: 2px; + background: var(--border-color); + content: ''; +} +.keep-tabs .tabs-nav .tab { + position: relative; + box-sizing: border-box; + margin-right: 0.8rem; + padding: 1rem 0.6rem; + overflow: hidden; + color: var(--text-color-3); + cursor: pointer; +} +.keep-tabs .tabs-nav .tab.active { + font-weight: 600; +} +.keep-tabs .tabs-nav .tab.active::before { + position: absolute; + bottom: 0; + left: 50%; + box-sizing: border-box; + width: 100%; + height: 2px; + background: var(--primary-color); + border-radius: 0.2rem; + transform: translateX(-50%); + content: ''; +} +.keep-tabs .tabs-content { + position: relative; + box-sizing: border-box; +} +.keep-tabs .tabs-content .tab-pane { + position: relative; + box-sizing: border-box; + width: 100%; + height: auto; + min-height: 10rem; + padding: 0.6rem 0.8rem; +} +.keep-tabs .tabs-content .tab-pane:not(.active) { + display: none; +} +.page-container { + width: 100%; + height: auto; + background: var(--background-color-1); +} +.page-container .page-main-content { + display: flex; + flex-direction: column; + justify-content: space-between; + min-height: 100vh; + padding-top: var(--header-height); +} +.page-container .page-main-content.is-home .transparent-1 { + background: var(--header-transparent-background-1); + backdrop-filter: blur(var(--header-backdrop-filter-blur)); +} +.page-container .page-main-content.is-home .transparent-2 { + background: var(--header-transparent-background-2); + backdrop-filter: blur(calc(var(--header-backdrop-filter-blur) * 1.25)); +} +.header-shrink .page-container .page-main-content { + padding-top: var(--header-shrink-height); +} +@media (max-width: 800px) { + .header-shrink .page-container .page-main-content { + padding-top: calc(var(--header-shrink-height) * 0.9); + } +} +@media (max-width: 500px) { + .header-shrink .page-container .page-main-content { + padding-top: calc(var(--header-shrink-height) * 0.8); + } +} +@media (max-width: 800px) { + .page-container .page-main-content { + padding-top: calc(var(--header-height) * 0.9); + } +} +@media (max-width: 500px) { + .page-container .page-main-content { + padding-top: calc(var(--header-height) * 0.8); + } +} +.page-container .page-main-content .page-main-content-top { + position: fixed; + top: 0; + right: 0; + z-index: 1005; + box-sizing: border-box; + width: 100%; + height: var(--header-height); + transition-delay: 0s, 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease-out, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.3s, 0.2s; + transition-property: color, background, box-shadow, border-color, transform, height; +} +.page-container .page-main-content .page-main-content-top.hide { + transform: translateY(-105%); +} +.header-shrink .page-container .page-main-content .page-main-content-top { + height: var(--header-shrink-height); +} +@media (max-width: 800px) { + .header-shrink .page-container .page-main-content .page-main-content-top { + height: calc(var(--header-shrink-height) * 0.9); + } +} +@media (max-width: 500px) { + .header-shrink .page-container .page-main-content .page-main-content-top { + height: calc(var(--header-shrink-height) * 0.8); + } +} +@media (max-width: 800px) { + .page-container .page-main-content .page-main-content-top { + height: calc(var(--header-height) * 0.9); + } +} +@media (max-width: 500px) { + .page-container .page-main-content .page-main-content-top { + height: calc(var(--header-height) * 0.8); + } +} +.page-container .page-main-content .page-main-content-middle { + display: flex; + justify-content: center; + width: 100%; + padding: var(--components-spacing) 0; +} +@media (max-width: 800px) { + .page-container .page-main-content .page-main-content-middle { + padding: calc(var(--components-spacing) * 0.8) 0; + } +} +@media (max-width: 500px) { + .page-container .page-main-content .page-main-content-middle { + padding: calc(var(--components-spacing) * 0.6) 0; + } +} +.page-container .page-main-content .page-main-content-middle .main-content { + width: var(--page-content-width); + max-width: var(--page-content-max-width); + height: 100%; + transition-delay: 0s, 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.1s, 0.1s; + transition-property: color, background, box-shadow, border-color, max-width, width; +} +.has-toc .page-container .page-main-content .page-main-content-middle .main-content { + max-width: var(--page-content-max-width-2); +} +@media (max-width: 800px) { + .page-container .page-main-content .page-main-content-middle .main-content { + width: var(--page-content-width-tablet); + } +} +@media (max-width: 500px) { + .page-container .page-main-content .page-main-content-middle .main-content { + width: var(--page-content-width-mobile); + } +} +.page-container .page-main-content .page-main-content-bottom { + width: 100%; +} +.page-container .post-tools { + position: fixed; + top: calc(var(--header-height) + var(--components-spacing)); + box-sizing: border-box; + opacity: 0; + transition-delay: 0s, 0s, 0s, 0s, 0s, 0s, 0.2s, 0s; + transition-timing-function: ease, ease, ease, ease, ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.2s, 0.2s, 0.2s, 0.2s; + transition-property: color, background, box-shadow, border-color, top, transform, opacity, left; +} +.page-container .post-tools.right-toc { + left: calc((100vw - var(--page-content-max-width) / 2) - 5rem); +} +.page-container .post-tools.left-toc { + right: calc((100vw - var(--page-content-max-width) / 2) - 5rem); +} +.header-shrink .page-container .post-tools { + top: calc(var(--header-shrink-height) + var(--components-spacing)); +} +@media (max-width: 800px) { + .page-container .post-tools { + display: none !important; + } +} +.page-container .side-tools { + position: fixed; + right: 0; + bottom: 1.6rem; + box-sizing: border-box; + padding-right: 1rem; +} +@media (max-width: 800px) { + .page-container .side-tools { + padding-right: 0; + } +} +.page-container .side-tools:hover .side-tools-container { + transform: translateX(0); + opacity: 1; +} +.page-container .tablet-post-toc-mask { + position: fixed; + top: 0; + right: 0; + z-index: 1010; + box-sizing: border-box; + width: 100%; + height: 100%; + background: rgba(0,0,0,0); + visibility: hidden; + transition-delay: 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.2s; + transition-property: color, background, box-shadow, border-color, visibility; +} +.page-container .tablet-post-toc-mask .tablet-post-toc { + position: absolute; + top: 0; + left: 0; + box-sizing: border-box; + width: 22rem; + height: 100%; + padding: 1.2rem 1rem; + background: var(--background-color-1); + transform: translateX(-100%); + transition-delay: 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.4s; + transition-property: color, background, box-shadow, border-color, transform; +} +@media (max-width: 500px) { + .page-container .tablet-post-toc-mask .tablet-post-toc { + width: 70%; + } +} +.search-pop-overlay { + position: fixed; + top: 0; + left: 0; + z-index: 1008; + display: flex; + width: 100%; + height: 100%; + background: rgba(0,0,0,0); + visibility: hidden; + transition-delay: 0s, 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.3s, 0.3s; + transition-property: color, background, box-shadow, border-color, visibility, background; +} +.search-pop-overlay.active { + background: rgba(0,0,0,0.35); + visibility: visible; +} +.search-pop-overlay.active .search-popup { + transform: scale(1); +} +.search-pop-overlay .search-popup { + z-index: 1006; + width: 70%; + height: 80%; + margin: auto; + background: var(--background-color-1); + border-radius: 0.4rem; + transform: scale(0); + transition-delay: 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.3s; + transition-property: color, background, box-shadow, border-color, transform; +} +@media (max-width: 800px) { + .search-pop-overlay .search-popup { + width: 80%; + } +} +@media (max-width: 500px) { + .search-pop-overlay .search-popup { + width: 90%; + } +} +.search-pop-overlay .search-popup .search-header { + display: flex; + align-items: center; + height: 3rem; + padding: 0 1rem; + background: var(--text-color-6); + border-top-left-radius: 0.2rem; + border-top-right-radius: 0.2rem; +} +.search-pop-overlay .search-popup .search-header .search-input-field-pre { + margin-right: 0.2rem; + color: var(--text-color-3); + font-size: 1.3rem; + cursor: pointer; +} +.search-pop-overlay .search-popup .search-header .search-input-container { + flex-grow: 1; + padding: 0.2rem; +} +.search-pop-overlay .search-popup .search-header .search-input-container .search-input { + width: 100%; + color: var(--text-color-3); + font-size: 1.2rem; + background: transparent; + border: 0; + outline: 0; +} +.search-pop-overlay .search-popup .search-header .search-input-container .search-input::-webkit-search-cancel-button { + display: none; +} +.search-pop-overlay .search-popup .search-header .search-input-container .search-input::-webkit-input-placeholder { + color: var(--text-color-4); + font-size: 1rem; +} +.search-pop-overlay .search-popup .search-header .close-popup-btn { + color: var(--text-color-3); + font-size: 1.2rem; + cursor: pointer; +} +.search-pop-overlay .search-popup .search-header .close-popup-btn:hover { + color: var(--text-color-1); +} +.search-pop-overlay .search-popup #search-result { + position: relative; + display: flex; + box-sizing: border-box; + height: calc(100% - 3rem); + padding: 0.3rem 1.5rem; + overflow: auto; +} +.search-pop-overlay .search-popup #search-result .search-result-list { + width: 100%; + height: 100%; + font-size: 1rem; +} +.search-pop-overlay .search-popup #search-result .search-result-list li { + box-sizing: border-box; + margin: 0.8rem 0; + padding: 0.8rem 0; + border-bottom: 0.1rem dashed var(--border-color); +} +.search-pop-overlay .search-popup #search-result .search-result-list li:last-child { + border-bottom: none; +} +.search-pop-overlay .search-popup #search-result .search-result-list li .search-result-title { + position: relative; + display: flex; + align-items: center; + margin-bottom: 0.8rem; + padding-left: 1rem; + font-weight: bold; +} +.search-pop-overlay .search-popup #search-result .search-result-list li .search-result-title::after { + position: absolute; + top: 50%; + left: 0; + width: 0.4rem; + height: 0.4rem; + background: var(--text-color-3); + border-radius: 50%; + transform: translateY(-50%); + content: ''; +} +.search-pop-overlay .search-popup #search-result .search-result-list li .search-result { + margin: 0; + padding-left: 1rem; + line-height: 2rem; + word-wrap: break-word; +} +.search-pop-overlay .search-popup #search-result .search-result-list li a:hover { + color: var(--text-color-3); +} +.search-pop-overlay .search-popup #search-result .search-result-list li .search-keyword { + color: var(--primary-color); + font-weight: bold; + border-bottom: 0.1rem dashed var(--primary-color); +} +.search-pop-overlay .search-popup #search-result #no-result { + margin: auto; + color: var(--text-color-4); +} +.post-toc-wrap { + width: 100%; + height: 100%; + overflow-y: auto; + font-size: 0.92rem; +} +.post-toc-wrap::-webkit-scrollbar-thumb { + background: var(--toc-scrollbar-color); + border-radius: 0.3rem; +} +.post-toc-wrap::-webkit-scrollbar-track { + background: transparent; +} +.post-toc-wrap .post-toc ol { + position: relative; + box-sizing: border-box; + margin: 0; + text-align: left; + list-style: none; +} +.post-toc-wrap .post-toc ol a { + transition-property: all; +} +.post-toc-wrap .post-toc .nav .nav-item { + position: relative; + box-sizing: border-box; + overflow: hidden; + line-height: 2; + white-space: nowrap; + text-overflow: ellipsis; +} +.post-toc-wrap .post-toc .nav .nav-item.active > .nav-child { + display: block; +} +.post-toc-wrap .post-toc .nav .nav-item .nav-link { + position: relative; + box-sizing: border-box; + padding-left: 0.8rem; +} +.post-toc-wrap .post-toc .nav .nav-item .nav-link:hover .nav-number, +.post-toc-wrap .post-toc .nav .nav-item .nav-link:hover .nav-text { + color: var(--primary-color); +} +.post-toc-wrap .post-toc .nav .nav-item .nav-link.active-current > .nav-child { + display: block; +} +.post-toc-wrap .post-toc .nav .nav-item .nav-link.active-current > .nav-child > .nav-item { + display: block; +} +.post-toc-wrap .post-toc .nav .nav-item .nav-link.active-current::before { + position: absolute; + top: 50%; + left: 0; + width: 0.2rem; + height: 1.2rem; + background: var(--primary-color); + border-radius: 0.2rem; + transform: translateY(-50%); + content: ''; +} +.post-toc-wrap .post-toc .nav .nav-item .nav-link.active-current .nav-number, +.post-toc-wrap .post-toc .nav .nav-item .nav-link.active-current .nav-text { + color: var(--primary-color); +} +.post-toc-wrap .post-toc .nav .nav-item .nav-link .nav-number, +.post-toc-wrap .post-toc .nav .nav-item .nav-link .nav-text { + color: var(--toc-text-color); +} +.post-toc-wrap .post-toc .nav .nav-item .nav-child { + padding-left: 1rem; + display: block; +} +.comments-container { + display: inline-block; + width: 100%; + margin-top: var(--components-spacing); +} +.comments-container .comment-area-title { + width: 100%; + color: var(--text-color-3); + font-size: 1.38rem; + line-height: 2; +} +.comments-container .comment-area-title i { + color: var(--text-color-3); +} +@media (max-width: 800px) { + .comments-container .comment-area-title { + font-size: 1.2rem; + } +} +.comments-container .error-tips { + margin-top: 1rem; + color: var(--text-color-3); + font-size: 1rem; +} +.progress-bar-container { + position: fixed; + top: 0; + left: 0; + z-index: 1009; + width: 100%; +} +.progress-bar-container .pjax-progress-bar { + position: absolute; + top: 0; + left: 0; + z-index: 1008; + width: 0; + height: 2px; + background: var(--pjax-progress-bar-color); + visibility: hidden; + opacity: 0; + transition-delay: 0s, 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, linear; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.1s, 0.1s; + transition-property: color, background, box-shadow, border-color, width, opacity; +} +.progress-bar-container .pjax-progress-bar.show { + visibility: visible; + opacity: 1; +} +.progress-bar-container .pjax-progress-icon { + position: absolute; + top: 0.4rem; + right: 0.3rem; + z-index: 1008; + color: var(--text-color-3); + font-size: 1.1rem; + visibility: hidden; +} +@media (max-width: 800px) { + .progress-bar-container .pjax-progress-icon { + top: 0.3rem; + right: 0.2rem; + font-size: 1rem; + } +} +.progress-bar-container .pjax-progress-icon.show { + visibility: visible; +} +.progress-bar-container .scroll-progress-bar { + position: absolute; + top: 0; + left: 0; + z-index: 1007; + width: 0; + height: var(--header-scroll-progress-bar-height); + background: var(--primary-color); + visibility: hidden; + transition-delay: 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, linear; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.1s; + transition-property: color, background, box-shadow, border-color, width; +} +.progress-bar-container .scroll-progress-bar.hide { + display: none !important; +} +.header-wrapper { + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + width: 100%; + height: 100%; + padding-top: var(--header-scroll-progress-bar-height); + background: var(--background-color-1); + box-shadow: 1px 2px 6px var(--shadow-color); +} +.header-wrapper:hover { + box-shadow: 1px 2px 10px var(--shadow-hover-color); +} +.header-wrapper .header-content { + z-index: 1005; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + width: var(--page-content-width); + max-width: var(--page-content-max-width); + height: 100%; + transition-delay: 0s, 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.1s, 0.1s; + transition-property: color, background, box-shadow, border-color, max-width, width; +} +.header-wrapper .header-content.has-first-screen { + max-width: var(--page-content-max-width-2); +} +.has-toc .header-wrapper .header-content { + max-width: var(--page-content-max-width-2); +} +@media (max-width: 800px) { + .header-wrapper .header-content { + width: var(--page-content-width-tablet); + } +} +@media (max-width: 500px) { + .header-wrapper .header-content { + width: var(--page-content-width-mobile); + } +} +.header-wrapper .header-content .left { + display: flex; + align-items: center; + justify-content: flex-start; + height: 100%; + font-size: 2rem; + transition-delay: 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, linear; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.2s; + transition-property: color, background, box-shadow, border-color, transform; +} +.header-shrink .header-wrapper .header-content .left { + transform: scale(0.72); + transform-origin: left; +} +.header-wrapper .header-content .left .logo-image { + flex-shrink: 0; + width: 2.8rem; + height: 2.8rem; + margin-right: 0.5rem; +} +@media (max-width: 800px) { + .header-wrapper .header-content .left .logo-image { + width: 2.52rem; + height: 2.52rem; + } +} +@media (max-width: 500px) { + .header-wrapper .header-content .left .logo-image { + width: 2.24rem; + height: 2.24rem; + } +} +.header-wrapper .header-content .left .logo-image img { + width: 100%; + border-radius: 0.4rem; +} +.header-wrapper .header-content .left .site-name { + color: var(--text-color-1); + font-weight: 600; + font-size: 1.88rem; + line-height: 1; + letter-spacing: 1px; +} +@media (max-width: 800px) { + .header-wrapper .header-content .left .site-name { + font-size: 1.692rem; + } +} +@media (max-width: 500px) { + .header-wrapper .header-content .left .site-name { + font-size: 1.504rem; + } +} +.header-wrapper .header-content .right .pc .menu-list { + display: flex; + align-items: center; +} +@media (max-width: 800px) { + .header-wrapper .header-content .right .pc .menu-list { + display: none; + } +} +.header-wrapper .header-content .right .pc .menu-list .menu-item { + position: relative; + float: left; + box-sizing: border-box; + margin-left: 2rem; + color: var(--text-color-3); + font-size: 1rem; + cursor: pointer; +} +.header-wrapper .header-content .right .pc .menu-list .menu-item:first-child { + margin-left: 0; +} +.header-wrapper .header-content .right .pc .menu-list .menu-item a:hover::after, +.header-wrapper .header-content .right .pc .menu-list .menu-item .active::after { + position: absolute; + bottom: -10px; + left: 50%; + box-sizing: border-box; + width: 100%; + height: 2px; + background: var(--primary-color); + transform: translateX(-50%); + content: ''; + transition-delay: 0s, 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, linear, linear; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.2s, 0.2s; + transition-property: color, background, box-shadow, border-color, transform, bottom; +} +.header-shrink .header-wrapper .header-content .right .pc .menu-list .menu-item a:hover::after, +.header-shrink .header-wrapper .header-content .right .pc .menu-list .menu-item .active::after { + bottom: calc(-1 * calc(var(--header-shrink-height) * 0.5 - 12px)); +} +.header-wrapper .header-content .right .pc .menu-list .menu-item.search { + margin-left: 26px; + font-size: 1.5rem; +} +.header-wrapper .header-content .right .pc .menu-list .menu-item.search i { + color: var(--text-color-3); +} +.header-wrapper .header-content .right .mobile { + display: flex; + align-items: center; + justify-content: space-between; +} +.header-wrapper .header-content .right .mobile .icon-item { + position: relative; + display: none; + width: 20px; + height: 20px; + margin-left: 12px; + color: var(--text-color-3); + font-size: 18px; + cursor: pointer; +} +.header-wrapper .header-content .right .mobile .icon-item i { + color: var(--text-color-3); +} +.header-wrapper .header-content .right .mobile .icon-item:first-child { + margin-left: 0; +} +@media (max-width: 800px) { + .header-wrapper .header-content .right .mobile .icon-item { + display: flex; + align-items: center; + justify-content: center; + } +} +.header-wrapper .header-content .right .mobile .menu-bar .menu-bar-middle { + position: relative; + width: 18px; + height: 2.5px; + background: var(--text-color-3); +} +.header-drawer-show .header-wrapper .header-content .right .mobile .menu-bar .menu-bar-middle { + background: transparent; +} +.header-wrapper .header-content .right .mobile .menu-bar .menu-bar-middle::before, +.header-wrapper .header-content .right .mobile .menu-bar .menu-bar-middle::after { + position: absolute; + left: 0; + width: 100%; + height: 2.5px; + background: var(--text-color-3); + content: ''; + transition-delay: 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.38s; + transition-property: color, background, box-shadow, border-color, transform; +} +.header-wrapper .header-content .right .mobile .menu-bar .menu-bar-middle::before { + top: -6px; +} +.header-drawer-show .header-wrapper .header-content .right .mobile .menu-bar .menu-bar-middle::before { + transform: translateY(6px) rotate(45deg); +} +.header-wrapper .header-content .right .mobile .menu-bar .menu-bar-middle::after { + bottom: -6px; +} +.header-drawer-show .header-wrapper .header-content .right .mobile .menu-bar .menu-bar-middle::after { + transform: translateY(-6px) rotate(-45deg); +} +.header-wrapper .header-drawer { + position: absolute; + top: 0; + left: 0; + z-index: 1002; + width: 100%; + padding: var(--header-height) 0 20px 0; + background: var(--background-color-1); + transform: scaleY(0); + transform-origin: top; + transition-delay: 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.38s; + transition-property: color, background, box-shadow, border-color, transform; +} +.header-drawer-show .header-wrapper .header-drawer { + transform: scaleY(1); +} +.header-wrapper .header-drawer .drawer-menu-list { + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; +} +.header-wrapper .header-drawer .drawer-menu-list .drawer-menu-item { + height: 38px; + margin: 6px 0; + font-size: 1rem; +} +.header-wrapper .header-drawer .drawer-menu-list .drawer-menu-item a { + padding: 6px 20px; + color: var(--text-color-3); + border-radius: 20px; +} +.header-wrapper .header-drawer .drawer-menu-list .drawer-menu-item a:hover { + color: var(--text-color-2); + border: 1px solid var(--text-color-3); +} +.header-wrapper .header-drawer .drawer-menu-list .drawer-menu-item a.active { + color: var(--text-color-2); + border: 1px solid var(--text-color-3); +} +.header-wrapper .window-mask { + position: absolute; + top: 0; + z-index: 1001; + width: 100%; + height: 100vh; + background: rgba(0,0,0,0.4); + visibility: hidden; + opacity: 0; + transition-delay: 0s, 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.38s, 0.38s; + transition-property: color, background, box-shadow, border-color, transform, opacity; +} +.header-drawer-show .header-wrapper .window-mask { + visibility: visible; + opacity: 1; +} +.header-drawer-show { + overflow: hidden; +} +.post-tools-container { + padding-top: var(--components-spacing); +} +.post-tools-container .tools-list li { + position: relative; + box-sizing: border-box; + width: 2.5rem; + height: 2.5rem; + margin-bottom: 0.8rem; + color: var(--text-color-3); + font-size: 1.2rem; + background: var(--background-color-1); + border-radius: 50%; + box-shadow: 2px 2px 5px var(--shadow-color); + cursor: pointer; +} +.post-tools-container .tools-list li:hover { + box-shadow: 2px 2px 8px var(--shadow-hover-color); +} +.post-tools-container .tools-list li i { + color: var(--text-color-3); +} +.post-tools-container .tools-list li:hover { + color: var(--background-color-1); + background: var(--primary-color); +} +.post-tools-container .tools-list li:hover i { + color: var(--background-color-1); +} +.post-tools-container .tools-list li:last-child { + margin-bottom: 0; +} +.post-tools-container .tools-list li.toggle-show-toc { + display: none; +} +.post-tools-container .tools-list li.go-to-comments .post-comments-count { + position: absolute; + top: 0; + right: -1rem; + display: none; + align-items: center; + justify-content: center; + box-sizing: border-box; + min-width: 1.1rem; + height: 1.1rem; + padding: 0 0.2rem; + color: var(--badge-color); + font-size: 12px; + background: var(--badge-background-color); + border-radius: 0.4rem; +} +@media (max-width: 800px) { + .post-tools-container .tools-list li.go-to-comments .post-comments-count { + display: none !important; + } +} +.side-tools-show-handle { + transform: translateX(100%); + opacity: 0; + transition-delay: 0s, 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, linear, linear; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.26s, 0.26s; + transition-property: color, background, box-shadow, border-color, transform, opacity; +} +.side-tools-show-handle.show { + transform: translateX(0); + opacity: 1; +} +.side-tools-container .tools-item { + width: 2.2rem; + height: 2.2rem; + margin-bottom: 0.25rem; + color: var(--text-color-3); + font-size: 1.1rem; + background: var(--background-color-1); + border-right: none; + border-radius: 0.2rem; + box-shadow: 0.1rem 0.1rem 0.2rem var(--shadow-color); + cursor: pointer; +} +.side-tools-container .tools-item i { + color: var(--text-color-3); +} +.side-tools-container .tools-item:hover { + color: var(--background-color-1); + background: var(--primary-color); + box-shadow: 0.2rem 0.2rem 0.4rem var(--shadow-color); +} +.side-tools-container .tools-item:hover i { + color: var(--background-color-1); +} +@media (max-width: 800px) { + .side-tools-container .tools-item { + width: 1.98rem; + height: 1.98rem; + font-size: 0.99rem; + } + .side-tools-container .tools-item.toggle-show-toc-tablet, + .side-tools-container .tools-item.go-to-comments-tablet { + display: flex !important; + } +} +.side-tools-container .tools-item.rss a { + width: 100%; + height: 100%; + border-radius: 0.2rem; +} +.side-tools-container .tools-item.rss a:hover { + color: var(--background-color-1); + background: var(--primary-color); + box-shadow: 0.2rem 0.2rem 0.4rem var(--shadow-color); +} +.side-tools-container .tools-item.toggle-show-toc-tablet, +.side-tools-container .tools-item.go-to-comments-tablet { + display: none; +} +.side-tools-container .exposed-tools-list .tool-scroll-to-top { + display: none; +} +.side-tools-container .exposed-tools-list .tool-scroll-to-top.show { + display: flex; +} +.side-tools-container .exposed-tools-list .tool-scroll-to-top.show-arrow .percent { + display: none; +} +.side-tools-container .exposed-tools-list .tool-scroll-to-top.show-arrow .arrow { + display: flex; +} +.side-tools-container .exposed-tools-list .tool-scroll-to-top.show-percent .percent { + display: flex; +} +.side-tools-container .exposed-tools-list .tool-scroll-to-top.show-percent .arrow { + display: none; +} +.side-tools-container .exposed-tools-list .tool-scroll-to-top:hover .percent { + display: none; +} +.side-tools-container .exposed-tools-list .tool-scroll-to-top:hover .arrow { + display: flex; +} +.side-tools-container .exposed-tools-list .tool-scroll-to-top .percent { + font-size: 1rem; +} +.archive-list-container .archive-item { + margin-bottom: var(--components-spacing); +} +.archive-list-container .archive-item:last-child { + margin-bottom: 0; +} +.archive-list-container .archive-item .archive-item-header { + margin-bottom: 0.8rem; +} +.archive-list-container .archive-item .archive-item-header .archive-year { + margin-right: 6px; + color: var(--text-color-2); + font-weight: 600; + font-size: 1.6rem; +} +@media (max-width: 800px) { + .archive-list-container .archive-item .archive-item-header .archive-year { + font-size: 1.44rem; + } +} +.archive-list-container .archive-item .article-list { + padding-left: 1.2rem; +} +.archive-list-container .archive-item .article-list .article-item { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + padding: 0.816rem 0 0.816rem 1.2rem; + font-size: 1rem; + border-left: 1px dashed var(--border-color); +} +.archive-list-container .archive-item .article-list .article-item::before { + position: absolute; + top: 50%; + left: -0.3rem; + z-index: 1001; + box-sizing: border-box; + width: 0.6rem; + height: 0.6rem; + background: var(--text-color-5); + border-radius: 50%; + transform: translateY(-50%); + content: ''; +} +.archive-list-container .archive-item .article-list .article-item:hover::before { + background: var(--text-color-4); +} +.archive-list-container .archive-item .article-list .article-item:hover a.article-title { + color: var(--primary-color); +} +.archive-list-container .archive-item .article-list .article-item .article-date { + width: 3.6rem; + margin-right: 1.2rem; + color: var(--text-color-3); + font-size: 1rem; +} +.archive-list-container .archive-item .article-list .article-item a.article-title { + width: calc(100% - 3.6rem); + color: var(--text-color-3); +} +.footer { + padding: 1rem 0; + color: var(--text-color-4); + font-size: 1rem; +} +.footer a { + color: var(--text-color-4); +} +.footer a:hover { + color: var(--primary-color); +} +.footer a.no-pointer { + pointer-events: none; +} +.footer .website-info-box { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} +.footer .website-info-box.shields { + padding: 0 1rem; +} +.footer .website-info-box.shields img { + height: 22px !important; +} +.footer .website-info-box.shields a { + position: relative; + display: inline-flex; + box-sizing: border-box; + text-decoration: none; +} +.footer .website-info-box.shields .shields-item, +.footer .website-info-box.shields .count-box { + margin: 0.3rem 0.2rem; + user-select: none; +} +@media (max-width: 500px) { + .footer .website-info-box.shields .shields-item, + .footer .website-info-box.shields .count-box { + margin: 0.12rem -0.3rem; + transform: scale(0.9); + transform-origin: center center; + } +} +.footer .website-info-box .info-item { + position: relative; + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + box-sizing: border-box; + color: var(--text-color-4); +} +.footer .website-info-box .info-item.shields .count-box { + display: flex; + justify-content: flex-start; + height: 22px; + color: #f2f2f2; + font-size: 12.6px; +} +.footer .website-info-box .info-item.shields .count-box .item-type, +.footer .website-info-box .info-item.shields .count-box .item-value { + display: flex; + align-items: center; + height: 100%; + padding: 0 6px; +} +.footer .website-info-box .info-item.shields .count-box .item-type { + margin-right: 0; + background: #555; +} +.footer .website-info-box .info-item.shields .count-box .item-value.uv { + background: #ff7f50; +} +.footer .website-info-box .info-item.shields .count-box .item-value.pv { + background: #ffa500; +} +.footer .website-info-box .info-item.shields .count-box .item-value.word { + background: #cd5c5c; +} +.footer .website-info-box .info-item.default { + margin: 0.2rem 0; +} +.footer .website-info-box .info-item.default .count-box { + margin-right: 15px; +} +.footer .website-info-box .info-item.default .count-box::before { + position: absolute; + top: 50%; + left: -10px; + box-sizing: border-box; + transform: translateY(-55%); + content: '|'; +} +.footer .website-info-box .info-item.default .count-box:first-child::before { + display: none; +} +.footer .website-info-box .info-item.default .count-box:last-child { + margin-right: 0; +} +.footer .website-info-box .info-item.default .count-box .item-type { + margin-right: 4px; +} +.footer .website-info-box .info-item.count-item { + display: flex; +} +.footer .website-info-box .info-item.count-item .count-box.uv, +.footer .website-info-box .info-item.count-item .count-box.pv { + display: none; +} +.footer .website-info-box .info-item.deploy-info { + display: flex; +} +.footer .website-info-box .info-item.deploy-info a, +.footer .website-info-box .info-item.deploy-info .tooltip { + display: flex; + align-items: center; +} +.footer .website-info-box .info-item.deploy-info img { + height: 1.08rem; + margin: 0 0.4rem; +} +.footer .icon-animate { + animation: heartbeat-animate 1.2s ease-in-out infinite; +} +.paginator { + margin-top: 2rem; + padding-top: 0.2rem; + user-select: none; +} +.paginator .btn-wrap { + cursor: not-allowed; +} +.paginator .btn-wrap.allowed { + cursor: pointer; +} +.paginator .btn-wrap.allowed a.jump-btn { + pointer-events: auto; +} +.paginator .btn-wrap.allowed a.jump-btn i { + color: var(--text-color-3); +} +.paginator .btn-wrap a.jump-btn { + padding: 0.2rem; + pointer-events: none; +} +.paginator .btn-wrap a.jump-btn i { + color: var(--text-color-4); + font-size: 1rem; +} +.paginator .base-color-size { + color: var(--text-color-3); + font-size: 1rem; +} +.paginator .page-number-box { + margin: 0 1rem; +} +.paginator .page-number-box .page-number-input { + width: 2.8rem; + height: 1.8rem; + margin: 0; + padding: 0; + font-weight: 400 !important; + text-align: center; + background: none; + border: 0.1rem solid var(--border-color); + border-radius: 0.3rem; + outline: none; + -webkit-appearance: none; +} +.paginator .page-number-box .page-number-input::-webkit-outer-spin-button, +.paginator .page-number-box .page-number-input::-webkit-inner-spin-button { + -webkit-appearance: none !important; +} +.paginator .page-number-box .page-number-input:hover, +.paginator .page-number-box .page-number-input:focus { + border-color: var(--primary-color); +} +.paginator .page-number-box .delimiter { + margin: 0 0.6rem; +} +.page-template-container { + box-sizing: border-box; + margin-bottom: 2rem; + padding: 2rem; + background: var(--content-background-color); + border-radius: var(--box-border-radius); + box-shadow: 0 0 8px var(--shadow-color); +} +.page-template-container:hover { + box-shadow: 0 0 12px var(--shadow-hover-color); +} +@media (max-width: 800px) { + .page-template-container { + margin-bottom: 1.6rem; + padding: 1.6rem; + border-radius: calc(var(--box-border-radius) * 0.8); + } +} +@media (max-width: 500px) { + .page-template-container { + margin-bottom: 1.2rem; + padding: 1.2rem; + border-radius: calc(var(--box-border-radius) * 0.6); + } +} +.page-template-container .page-template-content { + color: var(--text-color-3); +} +.page-template-container .page-template-content h1:first-child, +.page-template-container .page-template-content h2:first-child, +.page-template-container .page-template-content h3:first-child, +.page-template-container .page-template-content h4:first-child, +.page-template-container .page-template-content h5:first-child, +.page-template-container .page-template-content h6:first-child { + margin-top: 0; +} +.first-screen-container { + width: 100%; + height: 100vh; + overflow: hidden; + background: url("/images/bg.svg") center center/cover no-repeat; +} +.first-screen-container .first-screen-content { + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; + width: 80%; + height: 90%; + padding-top: calc(var(--header-height) * 0.5); +} +@media (max-width: 800px) { + .first-screen-container .first-screen-content { + padding-top: calc(var(--header-height) * 0.5 * 0.9); + } +} +@media (max-width: 500px) { + .first-screen-container .first-screen-content { + padding-top: calc(var(--header-height) * 0.5 * 0.8); + } +} +.first-screen-container .first-screen-content .description { + color: var(--first-screen-font-color-light); + font-weight: bold; + font-size: var(--first-screen-font-size); + line-height: 1.8; + text-align: center; + opacity: 0; +} +.dark-mode .first-screen-container .first-screen-content .description { + color: var(--first-screen-font-color-dark); +} +.dark-mode .first-screen-container .first-screen-content .description .desc-item .desc, +.dark-mode .first-screen-container .first-screen-content .description .desc-item .cursor { + color: var(--first-screen-font-color-dark); +} +.first-screen-container .first-screen-content .description .desc-item .desc { + color: var(--first-screen-font-color-light); +} +.first-screen-container .first-screen-content .description .desc-item .cursor { + color: var(--first-screen-font-color-light); + animation: blink-caret 0.8s step-end infinite; +} +@media (max-width: 800px) { + .first-screen-container .first-screen-content .description { + font-size: calc(var(--first-screen-font-size) * 0.9); + } +} +.first-screen-container .first-screen-content .bottom-placeholder { + width: 100%; +} +.first-screen-container .first-screen-content .bottom-placeholder .sc-icon-list { + display: flex; + justify-content: center; +} +.first-screen-container .first-screen-content .bottom-placeholder .sc-icon-list .sc-icon-item { + display: flex; + align-items: center; + margin: 0 1rem; + cursor: pointer; +} +.first-screen-container .first-screen-content .bottom-placeholder .sc-icon-list .sc-icon-item i { + color: var(--text-color-3); + font-size: var(--first-screen-icon-size); +} +.first-screen-container .first-screen-content .bottom-placeholder .sc-icon-list .sc-icon-item svg { + width: var(--first-screen-icon-size); + height: var(--first-screen-icon-size); +} +.first-screen-container .first-screen-content .bottom-placeholder .sc-icon-list .sc-icon-item svg path { + fill: var(--text-color-3); +} +@media (max-width: 800px) { + .first-screen-container .first-screen-content .bottom-placeholder .sc-icon-list .sc-icon-item i { + font-size: calc(var(--first-screen-icon-size) * 0.9); + } + .first-screen-container .first-screen-content .bottom-placeholder .sc-icon-list .sc-icon-item svg { + width: calc(var(--first-screen-icon-size) * 0.9); + height: calc(var(--first-screen-icon-size) * 0.9); + } +} +.first-screen-container .first-screen-content .bottom-placeholder .sc-icon-list .sc-icon-item a { + display: flex !important; + align-items: center !important; +} +.zoom-in-image-mask { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1008; + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + background: rgba(0,0,0,0); + visibility: hidden; + transition-delay: 0s, 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, linear, linear; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.3s, 0.3s; + transition-property: color, background, box-shadow, border-color, visibility, background; +} +.zoom-in-image-mask.show { + background: rgba(0,0,0,0.5); + visibility: visible; +} +.zoom-in-image-mask.show .zoom-in-image { + cursor: zoom-out; +} +.zoom-in-image-mask .zoom-in-image { + position: absolute; + z-index: 1009; + transform-origin: center center; + will-change: transform; + transition-delay: 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, linear; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.3s; + transition-property: color, background, box-shadow, border-color, transform; +} +.category-list-container { + box-sizing: border-box; + margin-bottom: 0; + padding: 2rem; + background: var(--content-background-color); + border-radius: var(--box-border-radius); + box-shadow: 0 0 8px var(--shadow-color); +} +.category-list-container:hover { + box-shadow: 0 0 12px var(--shadow-hover-color); +} +@media (max-width: 800px) { + .category-list-container { + margin-bottom: 0; + padding: 1.6rem; + border-radius: calc(var(--box-border-radius) * 0.8); + } +} +@media (max-width: 500px) { + .category-list-container { + margin-bottom: 0; + padding: 1.2rem; + border-radius: calc(var(--box-border-radius) * 0.6); + } +} +.category-list-container .category-list-content ul.site-all-category-list { + user-select: none; +} +.category-list-container .category-list-content ul.site-all-category-list > li.site-all-category-list-item { + position: relative; + box-sizing: border-box; + margin-bottom: 0.8rem; + font-size: 1rem; +} +.category-list-container .category-list-content ul.site-all-category-list > li.site-all-category-list-item:last-child { + margin-bottom: 0; +} +.category-list-container .category-list-content ul.site-all-category-list > li.site-all-category-list-item .self-category-info { + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + box-sizing: border-box; + padding: 0.5rem 0; +} +.category-list-container .category-list-content ul.site-all-category-list > li.site-all-category-list-item .self-category-info .left { + position: relative; + box-sizing: border-box; +} +.category-list-container .category-list-content ul.site-all-category-list > li.site-all-category-list-item .self-category-info .left .icon { + padding-right: 0.4rem; + color: var(--text-color-3); + font-size: 0.8rem; + cursor: pointer; +} +.category-list-container .category-list-content ul.site-all-category-list > li.site-all-category-list-item .self-category-info .right .site-all-category-list-count { + color: var(--text-color-3); +} +.category-list-container .category-list-content ul.site-all-category-list > li.site-all-category-list-item .site-all-category-list-child { + position: relative; + box-sizing: border-box; + height: 0; + margin-left: 1.5rem; + overflow: hidden; + visibility: hidden; +} +.article-meta-info-container { + color: var(--text-color-4); +} +.article-meta-info-container.post { + font-size: 0.86rem; +} +.article-meta-info-container.post i.icon, +.article-meta-info-container.post i.fas { + font-size: 0.86rem; +} +.article-meta-info-container.home { + display: flex; + align-items: center; + justify-content: space-between; + font-size: 0.92rem; +} +.article-meta-info-container.home i.icon, +.article-meta-info-container.home i.fas { + font-size: 0.92rem; +} +.article-meta-info-container a { + color: var(--text-color-4); +} +.article-meta-info-container a:hover { + color: var(--primary-color); +} +.article-meta-info-container .article-meta-info { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + letter-spacing: 0.6px; +} +.article-meta-info-container .article-meta-info .meta-info-item { + display: flex; + align-items: center; + justify-content: flex-start; + box-sizing: border-box; + margin-right: 0.6rem; +} +.article-meta-info-container .article-meta-info .meta-info-item:last-child { + margin-right: 0; +} +.article-meta-info-container .article-meta-info .meta-info-item ul, +.article-meta-info-container .article-meta-info .meta-info-item li { + display: inline; +} +.article-meta-info-container .article-meta-info .article-create-date { + display: var(--post-create-datetime); +} +.article-meta-info-container .article-meta-info .article-create-date .mobile { + display: none; +} +@media (max-width: 800px) { + .article-meta-info-container .article-meta-info .article-create-date .pc { + display: none; + } + .article-meta-info-container .article-meta-info .article-create-date .mobile { + display: inline-block; + } +} +.article-meta-info-container .article-meta-info .article-update-date { + display: var(--post-update-datetime); +} +@media (max-width: 800px) { + .article-meta-info-container .article-meta-info .article-update-date { + display: none; + } +} +.article-meta-info-container .article-meta-info .article-category { + word-spacing: 0; +} +.article-meta-info-container .article-meta-info .article-category .category-item i.icon { + font-size: 0.644rem; +} +@media (max-width: 500px) { + .article-meta-info-container .article-meta-info .article-category { + display: none !important; + } +} +.article-meta-info-container .article-meta-info .article-tag { + word-spacing: 0; +} +.article-meta-info-container .article-meta-info .article-tag .tag-item { + margin-right: 0.4rem; +} +.article-meta-info-container .article-meta-info .article-tag .tag-item:last-child { + margin-right: 0; +} +.article-meta-info-container .article-meta-info .article-tag .tag-item .tag-separator { + margin-right: 0.2rem; +} +.article-meta-info-container .article-meta-info .article-tag .tag-item .tag-separator i.icon { + font-size: 0.644rem; +} +@media (max-width: 800px) { + .article-meta-info-container .article-meta-info .article-tag { + display: none !important; + } +} +.article-meta-info-container .article-meta-info .article-pv { + display: none; +} +.article-meta-info-container .home-read-more { + display: flex; + align-items: center; + justify-content: space-between; + font-size: 0.92rem; +} +.article-meta-info-container .home-read-more i.fas { + color: var(--text-color-4); + font-size: 1.012rem; +} +.article-copyright-info-container { + width: 100%; + margin-top: 1rem; + background: var(--background-color-2); + border-radius: calc(var(--box-border-radius) * 0.6); +} +.article-copyright-info-container .copyright-info-content { + width: 100%; + padding: 1.3rem; + overflow: hidden; + font-size: 1rem; +} +.article-copyright-info-container .copyright-info-content .copyright-info-top { + width: 100%; +} +.article-copyright-info-container .copyright-info-content .copyright-info-top .post-title { + width: 100%; + color: var(--text-color-3); + font-size: 1.1rem; +} +.article-copyright-info-container .copyright-info-content .copyright-info-top .post-link { + width: 100%; + margin-top: 0.2rem; + color: var(--text-color-4); +} +.article-copyright-info-container .copyright-info-content .copyright-info-bottom { + display: flex; + justify-content: flex-start; + margin-top: 1rem; +} +.article-copyright-info-container .copyright-info-content .copyright-info-bottom .bottom-item { + z-index: 1010; + margin-right: 1.2rem; +} +.article-copyright-info-container .copyright-info-content .copyright-info-bottom .bottom-item .type { + color: var(--text-color-4); +} +.article-copyright-info-container .copyright-info-content .copyright-info-bottom .bottom-item .content { + margin-top: 0.2rem; + color: var(--text-color-3); +} +.article-copyright-info-container .copyright-info-content .copyright-info-bottom .post-license .content i { + font-size: 1.1rem; +} +.article-copyright-info-container .copyright-info-content .copyright-bg { + position: absolute; + top: 50%; + right: 2rem; + color: var(--copyright-icon-bg-color); + font-size: 14rem; + transform: translateY(-50%); +} +@media (max-width: 500px) { + .article-copyright-info-container .copyright-info-content .copyright-bg { + font-size: 12rem; + } +} +.article-copyright-info-container .copy-copyright-info { + position: absolute; + top: 0.5rem; + right: 0.5rem; + box-sizing: border-box; + padding: 0.3rem; + cursor: pointer; +} +.article-copyright-info-container .copy-copyright-info i { + color: var(--text-color-3); +} +.home-content-container { + background: var(--background-color-1); +} +.home-content-container .home-article-list .home-article-item { + position: relative; + box-sizing: border-box; + margin-bottom: 2.8rem; + padding: 0; + background: var(--content-background-color); + border-radius: var(--box-border-radius); + box-shadow: 0 0 8px var(--shadow-color); + transition-delay: 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, linear; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.2s; + transition-property: color, background, box-shadow, border-color, transform; +} +.home-content-container .home-article-list .home-article-item:hover { + transform: scale(var(--home-post-hover-scale)); + box-shadow: 0 0 12px var(--shadow-hover-color); +} +@media (max-width: 800px) { + .home-content-container .home-article-list .home-article-item { + margin-bottom: 2.24rem; + padding: 0; + border-radius: calc(var(--box-border-radius) * 0.8); + } +} +@media (max-width: 500px) { + .home-content-container .home-article-list .home-article-item { + margin-bottom: 1.68rem; + padding: 0; + border-radius: calc(var(--box-border-radius) * 0.6); + } +} +.home-content-container .home-article-list .home-article-item .post-sticky-box { + position: absolute; + z-index: 1001; + display: flex; + align-items: center; + box-sizing: border-box; + padding: 0.1rem 0.4rem; + color: var(--text-color-4); + font-size: 12px; + letter-spacing: 1px; + background: var(--background-color-3); + border-radius: 0.3rem; + transform: scale(0.86); + cursor: default; + -moz-user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; +} +.home-content-container .home-article-list .home-article-item .post-sticky-box.cover { + top: 0.6rem; + right: 0.6rem; +} +.home-content-container .home-article-list .home-article-item .post-sticky-box.card { + top: 0.8rem; + right: 0.8rem; +} +@media (max-width: 500px) { + .home-content-container .home-article-list .home-article-item .post-sticky-box { + padding: 0; + background: none; + transform: rotate(45deg); + } + .home-content-container .home-article-list .home-article-item .post-sticky-box .sticky-name { + display: none; + } +} +.home-content-container .home-article-list .home-article-item .home-article-item-top { + width: 100%; + overflow: hidden; + background-color: var(--background-color-2); + border-top-left-radius: var(--box-border-radius); + border-top-right-radius: var(--box-border-radius); +} +@media (max-width: 800px) { + .home-content-container .home-article-list .home-article-item .home-article-item-top { + height: 7rem; + } +} +@media (max-width: 500px) { + .home-content-container .home-article-list .home-article-item .home-article-item-top { + height: 6rem; + } +} +.home-content-container .home-article-list .home-article-item .home-article-item-top .home-cover { + width: 100%; + height: 100%; + object-fit: cover; + transform-origin: center center; + transition-delay: 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, linear; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 1s; + transition-property: color, background, box-shadow, border-color, transform; +} +.home-content-container .home-article-list .home-article-item .home-article-item-top .home-cover:hover { + transform: scale(1.03); +} +.home-content-container .home-article-list .home-article-item .home-article-item-bottom { + padding: 2rem; +} +.home-content-container .home-article-list .home-article-item .home-article-item-bottom .sticky-icon { + position: absolute; + top: 1.2rem; + right: 1.2rem; + color: var(--text-color-4); + font-size: 1.2rem; + transform: rotate(45deg); +} +.home-content-container .home-article-list .home-article-item .home-article-item-bottom .home-article-title { + margin: 0; + color: var(--text-color-2); + font-weight: 600; + font-size: 1.4rem; + line-height: 1.5; +} +@media (max-width: 800px) { + .home-content-container .home-article-list .home-article-item .home-article-item-bottom .home-article-title { + font-size: 1.3rem; + } +} +@media (max-width: 500px) { + .home-content-container .home-article-list .home-article-item .home-article-item-bottom .home-article-title { + font-size: 1.2rem; + } +} +.home-content-container .home-article-list .home-article-item .home-article-item-bottom .home-article-content { + margin: 1.8rem 0; + color: var(--text-color-3); + text-align: justify; + word-wrap: break-word; +} +.archives-container { + box-sizing: border-box; + margin-bottom: 2rem; + padding: 2rem; + background: var(--content-background-color); + border-radius: var(--box-border-radius); + box-shadow: 0 0 8px var(--shadow-color); +} +.archives-container:hover { + box-shadow: 0 0 12px var(--shadow-hover-color); +} +@media (max-width: 800px) { + .archives-container { + margin-bottom: 1.6rem; + padding: 1.6rem; + border-radius: calc(var(--box-border-radius) * 0.8); + } +} +@media (max-width: 500px) { + .archives-container { + margin-bottom: 1.2rem; + padding: 1.2rem; + border-radius: calc(var(--box-border-radius) * 0.6); + } +} +.post-page-container { + display: flex; + justify-content: space-between; + width: 100%; + height: 100%; +} +.post-page-container.show-toc .pc-post-toc { + display: block; +} +.post-page-container.show-toc .article-content-container { + width: calc(100% - 15rem); +} +@media (max-width: 800px) { + .post-page-container .pc-post-toc { + display: none !important; + } + .post-page-container .article-content-container { + width: 100% !important; + } +} +.post-page-container .article-content-container { + order: 0; + width: 100%; + height: 100%; + box-sizing: border-box; + margin-bottom: 2rem; + padding: 0; + background: var(--content-background-color); + border-radius: var(--box-border-radius); + box-shadow: 0 0 8px var(--shadow-color); +} +.post-page-container .article-content-container:hover { + box-shadow: 0 0 12px var(--shadow-hover-color); +} +@media (max-width: 800px) { + .post-page-container .article-content-container { + margin-bottom: 1.6rem; + padding: 0; + border-radius: calc(var(--box-border-radius) * 0.8); + } +} +@media (max-width: 500px) { + .post-page-container .article-content-container { + margin-bottom: 1.2rem; + padding: 0; + border-radius: calc(var(--box-border-radius) * 0.6); + } +} +@media (max-width: 500px) { + .post-page-container .article-content-container { + padding: 0 0.2rem !important; + box-shadow: none !important; + } + .post-page-container .article-content-container:hover { + box-shadow: none !important; + } +} +.post-page-container .article-content-container .article-content-top { + display: flex; + align-items: flex-end; + width: 100%; + padding-top: 2rem; + padding-right: 2rem; + overflow: hidden; + background-color: var(--background-color-2); + border-top-left-radius: var(--box-border-radius); + border-top-right-radius: var(--box-border-radius); +} +@media (max-width: 800px) { + .post-page-container .article-content-container .article-content-top { + height: 12.8rem; + padding-top: 1.6rem; + padding-right: 1.6rem; + } + .post-page-container .article-content-container .article-content-top .cover-article-title { + padding: 0.8rem 1.6rem !important; + font-size: 1.44rem !important; + line-height: 1.5 !important; + } +} +@media (max-width: 500px) { + .post-page-container .article-content-container .article-content-top { + height: 11.8rem; + padding-top: 1.2rem; + padding-right: 1.2rem; + } + .post-page-container .article-content-container .article-content-top .cover-article-title { + padding: 0.6rem 1.2rem !important; + font-size: 1.28rem !important; + line-height: 1.4 !important; + } +} +.post-page-container .article-content-container .article-content-top .cover-article-title { + position: relative; + z-index: 1001; + box-sizing: border-box; + max-width: 100%; + max-height: 100%; + padding: 1rem 2rem; + overflow-y: auto; + color: var(--text-color-2); + font-weight: 600; + font-size: 1.76rem; + line-height: 1.6; + background: var(--background-color-1-transparent); + border-top-right-radius: var(--box-border-radius); + backdrop-filter: blur(2px); +} +.post-page-container .article-content-container .article-content-top .post-cover { + position: absolute; + top: 0; + left: 0; + box-sizing: border-box; + width: 100%; + height: 100%; + object-fit: cover; +} +.post-page-container .article-content-container .article-content-bottom { + padding: 2rem; +} +.post-page-container .article-content-container .article-content-bottom.has-cover { + padding-top: 1rem; +} +@media (max-width: 500px) { + .post-page-container .article-content-container .article-content-bottom { + padding: 1rem 0 !important; + } +} +.post-page-container .article-content-container .article-content-bottom .article-title { + color: var(--text-color-2); + font-weight: 600; + font-size: 1.6rem; + line-height: 1.6; +} +@media (max-width: 800px) { + .post-page-container .article-content-container .article-content-bottom .article-title { + font-size: 1.44rem; + } +} +@media (max-width: 500px) { + .post-page-container .article-content-container .article-content-bottom .article-title { + font-size: 1.28rem; + } +} +.post-page-container .article-content-container .article-content-bottom .article-header { + display: flex; + justify-content: flex-start; + width: 100%; +} +.post-page-container .article-content-container .article-content-bottom .article-header .avatar-box { + display: var(--post-author-avatar); + flex-shrink: 0; + width: 3.2rem; + height: 3.2rem; + margin-right: 0.8rem; + padding: 0.1rem; + border: 1px solid var(--border-color); + border-radius: 50%; +} +.post-page-container .article-content-container .article-content-bottom .article-header .avatar-box img { + width: 100%; + height: 100%; + background: var(--avatar-background-color); + border-radius: 50%; +} +.post-page-container .article-content-container .article-content-bottom .article-header .info-box { + display: flex; + flex-direction: column; + justify-content: space-between; + box-sizing: border-box; + width: 100%; + height: 100%; + padding: 0.2rem 0; +} +.post-page-container .article-content-container .article-content-bottom .article-header .info-box .author { + display: flex; + align-items: center; + font-weight: 600; + font-size: 1.18rem; +} +.post-page-container .article-content-container .article-content-bottom .article-header .info-box .author .name { + color: var(--text-color-3); +} +.post-page-container .article-content-container .article-content-bottom .article-header .info-box .author .author-label { + margin-left: 0.8rem; + padding: 0 0.4rem; + color: #fff; + font-weight: 500; + font-size: 0.8rem; + background: var(--selection-color); + border-radius: 0.4rem; +} +.post-page-container .article-content-container .article-content-bottom .article-header .info-box .meta-info { + margin-top: 0.1rem; +} +.post-page-container .article-content-container .article-content-bottom .article-header, +.post-page-container .article-content-container .article-content-bottom .article-header-meta-info { + margin-top: 1rem; +} +@media (max-width: 800px) { + .post-page-container .article-content-container .article-content-bottom .article-header, + .post-page-container .article-content-container .article-content-bottom .article-header-meta-info { + transform: scale(0.9); + transform-origin: left top; + } +} +.post-page-container .article-content-container .article-content-bottom .article-content { + margin-top: calc(var(--components-spacing) * 0.8); + padding-bottom: calc(var(--components-spacing) * 0.8); + color: var(--text-color-3); + text-align: justify; + word-wrap: break-word; + border-bottom: 2px dashed var(--border-color); +} +@media (max-width: 800px) { + .post-page-container .article-content-container .article-content-bottom .article-content { + margin-top: calc(var(--components-spacing) * 0.6); + padding-bottom: calc(var(--components-spacing) * 0.6); + } +} +.post-page-container .article-content-container .article-content-bottom .article-content .article-aging-tips { + position: relative; + display: none; + box-sizing: border-box; + margin-bottom: 1.8rem; + padding: 1rem; + color: var(--keep-warning-color); + line-height: 1.6; + background: var(--keep-warning-background-color); + border: 0.1rem solid var(--keep-warning-border-color); + border-radius: 0.4rem; +} +.post-page-container .article-content-container .article-content-bottom .article-content .article-aging-tips i { + margin-right: 0.4rem; + color: var(--keep-warning-color); +} +.post-page-container .article-content-container .article-content-bottom .article-content .article-aging-tips .days { + color: var(--keep-warning-color); +} +.post-page-container .article-content-container .article-content-bottom .post-bottom-tags-and-share { + display: flex; + justify-content: space-between; + width: 100%; + margin-top: 1rem; +} +.post-page-container .article-content-container .article-content-bottom .post-bottom-tags-and-share .post-tags-box { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: flex-start; + width: 100%; + font-size: 0.96rem; +} +.post-page-container .article-content-container .article-content-bottom .post-bottom-tags-and-share .post-tags-box .tag-item { + margin-right: 0.4rem; + line-height: 2; +} +.post-page-container .article-content-container .article-content-bottom .post-bottom-tags-and-share .post-tags-box .tag-item .icon { + color: var(--text-color-3); + font-size: 0.88rem; +} +.post-page-container .article-content-container .article-content-bottom .article-nav { + height: 2.8rem; + margin-top: var(--components-spacing); +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-prev, +.post-page-container .article-content-container .article-content-bottom .article-nav .article-next { + box-sizing: border-box; + max-width: 14rem; + height: 100%; + box-sizing: border-box; + margin-bottom: 0; + padding: 0.8rem; + background: var(--content-background-color); + border-radius: var(--box-border-radius); + box-shadow: 0 0 8px var(--shadow-color); + border-radius: 0.32rem; +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-prev:hover, +.post-page-container .article-content-container .article-content-bottom .article-nav .article-next:hover { + box-shadow: 0 0 12px var(--shadow-hover-color); +} +@media (max-width: 800px) { + .post-page-container .article-content-container .article-content-bottom .article-nav .article-prev, + .post-page-container .article-content-container .article-content-bottom .article-nav .article-next { + margin-bottom: 0; + padding: 0.64rem; + border-radius: calc(var(--box-border-radius) * 0.8); + } +} +@media (max-width: 500px) { + .post-page-container .article-content-container .article-content-bottom .article-nav .article-prev, + .post-page-container .article-content-container .article-content-bottom .article-nav .article-next { + margin-bottom: 0; + padding: 0.48rem; + border-radius: calc(var(--box-border-radius) * 0.6); + } +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-prev a, +.post-page-container .article-content-container .article-content-bottom .article-nav .article-next a { + position: relative; + display: block; + box-sizing: border-box; + width: 100%; + height: 100%; +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-prev a.prev, +.post-page-container .article-content-container .article-content-bottom .article-nav .article-next a.prev { + padding-left: 1rem; +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-prev a.next, +.post-page-container .article-content-container .article-content-bottom .article-nav .article-next a.next { + padding-right: 1rem; +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-prev a .arrow-icon, +.post-page-container .article-content-container .article-content-bottom .article-nav .article-next a .arrow-icon { + position: absolute; + top: 0; + width: 1rem; + height: 100%; +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-prev a .arrow-icon.left, +.post-page-container .article-content-container .article-content-bottom .article-nav .article-next a .arrow-icon.left { + left: 0; +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-prev a .arrow-icon.right, +.post-page-container .article-content-container .article-content-bottom .article-nav .article-next a .arrow-icon.right { + right: 0; +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-prev a .title, +.post-page-container .article-content-container .article-content-bottom .article-nav .article-next a .title { + width: 100%; + height: 100%; +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-prev { + float: left; +} +.post-page-container .article-content-container .article-content-bottom .article-nav .article-next { + float: right; +} +.post-page-container .article-content-container .article-content-bottom .article-nav .post-nav-item { + display: none; +} +@media (max-width: 800px) { + .post-page-container .article-content-container .article-content-bottom .article-nav .post-nav-item { + display: inline-block; + } +} +@media (max-width: 800px) { + .post-page-container .article-content-container .article-content-bottom .article-nav .post-nav-title-item { + display: none; + } +} +.post-page-container .pc-post-toc { + position: sticky; + top: calc(var(--header-height) + var(--components-spacing)); + display: none; + flex-shrink: 0; + box-sizing: border-box; + width: 15rem; + max-height: calc(100vh - calc(var(--header-height) + var(--components-spacing))); + transition-delay: 0s, 0s, 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.2s, 0.1s, 0.2s; + transition-property: color, background, box-shadow, border-color, top, display, max-height; +} +.post-page-container .pc-post-toc.right-toc { + order: 1; + padding: 2rem 0 2rem 1.8rem; +} +.post-page-container .pc-post-toc.left-toc { + order: -1; + padding: 2rem 1.8rem 2rem 0; +} +.header-shrink .post-page-container .pc-post-toc { + top: calc(var(--header-shrink-height) + var(--components-spacing)); + max-height: calc(100vh - calc(var(--header-shrink-height) + var(--components-spacing))); +} +.post-page-container .hbe-container .hbe-input-field { + color: var(--text-color-3) !important; + background: var(--background-color-3) !important; +} +.post-page-container .hbe-container .hbe-input-label::before { + display: none !important; +} +.post-page-container .hbe-container .hbe-input-label::after { + background: var(--text-color-4) !important; +} +.post-page-container .hbe-container .hbe-input-label .hbe-input-label-content { + color: var(--text-color-4) !important; +} +.post-page-container .hbe-container .hbe-button { + margin-top: 2rem; +} +.category-container { + box-sizing: border-box; + margin-bottom: 2rem; + padding: 2rem; + background: var(--content-background-color); + border-radius: var(--box-border-radius); + box-shadow: 0 0 8px var(--shadow-color); +} +.category-container:hover { + box-shadow: 0 0 12px var(--shadow-hover-color); +} +@media (max-width: 800px) { + .category-container { + margin-bottom: 1.6rem; + padding: 1.6rem; + border-radius: calc(var(--box-border-radius) * 0.8); + } +} +@media (max-width: 500px) { + .category-container { + margin-bottom: 1.2rem; + padding: 1.2rem; + border-radius: calc(var(--box-border-radius) * 0.6); + } +} +.category-container .category-name { + margin-bottom: var(--components-spacing); + color: var(--text-color-2); + font-weight: 600; + font-size: 1.5rem; +} +.category-container .category-name i { + color: var(--text-color-2); +} +@media (max-width: 800px) { + .category-container .category-name { + font-size: 1.35rem; + } +} +@media (max-width: 500px) { + .category-container .category-name { + font-size: 1.2rem; + } +} +.tag-container { + box-sizing: border-box; + margin-bottom: 2rem; + padding: 2rem; + background: var(--content-background-color); + border-radius: var(--box-border-radius); + box-shadow: 0 0 8px var(--shadow-color); +} +.tag-container:hover { + box-shadow: 0 0 12px var(--shadow-hover-color); +} +@media (max-width: 800px) { + .tag-container { + margin-bottom: 1.6rem; + padding: 1.6rem; + border-radius: calc(var(--box-border-radius) * 0.8); + } +} +@media (max-width: 500px) { + .tag-container { + margin-bottom: 1.2rem; + padding: 1.2rem; + border-radius: calc(var(--box-border-radius) * 0.6); + } +} +.tag-container .tag-name { + margin-bottom: var(--components-spacing); + color: var(--text-color-2); + font-weight: 600; + font-size: 1.5rem; +} +.tag-container .tag-name i { + color: var(--text-color-2); +} +@media (max-width: 800px) { + .tag-container .tag-name { + font-size: 1.35rem; + } +} +@media (max-width: 500px) { + .tag-container .tag-name { + font-size: 1.2rem; + } +} +.tagcloud-container { + box-sizing: border-box; + margin-bottom: 0; + padding: 2rem; + background: var(--content-background-color); + border-radius: var(--box-border-radius); + box-shadow: 0 0 8px var(--shadow-color); +} +.tagcloud-container:hover { + box-shadow: 0 0 12px var(--shadow-hover-color); +} +@media (max-width: 800px) { + .tagcloud-container { + margin-bottom: 0; + padding: 1.6rem; + border-radius: calc(var(--box-border-radius) * 0.8); + } +} +@media (max-width: 500px) { + .tagcloud-container { + margin-bottom: 0; + padding: 1.2rem; + border-radius: calc(var(--box-border-radius) * 0.6); + } +} +.tagcloud-container .tagcloud-content { + text-align: justify; +} +.tagcloud-container .tagcloud-content a { + display: inline-block; + box-sizing: border-box; + padding: 0.68rem 0.5rem; +} +.empty-content-box { + display: flex; + justify-content: center; + width: 100%; + padding: 4rem 0; +} +.empty-content-box i.fa-solid { + color: var(--text-color-4); +} +.post-share-container { + flex-shrink: 0; +} +.post-share-container .share-list-wrap { + display: flex; + justify-content: flex-end; +} +.post-share-container .share-list-wrap .share-item { + width: 1.8rem; + height: 1.8rem; + margin-left: 0.5rem; + padding: 0.4rem; + border-style: solid; + border-width: 0.1rem; + border-radius: 50%; + cursor: pointer; + transition-delay: 0s, 0s, 0s, 0s, 0s; + transition-timing-function: ease, ease, ease, ease, ease; + transition-duration: 0.2s, 0.2s, 0.2s, 0.2s, 0.3s; + transition-property: color, background, box-shadow, border-color, background; +} +.post-share-container .share-list-wrap .share-item i { + color: inherit; + font-size: 1rem; +} +.post-share-container .share-list-wrap .share-item.qq { + color: var(--keep-primary-color); + border-color: var(--keep-primary-color); +} +.post-share-container .share-list-wrap .share-item.qq:hover { + color: var(--background-color-1); + background: var(--keep-primary-color); +} +.post-share-container .share-list-wrap .share-item.wechat { + color: var(--keep-success-color); + border-color: var(--keep-success-color); +} +.post-share-container .share-list-wrap .share-item.wechat img { + filter: brightness(1) !important; +} +.post-share-container .share-list-wrap .share-item.wechat img[lazyload]::before { + background: #fff !important; +} +.post-share-container .share-list-wrap .share-item.wechat:hover { + color: var(--background-color-1); + background: var(--keep-success-color); +} +.post-share-container .share-list-wrap .share-item.weibo { + color: var(--keep-danger-color); + border-color: var(--keep-danger-color); +} +.post-share-container .share-list-wrap .share-item.weibo:hover { + color: var(--background-color-1); + background: var(--keep-danger-color); +} +.reward-author-container { + margin-top: 2rem; +} +.reward-author-container .tooltip-img .tooltip-img-box img { + max-height: 12rem; +} +.reward-author-container .reward-btn { + width: 3rem; + height: 3rem; + border-radius: 50%; +} +.reward-author-container .reward-btn i { + font-size: 1.2rem; +} diff --git a/font/css/brands.min.css b/font/css/brands.min.css new file mode 100644 index 0000000..3c911a9 --- /dev/null +++ b/font/css/brands.min.css @@ -0,0 +1,6 @@ +/*! + * Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +:host,:root{--fa-style-family-brands:"Font Awesome 6 Brands";--fa-font-brands:normal 400 1em/1 "Font Awesome 6 Brands"}@font-face{font-family:"Font Awesome 6 Brands";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}.fa-brands,.fab{font-weight:400}.fa-monero:before{content:"\f3d0"}.fa-hooli:before{content:"\f427"}.fa-yelp:before{content:"\f1e9"}.fa-cc-visa:before{content:"\f1f0"}.fa-lastfm:before{content:"\f202"}.fa-shopware:before{content:"\f5b5"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-aws:before{content:"\f375"}.fa-redhat:before{content:"\f7bc"}.fa-yoast:before{content:"\f2b1"}.fa-cloudflare:before{content:"\e07d"}.fa-ups:before{content:"\f7e0"}.fa-wpexplorer:before{content:"\f2de"}.fa-dyalog:before{content:"\f399"}.fa-bity:before{content:"\f37a"}.fa-stackpath:before{content:"\f842"}.fa-buysellads:before{content:"\f20d"}.fa-first-order:before{content:"\f2b0"}.fa-modx:before{content:"\f285"}.fa-guilded:before{content:"\e07e"}.fa-vnv:before{content:"\f40b"}.fa-js-square:before,.fa-square-js:before{content:"\f3b9"}.fa-microsoft:before{content:"\f3ca"}.fa-qq:before{content:"\f1d6"}.fa-orcid:before{content:"\f8d2"}.fa-java:before{content:"\f4e4"}.fa-invision:before{content:"\f7b0"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-centercode:before{content:"\f380"}.fa-glide-g:before{content:"\f2a6"}.fa-drupal:before{content:"\f1a9"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-unity:before{content:"\e049"}.fa-whmcs:before{content:"\f40d"}.fa-rocketchat:before{content:"\f3e8"}.fa-vk:before{content:"\f189"}.fa-untappd:before{content:"\f405"}.fa-mailchimp:before{content:"\f59e"}.fa-css3-alt:before{content:"\f38b"}.fa-reddit-square:before,.fa-square-reddit:before{content:"\f1a2"}.fa-vimeo-v:before{content:"\f27d"}.fa-contao:before{content:"\f26d"}.fa-square-font-awesome:before{content:"\e5ad"}.fa-deskpro:before{content:"\f38f"}.fa-sistrix:before{content:"\f3ee"}.fa-instagram-square:before,.fa-square-instagram:before{content:"\e055"}.fa-battle-net:before{content:"\f835"}.fa-the-red-yeti:before{content:"\f69d"}.fa-hacker-news-square:before,.fa-square-hacker-news:before{content:"\f3af"}.fa-edge:before{content:"\f282"}.fa-threads:before{content:"\e618"}.fa-napster:before{content:"\f3d2"}.fa-snapchat-square:before,.fa-square-snapchat:before{content:"\f2ad"}.fa-google-plus-g:before{content:"\f0d5"}.fa-artstation:before{content:"\f77a"}.fa-markdown:before{content:"\f60f"}.fa-sourcetree:before{content:"\f7d3"}.fa-google-plus:before{content:"\f2b3"}.fa-diaspora:before{content:"\f791"}.fa-foursquare:before{content:"\f180"}.fa-stack-overflow:before{content:"\f16c"}.fa-github-alt:before{content:"\f113"}.fa-phoenix-squadron:before{content:"\f511"}.fa-pagelines:before{content:"\f18c"}.fa-algolia:before{content:"\f36c"}.fa-red-river:before{content:"\f3e3"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-safari:before{content:"\f267"}.fa-google:before{content:"\f1a0"}.fa-font-awesome-alt:before,.fa-square-font-awesome-stroke:before{content:"\f35c"}.fa-atlassian:before{content:"\f77b"}.fa-linkedin-in:before{content:"\f0e1"}.fa-digital-ocean:before{content:"\f391"}.fa-nimblr:before{content:"\f5a8"}.fa-chromecast:before{content:"\f838"}.fa-evernote:before{content:"\f839"}.fa-hacker-news:before{content:"\f1d4"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-adversal:before{content:"\f36a"}.fa-creative-commons:before{content:"\f25e"}.fa-watchman-monitoring:before{content:"\e087"}.fa-fonticons:before{content:"\f280"}.fa-weixin:before{content:"\f1d7"}.fa-shirtsinbulk:before{content:"\f214"}.fa-codepen:before{content:"\f1cb"}.fa-git-alt:before{content:"\f841"}.fa-lyft:before{content:"\f3c3"}.fa-rev:before{content:"\f5b2"}.fa-windows:before{content:"\f17a"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-square-viadeo:before,.fa-viadeo-square:before{content:"\f2aa"}.fa-meetup:before{content:"\f2e0"}.fa-centos:before{content:"\f789"}.fa-adn:before{content:"\f170"}.fa-cloudsmith:before{content:"\f384"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-dribbble-square:before,.fa-square-dribbble:before{content:"\f397"}.fa-codiepie:before{content:"\f284"}.fa-node:before{content:"\f419"}.fa-mix:before{content:"\f3cb"}.fa-steam:before{content:"\f1b6"}.fa-cc-apple-pay:before{content:"\f416"}.fa-scribd:before{content:"\f28a"}.fa-debian:before{content:"\e60b"}.fa-openid:before{content:"\f19b"}.fa-instalod:before{content:"\e081"}.fa-expeditedssl:before{content:"\f23e"}.fa-sellcast:before{content:"\f2da"}.fa-square-twitter:before,.fa-twitter-square:before{content:"\f081"}.fa-r-project:before{content:"\f4f7"}.fa-delicious:before{content:"\f1a5"}.fa-freebsd:before{content:"\f3a4"}.fa-vuejs:before{content:"\f41f"}.fa-accusoft:before{content:"\f369"}.fa-ioxhost:before{content:"\f208"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-app-store:before{content:"\f36f"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-itunes-note:before{content:"\f3b5"}.fa-golang:before{content:"\e40f"}.fa-kickstarter:before{content:"\f3bb"}.fa-grav:before{content:"\f2d6"}.fa-weibo:before{content:"\f18a"}.fa-uncharted:before{content:"\e084"}.fa-firstdraft:before{content:"\f3a1"}.fa-square-youtube:before,.fa-youtube-square:before{content:"\f431"}.fa-wikipedia-w:before{content:"\f266"}.fa-rendact:before,.fa-wpressr:before{content:"\f3e4"}.fa-angellist:before{content:"\f209"}.fa-galactic-republic:before{content:"\f50c"}.fa-nfc-directional:before{content:"\e530"}.fa-skype:before{content:"\f17e"}.fa-joget:before{content:"\f3b7"}.fa-fedora:before{content:"\f798"}.fa-stripe-s:before{content:"\f42a"}.fa-meta:before{content:"\e49b"}.fa-laravel:before{content:"\f3bd"}.fa-hotjar:before{content:"\f3b1"}.fa-bluetooth-b:before{content:"\f294"}.fa-sticker-mule:before{content:"\f3f7"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-hips:before{content:"\f452"}.fa-behance:before{content:"\f1b4"}.fa-reddit:before{content:"\f1a1"}.fa-discord:before{content:"\f392"}.fa-chrome:before{content:"\f268"}.fa-app-store-ios:before{content:"\f370"}.fa-cc-discover:before{content:"\f1f2"}.fa-wpbeginner:before{content:"\f297"}.fa-confluence:before{content:"\f78d"}.fa-mdb:before{content:"\f8ca"}.fa-dochub:before{content:"\f394"}.fa-accessible-icon:before{content:"\f368"}.fa-ebay:before{content:"\f4f4"}.fa-amazon:before{content:"\f270"}.fa-unsplash:before{content:"\e07c"}.fa-yarn:before{content:"\f7e3"}.fa-square-steam:before,.fa-steam-square:before{content:"\f1b7"}.fa-500px:before{content:"\f26e"}.fa-square-vimeo:before,.fa-vimeo-square:before{content:"\f194"}.fa-asymmetrik:before{content:"\f372"}.fa-font-awesome-flag:before,.fa-font-awesome-logo-full:before,.fa-font-awesome:before{content:"\f2b4"}.fa-gratipay:before{content:"\f184"}.fa-apple:before{content:"\f179"}.fa-hive:before{content:"\e07f"}.fa-gitkraken:before{content:"\f3a6"}.fa-keybase:before{content:"\f4f5"}.fa-apple-pay:before{content:"\f415"}.fa-padlet:before{content:"\e4a0"}.fa-amazon-pay:before{content:"\f42c"}.fa-github-square:before,.fa-square-github:before{content:"\f092"}.fa-stumbleupon:before{content:"\f1a4"}.fa-fedex:before{content:"\f797"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-shopify:before{content:"\e057"}.fa-neos:before{content:"\f612"}.fa-square-threads:before{content:"\e619"}.fa-hackerrank:before{content:"\f5f7"}.fa-researchgate:before{content:"\f4f8"}.fa-swift:before{content:"\f8e1"}.fa-angular:before{content:"\f420"}.fa-speakap:before{content:"\f3f3"}.fa-angrycreative:before{content:"\f36e"}.fa-y-combinator:before{content:"\f23b"}.fa-empire:before{content:"\f1d1"}.fa-envira:before{content:"\f299"}.fa-gitlab-square:before,.fa-square-gitlab:before{content:"\e5ae"}.fa-studiovinari:before{content:"\f3f8"}.fa-pied-piper:before{content:"\f2ae"}.fa-wordpress:before{content:"\f19a"}.fa-product-hunt:before{content:"\f288"}.fa-firefox:before{content:"\f269"}.fa-linode:before{content:"\f2b8"}.fa-goodreads:before{content:"\f3a8"}.fa-odnoklassniki-square:before,.fa-square-odnoklassniki:before{content:"\f264"}.fa-jsfiddle:before{content:"\f1cc"}.fa-sith:before{content:"\f512"}.fa-themeisle:before{content:"\f2b2"}.fa-page4:before{content:"\f3d7"}.fa-hashnode:before{content:"\e499"}.fa-react:before{content:"\f41b"}.fa-cc-paypal:before{content:"\f1f4"}.fa-squarespace:before{content:"\f5be"}.fa-cc-stripe:before{content:"\f1f5"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-bitcoin:before{content:"\f379"}.fa-keycdn:before{content:"\f3ba"}.fa-opera:before{content:"\f26a"}.fa-itch-io:before{content:"\f83a"}.fa-umbraco:before{content:"\f8e8"}.fa-galactic-senate:before{content:"\f50d"}.fa-ubuntu:before{content:"\f7df"}.fa-draft2digital:before{content:"\f396"}.fa-stripe:before{content:"\f429"}.fa-houzz:before{content:"\f27c"}.fa-gg:before{content:"\f260"}.fa-dhl:before{content:"\f790"}.fa-pinterest-square:before,.fa-square-pinterest:before{content:"\f0d3"}.fa-xing:before{content:"\f168"}.fa-blackberry:before{content:"\f37b"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-playstation:before{content:"\f3df"}.fa-quinscape:before{content:"\f459"}.fa-less:before{content:"\f41d"}.fa-blogger-b:before{content:"\f37d"}.fa-opencart:before{content:"\f23d"}.fa-vine:before{content:"\f1ca"}.fa-paypal:before{content:"\f1ed"}.fa-gitlab:before{content:"\f296"}.fa-typo3:before{content:"\f42b"}.fa-reddit-alien:before{content:"\f281"}.fa-yahoo:before{content:"\f19e"}.fa-dailymotion:before{content:"\e052"}.fa-affiliatetheme:before{content:"\f36b"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-bootstrap:before{content:"\f836"}.fa-odnoklassniki:before{content:"\f263"}.fa-nfc-symbol:before{content:"\e531"}.fa-ethereum:before{content:"\f42e"}.fa-speaker-deck:before{content:"\f83c"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-patreon:before{content:"\f3d9"}.fa-avianex:before{content:"\f374"}.fa-ello:before{content:"\f5f1"}.fa-gofore:before{content:"\f3a7"}.fa-bimobject:before{content:"\f378"}.fa-facebook-f:before{content:"\f39e"}.fa-google-plus-square:before,.fa-square-google-plus:before{content:"\f0d4"}.fa-mandalorian:before{content:"\f50f"}.fa-first-order-alt:before{content:"\f50a"}.fa-osi:before{content:"\f41a"}.fa-google-wallet:before{content:"\f1ee"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-periscope:before{content:"\f3da"}.fa-fulcrum:before{content:"\f50b"}.fa-cloudscale:before{content:"\f383"}.fa-forumbee:before{content:"\f211"}.fa-mizuni:before{content:"\f3cc"}.fa-schlix:before{content:"\f3ea"}.fa-square-xing:before,.fa-xing-square:before{content:"\f169"}.fa-bandcamp:before{content:"\f2d5"}.fa-wpforms:before{content:"\f298"}.fa-cloudversify:before{content:"\f385"}.fa-usps:before{content:"\f7e1"}.fa-megaport:before{content:"\f5a3"}.fa-magento:before{content:"\f3c4"}.fa-spotify:before{content:"\f1bc"}.fa-optin-monster:before{content:"\f23c"}.fa-fly:before{content:"\f417"}.fa-aviato:before{content:"\f421"}.fa-itunes:before{content:"\f3b4"}.fa-cuttlefish:before{content:"\f38c"}.fa-blogger:before{content:"\f37c"}.fa-flickr:before{content:"\f16e"}.fa-viber:before{content:"\f409"}.fa-soundcloud:before{content:"\f1be"}.fa-digg:before{content:"\f1a6"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-symfony:before{content:"\f83d"}.fa-maxcdn:before{content:"\f136"}.fa-etsy:before{content:"\f2d7"}.fa-facebook-messenger:before{content:"\f39f"}.fa-audible:before{content:"\f373"}.fa-think-peaks:before{content:"\f731"}.fa-bilibili:before{content:"\e3d9"}.fa-erlang:before{content:"\f39d"}.fa-x-twitter:before{content:"\e61b"}.fa-cotton-bureau:before{content:"\f89e"}.fa-dashcube:before{content:"\f210"}.fa-42-group:before,.fa-innosoft:before{content:"\e080"}.fa-stack-exchange:before{content:"\f18d"}.fa-elementor:before{content:"\f430"}.fa-pied-piper-square:before,.fa-square-pied-piper:before{content:"\e01e"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-palfed:before{content:"\f3d8"}.fa-superpowers:before{content:"\f2dd"}.fa-resolving:before{content:"\f3e7"}.fa-xbox:before{content:"\f412"}.fa-searchengin:before{content:"\f3eb"}.fa-tiktok:before{content:"\e07b"}.fa-facebook-square:before,.fa-square-facebook:before{content:"\f082"}.fa-renren:before{content:"\f18b"}.fa-linux:before{content:"\f17c"}.fa-glide:before{content:"\f2a5"}.fa-linkedin:before{content:"\f08c"}.fa-hubspot:before{content:"\f3b2"}.fa-deploydog:before{content:"\f38e"}.fa-twitch:before{content:"\f1e8"}.fa-ravelry:before{content:"\f2d9"}.fa-mixer:before{content:"\e056"}.fa-lastfm-square:before,.fa-square-lastfm:before{content:"\f203"}.fa-vimeo:before{content:"\f40a"}.fa-mendeley:before{content:"\f7b3"}.fa-uniregistry:before{content:"\f404"}.fa-figma:before{content:"\f799"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-dropbox:before{content:"\f16b"}.fa-instagram:before{content:"\f16d"}.fa-cmplid:before{content:"\e360"}.fa-facebook:before{content:"\f09a"}.fa-gripfire:before{content:"\f3ac"}.fa-jedi-order:before{content:"\f50e"}.fa-uikit:before{content:"\f403"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-phabricator:before{content:"\f3db"}.fa-ussunnah:before{content:"\f407"}.fa-earlybirds:before{content:"\f39a"}.fa-trade-federation:before{content:"\f513"}.fa-autoprefixer:before{content:"\f41c"}.fa-whatsapp:before{content:"\f232"}.fa-slideshare:before{content:"\f1e7"}.fa-google-play:before{content:"\f3ab"}.fa-viadeo:before{content:"\f2a9"}.fa-line:before{content:"\f3c0"}.fa-google-drive:before{content:"\f3aa"}.fa-servicestack:before{content:"\f3ec"}.fa-simplybuilt:before{content:"\f215"}.fa-bitbucket:before{content:"\f171"}.fa-imdb:before{content:"\f2d8"}.fa-deezer:before{content:"\e077"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-jira:before{content:"\f7b1"}.fa-docker:before{content:"\f395"}.fa-screenpal:before{content:"\e570"}.fa-bluetooth:before{content:"\f293"}.fa-gitter:before{content:"\f426"}.fa-d-and-d:before{content:"\f38d"}.fa-microblog:before{content:"\e01a"}.fa-cc-diners-club:before{content:"\f24c"}.fa-gg-circle:before{content:"\f261"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-yandex:before{content:"\f413"}.fa-readme:before{content:"\f4d5"}.fa-html5:before{content:"\f13b"}.fa-sellsy:before{content:"\f213"}.fa-sass:before{content:"\f41e"}.fa-wirsindhandwerk:before,.fa-wsh:before{content:"\e2d0"}.fa-buromobelexperte:before{content:"\f37f"}.fa-salesforce:before{content:"\f83b"}.fa-octopus-deploy:before{content:"\e082"}.fa-medapps:before{content:"\f3c6"}.fa-ns8:before{content:"\f3d5"}.fa-pinterest-p:before{content:"\f231"}.fa-apper:before{content:"\f371"}.fa-fort-awesome:before{content:"\f286"}.fa-waze:before{content:"\f83f"}.fa-cc-jcb:before{content:"\f24b"}.fa-snapchat-ghost:before,.fa-snapchat:before{content:"\f2ab"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-rust:before{content:"\e07a"}.fa-wix:before{content:"\f5cf"}.fa-behance-square:before,.fa-square-behance:before{content:"\f1b5"}.fa-supple:before{content:"\f3f9"}.fa-rebel:before{content:"\f1d0"}.fa-css3:before{content:"\f13c"}.fa-staylinked:before{content:"\f3f5"}.fa-kaggle:before{content:"\f5fa"}.fa-space-awesome:before{content:"\e5ac"}.fa-deviantart:before{content:"\f1bd"}.fa-cpanel:before{content:"\f388"}.fa-goodreads-g:before{content:"\f3a9"}.fa-git-square:before,.fa-square-git:before{content:"\f1d2"}.fa-square-tumblr:before,.fa-tumblr-square:before{content:"\f174"}.fa-trello:before{content:"\f181"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-get-pocket:before{content:"\f265"}.fa-perbyte:before{content:"\e083"}.fa-grunt:before{content:"\f3ad"}.fa-weebly:before{content:"\f5cc"}.fa-connectdevelop:before{content:"\f20e"}.fa-leanpub:before{content:"\f212"}.fa-black-tie:before{content:"\f27e"}.fa-themeco:before{content:"\f5c6"}.fa-python:before{content:"\f3e2"}.fa-android:before{content:"\f17b"}.fa-bots:before{content:"\e340"}.fa-free-code-camp:before{content:"\f2c5"}.fa-hornbill:before{content:"\f592"}.fa-js:before{content:"\f3b8"}.fa-ideal:before{content:"\e013"}.fa-git:before{content:"\f1d3"}.fa-dev:before{content:"\f6cc"}.fa-sketch:before{content:"\f7c6"}.fa-yandex-international:before{content:"\f414"}.fa-cc-amex:before{content:"\f1f3"}.fa-uber:before{content:"\f402"}.fa-github:before{content:"\f09b"}.fa-php:before{content:"\f457"}.fa-alipay:before{content:"\f642"}.fa-youtube:before{content:"\f167"}.fa-skyatlas:before{content:"\f216"}.fa-firefox-browser:before{content:"\e007"}.fa-replyd:before{content:"\f3e6"}.fa-suse:before{content:"\f7d6"}.fa-jenkins:before{content:"\f3b6"}.fa-twitter:before{content:"\f099"}.fa-rockrms:before{content:"\f3e9"}.fa-pinterest:before{content:"\f0d2"}.fa-buffer:before{content:"\f837"}.fa-npm:before{content:"\f3d4"}.fa-yammer:before{content:"\f840"}.fa-btc:before{content:"\f15a"}.fa-dribbble:before{content:"\f17d"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-internet-explorer:before{content:"\f26b"}.fa-stubber:before{content:"\e5c7"}.fa-telegram-plane:before,.fa-telegram:before{content:"\f2c6"}.fa-old-republic:before{content:"\f510"}.fa-odysee:before{content:"\e5c6"}.fa-square-whatsapp:before,.fa-whatsapp-square:before{content:"\f40c"}.fa-node-js:before{content:"\f3d3"}.fa-edge-legacy:before{content:"\e078"}.fa-slack-hash:before,.fa-slack:before{content:"\f198"}.fa-medrt:before{content:"\f3c8"}.fa-usb:before{content:"\f287"}.fa-tumblr:before{content:"\f173"}.fa-vaadin:before{content:"\f408"}.fa-quora:before{content:"\f2c4"}.fa-square-x-twitter:before{content:"\e61a"}.fa-reacteurope:before{content:"\f75d"}.fa-medium-m:before,.fa-medium:before{content:"\f23a"}.fa-amilia:before{content:"\f36d"}.fa-mixcloud:before{content:"\f289"}.fa-flipboard:before{content:"\f44d"}.fa-viacoin:before{content:"\f237"}.fa-critical-role:before{content:"\f6c9"}.fa-sitrox:before{content:"\e44a"}.fa-discourse:before{content:"\f393"}.fa-joomla:before{content:"\f1aa"}.fa-mastodon:before{content:"\f4f6"}.fa-airbnb:before{content:"\f834"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-buy-n-large:before{content:"\f8a6"}.fa-gulp:before{content:"\f3ae"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-strava:before{content:"\f428"}.fa-ember:before{content:"\f423"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-teamspeak:before{content:"\f4f9"}.fa-pushed:before{content:"\f3e1"}.fa-wordpress-simple:before{content:"\f411"}.fa-nutritionix:before{content:"\f3d6"}.fa-wodu:before{content:"\e088"}.fa-google-pay:before{content:"\e079"}.fa-intercom:before{content:"\f7af"}.fa-zhihu:before{content:"\f63f"}.fa-korvue:before{content:"\f42f"}.fa-pix:before{content:"\e43a"}.fa-steam-symbol:before{content:"\f3f6"} \ No newline at end of file diff --git a/font/css/fontawesome.min.css b/font/css/fontawesome.min.css new file mode 100644 index 0000000..9a90619 --- /dev/null +++ b/font/css/fontawesome.min.css @@ -0,0 +1,9 @@ +/*! + * Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +.fa{font-family:var(--fa-style-family,"Font Awesome 6 Free");font-weight:var(--fa-style,900)}.fa,.fa-brands,.fa-classic,.fa-regular,.fa-sharp,.fa-solid,.fab,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:var(--fa-display,inline-block);font-style:normal;font-variant:normal;line-height:1;text-rendering:auto}.fa-classic,.fa-regular,.fa-solid,.far,.fas{font-family:"Font Awesome 6 Free"}.fa-brands,.fab{font-family:"Font Awesome 6 Brands"}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-2xs{font-size:.625em;line-height:.1em;vertical-align:.225em}.fa-xs{font-size:.75em;line-height:.08333em;vertical-align:.125em}.fa-sm{font-size:.875em;line-height:.07143em;vertical-align:.05357em}.fa-lg{font-size:1.25em;line-height:.05em;vertical-align:-.075em}.fa-xl{font-size:1.5em;line-height:.04167em;vertical-align:-.125em}.fa-2xl{font-size:2em;line-height:.03125em;vertical-align:-.1875em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:var(--fa-li-margin,2.5em);padding-left:0}.fa-ul>li{position:relative}.fa-li{left:calc(var(--fa-li-width, 2em)*-1);position:absolute;text-align:center;width:var(--fa-li-width,2em);line-height:inherit}.fa-border{border-radius:var(--fa-border-radius,.1em);border:var(--fa-border-width,.08em) var(--fa-border-style,solid) var(--fa-border-color,#eee);padding:var(--fa-border-padding,.2em .25em .15em)}.fa-pull-left{float:left;margin-right:var(--fa-pull-margin,.3em)}.fa-pull-right{float:right;margin-left:var(--fa-pull-margin,.3em)}.fa-beat{-webkit-animation-name:fa-beat;animation-name:fa-beat;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-bounce{-webkit-animation-name:fa-bounce;animation-name:fa-bounce;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1))}.fa-fade{-webkit-animation-name:fa-fade;animation-name:fa-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-beat-fade,.fa-fade{-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s)}.fa-beat-fade{-webkit-animation-name:fa-beat-fade;animation-name:fa-beat-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-flip{-webkit-animation-name:fa-flip;animation-name:fa-flip;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-shake{-webkit-animation-name:fa-shake;animation-name:fa-shake;-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-shake,.fa-spin{-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal)}.fa-spin{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-duration:var(--fa-animation-duration,2s);animation-duration:var(--fa-animation-duration,2s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin-reverse{--fa-animation-direction:reverse}.fa-pulse,.fa-spin-pulse{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,steps(8));animation-timing-function:var(--fa-animation-timing,steps(8))}@media (prefers-reduced-motion:reduce){.fa-beat,.fa-beat-fade,.fa-bounce,.fa-fade,.fa-flip,.fa-pulse,.fa-shake,.fa-spin,.fa-spin-pulse{-webkit-animation-delay:-1ms;animation-delay:-1ms;-webkit-animation-duration:1ms;animation-duration:1ms;-webkit-animation-iteration-count:1;animation-iteration-count:1;-webkit-transition-delay:0s;transition-delay:0s;-webkit-transition-duration:0s;transition-duration:0s}}@-webkit-keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@-webkit-keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@-webkit-keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@-webkit-keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@-webkit-keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@-webkit-keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}.fa-rotate-by{-webkit-transform:rotate(var(--fa-rotate-angle,none));transform:rotate(var(--fa-rotate-angle,none))}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%;z-index:var(--fa-stack-z-index,auto)}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:var(--fa-inverse,#fff)} + +.fa-0:before{content:"\30"}.fa-1:before{content:"\31"}.fa-2:before{content:"\32"}.fa-3:before{content:"\33"}.fa-4:before{content:"\34"}.fa-5:before{content:"\35"}.fa-6:before{content:"\36"}.fa-7:before{content:"\37"}.fa-8:before{content:"\38"}.fa-9:before{content:"\39"}.fa-fill-drip:before{content:"\f576"}.fa-arrows-to-circle:before{content:"\e4bd"}.fa-chevron-circle-right:before,.fa-circle-chevron-right:before{content:"\f138"}.fa-at:before{content:"\40"}.fa-trash-alt:before,.fa-trash-can:before{content:"\f2ed"}.fa-text-height:before{content:"\f034"}.fa-user-times:before,.fa-user-xmark:before{content:"\f235"}.fa-stethoscope:before{content:"\f0f1"}.fa-comment-alt:before,.fa-message:before{content:"\f27a"}.fa-info:before{content:"\f129"}.fa-compress-alt:before,.fa-down-left-and-up-right-to-center:before{content:"\f422"}.fa-explosion:before{content:"\e4e9"}.fa-file-alt:before,.fa-file-lines:before,.fa-file-text:before{content:"\f15c"}.fa-wave-square:before{content:"\f83e"}.fa-ring:before{content:"\f70b"}.fa-building-un:before{content:"\e4d9"}.fa-dice-three:before{content:"\f527"}.fa-calendar-alt:before,.fa-calendar-days:before{content:"\f073"}.fa-anchor-circle-check:before{content:"\e4aa"}.fa-building-circle-arrow-right:before{content:"\e4d1"}.fa-volleyball-ball:before,.fa-volleyball:before{content:"\f45f"}.fa-arrows-up-to-line:before{content:"\e4c2"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-circle-minus:before,.fa-minus-circle:before{content:"\f056"}.fa-door-open:before{content:"\f52b"}.fa-right-from-bracket:before,.fa-sign-out-alt:before{content:"\f2f5"}.fa-atom:before{content:"\f5d2"}.fa-soap:before{content:"\e06e"}.fa-heart-music-camera-bolt:before,.fa-icons:before{content:"\f86d"}.fa-microphone-alt-slash:before,.fa-microphone-lines-slash:before{content:"\f539"}.fa-bridge-circle-check:before{content:"\e4c9"}.fa-pump-medical:before{content:"\e06a"}.fa-fingerprint:before{content:"\f577"}.fa-hand-point-right:before{content:"\f0a4"}.fa-magnifying-glass-location:before,.fa-search-location:before{content:"\f689"}.fa-forward-step:before,.fa-step-forward:before{content:"\f051"}.fa-face-smile-beam:before,.fa-smile-beam:before{content:"\f5b8"}.fa-flag-checkered:before{content:"\f11e"}.fa-football-ball:before,.fa-football:before{content:"\f44e"}.fa-school-circle-exclamation:before{content:"\e56c"}.fa-crop:before{content:"\f125"}.fa-angle-double-down:before,.fa-angles-down:before{content:"\f103"}.fa-users-rectangle:before{content:"\e594"}.fa-people-roof:before{content:"\e537"}.fa-people-line:before{content:"\e534"}.fa-beer-mug-empty:before,.fa-beer:before{content:"\f0fc"}.fa-diagram-predecessor:before{content:"\e477"}.fa-arrow-up-long:before,.fa-long-arrow-up:before{content:"\f176"}.fa-burn:before,.fa-fire-flame-simple:before{content:"\f46a"}.fa-male:before,.fa-person:before{content:"\f183"}.fa-laptop:before{content:"\f109"}.fa-file-csv:before{content:"\f6dd"}.fa-menorah:before{content:"\f676"}.fa-truck-plane:before{content:"\e58f"}.fa-record-vinyl:before{content:"\f8d9"}.fa-face-grin-stars:before,.fa-grin-stars:before{content:"\f587"}.fa-bong:before{content:"\f55c"}.fa-pastafarianism:before,.fa-spaghetti-monster-flying:before{content:"\f67b"}.fa-arrow-down-up-across-line:before{content:"\e4af"}.fa-spoon:before,.fa-utensil-spoon:before{content:"\f2e5"}.fa-jar-wheat:before{content:"\e517"}.fa-envelopes-bulk:before,.fa-mail-bulk:before{content:"\f674"}.fa-file-circle-exclamation:before{content:"\e4eb"}.fa-circle-h:before,.fa-hospital-symbol:before{content:"\f47e"}.fa-pager:before{content:"\f815"}.fa-address-book:before,.fa-contact-book:before{content:"\f2b9"}.fa-strikethrough:before{content:"\f0cc"}.fa-k:before{content:"\4b"}.fa-landmark-flag:before{content:"\e51c"}.fa-pencil-alt:before,.fa-pencil:before{content:"\f303"}.fa-backward:before{content:"\f04a"}.fa-caret-right:before{content:"\f0da"}.fa-comments:before{content:"\f086"}.fa-file-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-code-pull-request:before{content:"\e13c"}.fa-clipboard-list:before{content:"\f46d"}.fa-truck-loading:before,.fa-truck-ramp-box:before{content:"\f4de"}.fa-user-check:before{content:"\f4fc"}.fa-vial-virus:before{content:"\e597"}.fa-sheet-plastic:before{content:"\e571"}.fa-blog:before{content:"\f781"}.fa-user-ninja:before{content:"\f504"}.fa-person-arrow-up-from-line:before{content:"\e539"}.fa-scroll-torah:before,.fa-torah:before{content:"\f6a0"}.fa-broom-ball:before,.fa-quidditch-broom-ball:before,.fa-quidditch:before{content:"\f458"}.fa-toggle-off:before{content:"\f204"}.fa-archive:before,.fa-box-archive:before{content:"\f187"}.fa-person-drowning:before{content:"\e545"}.fa-arrow-down-9-1:before,.fa-sort-numeric-desc:before,.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-face-grin-tongue-squint:before,.fa-grin-tongue-squint:before{content:"\f58a"}.fa-spray-can:before{content:"\f5bd"}.fa-truck-monster:before{content:"\f63b"}.fa-w:before{content:"\57"}.fa-earth-africa:before,.fa-globe-africa:before{content:"\f57c"}.fa-rainbow:before{content:"\f75b"}.fa-circle-notch:before{content:"\f1ce"}.fa-tablet-alt:before,.fa-tablet-screen-button:before{content:"\f3fa"}.fa-paw:before{content:"\f1b0"}.fa-cloud:before{content:"\f0c2"}.fa-trowel-bricks:before{content:"\e58a"}.fa-face-flushed:before,.fa-flushed:before{content:"\f579"}.fa-hospital-user:before{content:"\f80d"}.fa-tent-arrow-left-right:before{content:"\e57f"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-binoculars:before{content:"\f1e5"}.fa-microphone-slash:before{content:"\f131"}.fa-box-tissue:before{content:"\e05b"}.fa-motorcycle:before{content:"\f21c"}.fa-bell-concierge:before,.fa-concierge-bell:before{content:"\f562"}.fa-pen-ruler:before,.fa-pencil-ruler:before{content:"\f5ae"}.fa-people-arrows-left-right:before,.fa-people-arrows:before{content:"\e068"}.fa-mars-and-venus-burst:before{content:"\e523"}.fa-caret-square-right:before,.fa-square-caret-right:before{content:"\f152"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-sun-plant-wilt:before{content:"\e57a"}.fa-toilets-portable:before{content:"\e584"}.fa-hockey-puck:before{content:"\f453"}.fa-table:before{content:"\f0ce"}.fa-magnifying-glass-arrow-right:before{content:"\e521"}.fa-digital-tachograph:before,.fa-tachograph-digital:before{content:"\f566"}.fa-users-slash:before{content:"\e073"}.fa-clover:before{content:"\e139"}.fa-mail-reply:before,.fa-reply:before{content:"\f3e5"}.fa-star-and-crescent:before{content:"\f699"}.fa-house-fire:before{content:"\e50c"}.fa-minus-square:before,.fa-square-minus:before{content:"\f146"}.fa-helicopter:before{content:"\f533"}.fa-compass:before{content:"\f14e"}.fa-caret-square-down:before,.fa-square-caret-down:before{content:"\f150"}.fa-file-circle-question:before{content:"\e4ef"}.fa-laptop-code:before{content:"\f5fc"}.fa-swatchbook:before{content:"\f5c3"}.fa-prescription-bottle:before{content:"\f485"}.fa-bars:before,.fa-navicon:before{content:"\f0c9"}.fa-people-group:before{content:"\e533"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-heart-broken:before,.fa-heart-crack:before{content:"\f7a9"}.fa-external-link-square-alt:before,.fa-square-up-right:before{content:"\f360"}.fa-face-kiss-beam:before,.fa-kiss-beam:before{content:"\f597"}.fa-film:before{content:"\f008"}.fa-ruler-horizontal:before{content:"\f547"}.fa-people-robbery:before{content:"\e536"}.fa-lightbulb:before{content:"\f0eb"}.fa-caret-left:before{content:"\f0d9"}.fa-circle-exclamation:before,.fa-exclamation-circle:before{content:"\f06a"}.fa-school-circle-xmark:before{content:"\e56d"}.fa-arrow-right-from-bracket:before,.fa-sign-out:before{content:"\f08b"}.fa-chevron-circle-down:before,.fa-circle-chevron-down:before{content:"\f13a"}.fa-unlock-alt:before,.fa-unlock-keyhole:before{content:"\f13e"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-headphones-alt:before,.fa-headphones-simple:before{content:"\f58f"}.fa-sitemap:before{content:"\f0e8"}.fa-circle-dollar-to-slot:before,.fa-donate:before{content:"\f4b9"}.fa-memory:before{content:"\f538"}.fa-road-spikes:before{content:"\e568"}.fa-fire-burner:before{content:"\e4f1"}.fa-flag:before{content:"\f024"}.fa-hanukiah:before{content:"\f6e6"}.fa-feather:before{content:"\f52d"}.fa-volume-down:before,.fa-volume-low:before{content:"\f027"}.fa-comment-slash:before{content:"\f4b3"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-compress:before{content:"\f066"}.fa-wheat-alt:before,.fa-wheat-awn:before{content:"\e2cd"}.fa-ankh:before{content:"\f644"}.fa-hands-holding-child:before{content:"\e4fa"}.fa-asterisk:before{content:"\2a"}.fa-check-square:before,.fa-square-check:before{content:"\f14a"}.fa-peseta-sign:before{content:"\e221"}.fa-header:before,.fa-heading:before{content:"\f1dc"}.fa-ghost:before{content:"\f6e2"}.fa-list-squares:before,.fa-list:before{content:"\f03a"}.fa-phone-square-alt:before,.fa-square-phone-flip:before{content:"\f87b"}.fa-cart-plus:before{content:"\f217"}.fa-gamepad:before{content:"\f11b"}.fa-circle-dot:before,.fa-dot-circle:before{content:"\f192"}.fa-dizzy:before,.fa-face-dizzy:before{content:"\f567"}.fa-egg:before{content:"\f7fb"}.fa-house-medical-circle-xmark:before{content:"\e513"}.fa-campground:before{content:"\f6bb"}.fa-folder-plus:before{content:"\f65e"}.fa-futbol-ball:before,.fa-futbol:before,.fa-soccer-ball:before{content:"\f1e3"}.fa-paint-brush:before,.fa-paintbrush:before{content:"\f1fc"}.fa-lock:before{content:"\f023"}.fa-gas-pump:before{content:"\f52f"}.fa-hot-tub-person:before,.fa-hot-tub:before{content:"\f593"}.fa-map-location:before,.fa-map-marked:before{content:"\f59f"}.fa-house-flood-water:before{content:"\e50e"}.fa-tree:before{content:"\f1bb"}.fa-bridge-lock:before{content:"\e4cc"}.fa-sack-dollar:before{content:"\f81d"}.fa-edit:before,.fa-pen-to-square:before{content:"\f044"}.fa-car-side:before{content:"\f5e4"}.fa-share-alt:before,.fa-share-nodes:before{content:"\f1e0"}.fa-heart-circle-minus:before{content:"\e4ff"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-microscope:before{content:"\f610"}.fa-sink:before{content:"\e06d"}.fa-bag-shopping:before,.fa-shopping-bag:before{content:"\f290"}.fa-arrow-down-z-a:before,.fa-sort-alpha-desc:before,.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-mitten:before{content:"\f7b5"}.fa-person-rays:before{content:"\e54d"}.fa-users:before{content:"\f0c0"}.fa-eye-slash:before{content:"\f070"}.fa-flask-vial:before{content:"\e4f3"}.fa-hand-paper:before,.fa-hand:before{content:"\f256"}.fa-om:before{content:"\f679"}.fa-worm:before{content:"\e599"}.fa-house-circle-xmark:before{content:"\e50b"}.fa-plug:before{content:"\f1e6"}.fa-chevron-up:before{content:"\f077"}.fa-hand-spock:before{content:"\f259"}.fa-stopwatch:before{content:"\f2f2"}.fa-face-kiss:before,.fa-kiss:before{content:"\f596"}.fa-bridge-circle-xmark:before{content:"\e4cb"}.fa-face-grin-tongue:before,.fa-grin-tongue:before{content:"\f589"}.fa-chess-bishop:before{content:"\f43a"}.fa-face-grin-wink:before,.fa-grin-wink:before{content:"\f58c"}.fa-deaf:before,.fa-deafness:before,.fa-ear-deaf:before,.fa-hard-of-hearing:before{content:"\f2a4"}.fa-road-circle-check:before{content:"\e564"}.fa-dice-five:before{content:"\f523"}.fa-rss-square:before,.fa-square-rss:before{content:"\f143"}.fa-land-mine-on:before{content:"\e51b"}.fa-i-cursor:before{content:"\f246"}.fa-stamp:before{content:"\f5bf"}.fa-stairs:before{content:"\e289"}.fa-i:before{content:"\49"}.fa-hryvnia-sign:before,.fa-hryvnia:before{content:"\f6f2"}.fa-pills:before{content:"\f484"}.fa-face-grin-wide:before,.fa-grin-alt:before{content:"\f581"}.fa-tooth:before{content:"\f5c9"}.fa-v:before{content:"\56"}.fa-bangladeshi-taka-sign:before{content:"\e2e6"}.fa-bicycle:before{content:"\f206"}.fa-rod-asclepius:before,.fa-rod-snake:before,.fa-staff-aesculapius:before,.fa-staff-snake:before{content:"\e579"}.fa-head-side-cough-slash:before{content:"\e062"}.fa-ambulance:before,.fa-truck-medical:before{content:"\f0f9"}.fa-wheat-awn-circle-exclamation:before{content:"\e598"}.fa-snowman:before{content:"\f7d0"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-road-barrier:before{content:"\e562"}.fa-school:before{content:"\f549"}.fa-igloo:before{content:"\f7ae"}.fa-joint:before{content:"\f595"}.fa-angle-right:before{content:"\f105"}.fa-horse:before{content:"\f6f0"}.fa-q:before{content:"\51"}.fa-g:before{content:"\47"}.fa-notes-medical:before{content:"\f481"}.fa-temperature-2:before,.fa-temperature-half:before,.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-dong-sign:before{content:"\e169"}.fa-capsules:before{content:"\f46b"}.fa-poo-bolt:before,.fa-poo-storm:before{content:"\f75a"}.fa-face-frown-open:before,.fa-frown-open:before{content:"\f57a"}.fa-hand-point-up:before{content:"\f0a6"}.fa-money-bill:before{content:"\f0d6"}.fa-bookmark:before{content:"\f02e"}.fa-align-justify:before{content:"\f039"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-helmet-un:before{content:"\e503"}.fa-bullseye:before{content:"\f140"}.fa-bacon:before{content:"\f7e5"}.fa-hand-point-down:before{content:"\f0a7"}.fa-arrow-up-from-bracket:before{content:"\e09a"}.fa-folder-blank:before,.fa-folder:before{content:"\f07b"}.fa-file-medical-alt:before,.fa-file-waveform:before{content:"\f478"}.fa-radiation:before{content:"\f7b9"}.fa-chart-simple:before{content:"\e473"}.fa-mars-stroke:before{content:"\f229"}.fa-vial:before{content:"\f492"}.fa-dashboard:before,.fa-gauge-med:before,.fa-gauge:before,.fa-tachometer-alt-average:before{content:"\f624"}.fa-magic-wand-sparkles:before,.fa-wand-magic-sparkles:before{content:"\e2ca"}.fa-e:before{content:"\45"}.fa-pen-alt:before,.fa-pen-clip:before{content:"\f305"}.fa-bridge-circle-exclamation:before{content:"\e4ca"}.fa-user:before{content:"\f007"}.fa-school-circle-check:before{content:"\e56b"}.fa-dumpster:before{content:"\f793"}.fa-shuttle-van:before,.fa-van-shuttle:before{content:"\f5b6"}.fa-building-user:before{content:"\e4da"}.fa-caret-square-left:before,.fa-square-caret-left:before{content:"\f191"}.fa-highlighter:before{content:"\f591"}.fa-key:before{content:"\f084"}.fa-bullhorn:before{content:"\f0a1"}.fa-globe:before{content:"\f0ac"}.fa-synagogue:before{content:"\f69b"}.fa-person-half-dress:before{content:"\e548"}.fa-road-bridge:before{content:"\e563"}.fa-location-arrow:before{content:"\f124"}.fa-c:before{content:"\43"}.fa-tablet-button:before{content:"\f10a"}.fa-building-lock:before{content:"\e4d6"}.fa-pizza-slice:before{content:"\f818"}.fa-money-bill-wave:before{content:"\f53a"}.fa-area-chart:before,.fa-chart-area:before{content:"\f1fe"}.fa-house-flag:before{content:"\e50d"}.fa-person-circle-minus:before{content:"\e540"}.fa-ban:before,.fa-cancel:before{content:"\f05e"}.fa-camera-rotate:before{content:"\e0d8"}.fa-air-freshener:before,.fa-spray-can-sparkles:before{content:"\f5d0"}.fa-star:before{content:"\f005"}.fa-repeat:before{content:"\f363"}.fa-cross:before{content:"\f654"}.fa-box:before{content:"\f466"}.fa-venus-mars:before{content:"\f228"}.fa-arrow-pointer:before,.fa-mouse-pointer:before{content:"\f245"}.fa-expand-arrows-alt:before,.fa-maximize:before{content:"\f31e"}.fa-charging-station:before{content:"\f5e7"}.fa-shapes:before,.fa-triangle-circle-square:before{content:"\f61f"}.fa-random:before,.fa-shuffle:before{content:"\f074"}.fa-person-running:before,.fa-running:before{content:"\f70c"}.fa-mobile-retro:before{content:"\e527"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-spider:before{content:"\f717"}.fa-hands-bound:before{content:"\e4f9"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-plane-circle-exclamation:before{content:"\e556"}.fa-x-ray:before{content:"\f497"}.fa-spell-check:before{content:"\f891"}.fa-slash:before{content:"\f715"}.fa-computer-mouse:before,.fa-mouse:before{content:"\f8cc"}.fa-arrow-right-to-bracket:before,.fa-sign-in:before{content:"\f090"}.fa-shop-slash:before,.fa-store-alt-slash:before{content:"\e070"}.fa-server:before{content:"\f233"}.fa-virus-covid-slash:before{content:"\e4a9"}.fa-shop-lock:before{content:"\e4a5"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-blender-phone:before{content:"\f6b6"}.fa-building-wheat:before{content:"\e4db"}.fa-person-breastfeeding:before{content:"\e53a"}.fa-right-to-bracket:before,.fa-sign-in-alt:before{content:"\f2f6"}.fa-venus:before{content:"\f221"}.fa-passport:before{content:"\f5ab"}.fa-heart-pulse:before,.fa-heartbeat:before{content:"\f21e"}.fa-people-carry-box:before,.fa-people-carry:before{content:"\f4ce"}.fa-temperature-high:before{content:"\f769"}.fa-microchip:before{content:"\f2db"}.fa-crown:before{content:"\f521"}.fa-weight-hanging:before{content:"\f5cd"}.fa-xmarks-lines:before{content:"\e59a"}.fa-file-prescription:before{content:"\f572"}.fa-weight-scale:before,.fa-weight:before{content:"\f496"}.fa-user-friends:before,.fa-user-group:before{content:"\f500"}.fa-arrow-up-a-z:before,.fa-sort-alpha-up:before{content:"\f15e"}.fa-chess-knight:before{content:"\f441"}.fa-face-laugh-squint:before,.fa-laugh-squint:before{content:"\f59b"}.fa-wheelchair:before{content:"\f193"}.fa-arrow-circle-up:before,.fa-circle-arrow-up:before{content:"\f0aa"}.fa-toggle-on:before{content:"\f205"}.fa-person-walking:before,.fa-walking:before{content:"\f554"}.fa-l:before{content:"\4c"}.fa-fire:before{content:"\f06d"}.fa-bed-pulse:before,.fa-procedures:before{content:"\f487"}.fa-shuttle-space:before,.fa-space-shuttle:before{content:"\f197"}.fa-face-laugh:before,.fa-laugh:before{content:"\f599"}.fa-folder-open:before{content:"\f07c"}.fa-heart-circle-plus:before{content:"\e500"}.fa-code-fork:before{content:"\e13b"}.fa-city:before{content:"\f64f"}.fa-microphone-alt:before,.fa-microphone-lines:before{content:"\f3c9"}.fa-pepper-hot:before{content:"\f816"}.fa-unlock:before{content:"\f09c"}.fa-colon-sign:before{content:"\e140"}.fa-headset:before{content:"\f590"}.fa-store-slash:before{content:"\e071"}.fa-road-circle-xmark:before{content:"\e566"}.fa-user-minus:before{content:"\f503"}.fa-mars-stroke-up:before,.fa-mars-stroke-v:before{content:"\f22a"}.fa-champagne-glasses:before,.fa-glass-cheers:before{content:"\f79f"}.fa-clipboard:before{content:"\f328"}.fa-house-circle-exclamation:before{content:"\e50a"}.fa-file-arrow-up:before,.fa-file-upload:before{content:"\f574"}.fa-wifi-3:before,.fa-wifi-strong:before,.fa-wifi:before{content:"\f1eb"}.fa-bath:before,.fa-bathtub:before{content:"\f2cd"}.fa-underline:before{content:"\f0cd"}.fa-user-edit:before,.fa-user-pen:before{content:"\f4ff"}.fa-signature:before{content:"\f5b7"}.fa-stroopwafel:before{content:"\f551"}.fa-bold:before{content:"\f032"}.fa-anchor-lock:before{content:"\e4ad"}.fa-building-ngo:before{content:"\e4d7"}.fa-manat-sign:before{content:"\e1d5"}.fa-not-equal:before{content:"\f53e"}.fa-border-style:before,.fa-border-top-left:before{content:"\f853"}.fa-map-location-dot:before,.fa-map-marked-alt:before{content:"\f5a0"}.fa-jedi:before{content:"\f669"}.fa-poll:before,.fa-square-poll-vertical:before{content:"\f681"}.fa-mug-hot:before{content:"\f7b6"}.fa-battery-car:before,.fa-car-battery:before{content:"\f5df"}.fa-gift:before{content:"\f06b"}.fa-dice-two:before{content:"\f528"}.fa-chess-queen:before{content:"\f445"}.fa-glasses:before{content:"\f530"}.fa-chess-board:before{content:"\f43c"}.fa-building-circle-check:before{content:"\e4d2"}.fa-person-chalkboard:before{content:"\e53d"}.fa-mars-stroke-h:before,.fa-mars-stroke-right:before{content:"\f22b"}.fa-hand-back-fist:before,.fa-hand-rock:before{content:"\f255"}.fa-caret-square-up:before,.fa-square-caret-up:before{content:"\f151"}.fa-cloud-showers-water:before{content:"\e4e4"}.fa-bar-chart:before,.fa-chart-bar:before{content:"\f080"}.fa-hands-bubbles:before,.fa-hands-wash:before{content:"\e05e"}.fa-less-than-equal:before{content:"\f537"}.fa-train:before{content:"\f238"}.fa-eye-low-vision:before,.fa-low-vision:before{content:"\f2a8"}.fa-crow:before{content:"\f520"}.fa-sailboat:before{content:"\e445"}.fa-window-restore:before{content:"\f2d2"}.fa-plus-square:before,.fa-square-plus:before{content:"\f0fe"}.fa-torii-gate:before{content:"\f6a1"}.fa-frog:before{content:"\f52e"}.fa-bucket:before{content:"\e4cf"}.fa-image:before{content:"\f03e"}.fa-microphone:before{content:"\f130"}.fa-cow:before{content:"\f6c8"}.fa-caret-up:before{content:"\f0d8"}.fa-screwdriver:before{content:"\f54a"}.fa-folder-closed:before{content:"\e185"}.fa-house-tsunami:before{content:"\e515"}.fa-square-nfi:before{content:"\e576"}.fa-arrow-up-from-ground-water:before{content:"\e4b5"}.fa-glass-martini-alt:before,.fa-martini-glass:before{content:"\f57b"}.fa-rotate-back:before,.fa-rotate-backward:before,.fa-rotate-left:before,.fa-undo-alt:before{content:"\f2ea"}.fa-columns:before,.fa-table-columns:before{content:"\f0db"}.fa-lemon:before{content:"\f094"}.fa-head-side-mask:before{content:"\e063"}.fa-handshake:before{content:"\f2b5"}.fa-gem:before{content:"\f3a5"}.fa-dolly-box:before,.fa-dolly:before{content:"\f472"}.fa-smoking:before{content:"\f48d"}.fa-compress-arrows-alt:before,.fa-minimize:before{content:"\f78c"}.fa-monument:before{content:"\f5a6"}.fa-snowplow:before{content:"\f7d2"}.fa-angle-double-right:before,.fa-angles-right:before{content:"\f101"}.fa-cannabis:before{content:"\f55f"}.fa-circle-play:before,.fa-play-circle:before{content:"\f144"}.fa-tablets:before{content:"\f490"}.fa-ethernet:before{content:"\f796"}.fa-eur:before,.fa-euro-sign:before,.fa-euro:before{content:"\f153"}.fa-chair:before{content:"\f6c0"}.fa-check-circle:before,.fa-circle-check:before{content:"\f058"}.fa-circle-stop:before,.fa-stop-circle:before{content:"\f28d"}.fa-compass-drafting:before,.fa-drafting-compass:before{content:"\f568"}.fa-plate-wheat:before{content:"\e55a"}.fa-icicles:before{content:"\f7ad"}.fa-person-shelter:before{content:"\e54f"}.fa-neuter:before{content:"\f22c"}.fa-id-badge:before{content:"\f2c1"}.fa-marker:before{content:"\f5a1"}.fa-face-laugh-beam:before,.fa-laugh-beam:before{content:"\f59a"}.fa-helicopter-symbol:before{content:"\e502"}.fa-universal-access:before{content:"\f29a"}.fa-chevron-circle-up:before,.fa-circle-chevron-up:before{content:"\f139"}.fa-lari-sign:before{content:"\e1c8"}.fa-volcano:before{content:"\f770"}.fa-person-walking-dashed-line-arrow-right:before{content:"\e553"}.fa-gbp:before,.fa-pound-sign:before,.fa-sterling-sign:before{content:"\f154"}.fa-viruses:before{content:"\e076"}.fa-square-person-confined:before{content:"\e577"}.fa-user-tie:before{content:"\f508"}.fa-arrow-down-long:before,.fa-long-arrow-down:before{content:"\f175"}.fa-tent-arrow-down-to-line:before{content:"\e57e"}.fa-certificate:before{content:"\f0a3"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-suitcase:before{content:"\f0f2"}.fa-person-skating:before,.fa-skating:before{content:"\f7c5"}.fa-filter-circle-dollar:before,.fa-funnel-dollar:before{content:"\f662"}.fa-camera-retro:before{content:"\f083"}.fa-arrow-circle-down:before,.fa-circle-arrow-down:before{content:"\f0ab"}.fa-arrow-right-to-file:before,.fa-file-import:before{content:"\f56f"}.fa-external-link-square:before,.fa-square-arrow-up-right:before{content:"\f14c"}.fa-box-open:before{content:"\f49e"}.fa-scroll:before{content:"\f70e"}.fa-spa:before{content:"\f5bb"}.fa-location-pin-lock:before{content:"\e51f"}.fa-pause:before{content:"\f04c"}.fa-hill-avalanche:before{content:"\e507"}.fa-temperature-0:before,.fa-temperature-empty:before,.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-bomb:before{content:"\f1e2"}.fa-registered:before{content:"\f25d"}.fa-address-card:before,.fa-contact-card:before,.fa-vcard:before{content:"\f2bb"}.fa-balance-scale-right:before,.fa-scale-unbalanced-flip:before{content:"\f516"}.fa-subscript:before{content:"\f12c"}.fa-diamond-turn-right:before,.fa-directions:before{content:"\f5eb"}.fa-burst:before{content:"\e4dc"}.fa-house-laptop:before,.fa-laptop-house:before{content:"\e066"}.fa-face-tired:before,.fa-tired:before{content:"\f5c8"}.fa-money-bills:before{content:"\e1f3"}.fa-smog:before{content:"\f75f"}.fa-crutch:before{content:"\f7f7"}.fa-cloud-arrow-up:before,.fa-cloud-upload-alt:before,.fa-cloud-upload:before{content:"\f0ee"}.fa-palette:before{content:"\f53f"}.fa-arrows-turn-right:before{content:"\e4c0"}.fa-vest:before{content:"\e085"}.fa-ferry:before{content:"\e4ea"}.fa-arrows-down-to-people:before{content:"\e4b9"}.fa-seedling:before,.fa-sprout:before{content:"\f4d8"}.fa-arrows-alt-h:before,.fa-left-right:before{content:"\f337"}.fa-boxes-packing:before{content:"\e4c7"}.fa-arrow-circle-left:before,.fa-circle-arrow-left:before{content:"\f0a8"}.fa-group-arrows-rotate:before{content:"\e4f6"}.fa-bowl-food:before{content:"\e4c6"}.fa-candy-cane:before{content:"\f786"}.fa-arrow-down-wide-short:before,.fa-sort-amount-asc:before,.fa-sort-amount-down:before{content:"\f160"}.fa-cloud-bolt:before,.fa-thunderstorm:before{content:"\f76c"}.fa-remove-format:before,.fa-text-slash:before{content:"\f87d"}.fa-face-smile-wink:before,.fa-smile-wink:before{content:"\f4da"}.fa-file-word:before{content:"\f1c2"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-arrows-h:before,.fa-arrows-left-right:before{content:"\f07e"}.fa-house-lock:before{content:"\e510"}.fa-cloud-arrow-down:before,.fa-cloud-download-alt:before,.fa-cloud-download:before{content:"\f0ed"}.fa-children:before{content:"\e4e1"}.fa-blackboard:before,.fa-chalkboard:before{content:"\f51b"}.fa-user-alt-slash:before,.fa-user-large-slash:before{content:"\f4fa"}.fa-envelope-open:before{content:"\f2b6"}.fa-handshake-alt-slash:before,.fa-handshake-simple-slash:before{content:"\e05f"}.fa-mattress-pillow:before{content:"\e525"}.fa-guarani-sign:before{content:"\e19a"}.fa-arrows-rotate:before,.fa-refresh:before,.fa-sync:before{content:"\f021"}.fa-fire-extinguisher:before{content:"\f134"}.fa-cruzeiro-sign:before{content:"\e152"}.fa-greater-than-equal:before{content:"\f532"}.fa-shield-alt:before,.fa-shield-halved:before{content:"\f3ed"}.fa-atlas:before,.fa-book-atlas:before{content:"\f558"}.fa-virus:before{content:"\e074"}.fa-envelope-circle-check:before{content:"\e4e8"}.fa-layer-group:before{content:"\f5fd"}.fa-arrows-to-dot:before{content:"\e4be"}.fa-archway:before{content:"\f557"}.fa-heart-circle-check:before{content:"\e4fd"}.fa-house-chimney-crack:before,.fa-house-damage:before{content:"\f6f1"}.fa-file-archive:before,.fa-file-zipper:before{content:"\f1c6"}.fa-square:before{content:"\f0c8"}.fa-glass-martini:before,.fa-martini-glass-empty:before{content:"\f000"}.fa-couch:before{content:"\f4b8"}.fa-cedi-sign:before{content:"\e0df"}.fa-italic:before{content:"\f033"}.fa-church:before{content:"\f51d"}.fa-comments-dollar:before{content:"\f653"}.fa-democrat:before{content:"\f747"}.fa-z:before{content:"\5a"}.fa-person-skiing:before,.fa-skiing:before{content:"\f7c9"}.fa-road-lock:before{content:"\e567"}.fa-a:before{content:"\41"}.fa-temperature-arrow-down:before,.fa-temperature-down:before{content:"\e03f"}.fa-feather-alt:before,.fa-feather-pointed:before{content:"\f56b"}.fa-p:before{content:"\50"}.fa-snowflake:before{content:"\f2dc"}.fa-newspaper:before{content:"\f1ea"}.fa-ad:before,.fa-rectangle-ad:before{content:"\f641"}.fa-arrow-circle-right:before,.fa-circle-arrow-right:before{content:"\f0a9"}.fa-filter-circle-xmark:before{content:"\e17b"}.fa-locust:before{content:"\e520"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-list-1-2:before,.fa-list-numeric:before,.fa-list-ol:before{content:"\f0cb"}.fa-person-dress-burst:before{content:"\e544"}.fa-money-check-alt:before,.fa-money-check-dollar:before{content:"\f53d"}.fa-vector-square:before{content:"\f5cb"}.fa-bread-slice:before{content:"\f7ec"}.fa-language:before{content:"\f1ab"}.fa-face-kiss-wink-heart:before,.fa-kiss-wink-heart:before{content:"\f598"}.fa-filter:before{content:"\f0b0"}.fa-question:before{content:"\3f"}.fa-file-signature:before{content:"\f573"}.fa-arrows-alt:before,.fa-up-down-left-right:before{content:"\f0b2"}.fa-house-chimney-user:before{content:"\e065"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-puzzle-piece:before{content:"\f12e"}.fa-money-check:before{content:"\f53c"}.fa-star-half-alt:before,.fa-star-half-stroke:before{content:"\f5c0"}.fa-code:before{content:"\f121"}.fa-glass-whiskey:before,.fa-whiskey-glass:before{content:"\f7a0"}.fa-building-circle-exclamation:before{content:"\e4d3"}.fa-magnifying-glass-chart:before{content:"\e522"}.fa-arrow-up-right-from-square:before,.fa-external-link:before{content:"\f08e"}.fa-cubes-stacked:before{content:"\e4e6"}.fa-krw:before,.fa-won-sign:before,.fa-won:before{content:"\f159"}.fa-virus-covid:before{content:"\e4a8"}.fa-austral-sign:before{content:"\e0a9"}.fa-f:before{content:"\46"}.fa-leaf:before{content:"\f06c"}.fa-road:before{content:"\f018"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-person-circle-plus:before{content:"\e541"}.fa-chart-pie:before,.fa-pie-chart:before{content:"\f200"}.fa-bolt-lightning:before{content:"\e0b7"}.fa-sack-xmark:before{content:"\e56a"}.fa-file-excel:before{content:"\f1c3"}.fa-file-contract:before{content:"\f56c"}.fa-fish-fins:before{content:"\e4f2"}.fa-building-flag:before{content:"\e4d5"}.fa-face-grin-beam:before,.fa-grin-beam:before{content:"\f582"}.fa-object-ungroup:before{content:"\f248"}.fa-poop:before{content:"\f619"}.fa-location-pin:before,.fa-map-marker:before{content:"\f041"}.fa-kaaba:before{content:"\f66b"}.fa-toilet-paper:before{content:"\f71e"}.fa-hard-hat:before,.fa-hat-hard:before,.fa-helmet-safety:before{content:"\f807"}.fa-eject:before{content:"\f052"}.fa-arrow-alt-circle-right:before,.fa-circle-right:before{content:"\f35a"}.fa-plane-circle-check:before{content:"\e555"}.fa-face-rolling-eyes:before,.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-object-group:before{content:"\f247"}.fa-chart-line:before,.fa-line-chart:before{content:"\f201"}.fa-mask-ventilator:before{content:"\e524"}.fa-arrow-right:before{content:"\f061"}.fa-map-signs:before,.fa-signs-post:before{content:"\f277"}.fa-cash-register:before{content:"\f788"}.fa-person-circle-question:before{content:"\e542"}.fa-h:before{content:"\48"}.fa-tarp:before{content:"\e57b"}.fa-screwdriver-wrench:before,.fa-tools:before{content:"\f7d9"}.fa-arrows-to-eye:before{content:"\e4bf"}.fa-plug-circle-bolt:before{content:"\e55b"}.fa-heart:before{content:"\f004"}.fa-mars-and-venus:before{content:"\f224"}.fa-home-user:before,.fa-house-user:before{content:"\e1b0"}.fa-dumpster-fire:before{content:"\f794"}.fa-house-crack:before{content:"\e3b1"}.fa-cocktail:before,.fa-martini-glass-citrus:before{content:"\f561"}.fa-face-surprise:before,.fa-surprise:before{content:"\f5c2"}.fa-bottle-water:before{content:"\e4c5"}.fa-circle-pause:before,.fa-pause-circle:before{content:"\f28b"}.fa-toilet-paper-slash:before{content:"\e072"}.fa-apple-alt:before,.fa-apple-whole:before{content:"\f5d1"}.fa-kitchen-set:before{content:"\e51a"}.fa-r:before{content:"\52"}.fa-temperature-1:before,.fa-temperature-quarter:before,.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-cube:before{content:"\f1b2"}.fa-bitcoin-sign:before{content:"\e0b4"}.fa-shield-dog:before{content:"\e573"}.fa-solar-panel:before{content:"\f5ba"}.fa-lock-open:before{content:"\f3c1"}.fa-elevator:before{content:"\e16d"}.fa-money-bill-transfer:before{content:"\e528"}.fa-money-bill-trend-up:before{content:"\e529"}.fa-house-flood-water-circle-arrow-right:before{content:"\e50f"}.fa-poll-h:before,.fa-square-poll-horizontal:before{content:"\f682"}.fa-circle:before{content:"\f111"}.fa-backward-fast:before,.fa-fast-backward:before{content:"\f049"}.fa-recycle:before{content:"\f1b8"}.fa-user-astronaut:before{content:"\f4fb"}.fa-plane-slash:before{content:"\e069"}.fa-trademark:before{content:"\f25c"}.fa-basketball-ball:before,.fa-basketball:before{content:"\f434"}.fa-satellite-dish:before{content:"\f7c0"}.fa-arrow-alt-circle-up:before,.fa-circle-up:before{content:"\f35b"}.fa-mobile-alt:before,.fa-mobile-screen-button:before{content:"\f3cd"}.fa-volume-high:before,.fa-volume-up:before{content:"\f028"}.fa-users-rays:before{content:"\e593"}.fa-wallet:before{content:"\f555"}.fa-clipboard-check:before{content:"\f46c"}.fa-file-audio:before{content:"\f1c7"}.fa-burger:before,.fa-hamburger:before{content:"\f805"}.fa-wrench:before{content:"\f0ad"}.fa-bugs:before{content:"\e4d0"}.fa-rupee-sign:before,.fa-rupee:before{content:"\f156"}.fa-file-image:before{content:"\f1c5"}.fa-circle-question:before,.fa-question-circle:before{content:"\f059"}.fa-plane-departure:before{content:"\f5b0"}.fa-handshake-slash:before{content:"\e060"}.fa-book-bookmark:before{content:"\e0bb"}.fa-code-branch:before{content:"\f126"}.fa-hat-cowboy:before{content:"\f8c0"}.fa-bridge:before{content:"\e4c8"}.fa-phone-alt:before,.fa-phone-flip:before{content:"\f879"}.fa-truck-front:before{content:"\e2b7"}.fa-cat:before{content:"\f6be"}.fa-anchor-circle-exclamation:before{content:"\e4ab"}.fa-truck-field:before{content:"\e58d"}.fa-route:before{content:"\f4d7"}.fa-clipboard-question:before{content:"\e4e3"}.fa-panorama:before{content:"\e209"}.fa-comment-medical:before{content:"\f7f5"}.fa-teeth-open:before{content:"\f62f"}.fa-file-circle-minus:before{content:"\e4ed"}.fa-tags:before{content:"\f02c"}.fa-wine-glass:before{content:"\f4e3"}.fa-fast-forward:before,.fa-forward-fast:before{content:"\f050"}.fa-face-meh-blank:before,.fa-meh-blank:before{content:"\f5a4"}.fa-parking:before,.fa-square-parking:before{content:"\f540"}.fa-house-signal:before{content:"\e012"}.fa-bars-progress:before,.fa-tasks-alt:before{content:"\f828"}.fa-faucet-drip:before{content:"\e006"}.fa-cart-flatbed:before,.fa-dolly-flatbed:before{content:"\f474"}.fa-ban-smoking:before,.fa-smoking-ban:before{content:"\f54d"}.fa-terminal:before{content:"\f120"}.fa-mobile-button:before{content:"\f10b"}.fa-house-medical-flag:before{content:"\e514"}.fa-basket-shopping:before,.fa-shopping-basket:before{content:"\f291"}.fa-tape:before{content:"\f4db"}.fa-bus-alt:before,.fa-bus-simple:before{content:"\f55e"}.fa-eye:before{content:"\f06e"}.fa-face-sad-cry:before,.fa-sad-cry:before{content:"\f5b3"}.fa-audio-description:before{content:"\f29e"}.fa-person-military-to-person:before{content:"\e54c"}.fa-file-shield:before{content:"\e4f0"}.fa-user-slash:before{content:"\f506"}.fa-pen:before{content:"\f304"}.fa-tower-observation:before{content:"\e586"}.fa-file-code:before{content:"\f1c9"}.fa-signal-5:before,.fa-signal-perfect:before,.fa-signal:before{content:"\f012"}.fa-bus:before{content:"\f207"}.fa-heart-circle-xmark:before{content:"\e501"}.fa-home-lg:before,.fa-house-chimney:before{content:"\e3af"}.fa-window-maximize:before{content:"\f2d0"}.fa-face-frown:before,.fa-frown:before{content:"\f119"}.fa-prescription:before{content:"\f5b1"}.fa-shop:before,.fa-store-alt:before{content:"\f54f"}.fa-floppy-disk:before,.fa-save:before{content:"\f0c7"}.fa-vihara:before{content:"\f6a7"}.fa-balance-scale-left:before,.fa-scale-unbalanced:before{content:"\f515"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-comment-dots:before,.fa-commenting:before{content:"\f4ad"}.fa-plant-wilt:before{content:"\e5aa"}.fa-diamond:before{content:"\f219"}.fa-face-grin-squint:before,.fa-grin-squint:before{content:"\f585"}.fa-hand-holding-dollar:before,.fa-hand-holding-usd:before{content:"\f4c0"}.fa-bacterium:before{content:"\e05a"}.fa-hand-pointer:before{content:"\f25a"}.fa-drum-steelpan:before{content:"\f56a"}.fa-hand-scissors:before{content:"\f257"}.fa-hands-praying:before,.fa-praying-hands:before{content:"\f684"}.fa-arrow-right-rotate:before,.fa-arrow-rotate-forward:before,.fa-arrow-rotate-right:before,.fa-redo:before{content:"\f01e"}.fa-biohazard:before{content:"\f780"}.fa-location-crosshairs:before,.fa-location:before{content:"\f601"}.fa-mars-double:before{content:"\f227"}.fa-child-dress:before{content:"\e59c"}.fa-users-between-lines:before{content:"\e591"}.fa-lungs-virus:before{content:"\e067"}.fa-face-grin-tears:before,.fa-grin-tears:before{content:"\f588"}.fa-phone:before{content:"\f095"}.fa-calendar-times:before,.fa-calendar-xmark:before{content:"\f273"}.fa-child-reaching:before{content:"\e59d"}.fa-head-side-virus:before{content:"\e064"}.fa-user-cog:before,.fa-user-gear:before{content:"\f4fe"}.fa-arrow-up-1-9:before,.fa-sort-numeric-up:before{content:"\f163"}.fa-door-closed:before{content:"\f52a"}.fa-shield-virus:before{content:"\e06c"}.fa-dice-six:before{content:"\f526"}.fa-mosquito-net:before{content:"\e52c"}.fa-bridge-water:before{content:"\e4ce"}.fa-person-booth:before{content:"\f756"}.fa-text-width:before{content:"\f035"}.fa-hat-wizard:before{content:"\f6e8"}.fa-pen-fancy:before{content:"\f5ac"}.fa-digging:before,.fa-person-digging:before{content:"\f85e"}.fa-trash:before{content:"\f1f8"}.fa-gauge-simple-med:before,.fa-gauge-simple:before,.fa-tachometer-average:before{content:"\f629"}.fa-book-medical:before{content:"\f7e6"}.fa-poo:before{content:"\f2fe"}.fa-quote-right-alt:before,.fa-quote-right:before{content:"\f10e"}.fa-shirt:before,.fa-t-shirt:before,.fa-tshirt:before{content:"\f553"}.fa-cubes:before{content:"\f1b3"}.fa-divide:before{content:"\f529"}.fa-tenge-sign:before,.fa-tenge:before{content:"\f7d7"}.fa-headphones:before{content:"\f025"}.fa-hands-holding:before{content:"\f4c2"}.fa-hands-clapping:before{content:"\e1a8"}.fa-republican:before{content:"\f75e"}.fa-arrow-left:before{content:"\f060"}.fa-person-circle-xmark:before{content:"\e543"}.fa-ruler:before{content:"\f545"}.fa-align-left:before{content:"\f036"}.fa-dice-d6:before{content:"\f6d1"}.fa-restroom:before{content:"\f7bd"}.fa-j:before{content:"\4a"}.fa-users-viewfinder:before{content:"\e595"}.fa-file-video:before{content:"\f1c8"}.fa-external-link-alt:before,.fa-up-right-from-square:before{content:"\f35d"}.fa-table-cells:before,.fa-th:before{content:"\f00a"}.fa-file-pdf:before{content:"\f1c1"}.fa-bible:before,.fa-book-bible:before{content:"\f647"}.fa-o:before{content:"\4f"}.fa-medkit:before,.fa-suitcase-medical:before{content:"\f0fa"}.fa-user-secret:before{content:"\f21b"}.fa-otter:before{content:"\f700"}.fa-female:before,.fa-person-dress:before{content:"\f182"}.fa-comment-dollar:before{content:"\f651"}.fa-briefcase-clock:before,.fa-business-time:before{content:"\f64a"}.fa-table-cells-large:before,.fa-th-large:before{content:"\f009"}.fa-book-tanakh:before,.fa-tanakh:before{content:"\f827"}.fa-phone-volume:before,.fa-volume-control-phone:before{content:"\f2a0"}.fa-hat-cowboy-side:before{content:"\f8c1"}.fa-clipboard-user:before{content:"\f7f3"}.fa-child:before{content:"\f1ae"}.fa-lira-sign:before{content:"\f195"}.fa-satellite:before{content:"\f7bf"}.fa-plane-lock:before{content:"\e558"}.fa-tag:before{content:"\f02b"}.fa-comment:before{content:"\f075"}.fa-birthday-cake:before,.fa-cake-candles:before,.fa-cake:before{content:"\f1fd"}.fa-envelope:before{content:"\f0e0"}.fa-angle-double-up:before,.fa-angles-up:before{content:"\f102"}.fa-paperclip:before{content:"\f0c6"}.fa-arrow-right-to-city:before{content:"\e4b3"}.fa-ribbon:before{content:"\f4d6"}.fa-lungs:before{content:"\f604"}.fa-arrow-up-9-1:before,.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-litecoin-sign:before{content:"\e1d3"}.fa-border-none:before{content:"\f850"}.fa-circle-nodes:before{content:"\e4e2"}.fa-parachute-box:before{content:"\f4cd"}.fa-indent:before{content:"\f03c"}.fa-truck-field-un:before{content:"\e58e"}.fa-hourglass-empty:before,.fa-hourglass:before{content:"\f254"}.fa-mountain:before{content:"\f6fc"}.fa-user-doctor:before,.fa-user-md:before{content:"\f0f0"}.fa-circle-info:before,.fa-info-circle:before{content:"\f05a"}.fa-cloud-meatball:before{content:"\f73b"}.fa-camera-alt:before,.fa-camera:before{content:"\f030"}.fa-square-virus:before{content:"\e578"}.fa-meteor:before{content:"\f753"}.fa-car-on:before{content:"\e4dd"}.fa-sleigh:before{content:"\f7cc"}.fa-arrow-down-1-9:before,.fa-sort-numeric-asc:before,.fa-sort-numeric-down:before{content:"\f162"}.fa-hand-holding-droplet:before,.fa-hand-holding-water:before{content:"\f4c1"}.fa-water:before{content:"\f773"}.fa-calendar-check:before{content:"\f274"}.fa-braille:before{content:"\f2a1"}.fa-prescription-bottle-alt:before,.fa-prescription-bottle-medical:before{content:"\f486"}.fa-landmark:before{content:"\f66f"}.fa-truck:before{content:"\f0d1"}.fa-crosshairs:before{content:"\f05b"}.fa-person-cane:before{content:"\e53c"}.fa-tent:before{content:"\e57d"}.fa-vest-patches:before{content:"\e086"}.fa-check-double:before{content:"\f560"}.fa-arrow-down-a-z:before,.fa-sort-alpha-asc:before,.fa-sort-alpha-down:before{content:"\f15d"}.fa-money-bill-wheat:before{content:"\e52a"}.fa-cookie:before{content:"\f563"}.fa-arrow-left-rotate:before,.fa-arrow-rotate-back:before,.fa-arrow-rotate-backward:before,.fa-arrow-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-hard-drive:before,.fa-hdd:before{content:"\f0a0"}.fa-face-grin-squint-tears:before,.fa-grin-squint-tears:before{content:"\f586"}.fa-dumbbell:before{content:"\f44b"}.fa-list-alt:before,.fa-rectangle-list:before{content:"\f022"}.fa-tarp-droplet:before{content:"\e57c"}.fa-house-medical-circle-check:before{content:"\e511"}.fa-person-skiing-nordic:before,.fa-skiing-nordic:before{content:"\f7ca"}.fa-calendar-plus:before{content:"\f271"}.fa-plane-arrival:before{content:"\f5af"}.fa-arrow-alt-circle-left:before,.fa-circle-left:before{content:"\f359"}.fa-subway:before,.fa-train-subway:before{content:"\f239"}.fa-chart-gantt:before{content:"\e0e4"}.fa-indian-rupee-sign:before,.fa-indian-rupee:before,.fa-inr:before{content:"\e1bc"}.fa-crop-alt:before,.fa-crop-simple:before{content:"\f565"}.fa-money-bill-1:before,.fa-money-bill-alt:before{content:"\f3d1"}.fa-left-long:before,.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-dna:before{content:"\f471"}.fa-virus-slash:before{content:"\e075"}.fa-minus:before,.fa-subtract:before{content:"\f068"}.fa-chess:before{content:"\f439"}.fa-arrow-left-long:before,.fa-long-arrow-left:before{content:"\f177"}.fa-plug-circle-check:before{content:"\e55c"}.fa-street-view:before{content:"\f21d"}.fa-franc-sign:before{content:"\e18f"}.fa-volume-off:before{content:"\f026"}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before,.fa-hands-american-sign-language-interpreting:before,.fa-hands-asl-interpreting:before{content:"\f2a3"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-droplet-slash:before,.fa-tint-slash:before{content:"\f5c7"}.fa-mosque:before{content:"\f678"}.fa-mosquito:before{content:"\e52b"}.fa-star-of-david:before{content:"\f69a"}.fa-person-military-rifle:before{content:"\e54b"}.fa-cart-shopping:before,.fa-shopping-cart:before{content:"\f07a"}.fa-vials:before{content:"\f493"}.fa-plug-circle-plus:before{content:"\e55f"}.fa-place-of-worship:before{content:"\f67f"}.fa-grip-vertical:before{content:"\f58e"}.fa-arrow-turn-up:before,.fa-level-up:before{content:"\f148"}.fa-u:before{content:"\55"}.fa-square-root-alt:before,.fa-square-root-variable:before{content:"\f698"}.fa-clock-four:before,.fa-clock:before{content:"\f017"}.fa-backward-step:before,.fa-step-backward:before{content:"\f048"}.fa-pallet:before{content:"\f482"}.fa-faucet:before{content:"\e005"}.fa-baseball-bat-ball:before{content:"\f432"}.fa-s:before{content:"\53"}.fa-timeline:before{content:"\e29c"}.fa-keyboard:before{content:"\f11c"}.fa-caret-down:before{content:"\f0d7"}.fa-clinic-medical:before,.fa-house-chimney-medical:before{content:"\f7f2"}.fa-temperature-3:before,.fa-temperature-three-quarters:before,.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-mobile-android-alt:before,.fa-mobile-screen:before{content:"\f3cf"}.fa-plane-up:before{content:"\e22d"}.fa-piggy-bank:before{content:"\f4d3"}.fa-battery-3:before,.fa-battery-half:before{content:"\f242"}.fa-mountain-city:before{content:"\e52e"}.fa-coins:before{content:"\f51e"}.fa-khanda:before{content:"\f66d"}.fa-sliders-h:before,.fa-sliders:before{content:"\f1de"}.fa-folder-tree:before{content:"\f802"}.fa-network-wired:before{content:"\f6ff"}.fa-map-pin:before{content:"\f276"}.fa-hamsa:before{content:"\f665"}.fa-cent-sign:before{content:"\e3f5"}.fa-flask:before{content:"\f0c3"}.fa-person-pregnant:before{content:"\e31e"}.fa-wand-sparkles:before{content:"\f72b"}.fa-ellipsis-v:before,.fa-ellipsis-vertical:before{content:"\f142"}.fa-ticket:before{content:"\f145"}.fa-power-off:before{content:"\f011"}.fa-long-arrow-alt-right:before,.fa-right-long:before{content:"\f30b"}.fa-flag-usa:before{content:"\f74d"}.fa-laptop-file:before{content:"\e51d"}.fa-teletype:before,.fa-tty:before{content:"\f1e4"}.fa-diagram-next:before{content:"\e476"}.fa-person-rifle:before{content:"\e54e"}.fa-house-medical-circle-exclamation:before{content:"\e512"}.fa-closed-captioning:before{content:"\f20a"}.fa-hiking:before,.fa-person-hiking:before{content:"\f6ec"}.fa-venus-double:before{content:"\f226"}.fa-images:before{content:"\f302"}.fa-calculator:before{content:"\f1ec"}.fa-people-pulling:before{content:"\e535"}.fa-n:before{content:"\4e"}.fa-cable-car:before,.fa-tram:before{content:"\f7da"}.fa-cloud-rain:before{content:"\f73d"}.fa-building-circle-xmark:before{content:"\e4d4"}.fa-ship:before{content:"\f21a"}.fa-arrows-down-to-line:before{content:"\e4b8"}.fa-download:before{content:"\f019"}.fa-face-grin:before,.fa-grin:before{content:"\f580"}.fa-backspace:before,.fa-delete-left:before{content:"\f55a"}.fa-eye-dropper-empty:before,.fa-eye-dropper:before,.fa-eyedropper:before{content:"\f1fb"}.fa-file-circle-check:before{content:"\e5a0"}.fa-forward:before{content:"\f04e"}.fa-mobile-android:before,.fa-mobile-phone:before,.fa-mobile:before{content:"\f3ce"}.fa-face-meh:before,.fa-meh:before{content:"\f11a"}.fa-align-center:before{content:"\f037"}.fa-book-dead:before,.fa-book-skull:before{content:"\f6b7"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-heart-circle-exclamation:before{content:"\e4fe"}.fa-home-alt:before,.fa-home-lg-alt:before,.fa-home:before,.fa-house:before{content:"\f015"}.fa-calendar-week:before{content:"\f784"}.fa-laptop-medical:before{content:"\f812"}.fa-b:before{content:"\42"}.fa-file-medical:before{content:"\f477"}.fa-dice-one:before{content:"\f525"}.fa-kiwi-bird:before{content:"\f535"}.fa-arrow-right-arrow-left:before,.fa-exchange:before{content:"\f0ec"}.fa-redo-alt:before,.fa-rotate-forward:before,.fa-rotate-right:before{content:"\f2f9"}.fa-cutlery:before,.fa-utensils:before{content:"\f2e7"}.fa-arrow-up-wide-short:before,.fa-sort-amount-up:before{content:"\f161"}.fa-mill-sign:before{content:"\e1ed"}.fa-bowl-rice:before{content:"\e2eb"}.fa-skull:before{content:"\f54c"}.fa-broadcast-tower:before,.fa-tower-broadcast:before{content:"\f519"}.fa-truck-pickup:before{content:"\f63c"}.fa-long-arrow-alt-up:before,.fa-up-long:before{content:"\f30c"}.fa-stop:before{content:"\f04d"}.fa-code-merge:before{content:"\f387"}.fa-upload:before{content:"\f093"}.fa-hurricane:before{content:"\f751"}.fa-mound:before{content:"\e52d"}.fa-toilet-portable:before{content:"\e583"}.fa-compact-disc:before{content:"\f51f"}.fa-file-arrow-down:before,.fa-file-download:before{content:"\f56d"}.fa-caravan:before{content:"\f8ff"}.fa-shield-cat:before{content:"\e572"}.fa-bolt:before,.fa-zap:before{content:"\f0e7"}.fa-glass-water:before{content:"\e4f4"}.fa-oil-well:before{content:"\e532"}.fa-vault:before{content:"\e2c5"}.fa-mars:before{content:"\f222"}.fa-toilet:before{content:"\f7d8"}.fa-plane-circle-xmark:before{content:"\e557"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen-sign:before,.fa-yen:before{content:"\f157"}.fa-rouble:before,.fa-rub:before,.fa-ruble-sign:before,.fa-ruble:before{content:"\f158"}.fa-sun:before{content:"\f185"}.fa-guitar:before{content:"\f7a6"}.fa-face-laugh-wink:before,.fa-laugh-wink:before{content:"\f59c"}.fa-horse-head:before{content:"\f7ab"}.fa-bore-hole:before{content:"\e4c3"}.fa-industry:before{content:"\f275"}.fa-arrow-alt-circle-down:before,.fa-circle-down:before{content:"\f358"}.fa-arrows-turn-to-dots:before{content:"\e4c1"}.fa-florin-sign:before{content:"\e184"}.fa-arrow-down-short-wide:before,.fa-sort-amount-desc:before,.fa-sort-amount-down-alt:before{content:"\f884"}.fa-less-than:before{content:"\3c"}.fa-angle-down:before{content:"\f107"}.fa-car-tunnel:before{content:"\e4de"}.fa-head-side-cough:before{content:"\e061"}.fa-grip-lines:before{content:"\f7a4"}.fa-thumbs-down:before{content:"\f165"}.fa-user-lock:before{content:"\f502"}.fa-arrow-right-long:before,.fa-long-arrow-right:before{content:"\f178"}.fa-anchor-circle-xmark:before{content:"\e4ac"}.fa-ellipsis-h:before,.fa-ellipsis:before{content:"\f141"}.fa-chess-pawn:before{content:"\f443"}.fa-first-aid:before,.fa-kit-medical:before{content:"\f479"}.fa-person-through-window:before{content:"\e5a9"}.fa-toolbox:before{content:"\f552"}.fa-hands-holding-circle:before{content:"\e4fb"}.fa-bug:before{content:"\f188"}.fa-credit-card-alt:before,.fa-credit-card:before{content:"\f09d"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-hand-holding-hand:before{content:"\e4f7"}.fa-book-open-reader:before,.fa-book-reader:before{content:"\f5da"}.fa-mountain-sun:before{content:"\e52f"}.fa-arrows-left-right-to-line:before{content:"\e4ba"}.fa-dice-d20:before{content:"\f6cf"}.fa-truck-droplet:before{content:"\e58c"}.fa-file-circle-xmark:before{content:"\e5a1"}.fa-temperature-arrow-up:before,.fa-temperature-up:before{content:"\e040"}.fa-medal:before{content:"\f5a2"}.fa-bed:before{content:"\f236"}.fa-h-square:before,.fa-square-h:before{content:"\f0fd"}.fa-podcast:before{content:"\f2ce"}.fa-temperature-4:before,.fa-temperature-full:before,.fa-thermometer-4:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-bell:before{content:"\f0f3"}.fa-superscript:before{content:"\f12b"}.fa-plug-circle-xmark:before{content:"\e560"}.fa-star-of-life:before{content:"\f621"}.fa-phone-slash:before{content:"\f3dd"}.fa-paint-roller:before{content:"\f5aa"}.fa-hands-helping:before,.fa-handshake-angle:before{content:"\f4c4"}.fa-location-dot:before,.fa-map-marker-alt:before{content:"\f3c5"}.fa-file:before{content:"\f15b"}.fa-greater-than:before{content:"\3e"}.fa-person-swimming:before,.fa-swimmer:before{content:"\f5c4"}.fa-arrow-down:before{content:"\f063"}.fa-droplet:before,.fa-tint:before{content:"\f043"}.fa-eraser:before{content:"\f12d"}.fa-earth-america:before,.fa-earth-americas:before,.fa-earth:before,.fa-globe-americas:before{content:"\f57d"}.fa-person-burst:before{content:"\e53b"}.fa-dove:before{content:"\f4ba"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-socks:before{content:"\f696"}.fa-inbox:before{content:"\f01c"}.fa-section:before{content:"\e447"}.fa-gauge-high:before,.fa-tachometer-alt-fast:before,.fa-tachometer-alt:before{content:"\f625"}.fa-envelope-open-text:before{content:"\f658"}.fa-hospital-alt:before,.fa-hospital-wide:before,.fa-hospital:before{content:"\f0f8"}.fa-wine-bottle:before{content:"\f72f"}.fa-chess-rook:before{content:"\f447"}.fa-bars-staggered:before,.fa-reorder:before,.fa-stream:before{content:"\f550"}.fa-dharmachakra:before{content:"\f655"}.fa-hotdog:before{content:"\f80f"}.fa-blind:before,.fa-person-walking-with-cane:before{content:"\f29d"}.fa-drum:before{content:"\f569"}.fa-ice-cream:before{content:"\f810"}.fa-heart-circle-bolt:before{content:"\e4fc"}.fa-fax:before{content:"\f1ac"}.fa-paragraph:before{content:"\f1dd"}.fa-check-to-slot:before,.fa-vote-yea:before{content:"\f772"}.fa-star-half:before{content:"\f089"}.fa-boxes-alt:before,.fa-boxes-stacked:before,.fa-boxes:before{content:"\f468"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-assistive-listening-systems:before,.fa-ear-listen:before{content:"\f2a2"}.fa-tree-city:before{content:"\e587"}.fa-play:before{content:"\f04b"}.fa-font:before{content:"\f031"}.fa-rupiah-sign:before{content:"\e23d"}.fa-magnifying-glass:before,.fa-search:before{content:"\f002"}.fa-ping-pong-paddle-ball:before,.fa-table-tennis-paddle-ball:before,.fa-table-tennis:before{content:"\f45d"}.fa-diagnoses:before,.fa-person-dots-from-line:before{content:"\f470"}.fa-trash-can-arrow-up:before,.fa-trash-restore-alt:before{content:"\f82a"}.fa-naira-sign:before{content:"\e1f6"}.fa-cart-arrow-down:before{content:"\f218"}.fa-walkie-talkie:before{content:"\f8ef"}.fa-file-edit:before,.fa-file-pen:before{content:"\f31c"}.fa-receipt:before{content:"\f543"}.fa-pen-square:before,.fa-pencil-square:before,.fa-square-pen:before{content:"\f14b"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-person-circle-exclamation:before{content:"\e53f"}.fa-chevron-down:before{content:"\f078"}.fa-battery-5:before,.fa-battery-full:before,.fa-battery:before{content:"\f240"}.fa-skull-crossbones:before{content:"\f714"}.fa-code-compare:before{content:"\e13a"}.fa-list-dots:before,.fa-list-ul:before{content:"\f0ca"}.fa-school-lock:before{content:"\e56f"}.fa-tower-cell:before{content:"\e585"}.fa-down-long:before,.fa-long-arrow-alt-down:before{content:"\f309"}.fa-ranking-star:before{content:"\e561"}.fa-chess-king:before{content:"\f43f"}.fa-person-harassing:before{content:"\e549"}.fa-brazilian-real-sign:before{content:"\e46c"}.fa-landmark-alt:before,.fa-landmark-dome:before{content:"\f752"}.fa-arrow-up:before{content:"\f062"}.fa-television:before,.fa-tv-alt:before,.fa-tv:before{content:"\f26c"}.fa-shrimp:before{content:"\e448"}.fa-list-check:before,.fa-tasks:before{content:"\f0ae"}.fa-jug-detergent:before{content:"\e519"}.fa-circle-user:before,.fa-user-circle:before{content:"\f2bd"}.fa-user-shield:before{content:"\f505"}.fa-wind:before{content:"\f72e"}.fa-car-burst:before,.fa-car-crash:before{content:"\f5e1"}.fa-y:before{content:"\59"}.fa-person-snowboarding:before,.fa-snowboarding:before{content:"\f7ce"}.fa-shipping-fast:before,.fa-truck-fast:before{content:"\f48b"}.fa-fish:before{content:"\f578"}.fa-user-graduate:before{content:"\f501"}.fa-adjust:before,.fa-circle-half-stroke:before{content:"\f042"}.fa-clapperboard:before{content:"\e131"}.fa-circle-radiation:before,.fa-radiation-alt:before{content:"\f7ba"}.fa-baseball-ball:before,.fa-baseball:before{content:"\f433"}.fa-jet-fighter-up:before{content:"\e518"}.fa-diagram-project:before,.fa-project-diagram:before{content:"\f542"}.fa-copy:before{content:"\f0c5"}.fa-volume-mute:before,.fa-volume-times:before,.fa-volume-xmark:before{content:"\f6a9"}.fa-hand-sparkles:before{content:"\e05d"}.fa-grip-horizontal:before,.fa-grip:before{content:"\f58d"}.fa-share-from-square:before,.fa-share-square:before{content:"\f14d"}.fa-child-combatant:before,.fa-child-rifle:before{content:"\e4e0"}.fa-gun:before{content:"\e19b"}.fa-phone-square:before,.fa-square-phone:before{content:"\f098"}.fa-add:before,.fa-plus:before{content:"\2b"}.fa-expand:before{content:"\f065"}.fa-computer:before{content:"\e4e5"}.fa-close:before,.fa-multiply:before,.fa-remove:before,.fa-times:before,.fa-xmark:before{content:"\f00d"}.fa-arrows-up-down-left-right:before,.fa-arrows:before{content:"\f047"}.fa-chalkboard-teacher:before,.fa-chalkboard-user:before{content:"\f51c"}.fa-peso-sign:before{content:"\e222"}.fa-building-shield:before{content:"\e4d8"}.fa-baby:before{content:"\f77c"}.fa-users-line:before{content:"\e592"}.fa-quote-left-alt:before,.fa-quote-left:before{content:"\f10d"}.fa-tractor:before{content:"\f722"}.fa-trash-arrow-up:before,.fa-trash-restore:before{content:"\f829"}.fa-arrow-down-up-lock:before{content:"\e4b0"}.fa-lines-leaning:before{content:"\e51e"}.fa-ruler-combined:before{content:"\f546"}.fa-copyright:before{content:"\f1f9"}.fa-equals:before{content:"\3d"}.fa-blender:before{content:"\f517"}.fa-teeth:before{content:"\f62e"}.fa-ils:before,.fa-shekel-sign:before,.fa-shekel:before,.fa-sheqel-sign:before,.fa-sheqel:before{content:"\f20b"}.fa-map:before{content:"\f279"}.fa-rocket:before{content:"\f135"}.fa-photo-film:before,.fa-photo-video:before{content:"\f87c"}.fa-folder-minus:before{content:"\f65d"}.fa-store:before{content:"\f54e"}.fa-arrow-trend-up:before{content:"\e098"}.fa-plug-circle-minus:before{content:"\e55e"}.fa-sign-hanging:before,.fa-sign:before{content:"\f4d9"}.fa-bezier-curve:before{content:"\f55b"}.fa-bell-slash:before{content:"\f1f6"}.fa-tablet-android:before,.fa-tablet:before{content:"\f3fb"}.fa-school-flag:before{content:"\e56e"}.fa-fill:before{content:"\f575"}.fa-angle-up:before{content:"\f106"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-holly-berry:before{content:"\f7aa"}.fa-chevron-left:before{content:"\f053"}.fa-bacteria:before{content:"\e059"}.fa-hand-lizard:before{content:"\f258"}.fa-notdef:before{content:"\e1fe"}.fa-disease:before{content:"\f7fa"}.fa-briefcase-medical:before{content:"\f469"}.fa-genderless:before{content:"\f22d"}.fa-chevron-right:before{content:"\f054"}.fa-retweet:before{content:"\f079"}.fa-car-alt:before,.fa-car-rear:before{content:"\f5de"}.fa-pump-soap:before{content:"\e06b"}.fa-video-slash:before{content:"\f4e2"}.fa-battery-2:before,.fa-battery-quarter:before{content:"\f243"}.fa-radio:before{content:"\f8d7"}.fa-baby-carriage:before,.fa-carriage-baby:before{content:"\f77d"}.fa-traffic-light:before{content:"\f637"}.fa-thermometer:before{content:"\f491"}.fa-vr-cardboard:before{content:"\f729"}.fa-hand-middle-finger:before{content:"\f806"}.fa-percent:before,.fa-percentage:before{content:"\25"}.fa-truck-moving:before{content:"\f4df"}.fa-glass-water-droplet:before{content:"\e4f5"}.fa-display:before{content:"\e163"}.fa-face-smile:before,.fa-smile:before{content:"\f118"}.fa-thumb-tack:before,.fa-thumbtack:before{content:"\f08d"}.fa-trophy:before{content:"\f091"}.fa-person-praying:before,.fa-pray:before{content:"\f683"}.fa-hammer:before{content:"\f6e3"}.fa-hand-peace:before{content:"\f25b"}.fa-rotate:before,.fa-sync-alt:before{content:"\f2f1"}.fa-spinner:before{content:"\f110"}.fa-robot:before{content:"\f544"}.fa-peace:before{content:"\f67c"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-warehouse:before{content:"\f494"}.fa-arrow-up-right-dots:before{content:"\e4b7"}.fa-splotch:before{content:"\f5bc"}.fa-face-grin-hearts:before,.fa-grin-hearts:before{content:"\f584"}.fa-dice-four:before{content:"\f524"}.fa-sim-card:before{content:"\f7c4"}.fa-transgender-alt:before,.fa-transgender:before{content:"\f225"}.fa-mercury:before{content:"\f223"}.fa-arrow-turn-down:before,.fa-level-down:before{content:"\f149"}.fa-person-falling-burst:before{content:"\e547"}.fa-award:before{content:"\f559"}.fa-ticket-alt:before,.fa-ticket-simple:before{content:"\f3ff"}.fa-building:before{content:"\f1ad"}.fa-angle-double-left:before,.fa-angles-left:before{content:"\f100"}.fa-qrcode:before{content:"\f029"}.fa-clock-rotate-left:before,.fa-history:before{content:"\f1da"}.fa-face-grin-beam-sweat:before,.fa-grin-beam-sweat:before{content:"\f583"}.fa-arrow-right-from-file:before,.fa-file-export:before{content:"\f56e"}.fa-shield-blank:before,.fa-shield:before{content:"\f132"}.fa-arrow-up-short-wide:before,.fa-sort-amount-up-alt:before{content:"\f885"}.fa-house-medical:before{content:"\e3b2"}.fa-golf-ball-tee:before,.fa-golf-ball:before{content:"\f450"}.fa-chevron-circle-left:before,.fa-circle-chevron-left:before{content:"\f137"}.fa-house-chimney-window:before{content:"\e00d"}.fa-pen-nib:before{content:"\f5ad"}.fa-tent-arrow-turn-left:before{content:"\e580"}.fa-tents:before{content:"\e582"}.fa-magic:before,.fa-wand-magic:before{content:"\f0d0"}.fa-dog:before{content:"\f6d3"}.fa-carrot:before{content:"\f787"}.fa-moon:before{content:"\f186"}.fa-wine-glass-alt:before,.fa-wine-glass-empty:before{content:"\f5ce"}.fa-cheese:before{content:"\f7ef"}.fa-yin-yang:before{content:"\f6ad"}.fa-music:before{content:"\f001"}.fa-code-commit:before{content:"\f386"}.fa-temperature-low:before{content:"\f76b"}.fa-biking:before,.fa-person-biking:before{content:"\f84a"}.fa-broom:before{content:"\f51a"}.fa-shield-heart:before{content:"\e574"}.fa-gopuram:before{content:"\f664"}.fa-earth-oceania:before,.fa-globe-oceania:before{content:"\e47b"}.fa-square-xmark:before,.fa-times-square:before,.fa-xmark-square:before{content:"\f2d3"}.fa-hashtag:before{content:"\23"}.fa-expand-alt:before,.fa-up-right-and-down-left-from-center:before{content:"\f424"}.fa-oil-can:before{content:"\f613"}.fa-t:before{content:"\54"}.fa-hippo:before{content:"\f6ed"}.fa-chart-column:before{content:"\e0e3"}.fa-infinity:before{content:"\f534"}.fa-vial-circle-check:before{content:"\e596"}.fa-person-arrow-down-to-line:before{content:"\e538"}.fa-voicemail:before{content:"\f897"}.fa-fan:before{content:"\f863"}.fa-person-walking-luggage:before{content:"\e554"}.fa-arrows-alt-v:before,.fa-up-down:before{content:"\f338"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-calendar:before{content:"\f133"}.fa-trailer:before{content:"\e041"}.fa-bahai:before,.fa-haykal:before{content:"\f666"}.fa-sd-card:before{content:"\f7c2"}.fa-dragon:before{content:"\f6d5"}.fa-shoe-prints:before{content:"\f54b"}.fa-circle-plus:before,.fa-plus-circle:before{content:"\f055"}.fa-face-grin-tongue-wink:before,.fa-grin-tongue-wink:before{content:"\f58b"}.fa-hand-holding:before{content:"\f4bd"}.fa-plug-circle-exclamation:before{content:"\e55d"}.fa-chain-broken:before,.fa-chain-slash:before,.fa-link-slash:before,.fa-unlink:before{content:"\f127"}.fa-clone:before{content:"\f24d"}.fa-person-walking-arrow-loop-left:before{content:"\e551"}.fa-arrow-up-z-a:before,.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-fire-alt:before,.fa-fire-flame-curved:before{content:"\f7e4"}.fa-tornado:before{content:"\f76f"}.fa-file-circle-plus:before{content:"\e494"}.fa-book-quran:before,.fa-quran:before{content:"\f687"}.fa-anchor:before{content:"\f13d"}.fa-border-all:before{content:"\f84c"}.fa-angry:before,.fa-face-angry:before{content:"\f556"}.fa-cookie-bite:before{content:"\f564"}.fa-arrow-trend-down:before{content:"\e097"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-draw-polygon:before{content:"\f5ee"}.fa-balance-scale:before,.fa-scale-balanced:before{content:"\f24e"}.fa-gauge-simple-high:before,.fa-tachometer-fast:before,.fa-tachometer:before{content:"\f62a"}.fa-shower:before{content:"\f2cc"}.fa-desktop-alt:before,.fa-desktop:before{content:"\f390"}.fa-m:before{content:"\4d"}.fa-table-list:before,.fa-th-list:before{content:"\f00b"}.fa-comment-sms:before,.fa-sms:before{content:"\f7cd"}.fa-book:before{content:"\f02d"}.fa-user-plus:before{content:"\f234"}.fa-check:before{content:"\f00c"}.fa-battery-4:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-house-circle-check:before{content:"\e509"}.fa-angle-left:before{content:"\f104"}.fa-diagram-successor:before{content:"\e47a"}.fa-truck-arrow-right:before{content:"\e58b"}.fa-arrows-split-up-and-left:before{content:"\e4bc"}.fa-fist-raised:before,.fa-hand-fist:before{content:"\f6de"}.fa-cloud-moon:before{content:"\f6c3"}.fa-briefcase:before{content:"\f0b1"}.fa-person-falling:before{content:"\e546"}.fa-image-portrait:before,.fa-portrait:before{content:"\f3e0"}.fa-user-tag:before{content:"\f507"}.fa-rug:before{content:"\e569"}.fa-earth-europe:before,.fa-globe-europe:before{content:"\f7a2"}.fa-cart-flatbed-suitcase:before,.fa-luggage-cart:before{content:"\f59d"}.fa-rectangle-times:before,.fa-rectangle-xmark:before,.fa-times-rectangle:before,.fa-window-close:before{content:"\f410"}.fa-baht-sign:before{content:"\e0ac"}.fa-book-open:before{content:"\f518"}.fa-book-journal-whills:before,.fa-journal-whills:before{content:"\f66a"}.fa-handcuffs:before{content:"\e4f8"}.fa-exclamation-triangle:before,.fa-triangle-exclamation:before,.fa-warning:before{content:"\f071"}.fa-database:before{content:"\f1c0"}.fa-arrow-turn-right:before,.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-bottle-droplet:before{content:"\e4c4"}.fa-mask-face:before{content:"\e1d7"}.fa-hill-rockslide:before{content:"\e508"}.fa-exchange-alt:before,.fa-right-left:before{content:"\f362"}.fa-paper-plane:before{content:"\f1d8"}.fa-road-circle-exclamation:before{content:"\e565"}.fa-dungeon:before{content:"\f6d9"}.fa-align-right:before{content:"\f038"}.fa-money-bill-1-wave:before,.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-life-ring:before{content:"\f1cd"}.fa-hands:before,.fa-sign-language:before,.fa-signing:before{content:"\f2a7"}.fa-calendar-day:before{content:"\f783"}.fa-ladder-water:before,.fa-swimming-pool:before,.fa-water-ladder:before{content:"\f5c5"}.fa-arrows-up-down:before,.fa-arrows-v:before{content:"\f07d"}.fa-face-grimace:before,.fa-grimace:before{content:"\f57f"}.fa-wheelchair-alt:before,.fa-wheelchair-move:before{content:"\e2ce"}.fa-level-down-alt:before,.fa-turn-down:before{content:"\f3be"}.fa-person-walking-arrow-right:before{content:"\e552"}.fa-envelope-square:before,.fa-square-envelope:before{content:"\f199"}.fa-dice:before{content:"\f522"}.fa-bowling-ball:before{content:"\f436"}.fa-brain:before{content:"\f5dc"}.fa-band-aid:before,.fa-bandage:before{content:"\f462"}.fa-calendar-minus:before{content:"\f272"}.fa-circle-xmark:before,.fa-times-circle:before,.fa-xmark-circle:before{content:"\f057"}.fa-gifts:before{content:"\f79c"}.fa-hotel:before{content:"\f594"}.fa-earth-asia:before,.fa-globe-asia:before{content:"\f57e"}.fa-id-card-alt:before,.fa-id-card-clip:before{content:"\f47f"}.fa-magnifying-glass-plus:before,.fa-search-plus:before{content:"\f00e"}.fa-thumbs-up:before{content:"\f164"}.fa-user-clock:before{content:"\f4fd"}.fa-allergies:before,.fa-hand-dots:before{content:"\f461"}.fa-file-invoice:before{content:"\f570"}.fa-window-minimize:before{content:"\f2d1"}.fa-coffee:before,.fa-mug-saucer:before{content:"\f0f4"}.fa-brush:before{content:"\f55d"}.fa-mask:before{content:"\f6fa"}.fa-magnifying-glass-minus:before,.fa-search-minus:before{content:"\f010"}.fa-ruler-vertical:before{content:"\f548"}.fa-user-alt:before,.fa-user-large:before{content:"\f406"}.fa-train-tram:before{content:"\e5b4"}.fa-user-nurse:before{content:"\f82f"}.fa-syringe:before{content:"\f48e"}.fa-cloud-sun:before{content:"\f6c4"}.fa-stopwatch-20:before{content:"\e06f"}.fa-square-full:before{content:"\f45c"}.fa-magnet:before{content:"\f076"}.fa-jar:before{content:"\e516"}.fa-note-sticky:before,.fa-sticky-note:before{content:"\f249"}.fa-bug-slash:before{content:"\e490"}.fa-arrow-up-from-water-pump:before{content:"\e4b6"}.fa-bone:before{content:"\f5d7"}.fa-user-injured:before{content:"\f728"}.fa-face-sad-tear:before,.fa-sad-tear:before{content:"\f5b4"}.fa-plane:before{content:"\f072"}.fa-tent-arrows-down:before{content:"\e581"}.fa-exclamation:before{content:"\21"}.fa-arrows-spin:before{content:"\e4bb"}.fa-print:before{content:"\f02f"}.fa-try:before,.fa-turkish-lira-sign:before,.fa-turkish-lira:before{content:"\e2bb"}.fa-dollar-sign:before,.fa-dollar:before,.fa-usd:before{content:"\24"}.fa-x:before{content:"\58"}.fa-magnifying-glass-dollar:before,.fa-search-dollar:before{content:"\f688"}.fa-users-cog:before,.fa-users-gear:before{content:"\f509"}.fa-person-military-pointing:before{content:"\e54a"}.fa-bank:before,.fa-building-columns:before,.fa-institution:before,.fa-museum:before,.fa-university:before{content:"\f19c"}.fa-umbrella:before{content:"\f0e9"}.fa-trowel:before{content:"\e589"}.fa-d:before{content:"\44"}.fa-stapler:before{content:"\e5af"}.fa-masks-theater:before,.fa-theater-masks:before{content:"\f630"}.fa-kip-sign:before{content:"\e1c4"}.fa-hand-point-left:before{content:"\f0a5"}.fa-handshake-alt:before,.fa-handshake-simple:before{content:"\f4c6"}.fa-fighter-jet:before,.fa-jet-fighter:before{content:"\f0fb"}.fa-share-alt-square:before,.fa-square-share-nodes:before{content:"\f1e1"}.fa-barcode:before{content:"\f02a"}.fa-plus-minus:before{content:"\e43c"}.fa-video-camera:before,.fa-video:before{content:"\f03d"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-hand-holding-medical:before{content:"\e05c"}.fa-person-circle-check:before{content:"\e53e"}.fa-level-up-alt:before,.fa-turn-up:before{content:"\f3bf"} +.fa-sr-only,.fa-sr-only-focusable:not(:focus),.sr-only,.sr-only-focusable:not(:focus){position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0} \ No newline at end of file diff --git a/font/css/regular.min.css b/font/css/regular.min.css new file mode 100644 index 0000000..1feae61 --- /dev/null +++ b/font/css/regular.min.css @@ -0,0 +1,6 @@ +/*! + * Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-regular:normal 400 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}.fa-regular,.far{font-weight:400} \ No newline at end of file diff --git a/font/css/solid.min.css b/font/css/solid.min.css new file mode 100644 index 0000000..770d98f --- /dev/null +++ b/font/css/solid.min.css @@ -0,0 +1,6 @@ +/*! + * Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-weight:900} \ No newline at end of file diff --git a/font/webfonts/fa-brands-400.ttf b/font/webfonts/fa-brands-400.ttf new file mode 100644 index 0000000..30f55b7 Binary files /dev/null and b/font/webfonts/fa-brands-400.ttf differ diff --git a/font/webfonts/fa-brands-400.woff2 b/font/webfonts/fa-brands-400.woff2 new file mode 100644 index 0000000..8a480d9 Binary files /dev/null and b/font/webfonts/fa-brands-400.woff2 differ diff --git a/font/webfonts/fa-regular-400.ttf b/font/webfonts/fa-regular-400.ttf new file mode 100644 index 0000000..c79589d Binary files /dev/null and b/font/webfonts/fa-regular-400.ttf differ diff --git a/font/webfonts/fa-regular-400.woff2 b/font/webfonts/fa-regular-400.woff2 new file mode 100644 index 0000000..059a94e Binary files /dev/null and b/font/webfonts/fa-regular-400.woff2 differ diff --git a/font/webfonts/fa-solid-900.ttf b/font/webfonts/fa-solid-900.ttf new file mode 100644 index 0000000..e479fb2 Binary files /dev/null and b/font/webfonts/fa-solid-900.ttf differ diff --git a/font/webfonts/fa-solid-900.woff2 b/font/webfonts/fa-solid-900.woff2 new file mode 100644 index 0000000..88b0367 Binary files /dev/null and b/font/webfonts/fa-solid-900.woff2 differ diff --git a/font/webfonts/fa-v4compatibility.ttf b/font/webfonts/fa-v4compatibility.ttf new file mode 100644 index 0000000..ba6cb25 Binary files /dev/null and b/font/webfonts/fa-v4compatibility.ttf differ diff --git a/font/webfonts/fa-v4compatibility.woff2 b/font/webfonts/fa-v4compatibility.woff2 new file mode 100644 index 0000000..23b1c47 Binary files /dev/null and b/font/webfonts/fa-v4compatibility.woff2 differ diff --git a/images/avatar.svg b/images/avatar.svg new file mode 100644 index 0000000..cfe6832 --- /dev/null +++ b/images/avatar.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/bg.svg b/images/bg.svg new file mode 100644 index 0000000..f576b34 --- /dev/null +++ b/images/bg.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/deploy-provider/aliyun.png b/images/deploy-provider/aliyun.png new file mode 100644 index 0000000..c4e9c7f Binary files /dev/null and b/images/deploy-provider/aliyun.png differ diff --git a/images/deploy-provider/gitee.png b/images/deploy-provider/gitee.png new file mode 100644 index 0000000..4701bbf Binary files /dev/null and b/images/deploy-provider/gitee.png differ diff --git a/images/deploy-provider/github.png b/images/deploy-provider/github.png new file mode 100644 index 0000000..0bd612d Binary files /dev/null and b/images/deploy-provider/github.png differ diff --git a/images/deploy-provider/netlify.png b/images/deploy-provider/netlify.png new file mode 100644 index 0000000..42e7e42 Binary files /dev/null and b/images/deploy-provider/netlify.png differ diff --git a/images/deploy-provider/tencent_cloud.png b/images/deploy-provider/tencent_cloud.png new file mode 100644 index 0000000..9abe109 Binary files /dev/null and b/images/deploy-provider/tencent_cloud.png differ diff --git a/images/deploy-provider/upyun.png b/images/deploy-provider/upyun.png new file mode 100644 index 0000000..98feb4d Binary files /dev/null and b/images/deploy-provider/upyun.png differ diff --git a/images/deploy-provider/vercel.png b/images/deploy-provider/vercel.png new file mode 100644 index 0000000..fc66aff Binary files /dev/null and b/images/deploy-provider/vercel.png differ diff --git a/images/logo.svg b/images/logo.svg new file mode 100644 index 0000000..9933ffc --- /dev/null +++ b/images/logo.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/lsk_avatar.jpg b/images/lsk_avatar.jpg new file mode 100644 index 0000000..dbde539 Binary files /dev/null and b/images/lsk_avatar.jpg differ diff --git a/images/lsk_bg.jpg b/images/lsk_bg.jpg new file mode 100644 index 0000000..15a7916 Binary files /dev/null and b/images/lsk_bg.jpg differ diff --git a/images/lsk_favicon.png b/images/lsk_favicon.png new file mode 100644 index 0000000..1a8a953 Binary files /dev/null and b/images/lsk_favicon.png differ diff --git a/images/lsk_logo.png b/images/lsk_logo.png new file mode 100644 index 0000000..61cfcf0 Binary files /dev/null and b/images/lsk_logo.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..01aafc9 --- /dev/null +++ b/index.html @@ -0,0 +1,1093 @@ + + + + + + + + + + + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + +
+
+
+ +
+ +
+ + + +
Keep writing and Keep loving.
+ + + +
+ + + +
+ +
+ +
+ + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+
+
+ + + + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + +
+
    + +
  • + + + +
    + +

    + + Flask一个g引发的思考 + +

    + +
    + + 背景 +最近有面试一家公司,感觉准备的不是很充分,感觉很多东西都答的挺菜的,自己写的文章里面的问题都没答上来更是汗流浃背。所以大概列了一下其中的问题,进行了一番总结补充,重新制定一下复习的计划。其中里面我觉得有个问题我觉得挺有意思的,我记得大概是问了一... + +
    + + + + + +
    +
  • + +
  • + + + +
    + +

    + + Gorm框架-CRUD操作 + +

    + +
    + + 简介关于ORM(Object-Relational Mapping)ORM其实指的是将关系型数据库中的数据和面向对象程序中对象模型进行映射的技术;ORM可以用来自动化处理SQL语句的生成和执行,程序员可以更专注于业务逻辑的实现而不是数据的细节。 +为什... + +
    + + + + + +
    +
  • + +
  • + + + +
    + +

    + + Python Socket编程 + +

    + +
    + + 背景关于Python Socket编程,首先需要了解几个计算机网络的知识,通过以下的几个问题,有助于更好的理解Socket编程的意义,以及整个框架方面的知识: + +TCP和UDP协议本质上的区别? + +TCP协议,面向连接,可靠,基于字节流的传输层通信协... + +
    + + + + + +
    +
  • + +
  • + + + +
    + +

    + + 浅析Python项目部署 + +

    + +
    + + 基础理论关于Web服务器和应用服务器基本概念: + +Web服务器主要功能就是存储、处理、传递网页,客户端和服务器之间基于HTTP协议进行通信。 +应用服务器主要是处理动态请求,调用相应的对象完成对请求的处理,返回响应的结果。 + +两者之间的区别: + +Web... + +
    + + + + + +
    +
  • + +
  • + + + +
    + +

    + + Python定时任务利器—Apscheduler + +

    + +
    + + 导语在工作场景遇到了这么一个场景,就是需要定期去执行一个缓存接口,用于同步设备配置。首先想到的就是Linux上的crontab,可以定期,或者间隔一段时间去执行任务。但是如果你想要把这个定时任务作为一个模块集成到Python项目中,或者想持久化任务,... + +
    + + + + + +
    +
  • + +
  • + + + +
    + +

    + + Python命令行模块(sys.argv,argparse,click) + +

    + +
    + + +Python作为一门脚本语言,经常作为脚本接受命令行传入参数,Python接受命令行参数大概有三种方式。因为在日常工作场景会经常使用到,这里对这几种方式进行总结。 + +命令行参数模块这里命令行参数模块平时工作中用到最多就是这三种模块:sys.argv... + +
    + + + + + +
    +
  • + +
  • + + + +
    + +

    + + 总结技术文章写作技巧 + +

    + +
    + + 1、端正心态  对于写作不要产生恐惧,起点期望放低点,没有什么事情能一蹴而就,同时可以通过阅读其他的技术文章,模仿行文方式,不断重复练习,对比吸取相关长处,补足自己的缺点。 +2、明确定位 对于这篇文章需要给什么类型的读者看,需要怎... + +
    + + + + + +
    +
  • + +
  • + + + +
    + +

    + + 明朝那些事儿 + +

    + +
    + + +关于这个系列的丛书,掰掰手指头估计读了有三次遍了,都是在人生不同的阶段去阅读,或者懵懂,或者无聊,或者失意,每次隔段时间再次捧起这些书的时候,心中各种人物仿佛唤醒你脑中最深处的回忆。唤起那份第一次阅读时带来的惊喜,各种人物形形色色也顿时鲜明起来。其... + +
    + + + + + +
    +
  • + +
+ + + + +
+ + + +
+ +
+ +
+ +
+
+ + + +
+ 由 Hexo 驱动 & 主题 Keep +
+ + + + + + +
+ + + + + 访客数 + + + + + + + 访问量 + + + +
+
+
+ +
+
+ + + + + +
+
+
    +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + + + + +
  • + +
  • +
+ +
    + + + + +
  • + +
  • + +
  • + + +
  • +
+
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + diff --git a/js/back2top.js b/js/back2top.js new file mode 100644 index 0000000..9edffbe --- /dev/null +++ b/js/back2top.js @@ -0,0 +1,42 @@ +/* global KEEP */ + +KEEP.initBack2Top = () => { + KEEP.utils.back2Top = { + back2BottomBtn: document.querySelector('.tool-scroll-to-bottom'), + back2TopBtn: document.querySelector('.tool-scroll-to-top'), + + back2top() { + window.anime({ + targets: document.scrollingElement, + easing: 'easeOutExpo', + scrollTop: 0 + }) + }, + + back2Bottom() { + const scrollHeight = document.body.scrollHeight || document.documentElement.scrollHeight + window.anime({ + targets: document.scrollingElement, + easing: 'easeInExpo', + scrollTop: scrollHeight + }) + }, + + initBack2Top() { + this.back2TopBtn && + this.back2TopBtn.addEventListener('click', () => { + this.back2top() + }) + }, + + initBack2Bottom() { + this.back2BottomBtn && + this.back2BottomBtn.addEventListener('click', () => { + this.back2Bottom() + }) + } + } + + KEEP.utils.back2Top.initBack2Top() + KEEP.utils.back2Top.initBack2Bottom() +} diff --git a/js/category-page.js b/js/category-page.js new file mode 100644 index 0000000..0d1bc90 --- /dev/null +++ b/js/category-page.js @@ -0,0 +1,66 @@ +/* global KEEP */ + +function resetCategoriesPage() { + const resetCategoryDom = (domList) => { + domList.forEach((dom) => { + const categoryNameDom = dom.querySelector('.site-all-category-list-link') + const categoryCountDom = dom.querySelector('.site-all-category-list-count') + const childCategoryInfo = dom.querySelector('.site-all-category-list-child') + + let domTemplate = ` +
+
${ + childCategoryInfo ? ' ' : '' + }${categoryNameDom.outerHTML}
+
${categoryCountDom.outerHTML}
+
+ ` + + if (childCategoryInfo) { + resetCategoryDom(childCategoryInfo.querySelectorAll('.site-all-category-list-item')) + domTemplate += childCategoryInfo.outerHTML + } + + dom.innerHTML = domTemplate + }) + } + + const expandHandle = () => { + const selfCategoryInfoDom = document.querySelectorAll('.site-all-category-list-item') + selfCategoryInfoDom.forEach((dom) => { + let isExpand = false + const iconDom = dom.querySelector('.self-category-info .left .icon') + const childDom = dom.querySelector('.site-all-category-list-child') + + iconDom && + iconDom.addEventListener('click', () => { + isExpand = !isExpand + if (childDom) { + if (isExpand) { + childDom.style.height = 'auto' + childDom.style.visibility = 'visible' + iconDom.classList.add('fa-chevron-down') + iconDom.classList.remove('fa-chevron-right') + } else { + childDom.style.height = '0' + childDom.style.visibility = 'hidden' + iconDom.classList.add('fa-chevron-right') + iconDom.classList.remove('fa-chevron-down') + } + } + }) + }) + } + + resetCategoryDom( + document.querySelectorAll('.site-all-category-list .site-all-category-list-item') + ) + + expandHandle() +} + +if (KEEP.theme_config.pjax.enable === true && KEEP.utils) { + resetCategoriesPage() +} else { + window.addEventListener('DOMContentLoaded', resetCategoriesPage) +} diff --git a/js/code-block.js b/js/code-block.js new file mode 100644 index 0000000..0f3b69c --- /dev/null +++ b/js/code-block.js @@ -0,0 +1,113 @@ +/* global KEEP */ + +KEEP.initCodeBlockTools = () => { + HTMLElement.prototype.wrap = function (wrapper) { + this.parentNode.insertBefore(wrapper, this) + this.parentNode.removeChild(this) + wrapper.appendChild(this) + } + + const { style: codeBlockStyle } = KEEP.theme_config?.code_block || {} + const { style: codeBlockToolsStyle } = KEEP.theme_config?.code_block?.tools || {} + + const isMac = (codeBlockStyle || codeBlockToolsStyle || 'default') === 'mac' + const foldedIconClassName = isMac ? 'fas fa-chevron-left' : 'fas fa-chevron-right' + const { + copy: copyLang, + copied: copiedLang, + fold: foldLang, + folded: foldedLang + } = KEEP.language_code_block + const foldDom = `` + + document.querySelectorAll('figure.highlight').forEach((element) => { + let codeLang = element.classList.length ? element.classList[1].toUpperCase() : '' + if (codeLang === 'PLAINTEXT') { + codeLang = '' + } + const highlightContainer = document.createElement('div') + highlightContainer.classList.add('highlight-container') + if (isMac) { + highlightContainer.classList.add('mac') + } + element.wrap(highlightContainer) + + const codeLangDom = `${codeLang ? '' + codeLang + '' : ''}` + + highlightContainer.insertAdjacentHTML( + 'afterbegin', + `
+ ${isMac ? foldDom + codeLangDom : '' + foldDom + codeLangDom + ''} + +
` + ) + const codeToolsBox = element.parentNode.querySelector('.code-tools-box') + const copyDom = codeToolsBox.querySelector('.copy') + const targetFoldDom = codeToolsBox.querySelector('.fold') + + copyDom.addEventListener('click', (event) => { + const target = event.currentTarget + const code = [...element.querySelectorAll('.code .line')] + .map((line) => line.innerText) + .join('\n') + const tta = document.createElement('textarea') + tta.style.top = window.scrollY + 'px' + tta.style.position = 'absolute' + tta.style.opacity = '0' + tta.readOnly = true + tta.value = code + document.body.append(tta) + const selection = document.getSelection() + const selected = selection.rangeCount > 0 ? selection.getRangeAt(0) : false + tta.select() + tta.setSelectionRange(0, code.length) + tta.readOnly = false + const result = document.execCommand('copy') + + const copyIconDom = target.querySelector('i') + const copyTooltipDom = codeToolsBox.querySelector('.copy .tooltip-content') + + if (result) { + copyIconDom.className = 'fas fa-check' + copyTooltipDom && (copyTooltipDom.innerHTML = copiedLang) + } else { + copyIconDom.className = 'fas fa-times' + } + + tta.blur() + target.blur() + if (selected) { + selection.removeAllRanges() + selection.addRange(selected) + } + document.body.removeChild(tta) + }) + + copyDom.addEventListener('mouseleave', (event) => { + setTimeout(() => { + event.target.querySelector('i').className = 'fas fa-copy' + const copyTooltipDom = codeToolsBox.querySelector('.copy .tooltip-content') + copyTooltipDom && (copyTooltipDom.innerHTML = copyLang) + }, 500) + }) + + let isFold = false + targetFoldDom.addEventListener('click', (event) => { + const target = event.currentTarget + const icon = target.querySelector('i') + const foldTooltipDom = codeToolsBox.querySelector('.fold .tooltip-content') + isFold = !isFold + if (isFold) { + icon.className = foldedIconClassName + element.classList.add('folded') + codeToolsBox.classList.add('folded') + foldTooltipDom && (foldTooltipDom.innerHTML = foldedLang) + } else { + icon.className = 'fas fa-chevron-down' + element.classList.remove('folded') + codeToolsBox.classList.remove('folded') + foldTooltipDom && (foldTooltipDom.innerHTML = foldLang) + } + }) + }) +} diff --git a/js/dark-light-toggle.js b/js/dark-light-toggle.js new file mode 100644 index 0000000..08d853d --- /dev/null +++ b/js/dark-light-toggle.js @@ -0,0 +1,56 @@ +/* global KEEP */ + +KEEP.initModeToggle = () => { + KEEP.utils.modeToggle = { + themeModeToggleBtn: document.querySelector('.tool-dark-light-toggle'), + iconDom: document.querySelector('.tool-dark-light-toggle i'), + + enableLightMode() { + document.body.classList.remove('dark-mode') + document.body.classList.add('light-mode') + this.iconDom.className = 'fas fa-moon' + KEEP.styleStatus.isDark = false + KEEP.setStyleStatus() + }, + + enableDarkMode() { + document.body.classList.add('dark-mode') + document.body.classList.remove('light-mode') + this.iconDom.className = 'fas fa-sun' + KEEP.styleStatus.isDark = true + KEEP.setStyleStatus() + }, + + isDarkPrefersColorScheme() { + return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)') + }, + + initModeStatus() { + const styleStatus = KEEP.getStyleStatus() + + if (styleStatus) { + styleStatus.isDark ? this.enableDarkMode() : this.enableLightMode() + } else { + this.isDarkPrefersColorScheme().matches ? this.enableDarkMode() : this.enableLightMode() + } + }, + + initModeToggleButton() { + this.themeModeToggleBtn.addEventListener('click', () => { + const isDark = document.body.classList.contains('dark-mode') + isDark ? this.enableLightMode() : this.enableDarkMode() + }) + }, + + initModeAutoTrigger() { + const isDarkMode = this.isDarkPrefersColorScheme() + isDarkMode.addEventListener('change', (e) => { + e.matches ? this.enableDarkMode() : this.enableLightMode() + }) + } + } + + KEEP.utils.modeToggle.initModeStatus() + KEEP.utils.modeToggle.initModeToggleButton() + KEEP.utils.modeToggle.initModeAutoTrigger() +} diff --git a/js/header-shrink.js b/js/header-shrink.js new file mode 100644 index 0000000..5aa5de7 --- /dev/null +++ b/js/header-shrink.js @@ -0,0 +1,74 @@ +/* global KEEP */ + +KEEP.initHeaderShrink = () => { + KEEP.utils.headerShrink = { + headerWrapperDom: null, + isHeaderShrink: false, + headerHeight: 70, + + init() { + this.headerWrapperDom = document.querySelector('.header-wrapper') + if (this.headerWrapperDom) { + this.headerHeight = this.headerWrapperDom.getBoundingClientRect().height + } + }, + + headerShrink() { + const fullPageHeight = Math.max( + document.body.scrollHeight, + document.documentElement.scrollHeight + ) + if (fullPageHeight < window.innerHeight + 2 * this.headerHeight) { + return + } + + const scrollTop = document.body.scrollTop || document.documentElement.scrollTop + const isHeaderTransparent = + KEEP.theme_config?.style?.first_screen?.enable === true && + !window.location.pathname.includes('/page/') + + if (!this.isHeaderShrink && scrollTop > this.headerHeight) { + this.isHeaderShrink = true + document.body.classList.add('header-shrink') + if (isHeaderTransparent) { + this.headerWrapperDom.classList.add('transparent-2') + } + } else if (this.isHeaderShrink && scrollTop <= this.headerHeight) { + this.isHeaderShrink = false + document.body.classList.remove('header-shrink') + if (isHeaderTransparent) { + this.headerWrapperDom.classList.remove('transparent-2') + } + } + }, + + sideToolsBarShowHandle() { + const scrollTop = document.body.scrollTop || document.documentElement.scrollTop + const sideToolsDom = document.querySelector('.side-tools .side-tools-container') + if (scrollTop > this.headerHeight / 2) { + sideToolsDom.classList.add('show') + } else { + sideToolsDom.classList.remove('show') + } + }, + + toggleHeaderDrawerShow() { + const domList = [document.querySelector('.window-mask'), document.querySelector('.menu-bar')] + + if (KEEP.theme_config?.pjax?.enable === true) { + domList.push( + ...document.querySelectorAll('.header-drawer .drawer-menu-list .drawer-menu-item') + ) + } + + domList.forEach((v) => { + v.addEventListener('click', () => { + document.body.classList.toggle('header-drawer-show') + }) + }) + } + } + KEEP.utils.headerShrink.init() + KEEP.utils.headerShrink.headerShrink() + KEEP.utils.headerShrink.toggleHeaderDrawerShow() +} diff --git a/js/lazyload.js b/js/lazyload.js new file mode 100644 index 0000000..8b7cddf --- /dev/null +++ b/js/lazyload.js @@ -0,0 +1,42 @@ +/* global KEEP */ + +KEEP.initLazyLoad = () => { + const imgs = document.querySelectorAll('img') + let now = Date.now() + let needLoad = true + + function lazyload(imgs) { + now = Date.now() + needLoad = Array.from(imgs).some((i) => i.hasAttribute('lazyload')) + + const h = window.innerHeight + const s = document.documentElement.scrollTop || document.body.scrollTop + + imgs.forEach((img) => { + if (img.hasAttribute('lazyload') && !img.hasAttribute('loading')) { + if (h + s > img.offsetTop) { + img.setAttribute('loading', true) + const loadImageTimeout = setTimeout(() => { + const temp = new Image() + const src = img.getAttribute('data-src') + temp.src = src + temp.onload = () => { + img.src = src + img.removeAttribute('lazyload') + img.removeAttribute('loading') + clearTimeout(loadImageTimeout) + } + }, 500) + } + } + }) + } + + lazyload(imgs) + + window.onscroll = () => { + if (Date.now() - now > 50 && needLoad) { + lazyload(imgs) + } + } +} diff --git a/js/libs/anime.min.js b/js/libs/anime.min.js new file mode 100644 index 0000000..99b263a --- /dev/null +++ b/js/libs/anime.min.js @@ -0,0 +1,8 @@ +/* + * anime.js v3.1.0 + * (c) 2019 Julian Garnier + * Released under the MIT license + * animejs.com + */ + +!function(n,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):n.anime=e()}(this,function(){"use strict";var n={update:null,begin:null,loopBegin:null,changeBegin:null,change:null,changeComplete:null,loopComplete:null,complete:null,loop:1,direction:"normal",autoplay:!0,timelineOffset:0},e={duration:1e3,delay:0,endDelay:0,easing:"easeOutElastic(1, .5)",round:0},r=["translateX","translateY","translateZ","rotate","rotateX","rotateY","rotateZ","scale","scaleX","scaleY","scaleZ","skew","skewX","skewY","perspective"],t={CSS:{},springs:{}};function a(n,e,r){return Math.min(Math.max(n,e),r)}function o(n,e){return n.indexOf(e)>-1}function u(n,e){return n.apply(null,e)}var i={arr:function(n){return Array.isArray(n)},obj:function(n){return o(Object.prototype.toString.call(n),"Object")},pth:function(n){return i.obj(n)&&n.hasOwnProperty("totalLength")},svg:function(n){return n instanceof SVGElement},inp:function(n){return n instanceof HTMLInputElement},dom:function(n){return n.nodeType||i.svg(n)},str:function(n){return"string"==typeof n},fnc:function(n){return"function"==typeof n},und:function(n){return void 0===n},hex:function(n){return/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(n)},rgb:function(n){return/^rgb/.test(n)},hsl:function(n){return/^hsl/.test(n)},col:function(n){return i.hex(n)||i.rgb(n)||i.hsl(n)},key:function(r){return!n.hasOwnProperty(r)&&!e.hasOwnProperty(r)&&"targets"!==r&&"keyframes"!==r}};function c(n){var e=/\(([^)]+)\)/.exec(n);return e?e[1].split(",").map(function(n){return parseFloat(n)}):[]}function s(n,e){var r=c(n),o=a(i.und(r[0])?1:r[0],.1,100),u=a(i.und(r[1])?100:r[1],.1,100),s=a(i.und(r[2])?10:r[2],.1,100),f=a(i.und(r[3])?0:r[3],.1,100),l=Math.sqrt(u/o),d=s/(2*Math.sqrt(u*o)),p=d<1?l*Math.sqrt(1-d*d):0,h=1,v=d<1?(d*l-f)/p:-f+l;function g(n){var r=e?e*n/1e3:n;return r=d<1?Math.exp(-r*d*l)*(h*Math.cos(p*r)+v*Math.sin(p*r)):(h+v*r)*Math.exp(-r*l),0===n||1===n?n:1-r}return e?g:function(){var e=t.springs[n];if(e)return e;for(var r=0,a=0;;)if(1===g(r+=1/6)){if(++a>=16)break}else a=0;var o=r*(1/6)*1e3;return t.springs[n]=o,o}}function f(n){return void 0===n&&(n=10),function(e){return Math.round(e*n)*(1/n)}}var l,d,p=function(){var n=11,e=1/(n-1);function r(n,e){return 1-3*e+3*n}function t(n,e){return 3*e-6*n}function a(n){return 3*n}function o(n,e,o){return((r(e,o)*n+t(e,o))*n+a(e))*n}function u(n,e,o){return 3*r(e,o)*n*n+2*t(e,o)*n+a(e)}return function(r,t,a,i){if(0<=r&&r<=1&&0<=a&&a<=1){var c=new Float32Array(n);if(r!==t||a!==i)for(var s=0;s=.001?function(n,e,r,t){for(var a=0;a<4;++a){var i=u(e,r,t);if(0===i)return e;e-=(o(e,r,t)-n)/i}return e}(t,l,r,a):0===d?l:function(n,e,r,t,a){for(var u,i,c=0;(u=o(i=e+(r-e)/2,t,a)-n)>0?r=i:e=i,Math.abs(u)>1e-7&&++c<10;);return i}(t,i,i+e,r,a)}}}(),h=(l={linear:function(){return function(n){return n}}},d={Sine:function(){return function(n){return 1-Math.cos(n*Math.PI/2)}},Circ:function(){return function(n){return 1-Math.sqrt(1-n*n)}},Back:function(){return function(n){return n*n*(3*n-2)}},Bounce:function(){return function(n){for(var e,r=4;n<((e=Math.pow(2,--r))-1)/11;);return 1/Math.pow(4,3-r)-7.5625*Math.pow((3*e-2)/22-n,2)}},Elastic:function(n,e){void 0===n&&(n=1),void 0===e&&(e=.5);var r=a(n,1,10),t=a(e,.1,2);return function(n){return 0===n||1===n?n:-r*Math.pow(2,10*(n-1))*Math.sin((n-1-t/(2*Math.PI)*Math.asin(1/r))*(2*Math.PI)/t)}}},["Quad","Cubic","Quart","Quint","Expo"].forEach(function(n,e){d[n]=function(){return function(n){return Math.pow(n,e+2)}}}),Object.keys(d).forEach(function(n){var e=d[n];l["easeIn"+n]=e,l["easeOut"+n]=function(n,r){return function(t){return 1-e(n,r)(1-t)}},l["easeInOut"+n]=function(n,r){return function(t){return t<.5?e(n,r)(2*t)/2:1-e(n,r)(-2*t+2)/2}}}),l);function v(n,e){if(i.fnc(n))return n;var r=n.split("(")[0],t=h[r],a=c(n);switch(r){case"spring":return s(n,e);case"cubicBezier":return u(p,a);case"steps":return u(f,a);default:return u(t,a)}}function g(n){try{return document.querySelectorAll(n)}catch(n){return}}function m(n,e){for(var r=n.length,t=arguments.length>=2?arguments[1]:void 0,a=[],o=0;o1&&(r-=1),r<1/6?n+6*(e-n)*r:r<.5?e:r<2/3?n+(e-n)*(2/3-r)*6:n}if(0==u)e=r=t=i;else{var f=i<.5?i*(1+u):i+u-i*u,l=2*i-f;e=s(l,f,o+1/3),r=s(l,f,o),t=s(l,f,o-1/3)}return"rgba("+255*e+","+255*r+","+255*t+","+c+")"}(n):void 0;var e,r,t,a}function C(n){var e=/[+-]?\d*\.?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?(%|px|pt|em|rem|in|cm|mm|ex|ch|pc|vw|vh|vmin|vmax|deg|rad|turn)?$/.exec(n);if(e)return e[1]}function B(n,e){return i.fnc(n)?n(e.target,e.id,e.total):n}function P(n,e){return n.getAttribute(e)}function I(n,e,r){if(M([r,"deg","rad","turn"],C(e)))return e;var a=t.CSS[e+r];if(!i.und(a))return a;var o=document.createElement(n.tagName),u=n.parentNode&&n.parentNode!==document?n.parentNode:document.body;u.appendChild(o),o.style.position="absolute",o.style.width=100+r;var c=100/o.offsetWidth;u.removeChild(o);var s=c*parseFloat(e);return t.CSS[e+r]=s,s}function T(n,e,r){if(e in n.style){var t=e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase(),a=n.style[e]||getComputedStyle(n).getPropertyValue(t)||"0";return r?I(n,a,r):a}}function D(n,e){return i.dom(n)&&!i.inp(n)&&(P(n,e)||i.svg(n)&&n[e])?"attribute":i.dom(n)&&M(r,e)?"transform":i.dom(n)&&"transform"!==e&&T(n,e)?"css":null!=n[e]?"object":void 0}function E(n){if(i.dom(n)){for(var e,r=n.style.transform||"",t=/(\w+)\(([^)]*)\)/g,a=new Map;e=t.exec(r);)a.set(e[1],e[2]);return a}}function F(n,e,r,t){var a,u=o(e,"scale")?1:0+(o(a=e,"translate")||"perspective"===a?"px":o(a,"rotate")||o(a,"skew")?"deg":void 0),i=E(n).get(e)||u;return r&&(r.transforms.list.set(e,i),r.transforms.last=e),t?I(n,i,t):i}function N(n,e,r,t){switch(D(n,e)){case"transform":return F(n,e,t,r);case"css":return T(n,e,r);case"attribute":return P(n,e);default:return n[e]||0}}function A(n,e){var r=/^(\*=|\+=|-=)/.exec(n);if(!r)return n;var t=C(n)||0,a=parseFloat(e),o=parseFloat(n.replace(r[0],""));switch(r[0][0]){case"+":return a+o+t;case"-":return a-o+t;case"*":return a*o+t}}function L(n,e){if(i.col(n))return O(n);if(/\s/g.test(n))return n;var r=C(n),t=r?n.substr(0,n.length-r.length):n;return e?t+e:t}function j(n,e){return Math.sqrt(Math.pow(e.x-n.x,2)+Math.pow(e.y-n.y,2))}function S(n){for(var e,r=n.points,t=0,a=0;a0&&(t+=j(e,o)),e=o}return t}function q(n){if(n.getTotalLength)return n.getTotalLength();switch(n.tagName.toLowerCase()){case"circle":return o=n,2*Math.PI*P(o,"r");case"rect":return 2*P(a=n,"width")+2*P(a,"height");case"line":return j({x:P(t=n,"x1"),y:P(t,"y1")},{x:P(t,"x2"),y:P(t,"y2")});case"polyline":return S(n);case"polygon":return r=(e=n).points,S(e)+j(r.getItem(r.numberOfItems-1),r.getItem(0))}var e,r,t,a,o}function $(n,e){var r=e||{},t=r.el||function(n){for(var e=n.parentNode;i.svg(e)&&i.svg(e.parentNode);)e=e.parentNode;return e}(n),a=t.getBoundingClientRect(),o=P(t,"viewBox"),u=a.width,c=a.height,s=r.viewBox||(o?o.split(" "):[0,0,u,c]);return{el:t,viewBox:s,x:s[0]/1,y:s[1]/1,w:u/s[2],h:c/s[3]}}function X(n,e){function r(r){void 0===r&&(r=0);var t=e+r>=1?e+r:0;return n.el.getPointAtLength(t)}var t=$(n.el,n.svg),a=r(),o=r(-1),u=r(1);switch(n.property){case"x":return(a.x-t.x)*t.w;case"y":return(a.y-t.y)*t.h;case"angle":return 180*Math.atan2(u.y-o.y,u.x-o.x)/Math.PI}}function Y(n,e){var r=/[+-]?\d*\.?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/g,t=L(i.pth(n)?n.totalLength:n,e)+"";return{original:t,numbers:t.match(r)?t.match(r).map(Number):[0],strings:i.str(n)||e?t.split(r):[]}}function Z(n){return m(n?y(i.arr(n)?n.map(b):b(n)):[],function(n,e,r){return r.indexOf(n)===e})}function Q(n){var e=Z(n);return e.map(function(n,r){return{target:n,id:r,total:e.length,transforms:{list:E(n)}}})}function V(n,e){var r=x(e);if(/^spring/.test(r.easing)&&(r.duration=s(r.easing)),i.arr(n)){var t=n.length;2===t&&!i.obj(n[0])?n={value:n}:i.fnc(e.duration)||(r.duration=e.duration/t)}var a=i.arr(n)?n:[n];return a.map(function(n,r){var t=i.obj(n)&&!i.pth(n)?n:{value:n};return i.und(t.delay)&&(t.delay=r?0:e.delay),i.und(t.endDelay)&&(t.endDelay=r===a.length-1?e.endDelay:0),t}).map(function(n){return k(n,r)})}function z(n,e){var r=[],t=e.keyframes;for(var a in t&&(e=k(function(n){for(var e=m(y(n.map(function(n){return Object.keys(n)})),function(n){return i.key(n)}).reduce(function(n,e){return n.indexOf(e)<0&&n.push(e),n},[]),r={},t=function(t){var a=e[t];r[a]=n.map(function(n){var e={};for(var r in n)i.key(r)?r==a&&(e.value=n[r]):e[r]=n[r];return e})},a=0;a-1&&(_.splice(o,1),r=_.length)}else a.tick(e);t++}n()}else U=cancelAnimationFrame(U)}return n}();function rn(r){void 0===r&&(r={});var t,o=0,u=0,i=0,c=0,s=null;function f(n){var e=window.Promise&&new Promise(function(n){return s=n});return n.finished=e,e}var l,d,p,h,v,g,y,b,M=(d=w(n,l=r),p=w(e,l),h=z(p,l),v=Q(l.targets),g=W(v,h),y=J(g,p),b=K,K++,k(d,{id:b,children:[],animatables:v,animations:g,duration:y.duration,delay:y.delay,endDelay:y.endDelay}));f(M);function x(){var n=M.direction;"alternate"!==n&&(M.direction="normal"!==n?"normal":"reverse"),M.reversed=!M.reversed,t.forEach(function(n){return n.reversed=M.reversed})}function O(n){return M.reversed?M.duration-n:n}function C(){o=0,u=O(M.currentTime)*(1/rn.speed)}function B(n,e){e&&e.seek(n-e.timelineOffset)}function P(n){for(var e=0,r=M.animations,t=r.length;e2||(b=Math.round(b*p)/p)),h.push(b)}var k=d.length;if(k){g=d[0];for(var O=0;O0&&(M.began=!0,I("begin")),!M.loopBegan&&M.currentTime>0&&(M.loopBegan=!0,I("loopBegin")),d<=r&&0!==M.currentTime&&P(0),(d>=l&&M.currentTime!==e||!e)&&P(e),d>r&&d=e&&(u=0,M.remaining&&!0!==M.remaining&&M.remaining--,M.remaining?(o=i,I("loopComplete"),M.loopBegan=!1,"alternate"===M.direction&&x()):(M.paused=!0,M.completed||(M.completed=!0,I("loopComplete"),I("complete"),!M.passThrough&&"Promise"in window&&(s(),f(M)))))}return M.reset=function(){var n=M.direction;M.passThrough=!1,M.currentTime=0,M.progress=0,M.paused=!0,M.began=!1,M.loopBegan=!1,M.changeBegan=!1,M.completed=!1,M.changeCompleted=!1,M.reversePlayback=!1,M.reversed="reverse"===n,M.remaining=M.loop,t=M.children;for(var e=c=t.length;e--;)M.children[e].reset();(M.reversed&&!0!==M.loop||"alternate"===n&&1===M.loop)&&M.remaining++,P(M.reversed?M.duration:0)},M.set=function(n,e){return R(n,e),M},M.tick=function(n){i=n,o||(o=i),T((i+(u-o))*rn.speed)},M.seek=function(n){T(O(n))},M.pause=function(){M.paused=!0,C()},M.play=function(){M.paused&&(M.completed&&M.reset(),M.paused=!1,_.push(M),C(),U||en())},M.reverse=function(){x(),C()},M.restart=function(){M.reset(),M.play()},M.reset(),M.autoplay&&M.play(),M}function tn(n,e){for(var r=e.length;r--;)M(n,e[r].animatable.target)&&e.splice(r,1)}return"undefined"!=typeof document&&document.addEventListener("visibilitychange",function(){document.hidden?(_.forEach(function(n){return n.pause()}),nn=_.slice(0),rn.running=_=[]):nn.forEach(function(n){return n.play()})}),rn.version="3.1.0",rn.speed=1,rn.running=_,rn.remove=function(n){for(var e=Z(n),r=_.length;r--;){var t=_[r],a=t.animations,o=t.children;tn(e,a);for(var u=o.length;u--;){var i=o[u],c=i.animations;tn(e,c),c.length||i.children.length||o.splice(u,1)}a.length||o.length||t.pause()}},rn.get=N,rn.set=R,rn.convertPx=I,rn.path=function(n,e){var r=i.str(n)?g(n)[0]:n,t=e||100;return function(n){return{property:n,el:r,svg:$(r),totalLength:q(r)*(t/100)}}},rn.setDashoffset=function(n){var e=q(n);return n.setAttribute("stroke-dasharray",e),e},rn.stagger=function(n,e){void 0===e&&(e={});var r=e.direction||"normal",t=e.easing?v(e.easing):null,a=e.grid,o=e.axis,u=e.from||0,c="first"===u,s="center"===u,f="last"===u,l=i.arr(n),d=l?parseFloat(n[0]):parseFloat(n),p=l?parseFloat(n[1]):0,h=C(l?n[1]:n)||0,g=e.start||0+(l?d:0),m=[],y=0;return function(n,e,i){if(c&&(u=0),s&&(u=(i-1)/2),f&&(u=i-1),!m.length){for(var v=0;v-1&&_.splice(o,1);for(var s=0;s]+>/gi;var htmlAttribsRegex=/\s?[a-z:]+(?:=['"][^'">]+['"])*/gi;var matches=html.match(htmlRegex);if(matches&&matches.length){matches=matches[0].match(htmlAttribsRegex);if(matches.length){matches.shift();matches.forEach(function(htmlAttrib){var attr=htmlAttrib.trim().split("=");if(attr.length===1){tmpEl.documentElement.setAttribute(attr[0],true)}else{tmpEl.documentElement.setAttribute(attr[0],attr[1].slice(1,-1))}})}}tmpEl.documentElement.innerHTML=html;this.log("load content",tmpEl.documentElement.attributes,tmpEl.documentElement.innerHTML.length);if(document.activeElement&&contains(document,this.options.selectors,document.activeElement)){try{document.activeElement.blur()}catch(e){}}this.switchSelectors(this.options.selectors,tmpEl,document,options)},abortRequest:require("./lib/abort-request"),doRequest:require("./lib/send-request"),handleResponse:require("./lib/proto/handle-response"),loadUrl:function(href,options){options=typeof options==="object"?extend({},this.options,options):clone(this.options);this.log("load href",href,options);this.abortRequest(this.request);trigger(document,"pjax:send",options);this.request=this.doRequest(href,options,this.handleResponse.bind(this))},executeScripts:function(elements){elements.forEach(function(element){var code=element.text||element.textContent||element.innerHTML||"";var script=document.createElement("script");if(element.id){script.id=element.id}if(element.className){script.className=element.className}if(element.type){script.type=element.type}if(element.src){script.src=element.src;script.async=false}if(element.dataset.pjax!==undefined){script.dataset.pjax=""}if(code!==""){script.appendChild(document.createTextNode(code))}element.parentNode.replaceChild(script,element)})},afterAllSwitches:function(){var autofocusEl=Array.prototype.slice.call(document.querySelectorAll("[autofocus]")).pop();if(autofocusEl&&document.activeElement!==autofocusEl){autofocusEl.focus()}this.options.selectors.forEach(function(selector){forEachEls(document.querySelectorAll(selector),function(el){if(el===0);})});var state=this.state;if(state.options.history){if(!window.history.state){this.lastUid=this.maxUid=newUid();window.history.replaceState({url:window.location.href,title:document.title,uid:this.maxUid,scrollPos:[0,0]},document.title)}this.lastUid=this.maxUid=newUid();window.history.pushState({url:state.href,title:state.options.title,uid:this.maxUid,scrollPos:[0,0]},state.options.title,state.href)}this.forEachSelectors(function(el){this.parseDOM(el)},this);trigger(document,"pjax:complete pjax:success",state.options);if(typeof state.options.analytics==="function"){state.options.analytics()}if(state.options.history){var a=document.createElement("a");a.href=this.state.href;if(a.hash){var name=a.hash.slice(1);name=decodeURIComponent(name);var curtop=0;var target=document.getElementById(name)||document.getElementsByName(name)[0];if(target){if(target.offsetParent){do{curtop+=target.offsetTop;target=target.offsetParent}while(target)}}window.scrollTo(0,curtop)}else if(state.options.scrollTo!==false){if(state.options.scrollTo.length>1){window.scrollTo(state.options.scrollTo[0],state.options.scrollTo[1])}else{window.scrollTo(0,state.options.scrollTo)}}}else if(state.options.scrollRestoration&&state.options.scrollPos){window.scrollTo(state.options.scrollPos[0],state.options.scrollPos[1])}this.state={numPendingSwitches:0,href:null,options:null}}};Pjax.isSupported=require("./lib/is-supported");if(Pjax.isSupported()){module.exports=Pjax}else{var stupidPjax=noop;for(var key in Pjax.prototype){if(Pjax.prototype.hasOwnProperty(key)&&typeof Pjax.prototype[key]==="function"){stupidPjax[key]=noop}}module.exports=stupidPjax}},{"./lib/abort-request":2,"./lib/events/on":3,"./lib/events/trigger":4,"./lib/foreach-els":5,"./lib/foreach-selectors":6,"./lib/is-supported":7,"./lib/parse-options":8,"./lib/proto/attach-form":9,"./lib/proto/attach-link":10,"./lib/proto/handle-response":11,"./lib/proto/log":12,"./lib/proto/parse-element":13,"./lib/send-request":14,"./lib/switches":16,"./lib/switches-selectors":15,"./lib/uniqueid":17,"./lib/util/clone":18,"./lib/util/contains":19,"./lib/util/extend":20,"./lib/util/noop":21}],2:[function(require,module,exports){var noop=require("./util/noop");module.exports=function(request){if(request&&request.readyState<4){request.onreadystatechange=noop;request.abort()}}},{"./util/noop":21}],3:[function(require,module,exports){var forEachEls=require("../foreach-els");module.exports=function(els,events,listener,useCapture){events=typeof events==="string"?events.split(" "):events;events.forEach(function(e){forEachEls(els,function(el){el.addEventListener(e,listener,useCapture)})})}},{"../foreach-els":5}],4:[function(require,module,exports){var forEachEls=require("../foreach-els");module.exports=function(els,events,opts){events=typeof events==="string"?events.split(" "):events;events.forEach(function(e){var event;event=document.createEvent("HTMLEvents");event.initEvent(e,true,true);event.eventName=e;if(opts){Object.keys(opts).forEach(function(key){event[key]=opts[key]})}forEachEls(els,function(el){var domFix=false;if(!el.parentNode&&el!==document&&el!==window){domFix=true;document.body.appendChild(el)}el.dispatchEvent(event);if(domFix){el.parentNode.removeChild(el)}})})}},{"../foreach-els":5}],5:[function(require,module,exports){module.exports=function(els,fn,context){if(els instanceof HTMLCollection||els instanceof NodeList||els instanceof Array){return Array.prototype.forEach.call(els,fn,context)}return fn.call(context,els)}},{}],6:[function(require,module,exports){var forEachEls=require("./foreach-els");module.exports=function(selectors,cb,context,DOMcontext){DOMcontext=DOMcontext||document;selectors.forEach(function(selector){forEachEls(DOMcontext.querySelectorAll(selector),cb,context)})}},{"./foreach-els":5}],7:[function(require,module,exports){module.exports=function(){return window.history&&window.history.pushState&&window.history.replaceState&&!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/)}},{}],8:[function(require,module,exports){var defaultSwitches=require("./switches");module.exports=function(options){options=options||{};options.elements=options.elements||"a[href], form[action]";options.selectors=options.selectors||["title",".js-Pjax"];options.switches=options.switches||{};options.switchesOptions=options.switchesOptions||{};options.history=typeof options.history==="undefined"?true:options.history;options.analytics=typeof options.analytics==="function"||options.analytics===false?options.analytics:defaultAnalytics;options.scrollTo=typeof options.scrollTo==="undefined"?0:options.scrollTo;options.scrollRestoration=typeof options.scrollRestoration!=="undefined"?options.scrollRestoration:true;options.cacheBust=typeof options.cacheBust==="undefined"?true:options.cacheBust;options.debug=options.debug||false;options.timeout=options.timeout||0;options.currentUrlFullReload=typeof options.currentUrlFullReload==="undefined"?false:options.currentUrlFullReload;if(!options.switches.head){options.switches.head=defaultSwitches.switchElementsAlt}if(!options.switches.body){options.switches.body=defaultSwitches.switchElementsAlt}return options};function defaultAnalytics(){if(window._gaq){_gaq.push(["_trackPageview"])}if(window.ga){ga("send","pageview",{page:location.pathname,title:document.title})}}},{"./switches":16}],9:[function(require,module,exports){var on=require("../events/on");var clone=require("../util/clone");var attrState="data-pjax-state";var formAction=function(el,event){if(isDefaultPrevented(event)){return}var options=clone(this.options);options.requestOptions={requestUrl:el.getAttribute("action")||window.location.href,requestMethod:el.getAttribute("method")||"GET"};var virtLinkElement=document.createElement("a");virtLinkElement.setAttribute("href",options.requestOptions.requestUrl);var attrValue=checkIfShouldAbort(virtLinkElement,options);if(attrValue){el.setAttribute(attrState,attrValue);return}event.preventDefault();if(el.enctype==="multipart/form-data"){options.requestOptions.formData=new FormData(el)}else{options.requestOptions.requestParams=parseFormElements(el)}el.setAttribute(attrState,"submit");options.triggerElement=el;this.loadUrl(virtLinkElement.href,options)};function parseFormElements(el){var requestParams=[];var formElements=el.elements;for(var i=0;i1||event.metaKey||event.ctrlKey||event.shiftKey||event.altKey){return"modifier"}if(el.protocol!==window.location.protocol||el.host!==window.location.host){return"external"}if(el.hash&&el.href.replace(el.hash,"")===window.location.href.replace(location.hash,"")){return"anchor"}if(el.href===window.location.href.split("#")[0]+"#"){return"anchor-empty"}}var isDefaultPrevented=function(event){return event.defaultPrevented||event.returnValue===false};module.exports=function(el){var that=this;el.setAttribute(attrState,"");on(el,"click",function(event){linkAction.call(that,el,event)});on(el,"keyup",function(event){if(event.keyCode===13){linkAction.call(that,el,event)}}.bind(this))}},{"../events/on":3,"../util/clone":18}],11:[function(require,module,exports){var clone=require("../util/clone");var newUid=require("../uniqueid");var trigger=require("../events/trigger");module.exports=function(responseText,request,href,options){options=clone(options||this.options);options.request=request;if(responseText===false){trigger(document,"pjax:complete pjax:error",options);return}var currentState=window.history.state||{};window.history.replaceState({url:currentState.url||window.location.href,title:currentState.title||document.title,uid:currentState.uid||newUid(),scrollPos:[document.documentElement.scrollLeft||document.body.scrollLeft,document.documentElement.scrollTop||document.body.scrollTop]},document.title,window.location.href);var oldHref=href;if(request.responseURL){if(href!==request.responseURL){href=request.responseURL}}else if(request.getResponseHeader("X-PJAX-URL")){href=request.getResponseHeader("X-PJAX-URL")}else if(request.getResponseHeader("X-XHR-Redirected-To")){href=request.getResponseHeader("X-XHR-Redirected-To")}var a=document.createElement("a");a.href=oldHref;var oldHash=a.hash;a.href=href;if(oldHash&&!a.hash){a.hash=oldHash;href=a.href}this.state.href=href;this.state.options=options;try{this.loadContent(responseText,options)}catch(e){trigger(document,"pjax:error",options);if(!this.options.debug){if(console&&console.error){console.error("Pjax switch fail: ",e)}return this.latestChance(href)}else{throw e}}}},{"../events/trigger":4,"../uniqueid":17,"../util/clone":18}],12:[function(require,module,exports){module.exports=function(){if(this.options.debug&&console){if(typeof console.log==="function"){console.log.apply(console,arguments)}else if(console.log){console.log(arguments)}}}},{}],13:[function(require,module,exports){var attrState="data-pjax-state";module.exports=function(el){switch(el.tagName.toLowerCase()){case"a":if(!el.hasAttribute(attrState)){this.attachLink(el)}break;case"form":if(!el.hasAttribute(attrState)){this.attachForm(el)}break;default:throw"Pjax can only be applied on or
submit"}}},{}],14:[function(require,module,exports){var updateQueryString=require("./util/update-query-string");module.exports=function(location,options,callback){options=options||{};var queryString;var requestOptions=options.requestOptions||{};var requestMethod=(requestOptions.requestMethod||"GET").toUpperCase();var requestParams=requestOptions.requestParams||null;var formData=requestOptions.formData||null;var requestPayload=null;var request=new XMLHttpRequest;var timeout=options.timeout||0;request.onreadystatechange=function(){if(request.readyState===4){if(request.status===200){callback(request.responseText,request,location,options)}else if(request.status!==0){callback(null,request,location,options)}}};request.onerror=function(e){console.log(e);callback(null,request,location,options)};request.ontimeout=function(){callback(null,request,location,options)};if(requestParams&&requestParams.length){queryString=requestParams.map(function(param){return param.name+"="+param.value}).join("&");switch(requestMethod){case"GET":location=location.split("?")[0];location+="?"+queryString;break;case"POST":requestPayload=queryString;break}}else if(formData){requestPayload=formData}if(options.cacheBust){location=updateQueryString(location,"t",Date.now())}request.open(requestMethod,location,true);request.timeout=timeout;request.setRequestHeader("X-Requested-With","XMLHttpRequest");request.setRequestHeader("X-PJAX","true");request.setRequestHeader("X-PJAX-Selectors",JSON.stringify(options.selectors));if(requestPayload&&requestMethod==="POST"&&!formData){request.setRequestHeader("Content-Type","application/x-www-form-urlencoded")}request.send(requestPayload);return request}},{"./util/update-query-string":22}],15:[function(require,module,exports){var forEachEls=require("./foreach-els");var defaultSwitches=require("./switches");module.exports=function(switches,switchesOptions,selectors,fromEl,toEl,options){var switchesQueue=[];selectors.forEach(function(selector){var newEls=fromEl.querySelectorAll(selector);var oldEls=toEl.querySelectorAll(selector);if(this.log){this.log("Pjax switch",selector,newEls,oldEls)}if(newEls.length!==oldEls.length){throw"DOM doesn’t look the same on new loaded page: ’"+selector+"’ - new "+newEls.length+", old "+oldEls.length}forEachEls(newEls,function(newEl,i){var oldEl=oldEls[i];if(this.log){this.log("newEl",newEl,"oldEl",oldEl)}var callback=switches[selector]?switches[selector].bind(this,oldEl,newEl,options,switchesOptions[selector]):defaultSwitches.outerHTML.bind(this,oldEl,newEl,options);switchesQueue.push(callback)},this)},this);this.state.numPendingSwitches=switchesQueue.length;switchesQueue.forEach(function(queuedSwitch){queuedSwitch()})}},{"./foreach-els":5,"./switches":16}],16:[function(require,module,exports){var on=require("./events/on");module.exports={outerHTML:function(oldEl,newEl){oldEl.outerHTML=newEl.outerHTML;this.onSwitch()},innerHTML:function(oldEl,newEl){oldEl.innerHTML=newEl.innerHTML;if(newEl.className===""){oldEl.removeAttribute("class")}else{oldEl.className=newEl.className}this.onSwitch()},switchElementsAlt:function(oldEl,newEl){oldEl.innerHTML=newEl.innerHTML;if(newEl.hasAttributes()){var attrs=newEl.attributes;for(var i=0;i${text.substring(hit.position, end)}` + prevEnd = end + }) + result += text.substring(prevEnd, slice.end) + return result + } + + const inputEventFunction = () => { + if (!isfetched) return + let searchText = searchInputDom.value.trim().toLowerCase() + let keywords = searchText.split(/[-\s]+/) + if (keywords.length > 1) { + keywords.push(searchText) + } + let resultItems = [] + if (searchText.length > 0) { + // Perform local searching + datas.forEach(({ title, content, url }) => { + let titleInLowerCase = title.toLowerCase() + let contentInLowerCase = content.toLowerCase() + let indexOfTitle = [] + let indexOfContent = [] + let searchTextCount = 0 + keywords.forEach((keyword) => { + indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false)) + indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false)) + }) + + // Show search results + if (indexOfTitle.length > 0 || indexOfContent.length > 0) { + let hitCount = indexOfTitle.length + indexOfContent.length + // Sort index by position of keyword + ;[indexOfTitle, indexOfContent].forEach((index) => { + index.sort((itemLeft, itemRight) => { + if (itemRight.position !== itemLeft.position) { + return itemRight.position - itemLeft.position + } + return itemLeft.word.length - itemRight.word.length + }) + }) + + let slicesOfTitle = [] + if (indexOfTitle.length !== 0) { + let tmp = mergeIntoSlice(0, title.length, indexOfTitle, searchText) + searchTextCount += tmp.searchTextCountInSlice + slicesOfTitle.push(tmp) + } + + let slicesOfContent = [] + while (indexOfContent.length !== 0) { + let item = indexOfContent[indexOfContent.length - 1] + let { position, word } = item + // Cut out 100 characters + let start = position - 20 + let end = position + 80 + if (start < 0) { + start = 0 + } + if (end < position + word.length) { + end = position + word.length + } + if (end > content.length) { + end = content.length + } + let tmp = mergeIntoSlice(start, end, indexOfContent, searchText) + searchTextCount += tmp.searchTextCountInSlice + slicesOfContent.push(tmp) + } + + // Sort slices in content by search text's count and hits' count + slicesOfContent.sort((sliceLeft, sliceRight) => { + if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) { + return sliceRight.searchTextCount - sliceLeft.searchTextCount + } else if (sliceLeft.hits.length !== sliceRight.hits.length) { + return sliceRight.hits.length - sliceLeft.hits.length + } + return sliceLeft.start - sliceRight.start + }) + + // Select top N slices in content + let upperBound = parseInt( + KEEP.theme_config.local_search.top_n_per_article + ? KEEP.theme_config.local_search.top_n_per_article + : 1, + 10 + ) + if (upperBound >= 0) { + slicesOfContent = slicesOfContent.slice(0, upperBound) + } + + let resultItem = '' + + if (slicesOfTitle.length !== 0) { + resultItem += `
  • ${highlightKeyword( + title, + slicesOfTitle[0] + )}` + } else { + resultItem += `
  • ${title}` + } + + slicesOfContent.forEach((slice) => { + resultItem += `

    ${highlightKeyword( + content, + slice + )}...

    ` + }) + + resultItem += '
  • ' + resultItems.push({ + item: resultItem, + id: resultItems.length, + hitCount, + searchTextCount + }) + } + }) + } + if (keywords.length === 1 && keywords[0] === '') { + resultContent.innerHTML = '
    ' + } else if (resultItems.length === 0) { + resultContent.innerHTML = '
    ' + } else { + resultItems.sort((resultLeft, resultRight) => { + if (resultLeft.searchTextCount !== resultRight.searchTextCount) { + return resultRight.searchTextCount - resultLeft.searchTextCount + } else if (resultLeft.hitCount !== resultRight.hitCount) { + return resultRight.hitCount - resultLeft.hitCount + } + return resultRight.id - resultLeft.id + }) + let searchResultList = '
      ' + resultItems.forEach((result) => { + searchResultList += result.item + }) + searchResultList += '
    ' + resultContent.innerHTML = searchResultList + window.pjax && window.pjax.refresh(resultContent) + } + } + + const fetchData = () => { + fetch(KEEP.hexo_config.root + searchPath) + .then((response) => response.text()) + .then((res) => { + // Get the contents from search data + isfetched = true + datas = isXml + ? [...new DOMParser().parseFromString(res, 'text/xml').querySelectorAll('entry')].map( + (element) => { + return { + title: element.querySelector('title').textContent, + content: element.querySelector('content').textContent, + url: element.querySelector('url').textContent + } + } + ) + : JSON.parse(res) + // Only match articles with not empty titles + datas = datas + .filter((data) => data.title) + .map((data) => { + data.title = data.title.trim() + data.content = data.content ? data.content.trim().replace(/<[^>]+>/g, '') : '' + data.url = decodeURIComponent(data.url).replace(/\/{2,}/g, '/') + return data + }) + // Remove loading animation + const noResultDom = document.querySelector('#no-result') + noResultDom && (noResultDom.innerHTML = '') + }) + } + + if (KEEP.theme_config?.local_search?.preload === true) { + fetchData() + } + + if (searchInputDom) { + searchInputDom.addEventListener('input', inputEventFunction) + } + + // Handle and trigger popup window + document.querySelectorAll('.search-popup-trigger').forEach((element) => { + element.addEventListener('click', () => { + document.body.style.overflow = 'hidden' + document.querySelector('.search-pop-overlay').classList.add('active') + setTimeout(() => searchInputDom.focus(), 500) + if (!isfetched) fetchData() + }) + }) + + // Monitor main search box + const onPopupClose = () => { + document.body.style.overflow = '' + document.querySelector('.search-pop-overlay').classList.remove('active') + } + + document.querySelector('.search-pop-overlay').addEventListener('click', (event) => { + if (event.target === document.querySelector('.search-pop-overlay')) { + onPopupClose() + } + }) + document.querySelector('.search-input-field-pre').addEventListener('click', () => { + searchInputDom.value = '' + searchInputDom.focus() + inputEventFunction() + }) + document.querySelector('.close-popup-btn').addEventListener('click', onPopupClose) + window.addEventListener('pjax:success', onPopupClose) + window.addEventListener('keyup', (event) => { + if (event.key === 'Escape') { + onPopupClose() + } + }) +} diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..55f5890 --- /dev/null +++ b/js/main.js @@ -0,0 +1,77 @@ +/* global KEEP */ + +window.addEventListener('DOMContentLoaded', () => { + const { version, local_search, code_block, lazyload } = KEEP.theme_config + + KEEP.themeInfo = { + theme: `Keep v${version}`, + author: 'XPoet', + repository: 'https://github.com/XPoet/hexo-theme-keep' + } + + KEEP.localStorageKey = 'KEEP-THEME-STATUS' + + KEEP.styleStatus = { + isDark: false, + fontSizeLevel: 0, + isShowToc: true + } + + // print theme base info + KEEP.printThemeInfo = () => { + console.log( + `\n %c ${KEEP.themeInfo.theme} %c ${KEEP.themeInfo.repository} \n`, + `color: #fadfa3; background: #333; padding: 6px 0;`, + `padding: 6px 0;` + ) + } + KEEP.printThemeInfo() + + // set version number of footer + KEEP.setFooterVersion = () => { + const vd = document.querySelector('.footer .keep-version') + vd && (vd.innerHTML = KEEP.themeInfo.theme) + const vd2 = document.querySelector('.footer .shields-keep-version') + vd2 && (vd2.src = vd2.src.replace('Keep', KEEP.themeInfo.theme)) + } + + // set styleStatus to localStorage + KEEP.setStyleStatus = () => { + localStorage.setItem(KEEP.localStorageKey, JSON.stringify(KEEP.styleStatus)) + } + + // get styleStatus from localStorage + KEEP.getStyleStatus = () => { + let temp = localStorage.getItem(KEEP.localStorageKey) + if (temp) { + temp = JSON.parse(temp) + for (let key in KEEP.styleStatus) { + KEEP.styleStatus[key] = temp[key] + } + return temp + } else { + return null + } + } + + KEEP.initExecute = () => { + KEEP.initUtils() + KEEP.initHeaderShrink() + KEEP.initModeToggle() + KEEP.initBack2Top() + KEEP.setFooterVersion() + + if (local_search?.enable === true) { + KEEP.initLocalSearch() + } + + if (code_block?.tools?.enable === true) { + KEEP.initCodeBlockTools() + } + + if (lazyload?.enable === true) { + KEEP.initLazyLoad() + } + } + KEEP.initExecute() +}) diff --git a/js/post/copyright-info.js b/js/post/copyright-info.js new file mode 100644 index 0000000..d851007 --- /dev/null +++ b/js/post/copyright-info.js @@ -0,0 +1,61 @@ +/* global KEEP */ + +function initCopyrightInfoHelper() { + KEEP.utils.copyrightInfoHelper = { + // set post link + initSetPostLink() { + const postLinkContentDom = document.querySelector('.copyright-info-content .post-link') + postLinkContentDom && (postLinkContentDom.innerHTML = decodeURI(window.location.href)) + }, + + // copy copyright info + copyCopyrightInfo() { + const cicDom = document.querySelector('.copyright-info-content') + const copyDom = document.querySelector('.copy-copyright-info') + const copyIcon = copyDom.querySelector('i') + + const ccLang = KEEP.language_copy_copyright + const colon = KEEP.hexo_config?.language === 'en' ? ': ' : ':' + + let isCopied = false + + const setCopyDomContent = (class1, class2, content, copied) => { + if (copyIcon) { + copyIcon.classList.remove(class1) + copyIcon.classList.add(class2) + } + const tooltipDom = copyDom.querySelector('.tooltip-content') + tooltipDom && (tooltipDom.innerHTML = content) + isCopied = copied + } + + copyDom.addEventListener('click', () => { + if (!isCopied) { + const author = cicDom.querySelector('.post-author .content').innerHTML + const link = cicDom.querySelector('.post-link').innerHTML + const tgtTxt = `${ccLang.author}${colon}${author}\n${ccLang.link}${colon}${link}` + navigator.clipboard.writeText(tgtTxt).then(() => { + setCopyDomContent('fa-copy', 'fa-check', ccLang.copied, true) + }) + } + }) + + copyDom.addEventListener('mouseleave', () => { + setTimeout(() => { + setCopyDomContent('fa-check', 'fa-copy', ccLang.copy, false) + }, 500) + }) + } + } + + if (KEEP.theme_config.post?.copyright_info === true) { + KEEP.utils.copyrightInfoHelper.initSetPostLink() + KEEP.utils.copyrightInfoHelper.copyCopyrightInfo() + } +} + +if (KEEP.theme_config.pjax?.enable === true && KEEP.utils) { + initCopyrightInfoHelper() +} else { + window.addEventListener('DOMContentLoaded', initCopyrightInfoHelper) +} diff --git a/js/post/post-helper.js b/js/post/post-helper.js new file mode 100644 index 0000000..6462d4d --- /dev/null +++ b/js/post/post-helper.js @@ -0,0 +1,223 @@ +/* global KEEP */ + +function initPostHelper() { + KEEP.utils.postHelper = { + postPageContainerDom: document.querySelector('.post-page-container'), + toggleShowTocBtn: document.querySelector('.toggle-show-toc'), + toggleShowTocTabletBtn: document.querySelector('.toggle-show-toc-tablet'), + toggleShowTocIcon: document.querySelector('.toggle-show-toc i'), + mainContentDom: document.querySelector('.main-content'), + postToolsDom: document.querySelector('.post-tools'), + + isShowToc: false, + + initToggleToc() { + this.toggleShowTocBtn && + this.toggleShowTocBtn.addEventListener('click', () => { + this.isShowToc = !this.isShowToc + KEEP.styleStatus.isShowToc = this.isShowToc + KEEP.setStyleStatus() + this.handleToggleToc(this.isShowToc) + }) + + this.toggleShowTocTabletBtn && + this.toggleShowTocTabletBtn.addEventListener('click', () => { + const tabletTocMask = document.querySelector('.tablet-post-toc-mask') + const tabletToc = tabletTocMask.querySelector('.tablet-post-toc') + + document.body.style.overflow = `hidden` + tabletTocMask.style.background = `rgba(0, 0, 0, 0.25)` + tabletTocMask.style.visibility = `visible` + tabletToc.style.transform = `translateX(0)` + + tabletTocMask.addEventListener('click', () => { + document.body.style.overflow = '' + tabletTocMask.style.background = `rgba(0, 0, 0, 0)` + tabletTocMask.style.visibility = `hidden` + tabletToc.style.transform = `translateX(-100%)` + }) + }) + }, + + handleToggleToc(isOpen) { + if (isOpen) { + this.postPageContainerDom.classList.add('show-toc') + document.body.classList.add('has-toc') + } else { + this.postPageContainerDom.classList.remove('show-toc') + document.body.classList.remove('has-toc') + } + + setTimeout(() => { + this.setPostToolsLayout() + }, 120) + }, + + hasToc(isOpen) { + if (this.toggleShowTocBtn) { + this.toggleShowTocBtn.style.display = 'flex' + this.isShowToc = isOpen + this.handleToggleToc(isOpen) + } + }, + + setPostToolsLayout(mcw) { + const mainContainerWidth = mcw + ? mcw + : this.mainContentDom.getBoundingClientRect().width.toFixed(0) + let offsetX = 5 + + if (window.innerWidth <= 800) { + offsetX = 3 + } + + const layout = KEEP.theme_config.toc?.layout === 'left' ? 'right' : 'left' + this.postToolsDom.style.opacity = `1` + this.postToolsDom.style[ + layout + ] = `calc((100vw - ${mainContainerWidth}px) / 2 - ${offsetX}rem)` + }, + + initSetPostToolsLeft() { + setTimeout(() => { + this.setPostToolsLayout() + }, 150) + + window.addEventListener('resize', () => { + this.setPostToolsLayout() + }) + }, + + // go comment anchor + goToComments() { + const commentsAnchor = document.querySelector('#comments-anchor') + const goToCommentsBtnList = [ + document.querySelector('.post-tools .go-to-comments'), + document.querySelector('.exposed-tools-list .go-to-comments-tablet') + ] + + goToCommentsBtnList.forEach((btn) => { + if (btn && commentsAnchor) { + btn.addEventListener('click', (event) => { + event.preventDefault() + let winScrollY = window.scrollY + winScrollY = winScrollY === 0 ? -20 : winScrollY + const offset = commentsAnchor.getBoundingClientRect().top + winScrollY + window.anime({ + targets: document.scrollingElement, + duration: 300, + easing: 'linear', + scrollTop: offset, + complete: () => { + setTimeout(() => { + KEEP.utils.pageTopDom.classList.add('hide') + }, 150) + } + }) + }) + } + }) + }, + + // watch comments count + watchPostCommentsCount() { + const commentsCountDom = this.postToolsDom.querySelector('.post-comments-count') + if (!commentsCountDom) return + const config = { attributes: true, childList: true } + + const callback = function (mutationsList) { + mutationsList.forEach((item) => { + if (item.type === 'childList') { + const count = Number(item.target.innerHTML) + if (count > 0) { + commentsCountDom.style.display = 'flex' + if (count > 99) { + commentsCountDom.innerHTML = '99+' + observer.disconnect() + } + } + } + }) + } + + const observer = new MutationObserver(callback) + observer.observe(commentsCountDom, config) + }, + + // set article aging tips + setArticleAgingDays() { + const agingTipsDom = document.querySelector('.article-content .article-aging-tips') + if (agingTipsDom) { + const daysDom = agingTipsDom.querySelector('.days') + const nowTimestamp = Date.now() + const tmpTimeLength = 24 * 60 * 60 * 1000 + const agingDaysTimestamp = (agingTipsDom.dataset?.agingDays || 30) * tmpTimeLength + const postUpdateTimestamp = new Date(agingTipsDom.dataset.updateDate).getTime() + const timeDifference = nowTimestamp - postUpdateTimestamp + const timeDifferenceDays = (timeDifference / tmpTimeLength).toFixed(0) + if (timeDifference >= agingDaysTimestamp) { + daysDom.innerHTML = timeDifferenceDays + agingTipsDom.style.display = 'block' + } + } + }, + + formatDatetime(fmt = 'YYYY-MM-DD hh:mm:ss', timestamp = Date.now()) { + function padLeftZero(str) { + return `00${str}`.substr(str.length) + } + + const date = new Date(timestamp) + + if (/(y+)/.test(fmt) || /(Y+)/.test(fmt)) { + fmt = fmt.replace(RegExp.$1, `${date.getFullYear()}`.substr(4 - RegExp.$1.length)) + } + + const obj = { + 'M+': date.getMonth() + 1, + 'D+': date.getDate(), + 'd+': date.getDate(), + 'H+': date.getHours(), + 'h+': date.getHours(), + 'm+': date.getMinutes(), + 's+': date.getSeconds() + } + + for (const key in obj) { + if (new RegExp(`(${key})`).test(fmt)) { + const str = `${obj[key]}` + fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? str : padLeftZero(str)) + } + } + return fmt + }, + + resetPostUpdateDate() { + const updateDateDom = document.querySelector( + '.article-meta-info-container .article-update-date .pc' + ) + const updated = new Date(updateDateDom.dataset.updated).getTime() + const format = KEEP.theme_config.post?.datetime_format || 'YYYY-MM-DD HH:mm:ss' + updateDateDom.innerHTML = this.formatDatetime(format, updated) + } + } + + KEEP.utils.postHelper.initSetPostToolsLeft() + KEEP.utils.postHelper.setArticleAgingDays() + KEEP.utils.postHelper.resetPostUpdateDate() + + if (KEEP.theme_config.toc?.enable === true) { + KEEP.utils.postHelper.initToggleToc() + } + + if (KEEP.theme_config.comment?.enable === true) { + KEEP.utils.postHelper.goToComments() + KEEP.utils.postHelper.watchPostCommentsCount() + } +} + +if (KEEP.theme_config.pjax?.enable === true && KEEP.utils) { + initPostHelper() +} else { + window.addEventListener('DOMContentLoaded', initPostHelper) +} diff --git a/js/post/share.js b/js/post/share.js new file mode 100644 index 0000000..6a1bf22 --- /dev/null +++ b/js/post/share.js @@ -0,0 +1,46 @@ +/* global KEEP */ + +function initPostShareHelper() { + KEEP.utils.postShareHelper = { + postShareHandle() { + const pageUrl = window.location.href + const pageTitle = window.document.title + + const shareContainer = document.querySelector('.post-share-container .share-list-wrap') + + // WeChat share + const wechatShare = shareContainer.querySelector('.wechat') + wechatShare && + wechatShare.setAttribute( + 'data-tooltip-img-url', + `https://api.qrserver.com/v1/create-qr-code?data=${pageUrl}` + ) + + shareContainer.querySelectorAll('.share-item').forEach((item) => { + item.addEventListener('click', () => { + // QQ share + if (item.classList.contains('qq')) { + window.open(`https://connect.qq.com/widget/shareqq/index.html?url=${pageUrl}`) + } + + // WeiBo share + if (item.classList.contains('weibo')) { + window.open( + `https://service.weibo.com/share/share.php?url=${pageUrl}&title=${pageTitle}` + ) + } + }) + }) + } + } + + if (KEEP.theme_config.post?.share === true) { + KEEP.utils.postShareHelper.postShareHandle() + } +} + +if (KEEP.theme_config.pjax?.enable === true && KEEP.utils) { + initPostShareHelper() +} else { + window.addEventListener('DOMContentLoaded', initPostShareHelper) +} diff --git a/js/post/toc.js b/js/post/toc.js new file mode 100644 index 0000000..4255897 --- /dev/null +++ b/js/post/toc.js @@ -0,0 +1,134 @@ +/* global KEEP */ + +function initTOC() { + const pageContainer = document.querySelector('.page-container') + const postPageContainer = document.querySelector('.post-page-container') + const pcTocContainer = document.querySelector('.pc-post-toc') + const tabletTocContainer = document.querySelector('.tablet-post-toc') + + if (KEEP.utils.hasToc) { + KEEP.utils.tocHelper = { + pcTocNavSections: [], + + tabletTocNavSections: [], + + // get active index + getActiveIndex(navSections) { + if (!Array.isArray(navSections)) return + let index = navSections.findIndex((element) => { + return element && element.getBoundingClientRect().top - 20 > 0 + }) + if (index === -1) { + index = navSections.length - 1 + } else if (index > 0) { + index-- + } + return index + }, + + // active nav + activeNav() { + // pc + this.activateNavByIndex(pcTocContainer, this.getActiveIndex(this.pcTocNavSections)) + + // tablet + this.activateNavByIndex(tabletTocContainer, this.getActiveIndex(this.tabletTocNavSections)) + }, + + // register TOC Nav + registerTocNav() { + const register = (tocContainer) => { + return [...tocContainer.querySelectorAll('.post-toc li a.nav-link')].map((element) => { + const target = document.getElementById( + decodeURI(element.getAttribute('href')).replace('#', '') + ) + element.addEventListener('click', (event) => { + event.preventDefault() + let winScrollY = window.scrollY + winScrollY = winScrollY <= 1 ? -19 : winScrollY + const offset = target.getBoundingClientRect().top + winScrollY + window.anime({ + targets: document.scrollingElement, + duration: 500, + easing: 'linear', + scrollTop: offset, + complete: () => { + history.pushState(null, document.title, element.href) + setTimeout(() => { + KEEP.utils.pageTopDom.classList.add('hide') + }, 150) + } + }) + }) + return target + }) + } + // pc + this.pcTocNavSections = register(pcTocContainer) + + // tablet + this.tabletTocNavSections = register(tabletTocContainer) + }, + + activateNavByIndex(tocContainer, index) { + const target = tocContainer.querySelectorAll('.post-toc li a.nav-link')[index] + if (!target || target.classList.contains('active-current')) return + + tocContainer.querySelectorAll('.post-toc .active').forEach((element) => { + element.classList.remove('active', 'active-current') + }) + target.classList.add('active', 'active-current') + let parent = target.parentNode + while (!parent.matches('.post-toc')) { + if (parent.matches('li')) parent.classList.add('active') + parent = parent.parentNode + } + // Scrolling to center active TOC element if TOC content is taller than viewport. + const tocElement = tocContainer.querySelector('.post-toc-wrap') + window.anime({ + targets: tocElement, + duration: 200, + easing: 'linear', + scrollTop: + tocElement.scrollTop - + tocElement.offsetHeight / 2 + + target.getBoundingClientRect().top - + tocElement.getBoundingClientRect().top + }) + }, + + handleShowWhenHasToc() { + const openHandle = () => { + const styleStatus = KEEP.getStyleStatus() + const key = 'isShowToc' + if (styleStatus && styleStatus.hasOwnProperty(key)) { + KEEP.utils.postHelper.hasToc(styleStatus[key]) + } else { + KEEP.utils.postHelper.hasToc(true) + } + } + + const initOpenKey = 'init_open' + + if (KEEP.theme_config.toc.hasOwnProperty(initOpenKey)) { + KEEP.theme_config.toc[initOpenKey] ? openHandle() : KEEP.utils.postHelper.hasToc(false) + } else { + openHandle() + } + } + } + KEEP.utils.tocHelper.handleShowWhenHasToc() + KEEP.utils.tocHelper.registerTocNav() + } else { + pcTocContainer && postPageContainer.removeChild(pcTocContainer) + if (tabletTocContainer) { + pageContainer.removeChild(document.querySelector('.tablet-post-toc-mask')) + } + } +} + +if (KEEP.theme_config?.pjax?.enable === true && KEEP.utils) { + initTOC() +} else { + window.addEventListener('DOMContentLoaded', initTOC) +} diff --git a/js/utils.js b/js/utils.js new file mode 100644 index 0000000..8cbee56 --- /dev/null +++ b/js/utils.js @@ -0,0 +1,667 @@ +/* global KEEP */ + +KEEP.initUtils = () => { + KEEP.utils = { + rootHtmlDom: document.querySelector('html'), + pageTopDom: document.querySelector('.page-main-content-top'), + scrollProgressBarDom: document.querySelector('.scroll-progress-bar'), + pjaxProgressBarDom: document.querySelector('.pjax-progress-bar'), + pjaxProgressIcon: document.querySelector('.pjax-progress-icon'), + back2TopBtn: document.querySelector('.tool-scroll-to-top'), + headerWrapperDom: document.querySelector('.header-wrapper'), + + innerHeight: window.innerHeight, + pjaxProgressBarTimer: null, + prevScrollValue: 0, + fontSizeLevel: 0, + isHasScrollProgressBar: false, + isHasScrollPercent: false, + isHeaderTransparent: false, + hasToc: false, + + // initialization data + initData() { + const { scroll, first_screen } = KEEP.theme_config?.style || {} + this.isHasScrollProgressBar = scroll?.progress_bar === true + this.isHasScrollPercent = scroll?.percent === true + this.isHeaderTransparent = + first_screen?.enable === true && !window.location.pathname.includes('/page/') + if (!this.isHeaderTransparent) { + this.headerWrapperDom.classList.remove('transparent-1', 'transparent-2') + } + }, + + // scroll Style Handle + styleHandleWhenScroll() { + const scrollTop = document.body.scrollTop || document.documentElement.scrollTop + const scrollHeight = document.body.scrollHeight || document.documentElement.scrollHeight + const clientHeight = window.innerHeight || document.documentElement.clientHeight + + const percent = Math.round((scrollTop / (scrollHeight - clientHeight)) * 100) || 0 + + // back to top + if (scrollTop > 10) { + this.back2TopBtn.classList.add('show') + } else { + this.back2TopBtn.classList.remove('show') + } + + // scroll progress bar + if (this.isHasScrollProgressBar && this.scrollProgressBarDom) { + const progressPercent = ((scrollTop / (scrollHeight - clientHeight)) * 100).toFixed(3) + this.scrollProgressBarDom.style.visibility = percent === 0 ? 'hidden' : 'visible' + this.scrollProgressBarDom.style.width = `${progressPercent}%` + } + + // scroll percent + if (this.isHasScrollPercent && this.back2TopBtn) { + this.back2TopBtn.classList.add('show-percent') + const percentDom = this.back2TopBtn.querySelector('.percent') + if (percent === 0 || percent === undefined) { + this.back2TopBtn.classList.remove('show') + } else { + this.back2TopBtn.classList.add('show') + percentDom.innerHTML = percent.toFixed(0) + if (percent > 99) { + this.back2TopBtn.classList.add('show-arrow') + } else { + this.back2TopBtn.classList.remove('show-arrow') + } + } + } + + // hide header handle + if (scrollTop > this.prevScrollValue && scrollTop > this.innerHeight) { + this.pageTopDom.classList.add('hide') + if (this.isHeaderTransparent) { + this.headerWrapperDom.classList.remove('transparent-1', 'transparent-2') + } + } else { + this.pageTopDom.classList.remove('hide') + if (this.isHeaderTransparent) { + if (scrollTop <= this.headerWrapperDom.getBoundingClientRect().height) { + this.headerWrapperDom.classList.remove('transparent-2') + this.headerWrapperDom.classList.add('transparent-1') + } else if (scrollTop < this.innerHeight) { + this.headerWrapperDom.classList.add('transparent-2') + } + } + } + + this.prevScrollValue = scrollTop + }, + + // register window scroll event + registerWindowScroll() { + window.addEventListener('scroll', () => { + // style handle when scroll + this.styleHandleWhenScroll() + + // TOC scroll handle + if (KEEP.theme_config?.toc?.enable === true && KEEP.utils?.tocHelper) { + KEEP.utils.tocHelper.activeNav() + } + + // header shrink + KEEP.utils.headerShrink.headerShrink() + + // side tools bar show handle + KEEP.utils.headerShrink.sideToolsBarShowHandle() + }) + }, + + // toggle show tools list + toggleShowToolsList() { + const sideToolsListDom = document.querySelector('.side-tools-list') + const toggleShowToolsDom = document.querySelector('.tool-toggle-show') + toggleShowToolsDom.addEventListener('click', (e) => { + sideToolsListDom.classList.toggle('show') + e.stopPropagation() + }) + sideToolsListDom.querySelectorAll('.tools-item').forEach((item) => { + item.addEventListener('click', (e) => { + e.stopPropagation() + }) + }) + document.addEventListener('click', () => { + sideToolsListDom.classList.contains('show') && sideToolsListDom.classList.remove('show') + }) + }, + + // global font adjust + globalFontAdjust() { + const fontSize = document.defaultView.getComputedStyle(document.body).fontSize + const fs = parseFloat(fontSize) + + const initFontSize = () => { + const styleStatus = KEEP.getStyleStatus() + if (styleStatus) { + this.fontSizeLevel = styleStatus.fontSizeLevel + setFontSize(this.fontSizeLevel) + } + } + + const setFontSize = (fontSizeLevel) => { + this.rootHtmlDom.style.setProperty( + 'font-size', + `${fs * (1 + fontSizeLevel * 0.05)}px`, + 'important' + ) + KEEP.styleStatus.fontSizeLevel = fontSizeLevel + KEEP.setStyleStatus() + } + + initFontSize() + + document.querySelector('.tool-font-adjust-plus').addEventListener('click', () => { + if (this.fontSizeLevel === 5) return + this.fontSizeLevel++ + setFontSize(this.fontSizeLevel) + }) + + document.querySelector('.tool-font-adjust-minus').addEventListener('click', () => { + if (this.fontSizeLevel <= 0) return + this.fontSizeLevel-- + setFontSize(this.fontSizeLevel) + }) + }, + + // init has TOC + initHasToc() { + const tocNavDoms = document.querySelectorAll('.post-toc-wrap .post-toc li') + if (tocNavDoms.length > 0) { + this.hasToc = true + document.body.classList.add('has-toc') + } else { + this.hasToc = false + document.body.classList.remove('has-toc') + } + }, + + // zoom in image + zoomInImage() { + let SIDE_GAP = 40 + let isZoomIn = false + let curWinScrollY = 0 + let selectedImgDom = null + const imgDomList = document.querySelectorAll('.keep-markdown-body img') + const zoomInImgMask = document.querySelector('.zoom-in-image-mask') + const zoomInImg = zoomInImgMask?.querySelector('.zoom-in-image') + + const zoomOut = () => { + if (isZoomIn) { + isZoomIn = false + curWinScrollY = 0 + zoomInImg && (zoomInImg.style.transform = `scale(1)`) + zoomInImgMask && zoomInImgMask.classList.remove('show') + setTimeout(() => { + selectedImgDom && selectedImgDom.classList.remove('hide') + }, 300) + } + } + + const zoomOutHandle = () => { + zoomInImgMask && + zoomInImgMask.addEventListener('click', () => { + zoomOut() + }) + + document.addEventListener('scroll', () => { + if (isZoomIn && Math.abs(curWinScrollY - window.scrollY) >= 50) { + zoomOut() + } + }) + } + + const setSideGap = () => { + const w = document.body.offsetWidth + if (w <= 500) { + SIDE_GAP = 10 + } else if (w <= 800) { + SIDE_GAP = 20 + } else { + SIDE_GAP = 40 + } + } + + if (imgDomList.length) { + zoomOutHandle() + imgDomList.forEach((img) => { + img.addEventListener('click', () => { + curWinScrollY = window.scrollY + isZoomIn = !isZoomIn + setSideGap() + zoomInImg.setAttribute('src', img.getAttribute('src')) + selectedImgDom = img + if (isZoomIn) { + const imgRect = selectedImgDom.getBoundingClientRect() + const imgW = imgRect.width + const imgH = imgRect.height + const imgL = imgRect.left + const imgT = imgRect.top + const winW = document.body.offsetWidth - SIDE_GAP * 2 + const winH = document.body.offsetHeight - SIDE_GAP * 2 + const scaleX = winW / imgW + const scaleY = winH / imgH + const scale = (scaleX < scaleY ? scaleX : scaleY) || 1 + const translateX = winW / 2 - (imgRect.x + imgW / 2) + SIDE_GAP + const translateY = winH / 2 - (imgRect.y + imgH / 2) + SIDE_GAP + + selectedImgDom.classList.add('hide') + zoomInImgMask.classList.add('show') + zoomInImg.style.top = imgT + 'px' + zoomInImg.style.left = imgL + 'px' + zoomInImg.style.width = imgW + 'px' + zoomInImg.style.height = imgH + 'px' + zoomInImg.style.transform = `translateX(${translateX}px) translateY(${translateY}px) scale(${scale}) ` + } + }) + }) + } + }, + + // set how long ago language + setHowLongAgoLanguage(p1, p2) { + return p2.replace(/%s/g, p1) + }, + + // get how long ago + getHowLongAgo(timestamp) { + const lang = KEEP.language_ago + const __Y = Math.floor(timestamp / (60 * 60 * 24 * 30) / 12) + const __M = Math.floor(timestamp / (60 * 60 * 24 * 30)) + const __W = Math.floor(timestamp / (60 * 60 * 24) / 7) + const __d = Math.floor(timestamp / (60 * 60 * 24)) + const __h = Math.floor((timestamp / (60 * 60)) % 24) + const __m = Math.floor((timestamp / 60) % 60) + const __s = Math.floor(timestamp % 60) + + if (__Y > 0) { + return this.setHowLongAgoLanguage(__Y, lang.year) + } else if (__M > 0) { + return this.setHowLongAgoLanguage(__M, lang.month) + } else if (__W > 0) { + return this.setHowLongAgoLanguage(__W, lang.week) + } else if (__d > 0) { + return this.setHowLongAgoLanguage(__d, lang.day) + } else if (__h > 0) { + return this.setHowLongAgoLanguage(__h, lang.hour) + } else if (__m > 0) { + return this.setHowLongAgoLanguage(__m, lang.minute) + } else if (__s > 0) { + return this.setHowLongAgoLanguage(__s, lang.second) + } + }, + + // set how long age in home article block + setHowLongAgoInHome() { + const post = document.querySelectorAll('.article-meta-info .home-article-history') + post && + post.forEach((v) => { + const nowTimestamp = Date.now() + const updatedTimestamp = new Date(v.dataset.updated).getTime() + v.innerHTML = this.getHowLongAgo(Math.floor((nowTimestamp - updatedTimestamp) / 1000)) + }) + }, + + // loading progress bar start + pjaxProgressBarStart() { + this.pjaxProgressBarTimer && clearInterval(this.pjaxProgressBarTimer) + if (this.isHasScrollProgressBar) { + this.scrollProgressBarDom.classList.add('hide') + } + + this.pjaxProgressBarDom.style.width = '0' + this.pjaxProgressIcon.classList.add('show') + + let width = 1 + const maxWidth = 99 + + this.pjaxProgressBarDom.classList.add('show') + this.pjaxProgressBarDom.style.width = width + '%' + + this.pjaxProgressBarTimer = setInterval(() => { + width += 5 + if (width > maxWidth) width = maxWidth + this.pjaxProgressBarDom.style.width = width + '%' + }, 100) + }, + + // loading progress bar end + pjaxProgressBarEnd() { + this.pjaxProgressBarTimer && clearInterval(this.pjaxProgressBarTimer) + this.pjaxProgressBarDom.style.width = '100%' + + const temp_1 = setTimeout(() => { + this.pjaxProgressBarDom.classList.remove('show') + this.pjaxProgressIcon.classList.remove('show') + + if (this.isHasScrollProgressBar) { + this.scrollProgressBarDom.classList.remove('hide') + } + + const temp_2 = setTimeout(() => { + this.pjaxProgressBarDom.style.width = '0' + clearTimeout(temp_1), clearTimeout(temp_2) + }, 200) + }, 200) + }, + + // insert tooltip content dom + insertTooltipContent() { + const isLazyLoadImg = KEEP.theme_config?.lazyload?.enable === true + + const init = () => { + // tooltip + document.querySelectorAll('.tooltip').forEach((element) => { + const { tooltipContent, tooltipOffsetX, tooltipOffsetY } = element.dataset + + let styleCss = '' + + if (tooltipOffsetX) { + styleCss += `left: ${tooltipOffsetX};` + } + + if (tooltipOffsetY) { + styleCss += `top: ${tooltipOffsetY};` + } + + if (styleCss) { + styleCss = `style="${styleCss}"` + } + + if (tooltipContent) { + element.insertAdjacentHTML( + 'afterbegin', + `${tooltipContent}` + ) + } + }) + + // tooltip-img + const imgsSet = {} + + const hideTooltipImg = (dom, nameIdx, trigger = 'click') => { + if (trigger === 'hover') { + trigger = 'mouseout' + } + + document.addEventListener(trigger, () => { + if (imgsSet[nameIdx].isShowImg) { + dom.classList.remove('show-img') + imgsSet[nameIdx].isShowImg = false + } + }) + } + + const loadImg = (img, imgLoaded) => { + const temp = new Image() + const { src } = img.dataset + temp.src = src + temp.onload = () => { + img.src = src + img.removeAttribute('lazyload') + imgLoaded = true + } + } + + // tooltip-img + document.querySelectorAll('.tooltip-img').forEach((dom, idx) => { + const { + tooltipImgName, + tooltipImgUrl, + tooltipImgTip, + tooltipImgTrigger = 'click', + tooltipImgStyle + } = dom.dataset + + let styleCss = '' + + if (tooltipImgStyle) { + styleCss = `style="${tooltipImgStyle}"` + } + + let tipDom = '' + if (tooltipImgTip) { + tipDom = `
    ${tooltipImgTip}
    ` + } + + if (tooltipImgUrl) { + const imgDomClass = `tooltip-img-${idx}-${tooltipImgName ? tooltipImgName : Date.now()}` + const nameIdx = `${tooltipImgName}-${idx}` + + const imgDom = `${imgDomClass}` + + const imgTooltipBox = `
    ${imgDom}${tipDom}
    ` + + imgsSet[nameIdx] = { + imgLoaded: false, + isShowImg: false + } + + dom.insertAdjacentHTML('afterbegin', imgTooltipBox) + + let eventTrigger = 'click' + + if (tooltipImgTrigger === 'hover') { + eventTrigger = 'mouseover' + } + + dom.addEventListener(eventTrigger, (e) => { + if (isLazyLoadImg && !imgsSet[nameIdx].imgLoaded) { + loadImg( + document.querySelector(`.tooltip-img-box img.${imgDomClass}`), + imgsSet[nameIdx].imgLoaded + ) + } + imgsSet[nameIdx].isShowImg = !imgsSet[nameIdx].isShowImg + dom.classList.toggle('show-img') + e.stopPropagation() + }) + + hideTooltipImg(dom, nameIdx, tooltipImgTrigger) + } + }) + } + setTimeout(() => { + init() + }, 1000) + }, + + // busuanzi initialize handle + siteCountInitialize() { + if (KEEP.theme_config?.website_count?.busuanzi_count?.enable === true) { + const tmpId = 'busuanzi-js' + let script = document.body.querySelector(`#${tmpId}`) + + if (!script) { + script = document.createElement('script') + script.setAttribute('data-pjax', '') + script.setAttribute('id', tmpId) + script.async = true + script.src = '//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js' + document.body.appendChild(script) + } + + const getText = (selector) => { + return document.querySelector(selector)?.innerText + } + + script.onload = () => { + setTimeout(() => { + if ( + getText('#busuanzi_value_site_uv') || + getText('#busuanzi_value_site_pv') || + getText('#busuanzi_value_page_pv') + ) { + const tmpDom1 = document.querySelector('.footer .count-item .uv') + const tmpDom2 = document.querySelector('.footer .count-item .pv') + const tmpDom3 = document.querySelector('.article-meta-info .article-pv') + tmpDom1 && (tmpDom1.style.display = 'flex') + tmpDom2 && (tmpDom2.style.display = 'flex') + tmpDom3 && (tmpDom3.style.display = 'inline-block') + } + }, 1000) + } + } + }, + + // page number jump handle + pageNumberJump() { + const inputDom = document.querySelector('.paginator .page-number-input') + inputDom && + inputDom.addEventListener('change', (e) => { + const min = 1 + const max = Number(e.target.max) + let current = Number(e.target.value) + + if (current <= 0) { + inputDom.value = min + current = min + } + + if (current > max) { + inputDom.value = max + current = max + } + + const tempHref = window.location.href.replace(/\/$/, '').split('/page/')[0] + + if (current === 1) { + window.location.href = tempHref + } else { + window.location.href = tempHref + '/page/' + current + } + }) + }, + + // custom tabs tag active handle + tabsActiveHandle() { + const activeHandle = (navList, paneList, tab) => { + navList.forEach((nav) => { + if (tab.dataset.href === nav.dataset.href) { + nav.classList.add('active') + } else { + nav.classList.remove('active') + } + }) + + paneList.forEach((pane) => { + if (tab.dataset.href === pane.id) { + pane.classList.add('active') + } else { + pane.classList.remove('active') + } + }) + } + + const tabsList = document.querySelectorAll('.keep-tabs') + tabsList.length && + tabsList.forEach((tabs) => { + const tabNavList = tabs.querySelectorAll('.tabs-nav .tab') + const tabPaneList = tabs.querySelectorAll('.tabs-content .tab-pane') + tabNavList.forEach((tabNav) => { + tabNav.addEventListener('click', () => { + activeHandle(tabNavList, tabPaneList, tabNav) + }) + }) + }) + }, + + // first screen typewriter + initTypewriter() { + const fsc = KEEP.theme_config?.style?.first_screen || {} + const isHitokoto = fsc?.hitokoto === true + + if (fsc?.enable !== true) { + return + } + + if (fsc?.enable === true && !isHitokoto && !fsc?.description) { + return + } + + const descBox = document.querySelector('.first-screen-content .description') + if (descBox) { + descBox.style.opacity = '0' + + setTimeout( + () => { + descBox.style.opacity = '1' + const descItemList = descBox.querySelectorAll('.desc-item') + descItemList.forEach((descItem) => { + const desc = descItem.querySelector('.desc') + const cursor = descItem.querySelector('.cursor') + const text = desc.innerHTML + desc.innerHTML = '' + let charIndex = 0 + + if (text) { + const typewriter = () => { + if (charIndex < text.length) { + desc.textContent += text.charAt(charIndex) + charIndex++ + setTimeout(typewriter, 100) + } else { + cursor.style.display = 'none' + } + } + + typewriter() + } + }) + }, + isHitokoto ? 400 : 300 + ) + } + }, + + // remove white space between children + removeWhitespace(container) { + if (!container) { + return + } + + const childNodes = container.childNodes + const whitespaceNodes = [] + + for (let i = 0; i < childNodes.length; i++) { + const node = childNodes[i] + + if (node.nodeType === 3 && /^\s*$/.test(node.nodeValue)) { + whitespaceNodes.push(node) + } + } + + for (const whitespaceNode of whitespaceNodes) { + container.removeChild(whitespaceNode) + } + }, + trimPostMetaInfoBar() { + this.removeWhitespace( + document.querySelector('.article-meta-info-container .article-category-ul') + ) + this.removeWhitespace(document.querySelector('.article-meta-info-container .article-tag-ul')) + } + } + + KEEP.utils.initData() + KEEP.utils.registerWindowScroll() + KEEP.utils.toggleShowToolsList() + KEEP.utils.globalFontAdjust() + KEEP.utils.initHasToc() + KEEP.utils.zoomInImage() + KEEP.utils.setHowLongAgoInHome() + KEEP.utils.insertTooltipContent() + KEEP.utils.siteCountInitialize() + KEEP.utils.pageNumberJump() + KEEP.utils.tabsActiveHandle() + KEEP.utils.initTypewriter() + KEEP.utils.trimPostMetaInfoBar() +} diff --git a/search.xml b/search.xml new file mode 100644 index 0000000..4c94fd7 --- /dev/null +++ b/search.xml @@ -0,0 +1,579 @@ + + + + Flask一个g引发的思考 + /2023/12/03/Flask%E4%B8%80%E4%B8%AAg%E5%BC%95%E5%8F%91%E7%9A%84%E6%80%9D%E8%80%83/ + 背景
    +

    最近有面试一家公司,感觉准备的不是很充分,感觉很多东西都答的挺菜的,自己写的文章里面的问题都没答上来更是汗流浃背。
    所以大概列了一下其中的问题,进行了一番总结补充,重新制定一下复习的计划。
    其中里面我觉得有个问题我觉得挺有意思的,我记得大概是问了一下Flask的g有使用过?具体实现的原理是啥?
    我想了一下 ,这不就是平时用来存储一下数据的全局变量的么?有啥原理的,后面大概翻了一下Flask的源码,具体从头捋了一遍,所以就有这篇文章。

    +
    +

    源码阅读

    g

    首先我们从平时导入的”from flask import g”入手,定位到flask框架代码中g,作为起点继续往下探索;

    +
    _cv_app: ContextVar[AppContext] = ContextVar("flask.app_ctx")
    app_ctx: AppContext = LocalProxy( # type: ignore[assignment]
    _cv_app, unbound_message=_no_app_msg
    )
    current_app: Flask = LocalProxy( # type: ignore[assignment]
    _cv_app, "app", unbound_message=_no_app_msg
    )
    g: _AppCtxGlobals = LocalProxy( # type: ignore[assignment]
    _cv_app, "g", unbound_message=_no_app_msg
    )

    +

    这里面重要的就两块地方,_cv_app,g:

    +
      +
    • _cv_app这个对象实际上就是初始化一个上下文变量ContexVar,根据这个类型注解,这个上下文变量中的值是AppContext对象;
    • +
    • g实质就是_AppCtxGlobals对象,但是这里是通过LocalProxy代理对象进行访问;
    • +
    +

    LocalProxy.__init__

    接下来继续分析一下LocalProxy这个代理对象是怎么代理访问_AppCtxGlobals,也就是应用上下文中的对象:(代码过长,我截取初始化中最重要的几部分)

    +
    def __init__(
    self,
    local: ContextVar[T] | Local | LocalStack[T] | t.Callable[[], T],
    name: str | None = None,
    *,
    unbound_message: str | None = None,
    ) -> None:
    +
      +
    • 定义传入的参数,这里local就是_cv_app上下文变量,name就是”g”;
    • +
    +
    if name is None:
    get_name = _identity
    else:
    get_name = attrgetter(name)
    +
      +
    • 生成一个可调用对象,用于提取对象中的属性值。后续get_name(obj)相当于直接调用obj.name属性;
    • +
    +
    elif isinstance(local, ContextVar):

    def _get_current_object() -> T:
    try:
    obj = local.get()
    except LookupError:
    raise RuntimeError(unbound_message) from None

    return get_name(obj)
    +

    这部分有两块地方比较模糊,local.get(),get_name(obj)

    +
      +
    • 由于传入local是ContexVar类型,定位到if匹配的代码,这部分是定义_get_current_object方法
    • +
    • local.get()其实返回的就是set进去的AppContext对象,所以obj就是AppContext类型;
    • +
    • get_name方法前面定义好了,所以可以理解为AppContext.g;
    • +
    +
    object.__setattr__(self, "_get_current_object", _get_current_object)
    +

    LocalProxy代理器绑定_get_current_object方法

    +

    LocalProxy.__setattr__、__getattr__

    众所周知,当给一个类绑定属性时候,会调用类的__setattr__方法,当读取一个不存在的属性时,会调用__getattr__方法,调用一个已存在属性时,会调用__getattribute__方法;

    +
    __getattr__ = _ProxyLookup(getattr)
    # __getattribute__ triggered through __getattr__
    __setattr__ = _ProxyLookup(setattr) # type: ignore
    +
      +
    • 这部分有个地方挺有意思的,就是__getattribute__方法源代码是注释的了,并且配上通过__getattr__触发,这里可能需要绕个弯,本质上这里是代理器对象,目标对象并不是直接绑定到代理的。所以其实每次通过g去获取存储的对象是,这个代理器的__getattr__就会被触发了;
    • +
    • 设置__setattr__方法;可以理解为后续LocalProxy的实例对象设置属性时,_ProxyLookup(setattr)(self, key, value),这个self指的是LocalProxy的实例对象
    • +
    +

    _PorxyLookup.__init__

    接下来,我们接到看这个_PorxyLookup对象,这个本质上就是个描述器,用于查找对象的。

    +

    描述器开始有点熟悉了,上面提到_ProxyLookup的实例当成方法一样调用的时候,应该先定位到_PorxyLookup对象__call__方法,先看看_PorxyLookup的初始化__init__:

    +
    def __init__(
    self,
    f: t.Callable | None = None,
    fallback: t.Callable | None = None,
    class_value: t.Any | None = None,
    is_attr: bool = False,
    ) -> None:
    bind_f: t.Callable[[LocalProxy, t.Any], t.Callable] | None

    if hasattr(f, "__get__"):
    # A Python function, can be turned into a bound method.

    def bind_f(instance: LocalProxy, obj: t.Any) -> t.Callable:
    return f.__get__(obj, type(obj)) # type: ignore

    elif f is not None:
    # A C function, use partial to bind the first argument.

    def bind_f(instance: LocalProxy, obj: t.Any) -> t.Callable:
    return partial(f, obj)

    else:
    # Use getattr, which will produce a bound method.
    bind_f = None

    self.bind_f = bind_f
    self.fallback = fallback
    self.class_value = class_value
    self.is_attr = is_attr
    +
      +
    • 判断传入的f是否为描述器,很显然传入的setattr为否,且f不为None:
    • +
    • 声明一个bind_f函数,函数用于返回partial对象,partial函数是用于固定了obj参数;
    • +
    • 前面提到传入的是setattr,setattr的函数定义setattr(x,y,z),正常使用需要传入三个参数,才能达到x.y = z的效果;
    • +
    • 现在就是bind_f直接返回的是固定了第一个参数为obj的setattr函数,后续只需要调用只需要传入后两个参数即可;
      (注意这个self.bind_f,后面会用到)
    • +
    +

    _PorxyLookup.__call__

    接着继续看_PorxyLookup的__call__方法:

    +
    def __call__(self, instance: LocalProxy, *args: t.Any, **kwargs: t.Any) -> t.Any:
    """Support calling unbound methods from the class. For example,
    this happens with ``copy.copy``, which does
    ``type(x).__copy__(x)``. ``type(x)`` can't be proxied, so it
    returns the proxy type and descriptor.
    """
    return self.__get__(instance, type(instance))(*args, **kwargs)
    +
      +
    • 这里其实就是当实例当做函数一样调用时会触发的__call__方法,这里可以看到instance对应着我们传入的LocalProxy实例对象,*args就是我们传入设置的参数了;
    • +
    • 后面就是调用描述器的__get__方法了,直接传入实例对象,实例类型;
    • +
    +

    _PorxyLookup.__get__

    这部分算我们寻找设置g这个全局上下文变量的重点,把前面做的所有事情进行一次“回收”使用:

    +
    def __get__(self, instance: LocalProxy, owner: type | None = None) -> t.Any:
    if instance is None:
    if self.class_value is not None:
    return self.class_value

    return self

    try:
    obj = instance._get_current_object()
    except RuntimeError:
    if self.fallback is None:
    raise

    fallback = self.fallback.__get__(instance, owner)

    if self.is_attr:
    # __class__ and __doc__ are attributes, not methods.
    # Call the fallback to get the value.
    return fallback()

    return fallback

    if self.bind_f is not None:
    return self.bind_f(instance, obj)

    return getattr(obj, self.name)
    +
      +
    1. _get_current_object方法,前面在LocalProxy代理器对象绑定好的方法,方法里面其实最终返回的是就是一个AppContext.g对象;
    2. +
    3. self.bind_f前面初始化刚绑定好的,这里其实就是返回一个固定参数的setattr函数,setattr的第一个参数固定成obj了;
    4. +
    5. 结合上面__call__方法,再把剩余需要设置的参数传进来,假设在flask框架设置g全局变量是g.name = “flask”,其实在这里执行的就是setattr(AppContext.g, “name”, “flask”);
    6. +
    7. 所以就是调用目标对象AppContext中g属性的__setattr__方法;
    8. +
    +

    整体总结

    整体看下来就是,废了老大劲,其实就是调用AppContext的方法。而里面代码架构的核心就是代理模式,使用者就是通过g这个代理器去访问AppContext里面的g属性。而这个AppContext对象则是,存放在_cv_app这个上下文变量。而这个g实质就是在存储数据的应用上下文。

    +

    下面展示一下AppContext类:

    +
    class AppContext:
    """The app context contains application-specific information. An app
    context is created and pushed at the beginning of each request if
    one is not already active. An app context is also pushed when
    running CLI commands.
    """

    def __init__(self, app: Flask) -> None:
    self.app = app
    self.url_adapter = app.create_url_adapter(None)
    self.g: _AppCtxGlobals = app.app_ctx_globals_class()
    self._cv_tokens: list[contextvars.Token] = []

    def push(self) -> None:
    """Binds the app context to the current context."""
    self._cv_tokens.append(_cv_app.set(self))
    appcontext_pushed.send(self.app, _async_wrapper=self.app.ensure_sync)
    +
      +
    • 这里面有趣的点就是什么时候触发这个push方法,即需要把当前的AppContext保存到_cv_app这个上下文变量中,不然你直接在flask程序启动的前调用这个g,会发现抛出一个RuntimeError的异常,因为_cv_app里面是空的;
    • +
    • 这里不详细列了,有兴趣的再去仔细看看,答案是Flask对象中的wsgi_app方法中,当WSGI服务器调用Falsk对象作为应用程序时就会调用wsgi_app方法,wsgi_app就会推送应用程序上下文;
    • +
    +

    最后一试

    上面提到很关键的代理模式,众所周知,一般来说代理模式目的就是防止调用者和执行者发生关系,所以需要一个代理对象。如果上面这么绕的代码看不懂,其实可以简洁成以下的代码,
    或许你直接就豁然开朗了 。

    +
    from werkzeug.local import LocalProxy
    from contextvars import ContextVar


    class Info:
    pass


    class Person:
    def __init__(self):
    self.info = Info()


    if __name__ == "__main__":
    flask_var = ContextVar("flask.context")
    p = Person()
    flask_var.set(p)

    proxy = LocalProxy(flask_var, "info", unbound_message="Error bound msg")
    proxy.name = "zhangsan"
    proxy.age = 12
    print(p.info.name, p.info.age)
    + +

    所以梳理完整遍代码,好像大概知道了一点原理。。。

    +

    参考链接:

    +
    +

    https://www.cnblogs.com/cwp-bg/p/10084480.html

    +
    +
    +

    https://flask.palletsprojects.com/en/3.0.x/appcontext/

    +
    +]]>
    + + Python + + + Python + Flask + g + 源码阅读 + +
    + + Gorm框架-CRUD操作 + /2023/06/24/Gorm%E6%A1%86%E6%9E%B6-CRUD%E6%93%8D%E4%BD%9C/ + 简介

    关于ORM(Object-Relational Mapping)

    ORM其实指的是将关系型数据库中的数据和面向对象程序中对象模型进行映射的技术;
    ORM可以用来自动化处理SQL语句的生成和执行,程序员可以更专注于业务逻辑的实现而不是数据的细节。

    +

    为什么需要ORM?(参考gpt,结合自身使用过程)

    +
      +
    • 提高开发效率(自动化生成SQL,减少手动编写SQL时间)
    • +
    • 只需要定义好模型,可以自动处理不同数据库之间的差异;(如果传统编写,换数据库相当于需要吧原来的逻辑重写一遍)
    • +
    • 易于维护,方便拓展(传统编写SQL如果多起来,排查问题和重构时很痛苦);
    • +
    • 安全,参数化查询避免SQL注入
    • +
    +

    关于Gorm

    Gorm是一个全功能的ORM框架,主要针对Go语言而开发,支持处理主流的关系型数据库(MySQL、PostgreSQL、SQL Server)
    以及一些NoSQL数据库。由于这边是直接使用的是Gorm 2,关于Gorm的重要特性可以以gorm官网为准,可以直接参考:

    +
    +

    https://gorm.io/zh_CN/docs/index.html#%E5%AE%89%E8%A3%85

    +
    +

    安装和设置

    快速安装Gorm2,Mysql驱动

    go get gorm.io/gorm
    go get gorm.io/driver/mysql
    + +

    初始化Mysql连接(参考官网)

    import (
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    )

    func main() {
    // 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
    dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    }
    +

    备注:

    +
      +
    • 定义DSN(Data Source Name)连接数据库字符串dsn;
    • +
    • 传入MySQL驱动,初始化gorm数据库连接;
    • +
    +

    这是我自己定义初始化连接,读取对应配置文件

    +
    type Config struct {
    MysqlConfig DBConfig `json:"mysql"`
    }

    type DBConfig struct {
    Username string `json:"username"`
    Password string `json:"password"`
    Host string `json:"host"`
    Port int `json:"port"`
    DBName string `json:"prefix"`
    }

    func initDB() (db *gorm.DB, err error) {
    confPath := "conf.json"
    if _, err = os.Stat(confPath); err != nil {
    return
    }

    var config Config
    if err = configor.Load(&config, confPath); err != nil {
    return
    }

    // 新建Database Gorm连接
    mysqlConfig := &config.MysqlConfig
    dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4",
    mysqlConfig.Username, mysqlConfig.Password, mysqlConfig.Host,
    mysqlConfig.Port, mysqlConfig.DBName)
    db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})

    return
    }

    func main() {

    db, err := initDB()

    if err != nil {
    log.Fatalf(err.Error())
    return
    }

    db = db.Debug()
    execSQL(db)
    }
    +

    备注:

    +
      +
    • 这边我在项目中定义了关于数据库连接的结构体,通过读取预设好的config.json文件,加载好预设的配置信息;
    • +
    • 另外这边的db = db.Debug(),可以开启db的DEBUG模式,能够打印出执行的SQL语句以及对应的参数,以方便调试;
    • +
    +

    config.json参考

    +
    {
    "mysql": {
    "username": "root",
    "password": "root",
    "host": "127.0.0.1",
    "prefix": "GormDemo",
    "port": 3306
    }
    }
    + + +

    定义模型

    由于Gorm模型的结构体名的蛇形复数作为表名,字段名的蛇形作为列名,如果Mysql表的设计遵循了GORM约定,则可少写很多代码,
    但是实际情况,往往不是这样:

    +
    type User struct {
    ID string `gorm:"column:id" faker:"-"`
    Email string `gorm:"column:email" faker:"email"`
    Password string `gorm:"column:password" faker:"password"`
    PhoneNumber string `gorm:"column:phone_number" faker:"phone_number"`
    UserName string `gorm:"column:username" faker:"username"`
    FirstName string `gorm:"first_name" faker:"first_name"`
    LastName string `gorm:"last_name" faker:"last_name"`
    Century string `gorm:"century" faker:"century"`
    Date string `gorm:"date" faker:"date"`
    }

    func (u User) TableName() string {
    return "user"
    }
    +

    备注:

    +
      +
    • 这是我定义的结构体User,通过实现Tabler接口来更改默认表名,TableName()方法会将Music表名重写为user(如果不修改则会默认为users);
    • +
    • 修改对应字段对应数据的列名,修改标签column;
    • +
    • 模型中的faker的标签可以忽略,这边主要是生成假数据才会使用到的;
    • +
    +

    CRUD

    创建

    单挑记录插入

    +
    // insertOneRow 单挑记录插入
    func insertOneRow(db *gorm.DB){
    var tmpUser *User

    // 生成随机数据
    err := faker.FakeData(&tmpUser)
    if err != nil {
    log.Fatalf(err.Error())
    }

    result := db.Create(tmpUser)
    if result.Error != nil {
    log.Fatalf(result.Error.Error())
    }

    fmt.Println("result RowsAffected: ", result.RowsAffected)
    fmt.Printf("%+v\n", tmpUser)

    }
    +

    备注:

    +
      +
    • 由于想生成写测试数据,所以使用了github.com/go-faker/faker生成假数据,有兴趣可以了解,像前面的结构体定义Tag之后就能生成对应的假数据;
    • +
    • 基本创建单条记录没啥好说的,只要定义模型的结构体,补充数据,然后插入即可;
    • +
    +

    批量记录插入

    +
    // insertRows 批量插入
    func insertRows(db *gorm.DB) {
    var users []*User
    for i := 0; i < 10; i++ {
    tmpUser := User{}
    err := faker.FakeData(&tmpUser)
    if err != nil {
    log.Fatal(err.Error())
    }
    users = append(users, &tmpUser)
    }

    result := db.Create(users)

    if result.Error != nil {
    log.Fatalf(result.Error.Error())
    }

    fmt.Println("RowsAffected: ", result.RowsAffected)

    for _, m := range users {
    fmt.Printf("%+v\n", m)
    }
    }
    + +

    定义钩子函数

    +
    func (u *User) BeforeCreate(tx *gorm.DB) (err error){
    u.ID = uuid.New()
    return nil
    }
    +

    备注:

    +
      +
    • 在插入记录之前,生成uuid(具体根据实际业务场景补充处理的业务逻辑,包括检验等)
    • +
    • 官方还提供提供很多额外的钩子函数(BeforeSave,AfterSave等)具体参考官方提供的文档
    • +
    +

    查询

    简单查询

    +
    func printRecord(u *User, result *gorm.DB){
    fmt.Printf("%v\n", u)
    fmt.Println(result.Error, result.RowsAffected)
    }

    func printRecords(u []*User, result *gorm.DB){

    for _, u := range u {
    fmt.Println(u)
    }
    fmt.Println(result.Error, result.RowsAffected)

    }

    func simpleQueryRow(db *gorm.DB){

    // 查询第一条记录(主键升序)
    var firstUser *User
    result := db.First(&firstUser)
    printRecord(firstUser, result)

    // 仅当有一个ID主键时,可直接定义User时把ID初始化
    firstIDUser2 := &User{ID: "e8efff22-a497-4a88-be1e-5123eb23ff75"}
    result = db.First(&firstIDUser2)
    printRecord(firstIDUser2, result)

    // 查询表中第一条记录(没有指定排序字段)
    var firstUser2 *User
    result = db.Take(&firstUser2)
    printRecord(firstUser2, result)

    // 查询表中最后一条记录(主键排序)
    var lastUser *User
    result = db.Last(&lastUser)
    printRecord(lastUser, result)

    // 查询当前所有记录
    var users []*User
    result = db.Find(&users)
    printRecords(users, result)

    }
    +

    备注:

    +
      +
    • 这几个都是按照官方给出的示例,整理了一下日常比较常用到的查询操作
    • +
    +

    条件查询

    +
    func condQueryRow(db *gorm.DB){

    // 查询当前username为condQueryRow的第一条记录(Struct方式)
    var tmpUser1 *User
    result := db.Where(&User{UserName: "qNptxqb"}).First(&tmpUser1)
    printRecord(tmpUser1, result)

    // 查询当前username为condQueryRow的第一条记录(Map方式)
    var tmpUser2 *User
    result = db.Where(map[string]interface{}{"username": "qNptxqb"}).First(&tmpUser2)
    printRecord(tmpUser2, result)

    // 指定Century查询字段查询记录
    var tmpUser3 []User
    result = db.Where(&User{Century: "VII", UserName: "jaQlaFs"}, "Century").Find(&tmpUser3)
    printRecords(tmpUser3, result)

    // String 条件,直接写表达式
    var tmpUser4 *User
    result = db.Where("username = ?", "qNptxqb").First(&tmpUser4)
    printRecord(tmpUser4, result)

    var users []User
    result = db.Where("date > ?", "2010-10-1").Find(&users)
    printRecords(users, result)

    // Order排序(默认升序)
    var users2 []User
    result = db.Where("date > ?", "2010-10-1").Order("date").Find(&users2)
    printRecords(users2, result)

    // 查询特定的字段,不返回所有字段
    var tmpUser5 *User
    result = db.Select("username", "date").Where("username = ?", "qNptxqb").First(&tmpUser5)
    printRecord(tmpUser5, result)
    }
    +

    备注:

    +
      +
    • Gorm提供了很多条件查询的场景,目前对我而言基本的查询业务逻辑都能支持。具体可能出现一些复杂的sql,后面会介绍怎么直接使用sql查询;
    • +
    +

    高级查询

    +
    type APIUser struct {
    ID string `gorm:"primaryKey,column:id"`
    UserName string `gorm:"column:username"`
    FirstName string `gorm:"first_name"`
    LastName string `gorm:"last_name"`
    }

    func advancedQueryRow(db *gorm.DB){

    // 智能选择字段,如果经常只需要查询某些字段,可以重新定义小结构体
    var apiUser []APIUser
    result := db.Model(&User{}).Find(&apiUser)
    for _, user := range apiUser{
    fmt.Println(user)
    }
    fmt.Println(result.Error, result.RowsAffected)

    // 扫描结果绑定值map[string]interface{} 或者 []map[string]interface{}
    var users []map[string]interface{}
    result = db.Model(&User{}).Find(&users)
    for _, user := range users{
    fmt.Println(user)
    }
    fmt.Println(result.Error, result.RowsAffected)

    // Pluck查询单个列,并将结果扫描到切片
    var emails []string
    result = db.Model(&User{}).Pluck("email",&emails)
    fmt.Println(emails)
    fmt.Println(result.Error, result.RowsAffected)

    // Count查询
    var count int64
    result = db.Model(&User{}).Where("date > ?", "2012-10-22").Count(&count)
    fmt.Println(count)
    fmt.Println(result.Error, result.RowsAffected)
    }
    +

    备注:

    +
      +
    • 定义小结构体可以实现在调用API时自动选择特定字段
    • +
    • Pluck适合查询单个列,如果需要查询多个常用字段可以通过Select和Scan
    • +
    +

    更新

    常用更新操作

    +
    func updateRow(db *gorm.DB){
    // Save会保存所有字段,即使字段是零值,如果保存的值没有主键,就会创建,否则则是更新指定记录
    result := db.Save(&User{ID: "e8efff22-a497-4a88-be1e-5123eb23ff75", UserName: "zhangsan", Date: "2023-12-12"})
    fmt.Println(result.Error, result.RowsAffected)

    // 更新单个列
    result = db.Model(&User{}).Where("username = ?", "jaQlaFs").Update("first_name", "zhangsan")
    fmt.Println(result.Error, result.RowsAffected)

    // 更新多个列
    result = db.Model(&User{}).Where("username = ?", "zhangsan").Updates(User{FirstName: "zhangsan2", LastName: "zhangsan3"})
    fmt.Println(result.Error, result.RowsAffected)

    // 更新指定列(Select指定last_name)
    result = db.Model(&User{}).Where("username = ?", "zhangsan").Select("last_name").Updates(User{FirstName: "zhangsan2", LastName: "zhangsan4"})
    fmt.Println(result.Error, result.RowsAffected)
    }
    +

    备注:

    +
      +
    • 具体一些更新的操作和查询类似,具体区分即可;
    • +
    +

    删除

    常用删除操作

    +
    func deleteRows(db *gorm.DB){

    // 指定匹配字段删除数据
    result := db.Delete(&User{}, map[string]interface{}{"username": "NJrauTj"})
    fmt.Println(result.Error, result.RowsAffected)

    result = db.Delete(&User{}, "username = ?", "NJrauTj")
    fmt.Println(result.Error, result.RowsAffected)

    // Where指定字段匹配删除数据
    result = db.Where("username = ? and phone_number = ?", "jXQKmPv", "574-821-9631").Delete(&User{})
    fmt.Println(result.Error, result.RowsAffected)

    // 批量删除的两种方式
    result = db.Where("email like ?", "%.com%").Delete(&User{})
    fmt.Println(result.Error, result.RowsAffected)

    result = db.Delete(&User{}, "email like ?", "%.com%")
    fmt.Println(result.Error, result.RowsAffected)
    }
    +

    备注:

    +
      +
    • 普通删除常用场景匹配指定单挑数据删除以及批量删除,语法和更新类似;
    • +
    • Gorm文档中有涉及禁用全局删除,即当执行不带任何条件的批量删除时就会返回错误;以及关于删除的钩子函数有实用
      场景的同学,可以看官方文档,这里不再赘述;
    • +
    +

    原生SQL和SQL生成器

    // execSQL 执行原生SQL语句
    func execSQL(db *gorm.DB){

    // 将查询SQL的结果映射到指定的单个变量中
    var oneUser User
    result := db.Raw("SELECT * FROM user LIMIT 1").Scan(&oneUser)
    fmt.Println(oneUser)
    fmt.Println(result.Error, result.RowsAffected)

    // 将查询SQL的批量结果映射到列表中
    var users []User
    result = db.Raw("SELECT * FROM user").Scan(&users)
    for _, user := range users {
    fmt.Println(user)
    }
    fmt.Println(result.Error, result.RowsAffected)

    var updateUser User
    result = db.Raw("UPDATE users SET username = ? where id = ?", "toms jobs", "ab6f089b-3272-49b5-858f-a93ed5a43b4f").Scan(&updateUser)
    fmt.Println(updateUser)
    fmt.Println(result.Error, result.RowsAffected)

    // 直接通过Exec函数执行Update操作,不返回任何查询结果?
    result = db.Exec("UPDATE user SET username = ? where id = ?", "toms jobs", "ab6f089b-3272-49b5-858f-a93ed5a43b4f")
    fmt.Println(result.Error, result.RowsAffected)

    // DryRun模式,在不执行的情况下生成SQL及其参数,可以用于准备或测试的SQL
    var tmpUsers []APIUser
    stmt := db.Session(&gorm.Session{DryRun: true}).Model(&User{}).Find(&tmpUsers).Statement
    fmt.Println(stmt.SQL.String())
    fmt.Println(stmt.Vars)
    }
    +

    备注:

    +
      +
    • Scan函数是会将查询SQL的结果映射到定义的变量,如果不需要返回查询结果可以直接使用Exec函数执行原生SQL;
    • +
    • DryRun模式,可以直接生成SQL机器参数,但是不会直接执行;
    • +
    +

    总结

    关于Gorm的CURD日常使用就介绍到这里,如果同学对Gorm感兴趣,可以接去Gorm官网,我这里只是简单介绍一下在日常业务环境经常使用的操作,具体Gorm中文文档地址。

    +

    至于Gorm后面可能还会出几篇文章介绍Gorm模型之间的关联关系,以及关联模式下的CRUD;还有关于Gorm的性能优化,具体能搭配一些常用的插件。

    +

    上述文章的源码:

    +
    +

    https://github.com/libuliduobuqiuqiu/GoDemo/blob/master/GormDemo/gorm_demo.go

    +
    +]]>
    + + Go + + + Go + ORM + Gorm + Mysql + 数据库 + +
    + + Python Socket编程 + /2021/04/25/Python%20Socket%E7%BC%96%E7%A8%8B/ + 背景

    关于Python Socket编程,首先需要了解几个计算机网络的知识,通过以下的几个问题,有助于更好的理解Socket编程的意义,以及整个框架方面的知识:

    +
      +
    • TCP和UDP协议本质上的区别?

      +
      +

      TCP协议,面向连接,可靠,基于字节流的传输层通信协议;UDP协议无连接,不可靠,基于数据包的传输层协议。

      TCP协议在建立连接的过程需要经历三次握手,断开连接则需要经历四次挥手,而这建立连接的过程增加了传输过程中的安全性。
      而建立连接的过程则会消耗系统的资源,消耗更多的时间,而相比较UDP协议传输过程则不会出现这种问题。

      总结来讲,基于TCP协议传输,需要不断的确认对方是否收到信息,从而建立连接(确认过程次数有限制,即三次握手),UDP协议传输则
      不需要确认接收端是否收到信息,只需要将信息发给对方。

      +
      +
    • +
    • TCP/IP协议栈、HTTP协议、Socket之间的区别和联系?

      +
      +

      TCP/IP协议栈就是一系列网络协议,可以分为四层模型来分析:应用层、传输层、网络层、链路层;

      HTTP协议(超文本传输协议)就是在这一协议栈中的应用层协议;HTTP协议简单来说,它的作用就是规范数据的格式,让程序能够方便的识别,并且收发双方都需要遵循同样的协议格式进行数据传输。(应用层的协议也和HTTP协议的作用类似,不一样的是定义不同的数据格式。)

      Socket可以理解为TCP/IP协议栈提供的对外的操作接口,即应用层通过网络协议进行通信的接口。Socket可以使用不同的网络协议进行端对端的通信;

      +
      +
    • +
    • TCP Socket服务器的通信过程?

      +
      +

      Server端:

      建立连接(socket()函数创建socket描述符、bind()函数绑定特定的监听地址(ip+port)、listen()函数监听socket、accept()阻塞等待客户端连接)

      数据交互(read()函数阻塞等待客户端发送数据、write()函数发送给客户端数据)

      Client端:

      建立连接(socket()函数创建socket描述符、connect()函数向指定的监听地址发送连接请求)

      数据交互(wirte()函数发送服务端数据、read()函数足阻塞等待接受服务端发送的数据)

      +
      +
    • +
    • socket和websocket之间的联系?

      +
      +

      webosocket是一种通信协议,不同于HTTP请求,客户端请求服务端资源,服务端响应的通信过程;websocket允许服务端主动
      向客户端推送消息,同时做到客户端和服务端双向通讯的协议。(具体底层原理有待后面实践,暂时未接触)

      +
      +
    • +
    • HTTP,WSGI协议的联系和区别?

      +
      +

      HTTP协议(超文本传输协议),属于TCP/IP协议栈中应用层的协议。用于规范传输数据的格式,是一种客户端和服务端传输的规则。

      WSGI协议则是Python定义的Web服务器和框架程序通信的接口规则。两者联系不大,强行说的话,Python框架程序主要处理的是HTTP请求。

      (后期可以实现一个WSGI协议的Python框架,用于处理HTTP请求的实验。)

      +
      +
    • +
    • 主流Web框架,异步Web框架?

      +
      +

      主流Web框架:Django、Flask

      异步Web框架:Tornado(内置异步模块)、Snaic(Python自带asyncio)、FastAPI(基于Starlette库) 、aiohttp(基于asyncio)

      +
      +
    • +
    • asyncio,aiohttp之间的联系?(异步编程)

      +
      +

      asyncio是一个异步IO库,aiohttp就是基于asyncio的异步HTTP框架(支持客户端/服务端)

      +
      +
    • +
    +

    代码设计

    Python提供了基本的socket模块:

    +
      +
    1. socket模块;提供了标准的BSD Sockets API;
    2. +
    3. socketserver模块:提供了服务器中心类,简化服务器的开发;
    4. +
    +

    TCP Socket服务端

    socket模块:

    +
    # -*- coding: utf-8 -*-
    from socket import socket, AF_INET, SOCK_STREAM

    def echo_handler(sock ,address):
    print("Get Connection from address:", address)

    while True:
    response = sock.recv(8192)
    if not response:
    break
    print(f"Got {response}")
    sock.sendall(response)

    def echo_server(address, back_log=5):
    sock = socket(AF_INET, SOCK_STREAM)
    sock.bind(address)
    sock.listen(back_log)

    while True:
    sock_client, address = sock.accept()
    echo_handler(sock_client, address)

    if __name__ == "__main__":
    echo_server(('localhost', 5000))
    + +

    代码详解:

    +
      +
    • 创建一个基于IPV4和TCP协议的Socket,这里AF_INET指的是使用IPV4协议,SOCK_STREAM指定使用面向流的TCP协议,绑定监听端口,设置等待连接的最大数量
    • +
    • 创建一个永久循环,获取客户端请求的连接,accept()会等待并返回一个客户端的连接;
    • +
    • 连接建立后,等待客户端数据,接受完客户端数据,然后返回数据给客户端,最后关闭连接
    • +
    +

    存在的问题:当出现多个客户端请求时,由于是单个线程会发生阻塞的情况,所以如果需要多线程处理多个客户端请求,可以这样改;

    +
    from threading import Thread

    while True:
    client_sock, address = sock.accept()
    thread = Thread(target=echo_handler, args=(client_sock, address))
    thread.start()
    +

    这样的话,就会在每个客户端请求的时候,生成一个子线程然后处理请求;
    (但是存在一个问题:当突然大量请求连接,消耗系统资源达到上限后,很可能造成程序无法处理后续请求。)

    +

    socketserver模块:

    +
    from socketserver import BaseRequestHandler, TCPServer

    class EchoHandler(BaseRequestHandler):
    def handle(self):
    print("Got Connection From: %s" % str(self.client_address))
    while True:
    msg = self.request.recv(8192)
    if not msg:
    break
    self.request.send(msg)

    if __name__ == "__main__":
    server = TCPServer(("", 5000), EchoHandler)
    server.serve_forever()
    ```

    ```python
    from socketserver import StreamRequestHandler, TCPServer, ThreadingTCPServer
    import time

    class EchoHandler(StreamRequestHandler):
    def handle(self):
    print("Got Connection Address: %s" % str(self.client_address))
    for line in self.rfile:
    print(line)
    self.wfile.write(bytes("hello {}".format(line.decode('utf-8')).encode('utf-8')))

    if __name__ == "__main__":
    serv = ThreadingTCPServer(("", 5000), EchoHandler)
    serv.serve_forever()
    + +

    代码详解:

    +
      +
    • 处理多个客户端,初始化一个ThreadingTCPServer实例,ThreadingTCPServer处理客户端的连接,会为每个客户端创建一个线程进行交互。
    • +
    • 设置绑定的IP地址和端口,以及处理类;
    • +
    • 使用StreamRequestHandler(使用流的请求处理程序类,类似file-like对象,提供标准文件接口简化通信过程),重写里面的handle方法,获取请求数据,返回数据给客户端;
    • +
    +

    TCP Socket客户端

    socket模块:

    +
    # -*- coding: utf-8 -*-
    from socket import socket, AF_INET, SOCK_STREAM
    import time

    def request_handler():
    start_time = time.time()
    sock_client = socket(AF_INET, SOCK_STREAM)
    sock_client.connect(('localhost', 5000))

    book_content = ""
    with open("send_books.txt", "r") as f:
    book_content = f.read()

    content_list = book_content.split("\n")
    for content in content_list:
    if content:
    sock_client.send((content).encode())
    time.sleep(2)
    response = sock_client.recv(8192)
    print(response)

    end_time = time.time()
    print("总共耗时:", end_time-start_time)



    if __name__ == "__main__":
    request_handler()
    + + +

    UDP Socket

    Socket模块:

    +
    from socket import socket, AF_INET, SOCK_DGRAM
    import time

    def time_server(address):
    sock = socket(AF_INET, SOCK_DGRAM)
    sock.bind(address)

    while True:
    msg, addr = sock.recvfrom(8192)
    print('Get message from', addr)
    resp = time.ctime()
    sock.sendto(resp.encode('ascii'), addr)

    if __name__ == "__main__":
    time_server(('', 5000))
    +
    +

    代码不详解,和之前的差不多,注意不同的协议就完事了

    +
    +

    客户端测试:

    +
    from socket import socket, AF_INET, SOCK_DGRAM

    if __name__ == "__main__":
    s = socket(AF_INET, SOCK_DGRAM)
    s.sendto(b'hello', ('localhost', 5000))
    text = s.recvfrom(8192)
    print(text)
    + +

    socketserver模块:

    +
    from socketserver import BaseRequestHandler, UDPServer
    import time


    class TimeHandler(BaseRequestHandler):
    def handle(self):
    print("Got Connection %s".format(str(self.client_address)))
    data = self.request[0]
    print(data)
    msg, sock = self.request
    print(msg)
    data = time.ctime()
    sock.sendto(data.encode('ascii'), self.client_address)

    if __name__ == "__main__":
    u = UDPServer(("localhost", 9999), TimeHandler)
    u.serve_forever()
    +
    +

    代码不在赘述,如果需要多线程处理并发操作可以使用ThreadingUDPServer

    +
    +

    总结

    +

    关于本篇介绍Python Socket编程,大都是皮毛,只是谈到了Python实际处理socket的几个模块,
    关于socket底层方面的知识并未提及,先了解个大概,从实际使用方面出发,在实际使用过程中结合
    计算机网络知识,能够理解socket在整个TCP/IP协议栈中的作用。

    socket和socketserver模块都可以用来编写网络程序,不同的是socketserver省事很多,你可以专注
    业务逻辑,不用去理会socket的各种细节,包括不限于多线程/多进程,接收数据,发送数据,通信过程。

    +
    +]]>
    + + Python + + + Python + Socket + +
    + + Python命令行模块(sys.argv,argparse,click) + /2020/09/12/Python%E5%91%BD%E4%BB%A4%E8%A1%8C%E6%A8%A1%E5%9D%97%EF%BC%88sys.argv,argparse,click%EF%BC%89/ + +

    Python作为一门脚本语言,经常作为脚本接受命令行传入参数,Python接受命令行参数大概有三种方式。因为在日常工作场景会经常使用到,这里对这几种方式进行总结。

    + +

    命令行参数模块

    这里命令行参数模块平时工作中用到最多就是这三种模块:sys.argv,argparse,click。sys.argv和argparse都是内置模块,click则是第三方模块。

    +

    sys.argv模块(内置模块)

    先看一个简单的示例:

    +
    #!/usr/bin/python
    import sys

    def hello(name, age, sex, *args):
    print("Hello, My name is {name}.".format(name=name))
    print("I'm {age} years old.".format(age=age))
    print("I'm a {sex}".format(sex=sex))

    print("Other word:\n{args}".format(args="\n".join(args)))


    if __name__ == "__main__":
    file_name = sys.argv[0]
    name = sys.argv[1]
    age = sys.argv[2]
    sex = sys.argv[3]
    other = sys.argv[4:]
    hello(name, age, sex, *other)
    +

    调用脚本:

    +
    python test_sysargv.py zhangsan 13 man nibi ss
    +

    脚本输出:

    +
    Hello, My name is zhangsan.
    I'm 13 years old.
    I'm a man
    Other word:
    nibi
    ss
    +
    +

    sys.argv模块不难理解,命令参数作为列表传入Python脚本中,argv[0]是脚本的名字,argv[1]则是第一个参数,后面以此类推。所以在脚本中只需要提取列表中的参数即可使用。上面演示的是正确调用Python脚本的情况,下面则是调用失败的情况。

    +
    +

    错误调用脚本:

    +
    python test_sysargv.py zhangsan 13
    +

    错误输出:

    +
    Traceback (most recent call last):
    File "test_sysargv.py", line 16, in <module>
    sex = sys.argv[3]
    IndexError: list index out of range
    +
    +

    关于错误也很好理解,经典的列表索引超出范围,之所列表索引超出范围,没有传入足够的参数。当然你可以使用try…except捕获错误。但是这种做法太过死板,因为在命令行中必须按照脚本规定的参数顺序输入参数,所以这种模块使用一般是针对一些需要的参数比较少并且固定的脚本。

    +
    +

    argparse模块(内置模块)

    同样的先看一个简单的示例:

    +
    #!/usr/bin/python
    import argparse

    parser = argparse.ArgumentParser(description='Test for argparse module.') # 构建命令参数实例
    parser.add_argument("--name", "-n", help="name attribute: 非必要属性")
    parser.add_argument("--age", "-a", help="age attribute: 非必要属性")
    parser.add_argument("--sex", "-s", help="sex attribute: 非必要属性")
    parser.add_argument("--type", "-t", help="type attribute: 非必要属性", required=True)
    args = parser.parse_args()


    def hello(name, age, sex, *args):
    print("Hello, My name is {name}.".format(name=name))
    print("I'm {age} years old.".format(age=age))
    print("I'm a {sex}".format(sex=sex))

    print("Other word:\n{args}".format(args="\n".join(args)))


    if __name__ == "__main__":
    print("Format of transfer file: {type}".format(type=args.type))
    if args.name and args.age and args.sex:
    hello(args.name, args.age, args.sex)
    +

    执行脚本:

    +
    python3 test_argparse.py -t json -n zhangsan -a 13 -s man
    +

    脚本成功输出:

    +
    Format of transfer file: json
    Hello, My name is zhangsan.
    I'm 13 years old.
    I'm a man
    Other word:

    +
    +

    关于argparse模块的使用,首先需要生成一个命令行参数的实例,然后通过对这个对象添加属性,添加需要从命令行获取的参数,包括哪些是必要参数(required=True),哪些非必要参数,同时也可以对每个参数进行帮助提示(help=””)。

    +
    +
    +

    而上面示例中分别添加了四个属性,–name和-n同时可以再命令行中使用,都表示了参数name。ArgumentParser通过parse_ags()方法解析参数,检查命令行,将每个参数转换为适当的类型,所以在脚本中同时也可以使用args.n和args.name获取到参数,相对应的如果没有传入该参数,脚本中则获取到None。

    +
    +

    查看命令行参数之后脚本的帮助提示:

    +
    python3 test_argparse.py -h
    usage: test_argparse.py [-h] [--name NAME] [--age AGE] [--sex SEX] --type TYPE

    Test for argparse module.

    optional arguments:
    -h, --help show this help message and exit
    --name NAME, -n NAME name attribute: 非必要属性
    --age AGE, -a AGE age attribute: 非必要属性
    --sex SEX, -s SEX sex attribute: 非必要属性
    --type TYPE, -t TYPE type attribute: 非必要属性
    +
    +

    另外在添加命令行参数的属性中,还有更多的设置,多余的可以参考Python官方文档,里面都有详细的标注,这里就不展开来讲,总结的就是关于argparse模块的使用非常简便,同时十分人性化,也很符合日常工作的需要。

    +
    +

    click模块

    先开一个简单的使用示例:

    +
    #!/usr/bin/python
    import click

    @click.command()
    @click.option("--name", default="zhangsan", help="name attribute: 非必要属性")
    @click.option("--age", help="age attribute", type=int)
    @click.option("--sex", help="sex attribute")
    @click.option("-t", help="type attribute: 必要属性", required=True)
    def hello(t, name, age, sex, *args):
    print("Format of transfer file: {type}".format(type=t))
    print("Hello, My name is {name}.".format(name=name))
    print("I'm {age} years old.".format(age=age))
    print("I'm a {sex}".format(sex=sex))

    print("Other word:\n{args}".format(args="\n".join(args)))


    if __name__ == "__main__":
    hello()

    +

    执行脚本:

    +
    python3 test_click.py -t 1 --age 13 --sex man
    +

    脚本输出:

    +
    Format of transfer file: 1
    Hello, My name is zhangsan.
    I'm 13 years old.
    I'm a man
    Other word:

    +
    +

    click模块是Flask团队优秀的开源项目,使用方法和argparse模块很相似,同样为命令行封装了大量的方法,使用者只需要专注代码功能的实现。
    click模块和argparse模块不同的地方就是,click模块使用装饰器的方式给函数添加命令行属性,关于装饰器简单来讲就是能够在不修改原有函数的基础上添加功能。虽然使用装饰器但是添加命令行属性的方式和argparse模块很相似,包括options中常用的参数含义也有很多类似的地方。值得注意的就是一开始需要通过command()将函数成为命令行的接口。
    关于的click模块的就大致讲到这里,其余有兴趣的可以再去了解一下。

    +
    +

    总结

    关于这三个模块值得注意的是,尽量贴近自己应用场景去选择,真正的做到自己能够方便使用才是你去使用这些模块的原因。

    +]]>
    + + Python + + + Python + Argv + argparse + cli + +
    + + Python定时任务利器—Apscheduler + /2020/12/07/Python%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1%E5%88%A9%E5%99%A8%E2%80%94Apscheduler/ + 导语

    在工作场景遇到了这么一个场景,就是需要定期去执行一个缓存接口,用于同步设备配置。首先想到的就是Linux上的crontab,可以定期,或者间隔一段时间去执行任务。但是如果你想要把这个定时任务作为一个模块集成到Python项目中,或者想持久化任务,显然crontab不太适用。Python的APScheduler模块能够很好的解决此类问题,所以专门写这篇文章,从简单入门开始记录关于APScheduler最基础的使用场景,以及解决持久化任务的问题,最后结合其他框架深层次定制定时任务模块这几个点入手。

    +

    简单介绍

    先简单介绍一下Apscheduler模块包含的四种组件:

    +
      +
    • Trigger触发器
    • +
    • Job作业
    • +
    • Excutor执行器
    • +
    • Scheduler调度器
    • +
    +

    大概了解了Apscheduler包含的几种概念,现在先来看一下一个简单的示例:

    +
    # -*- coding: utf-8 -*-

    from apscheduler.schedulers.blocking import BlockingScheduler
    import time


    def hello():
    print(time.strftime("%c"))


    if __name__ == "__main__":
    scheduler = BlockingScheduler()
    scheduler.add_job(hello, 'interval', seconds=5)
    scheduler.start()
    +

    示例的输出:

    +
    Thu Dec  3 16:01:20 2020
    Thu Dec 3 16:01:25 2020
    Thu Dec 3 16:01:30 2020
    Thu Dec 3 16:01:35 2020
    Thu Dec 3 16:01:40 2020
    ..........
    +

    这个简单的示例,我们用上面提到几种组件分析一下运行逻辑:

    +
      +
    • 首先是Scheduler调度器,这个示例使用的BlockingScheduler调度器,在官方文档中的解释是,BlockingScheduler适合当你的这个定时任务程序是唯一运行的程序;换言之,则是BlockingScheduler调度器是一个阻塞调度器,当程序运行这种调度器,进程则会阻塞,无法执行其他操作;
    • +
    • 其次是Job作业和触发器,这两个放在一起讲是因为,在定义作业的时候,你就需要选择一个触发器,这里选择的是interval触发器,这种触发器会以固定时间间隔运行作业。换言之,为调度器添加一个hello的工作,并以每5秒的时间间隔执行任务。
    • +
    • 最后就是执行器,默认是ThreadPoolExcutor执行器,他们将任务中可调用对象交给线程池执行操作,等完成操作后,执行器会通知调度程序。
    • +
    +

    内置的三种Trigger触发器类型:

    +
      +
    • date:特定时间仅运行一次作业
    • +
    • interval: 固定的时间间隔内运行一次作业
    • +
    • cron: 在一天内特定的时间定期运行作业
    • +
    +

    常见的Scheduler调度器:

    +
      +
    • BlockingScheduler: 调度程序是流程中唯一运行的东西
    • +
    • BackgroundScheduler: 调度程序在应用程序内部的后台运行时使用
    • +
    • AsyncIOScheduler: 应用程序使用asyncio模块
    • +
    • GeventScheduler: 应用程序使用gevent模块
    • +
    • TornadoScheduler:构建Tornado应用程序时使用
    • +
    • TwistedScheduler: 构建Tornado应用程序时使用
    • +
    • QtScheduler: 在构建QT应用程序时使用
    • +
    +

    常见的JobStore:

    +
      +
    • MemoryJobStore
    • +
    • MongoDBJobStore
    • +
    • SQLAlchemyJobStore
    • +
    • RedisJobStore
    • +
    +

    进阶使用

    通过上面一个简单的示例了解大概的工作流程,以及各个组件在整个流程中的作用,以下的示例是Flask Web框架结合使用Apscheduler定时器,定时执行任务。

    +
    # -*- coding: utf-8 -*-

    from flask import Flask, Blueprint, request
    from apscheduler.executors.pool import ThreadPoolExecutor
    from apscheduler.schedulers.background import BackgroundScheduler
    from apscheduler.jobstores.redis import RedisJobStore
    import time

    app = Flask(__name__)
    executors = {"default": ThreadPoolExecutor(5)}
    default_redis_jobstore = RedisJobStore(db=2,
    jobs_key="apschedulers.default_jobs",
    run_times_key="apschedulers.default_run_times",
    host = '127.0.0.1',
    port = 6379
    )

    scheduler = BackgroundScheduler(executors=executors)
    scheduler.add_jobstore(default_redis_jobstore)
    scheduler.start()

    def say_hello():
    print(time.strftime("%c"))


    @app.route("/get_job", methods=['GET'])
    def get_job():
    if scheduler.get_job("say_hello_test"):
    return "YES"
    else:
    return "NO"

    @app.route("/start_job", methods=["GET"])
    def start_job():
    if not scheduler.get_job("say_hello_test"):
    scheduler.add_job(say_hello, "interval", seconds=5, id="say_hello_test")
    return "Start Scuessfully!"
    else:
    return "Started Failed"

    @app.route("/remove_job", methods=["GET"])
    def remove_job():
    if scheduler.get_job("say_hello_test"):
    scheduler.remove_job("say_hello_test")
    return "Delete Successfully!"
    else:
    return "Delete Failed"


    if __name__ == "__main__":
    app.run(host="127.0.0.1", port=8787, debug=True)
    +
      +
    • 先分析Jobstore,这里使用的是RedisJobstore,将任务序列化存入到Redis数据库中。这里顺便提一下,为什么需要设置作业存储器,原因是当调度器程序崩溃时,仍然能够保留作业,当然选择什么作业存储器,可以根据具体的工作场景,目前主流的mysql,mongodb,redis,SQLite基本都支持;
    • +
    • 然后再看看Scheduler,这里使用的时BackgroundScheduler,因为这里要求调度程序不能阻塞flask程序的正常接收请求,所以选在BackgrounScheduler让它在开始执行任务时是在后台运行的,不会阻塞主线程;
    • +
    • 最后看看工作的逻辑,这里get_job获取作业的状态,查看作业是否存在,start_job则是先判断作业是否启动,然后再决定启动操作,remove_job则是停止作业。而这里的作业定义则是通过interval触发器,每五秒执行一次say_hello任务;
    • +
    +

    总结

    最后总结一下,首先你要设置一个作业存储器用于在调度程序崩溃重新恢复时,还能够在作业存储器中获取到作业继续执行;然后你需要设置一个执行器,这个根据作业的类型,比如时一个CPU密集型的任务,那就可以用进程池执行器,默认是用线程池执行器;最后创建配置调度器,启动调度,可以在启动前添加作业,也可以在启动后添加,删除,获取作业。(在这里需要明白的一点就是应用程序不会直接去操作作业存储器,作业或者执行器,而是调度器提供适当的接口来处理这些接口。)

    ApScheduler是一个不错的定时任务库,能够动态的添加删除,同时也支持不同的触发器类型,这也是它的优势,相反一些如果是静态任务,其实可以用如linux的crontab工具去做定时任务。有关这方面的记录还会持续更新,如果有什么问题,可以提出来,大家一起探讨。

    +]]>
    + + Python + + + Python + Apscheduler + +
    + + 总结技术文章写作技巧 + /2020/07/30/%E6%80%BB%E7%BB%93%E6%8A%80%E6%9C%AF%E6%96%87%E7%AB%A0%E5%86%99%E4%BD%9C%E6%8A%80%E5%B7%A7/ + 1、端正心态

      对于写作不要产生恐惧,起点期望放低点,没有什么事情能一蹴而就,同时可以通过阅读其他的技术文章,模仿行文方式,不断重复练习,对比吸取相关长处,补足自己的缺点。

    +

    2、明确定位

     对于这篇文章需要给什么类型的读者看,需要怎样的技术背景,需要怎样的技术栈,以及相关的项目背景。明确了定位方便切入不同读者的阅读需求。

    +

    3、标题切入

      对于文章的标题需要提供给读者一个很好的切入角度,整篇文章围绕着标题,起到文章的中心作用。

    +

    4、简介与大纲

      针对不同的文章,如果对于一些小项目经验或者工具使用的分享,列提纲和简介能够给读者一目了然,在第一时间的情况下获取到需要的知识信息,简介中我一般喜欢写背景,即项目背景,或者技术背景,通过这些背景能够很好的把读者带到一个阅读环境。提纲的话就因人而异,如果仅仅是比较小型的分享,对于列提纲显得没那么重要。
      而针对大型的技术的分享,对于一些写到比较全的技术分享,就需要通过列提纲对自己的内容进行很好的归纳总结成每个点,同时提纲好处就是能够让读者随时从某个地方切入,省去一些自己知道的环节。

    +

    5、正文写作

      通过提问和问答模式:能够让读者目的明确地进行阅读,同时让读者带着问题去阅读,就能更好的抓住文章的重点和逻辑,同时读者的阅读效果和阅读感受就会更加好。
      通过讲故事的方法:讲故事的话,一般是可以设定某个情节,让读者代入到该场景,通过叙事的模式和节奏,让读者更加容易接受专业知识。

    +

    6、额外

      +
    • 通过合适的段落和句子,减轻读者阅读的压力;
    • +
    • 对于需要结合代码讲清楚专业知识,则需要将代码贴出来,并且在一些关键的代码中需要有清晰的注释;
    • +
    • 对于文章出现的Demo可以附加上demo的链接;
    • +
    • 如果对于一些比较专业的术语或者细节,可以通过类比形式解释清楚问题,如果文字描述不清楚可以选择配图的方式;
    • +
    +

    总结

      +
    • 取好标题,醒目突出中心
    • +
    • 图文并茂,适当配图说明
    • +
    • 篇幅适宜,不宜过短也避免冗长
    • +
    • 格式统一,基本排版规则需要遵守
    • +
    • 细节处理,错别字标点处理正确。
    • +
    +

    参考

    +

    参考文章:http://news.51cto.com/art/201904/594736.htm

    +
    +]]>
    + + 笔记 + + + 笔记 + 写作 + 技术文章 + +
    + + 明朝那些事儿 + /2020/07/30/%E6%98%8E%E6%9C%9D%E9%82%A3%E4%BA%9B%E4%BA%8B/ + +

    关于这个系列的丛书,掰掰手指头估计读了有三次遍了,都是在人生不同的阶段去阅读,或者懵懂,或者无聊,或者失意,每次隔段时间再次捧起这些书的时候,心中各种人物仿佛唤醒你脑中最深处的回忆。唤起那份第一次阅读时带来的惊喜,各种人物形形色色也顿时鲜明起来。
    其实每每一口气读完整套书籍,胸中总有种不吐不快的感觉,就像如鲠在喉,慢慢顺着明朝建立起来时候缓缓地回忆那时候地一切,顿时起了一身的“鸡皮疙瘩”,历史总是默默无语,总是安静的看着这一切事情的发生,大浪淘进了无数的英雄豪杰,他们的归宿总是惊人的相似,之所以我想些这篇文章,真的是有感于里面的许多人物给我带来不同的感受,他们或狡诈,或阴险,或偏执、或聪明、或愚蠢、或可爱、或勇敢、或自负等等等,我只是想把我已经看完的书,看完的一段历史,重新在我的视角里 ,说说自己的看法。

    + +

    朱元璋

    要说自己最开始知道明朝有关的历史,第一个毋庸置疑的是明朝的开国皇帝朱元璋,对于我来说,在没了解这段历史时,单纯的感觉认为这是一个很”励志“的人,为什么呢?因为他出生卑微,却靠着自己独有的战略眼光以及用人智慧,一步步走向属于他的巅峰。属实”励志“,但当我对这位人物有了一定的历史了解之后,我又开始发觉这是一个极其复杂的人。比如经常有人说他天生英雄,其实他是实在走投无路,被人告密之后,还让自己朋友周德兴占了一卦,才觉得跟着造反的。造反的过程中刚开始也十分不顺利,但是你在过程中慢慢的就会发现到朱元璋的过人之处,过人的胸怀,对自己的战略眼光视角的自信,对事物本质的洞悉能力。
    在他打败陈友谅、张士诚的时候,都能为他每次出色战略决定感慨,当然这些也少不了他手下那些骁勇善战的将士,以及足智多谋的谋士。在推翻元朝统治,建立大明之后,慢慢随着岁月的逝去,越来越多的老朋友离开了朱元璋的身边,这里面有很多都是为了自己后代能够统治安稳而解决掉。当他的马皇后最终离开了他的身边之后,他或许真的流泪了,那个在他坎坷流离的生命岁月中一直不放弃不抛弃的跟着他的女人走了,渐渐的也只剩给他一个人了。

    +

    朱棣

    朱棣可能是我在明朝中印象最深刻的一个皇帝,为什么呢?因为他可以说是一个双面皇帝,对自己敌人不择手段,宁杀错不放过,对自己的朋友,百姓都是关怀备至,一副仁君的模样。然后他本身就在军队中长大,练就了一身的指挥打仗的本领,在实战中也是经常出奇制胜。在最开始的时候,朱棣也是不太像造反的,毕竟这种一旦失败就要人头落地的买卖实在不划算,而身边的和尚姚广孝一直在劝说他造反,最后因为朱允炆听从黄子澄、齐泰的建议削藩,最终导致朱棣起兵造反。而让我铭记朱棣的名字,是因为朱棣一手建立了永乐盛世,万邦来朝的繁华景象。这背后是他数次北伐亲征,兢兢业业。以及派遣郑和下西洋传播和平理想的信念。他不是一个好人,但是一个好皇帝。

    +

    未完。。。

    可能因为胸中的那团气突然散了,感觉有许多东西其实没有讲出来,但是仿佛这其中犹如一团乱麻,始终牵绕着我。朱元璋,李善忠,刘伯温,徐达,常遇春,汤和,马皇后,朱棣,朱允炆,黄子澄,齐泰,方孝孺,姚广孝,郑和,朱高煦,朱瞻基,杨士奇,杨荣,杨溥,于谦,朱祁镇…..,这些人仿佛是历史长河中一粒粒沙子,他们任何一个人只是历史选中的角色,但是任何一个却无法阻挡历史的脚步。

    +]]>
    + + 笔记 + + + 笔记 + 写作 + 历史 + +
    + + 浅析Python项目部署 + /2020/12/15/%E6%B5%85%E6%9E%90Python%E9%A1%B9%E7%9B%AE%E9%83%A8%E7%BD%B2/ + 基础理论

    关于Web服务器和应用服务器

    基本概念:

    +
      +
    • Web服务器主要功能就是存储、处理、传递网页,客户端和服务器之间基于HTTP协议进行通信。
    • +
    • 应用服务器主要是处理动态请求,调用相应的对象完成对请求的处理,返回响应的结果。
    • +
    +

    两者之间的区别:

    +
      +
    • Web服务器主要是以HTTP为核心,WEB UI为向导的应用,处理一些静态请求。
    • +
    • 应用服务器虽然也支持HTTP协议,但应用服务器可以通过很多协议为应用程序提供商业逻辑。
    • +
    +

    Python项目部署架构

    在我们平时搭建一个Python Web项目时,比如Django框架的项目,这时候常见的服务端架构:

    +
      +
    1. Nginx服务器作为代理服务器,代理处理静态资源(js,css,图片)请求,以及动态请求(增删改查)转发以及返回处理结果。
    2. +
    3. uWSGI服务器负责接受Nginx服务器,处理过后转发给Django应用,接受Django应用处理返回的信息,在转发给nginx
    4. +
    5. Django应用从uWSGI服务器接收请求,调用相应的业务逻辑,处理数据渲染相应的页面并且返回给uWSGI服务器。
    6. +
    +

    关于cgi、wsgi、uwsgi、http协议

    接下来针对上文提到Django项目部署架构,聊聊这几个协议在这过程中的作用:

    +
      +
    • http协议:客户端程序和Nginx服务器通信就是基于http协议,而Nginx服务器作为一个代理服务器,会根据HTTP请求返回静态资源还是转发动态请求。
    • +
    • cgi协议:cgi协议是外部应用程序和Web服务器之间的接口标准,简而言之,就是规定如何让一个程序与Web服务器程序之间通信。
    • +
    • wsgi协议:基于现存的CGI标准设计,一个Python web框架编写的应用程序和Web服务器之间通信的规范。
    • +
    • uwsgi:uWSGI服务器独有的协议,用于uWSGI服务器和其他Web服务器之间的数据通信
    • +
    +

    关于cgi、fastcgi、php-fpm、FastCGI(额外)

      +
    • CGI:是 Web Server 与 Web Application 之间数据交换的一种协议。
    • +
    • FastCGI:同 CGI,是一种通信协议,但比 CGI 在效率上做了一些优化。同样,SCGI 协议与 FastCGI 类似。
    • +
    • PHP-CGI:是 PHP (Web Application)对 Web Server 提供的 CGI 协议的接口程序。
    • +
    • PHP-FPM:是 PHP(Web Application)对 Web Server 提供的 FastCGI 协议的接口程序,额外还提供了相对智能一些任务管理。
    • +
    +

    关于uWSGI、Nginx服务器

    简单来讲,uWSGI也是一个Web服务器,他虽然同时实现了http、uwsgi、wsgi协议,但是更多被作为应用服务器和应用程序之间进行通信。
    那么这里就有个疑问了,为什么uWSGI都能直接处理http请求了,还需要Nginx服务器?这样处理有几点原因:

    +
      +
    • 首先Nginx服务器在这过程中属于代理服务器的角色,每当一个http请求进来,就需要经过Nginx服务器,而Nginx服务器的优势就在于异步非阻塞的网络模型,它能够在单线程的情况下处理大量的请求,针对处理静态资源请求;而对于动态请求,能够通过缓存的功能,并且配合CDN进行优化,这样能够极大降低系统的负载,降低客户端响应时间。
    • +
    • 其次,Nginx服务器能够进行负载均衡,启用多个后端服务器,通过Nginx对HTTP请求进行分配,这样能够极大优化架构的效率,提高处理的性能。
    • +
    • 最后,Nginx有很多模块支持一些比如白名单,黑名单功能,并且配合keepalive实现高可用的架构.
      总而言之,Nginx服务器在处理http请求上,都有着uWSGI没有的优势,所以在日常部署环境中,经常会使用到Nginx+uWSGI。
    • +
    +

    uWSGI

    安装部署

    uWSGI有两种安装方式,一种是通过pip安装,一种是通过源码安装。这里就简单介绍pip安装方式,源码安装有兴趣可以私下自己了解。

    +
    pip install uwsgi
    + +

    相关文件

    uWSGI服务器在启动的过程中主要涉及到以下这几个文件,其中uwsgi.sock还会涉及到Nginx相关部署的问题,在Nginx配置的时候回继续展开讲。

    +
    (venv) [root@mbb-48 uwsgi]# tree .
    .
    |-- uwsgi.ini # uwsgi配置文件
    |-- uwsgi.log # uwsgi日志文件
    |-- uwsgi.pid # uwsgi运行进程pid
    |-- uwsgi.sock # uwsgi socket
    `-- uwsgi.status # uwsgi状态文件
    + +

    配置示例

    以下配置是自己一个项目中用到的,其中的配置参数都是比较常见通用。

    +
    [uwsgi]
    chdir=/data/Novel/novel_test
    module=novel_test.wsgi:application
    home=/data/Novel/venv
    static-map=/static=/data/Novel/novel_test/static
    threads=8
    http=0.0.0.0:23606
    master=true
    vacuum=true
    thunder-lock=true
    uid=root
    gid=root
    harakiri=30
    post-buffering=4096
    socket=%(chdir)/uwsgi/uwsgi.sock
    stats=%(chdir)/uwsgi/uwsgi.status
    pidfile=%(chdir)/uwsgi/uwsgi.pid
    daemonize=%(chdir)/uwsgi/uwsgi.log
    +

    配置解析

      +
    • chdir: 定义项目的目录
    • +
    • module: 要使用的 WSGI 模块,不同的Python web框架中使用方式也不太一样。
    • +
    • home: 指定Python执行环境,这个参数针对有不同Python运行环境,比如virtualenv创建独立Python环境的情况。
    • +
    • static-map: 映射静态目录
    • +
    • threads: 线程数量
    • +
    • http: 指定启动地址和端口
    • +
    • master: 启用主进程
    • +
    • vacuum: 退出时,清理产生的中间文件(sock,pid,stats)
    • +
    • thunder-lock: 序列化接收的内容
    • +
    • uid: 指定启动用户
    • +
    • gid: 指定启动组
    • +
    • harakiri: 设置服务器响应时间
    • +
    • post-buffering: 启用缓冲
    • +
    • socket: socket文件存放路径
    • +
    • stats: stats文件存放路径
    • +
    • pidfile: pid文件存放路径
    • +
    • daemonize: 日志文件输出文件路径
    • +
    +

    额外配置

      +
    • processes: 进程数量
    • +
    • buffer-size: 设置缓冲大小
    • +
    • listen: 设置监听队列大小(默认100)
    • +
    • max-requests: 每个工作进程请求书的最高上限
    • +
    • procname-prefix-spaced: 工作进程的前缀名
    • +
    • wsgi-file: 指定加载WSGI文件
    • +
    +

    常用命令

    上面介绍了相关常用的配置参数,当设置uWSGI参数后,需要启动指定配置文件,以及有关暂停,重启的动作。

    +
    uwsgi --ini uwsgi.ini   # 启动uWSGI
    uwsgi --stop uwsgi.pid # 暂停uWSGI
    uwsgi --reload uwsgi.pid # 重启uWSGI
    + +

    服务监控

    uWSGI之一Stats服务器机制,它能将uWSGI状态作为一个JSON对象导出一个socket中,只需要像我们前面配置uWSGI配置文件一样,添加stats选项,跟着一个有效的socket地址接口。

    当你配置完毕后,就可以通过客户端连接到指定的socket地址,将会获得一个包含uWSGI内部统计数据的JSON对象。

    +
    uwsgi --connect-and-read uwsgi.status
    +

    执行完这个命令后,读取的返回的结果是一个json字符串,字符串中包括每个线程的状态,整个应用的负载,版本,监听队列等详细的信息。

    +

    uwsgitop查看实时状态

    uwsgitop是一个开源的实时监控uWSGI服务器状态的工具,安装也十分简单:

    +
    pip install uwsgitop
    +

    具体uwsgitop像一个top命令,监听应用程序并且使用socket地址进行调用,查看进程运行状态,运行详细信息:

    +
    uwsgitop uwsgi/uwsgi.status
    uwsgi-2.0.19.1 - Sun Sep 20 15:58:48 2020 - req: 6 - RPS: 0 - lq: 0 - tx: 67.6K
    node: mbb-48 - cwd: /data/Novel/novel_test - uid: 0 - gid: 0 - masterpid: 15109
    WID % PID REQ RPS EXC SIG STATUS AVG RSS VSZ TX ReSpwn HC RunT LastSpwn
    1 100.0 15144 6 0 0 0 idle 15ms 0 0 67.6K 1 0 125.449 15:48:08
    :2 16.7 - 1 0 - - idle - - - - -
    :3 16.7 - 1 0 - - idle - - - - -
    :0 33.3 - 2 0 - - idle - - - - -
    :1 33.3 - 2 0 - - idle - - - - -

    +

    具体有兴趣的同学可以参考github地址:

    +
    +

    https://github.com/xrmx/uwsgitop

    +
    +

    总结

    关于uWSGI服务器相关使用就总结到这里,因为在平时工作中会经常接触到,但是由于没有应用到一些高并发的场景,对于uwsgi调优方面没有多少经验,等待以后遇到此类问题再继续总结性能调优方面的问题。同时有关Nginx方面的配置留到下一篇文章继续讲,未完待续。。。。。

    +

    参考文章:

    +

    https://uwsgi-docs-zh.readthedocs.io/zh_CN/latest/StatsServer.html

    https://uwsgi-docs.readthedocs.io/en/latest/Options.html#harakiri

    https://www.jianshu.com/p/c3b13b5ad3d7

    https://docs.djangoproject.com/zh-hans/2.2/howto/deployment/wsgi/uwsgi/

    https://blog.51cto.com/mbb97/2151933

    +
    +]]>
    + + Python + + + Python + Web开发 + 项目部署 + +
    +
    diff --git a/tags/Apscheduler/index.html b/tags/Apscheduler/index.html new file mode 100644 index 0000000..82254ec --- /dev/null +++ b/tags/Apscheduler/index.html @@ -0,0 +1,435 @@ + + + + + + + + + + + + 标签: Apscheduler | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + +
    +
    +
     Apscheduler +
    +
    + +
    + +
    +
    + 2020 +
    + +
    + +
    + +
    +
    + + + + +
    + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git a/tags/Argv/index.html b/tags/Argv/index.html new file mode 100644 index 0000000..51ead0e --- /dev/null +++ b/tags/Argv/index.html @@ -0,0 +1,435 @@ + + + + + + + + + + + + 标签: Argv | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + +
    +
    +
     Argv +
    +
    + +
    + +
    +
    + 2020 +
    + +
    + +
    + +
    +
    + + + + +
    + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git a/tags/Flask/index.html b/tags/Flask/index.html new file mode 100644 index 0000000..b61e4f9 --- /dev/null +++ b/tags/Flask/index.html @@ -0,0 +1,435 @@ + + + + + + + + + + + + 标签: Flask | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + +
    +
    +
     Flask +
    +
    + +
    + +
    +
    + 2023 +
    + +
    + +
    + +
    +
    + + + + +
    + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git a/tags/Go/index.html b/tags/Go/index.html new file mode 100644 index 0000000..8f3393a --- /dev/null +++ b/tags/Go/index.html @@ -0,0 +1,435 @@ + + + + + + + + + + + + 标签: Go | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + +
    +
    +
     Go +
    +
    + +
    + +
    +
    + 2023 +
    + +
    + +
    + +
    +
    + + + + +
    + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git a/tags/Gorm/index.html b/tags/Gorm/index.html new file mode 100644 index 0000000..6ed7b0a --- /dev/null +++ b/tags/Gorm/index.html @@ -0,0 +1,435 @@ + + + + + + + + + + + + 标签: Gorm | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + +
    +
    +
     Gorm +
    +
    + +
    + +
    +
    + 2023 +
    + +
    + +
    + +
    +
    + + + + +
    + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git a/tags/Mysql/index.html b/tags/Mysql/index.html new file mode 100644 index 0000000..ed5bed6 --- /dev/null +++ b/tags/Mysql/index.html @@ -0,0 +1,435 @@ + + + + + + + + + + + + 标签: Mysql | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + +
    +
    +
     Mysql +
    +
    + +
    + +
    +
    + 2023 +
    + +
    + +
    + +
    +
    + + + + +
    + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git a/tags/ORM/index.html b/tags/ORM/index.html new file mode 100644 index 0000000..fd04aec --- /dev/null +++ b/tags/ORM/index.html @@ -0,0 +1,435 @@ + + + + + + + + + + + + 标签: ORM | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + +
    +
    +
     ORM +
    +
    + +
    + +
    +
    + 2023 +
    + +
    + +
    + +
    +
    + + + + +
    + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git a/tags/Python/index.html b/tags/Python/index.html new file mode 100644 index 0000000..f353d13 --- /dev/null +++ b/tags/Python/index.html @@ -0,0 +1,481 @@ + + + + + + + + + + + + 标签: Python | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + +
    +
    +
     Python +
    +
    + +
    + +
    +
    + 2023 +
    + +
    + +
    +
    + 2021 +
    + +
    + +
    +
    + 2020 +
    + +
    + +
    + +
    +
    + + + + +
    + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git a/tags/Socket/index.html b/tags/Socket/index.html new file mode 100644 index 0000000..bfae0f3 --- /dev/null +++ b/tags/Socket/index.html @@ -0,0 +1,435 @@ + + + + + + + + + + + + 标签: Socket | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + +
    +
    +
     Socket +
    +
    + +
    + +
    +
    + 2021 +
    + +
    + +
    + +
    +
    + + + + +
    + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git "a/tags/Web\345\274\200\345\217\221/index.html" "b/tags/Web\345\274\200\345\217\221/index.html" new file mode 100644 index 0000000..bd3e02f --- /dev/null +++ "b/tags/Web\345\274\200\345\217\221/index.html" @@ -0,0 +1,435 @@ + + + + + + + + + + + + 标签: Web开发 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + +
    +
    +
     Web开发 +
    +
    + +
    + +
    +
    + 2020 +
    + +
    + +
    + +
    +
    + + + + +
    + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git a/tags/argparse/index.html b/tags/argparse/index.html new file mode 100644 index 0000000..33d4874 --- /dev/null +++ b/tags/argparse/index.html @@ -0,0 +1,435 @@ + + + + + + + + + + + + 标签: argparse | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + +
    +
    +
     argparse +
    +
    + +
    + +
    +
    + 2020 +
    + +
    + +
    + +
    +
    + + + + +
    + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git a/tags/cli/index.html b/tags/cli/index.html new file mode 100644 index 0000000..13a71ed --- /dev/null +++ b/tags/cli/index.html @@ -0,0 +1,435 @@ + + + + + + + + + + + + 标签: cli | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + +
    +
    +
     cli +
    +
    + +
    + +
    +
    + 2020 +
    + +
    + +
    + +
    +
    + + + + +
    + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git a/tags/g/index.html b/tags/g/index.html new file mode 100644 index 0000000..fe60620 --- /dev/null +++ b/tags/g/index.html @@ -0,0 +1,435 @@ + + + + + + + + + + + + 标签: g | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + +
    +
    +
     g +
    +
    + +
    + +
    +
    + 2023 +
    + +
    + +
    + +
    +
    + + + + +
    + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git a/tags/index.html b/tags/index.html new file mode 100644 index 0000000..3dc6940 --- /dev/null +++ b/tags/index.html @@ -0,0 +1,412 @@ + + + + + + + + + + + + 标签 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + + + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git "a/tags/\345\206\231\344\275\234/index.html" "b/tags/\345\206\231\344\275\234/index.html" new file mode 100644 index 0000000..16122fd --- /dev/null +++ "b/tags/\345\206\231\344\275\234/index.html" @@ -0,0 +1,442 @@ + + + + + + + + + + + + 标签: 写作 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + +
    +
    +
     写作 +
    +
    + +
    + +
    +
    + 2020 +
    + +
    + +
    + +
    +
    + + + + +
    + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git "a/tags/\345\216\206\345\217\262/index.html" "b/tags/\345\216\206\345\217\262/index.html" new file mode 100644 index 0000000..ba922a7 --- /dev/null +++ "b/tags/\345\216\206\345\217\262/index.html" @@ -0,0 +1,435 @@ + + + + + + + + + + + + 标签: 历史 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + +
    +
    +
     历史 +
    +
    + +
    + +
    +
    + 2020 +
    + +
    + +
    + +
    +
    + + + + +
    + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git "a/tags/\346\212\200\346\234\257\346\226\207\347\253\240/index.html" "b/tags/\346\212\200\346\234\257\346\226\207\347\253\240/index.html" new file mode 100644 index 0000000..85741df --- /dev/null +++ "b/tags/\346\212\200\346\234\257\346\226\207\347\253\240/index.html" @@ -0,0 +1,435 @@ + + + + + + + + + + + + 标签: 技术文章 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + +
    +
    +
     技术文章 +
    +
    + +
    + +
    +
    + 2020 +
    + +
    + +
    + +
    +
    + + + + +
    + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git "a/tags/\346\225\260\346\215\256\345\272\223/index.html" "b/tags/\346\225\260\346\215\256\345\272\223/index.html" new file mode 100644 index 0000000..bb8a7fa --- /dev/null +++ "b/tags/\346\225\260\346\215\256\345\272\223/index.html" @@ -0,0 +1,435 @@ + + + + + + + + + + + + 标签: 数据库 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + +
    +
    +
     数据库 +
    +
    + +
    + +
    +
    + 2023 +
    + +
    + +
    + +
    +
    + + + + +
    + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git "a/tags/\346\272\220\347\240\201\351\230\205\350\257\273/index.html" "b/tags/\346\272\220\347\240\201\351\230\205\350\257\273/index.html" new file mode 100644 index 0000000..958bcd8 --- /dev/null +++ "b/tags/\346\272\220\347\240\201\351\230\205\350\257\273/index.html" @@ -0,0 +1,435 @@ + + + + + + + + + + + + 标签: 源码阅读 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + +
    +
    +
     源码阅读 +
    +
    + +
    + +
    +
    + 2023 +
    + +
    + +
    + +
    +
    + + + + +
    + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git "a/tags/\347\254\224\350\256\260/index.html" "b/tags/\347\254\224\350\256\260/index.html" new file mode 100644 index 0000000..3d3728a --- /dev/null +++ "b/tags/\347\254\224\350\256\260/index.html" @@ -0,0 +1,442 @@ + + + + + + + + + + + + 标签: 笔记 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + +
    +
    +
     笔记 +
    +
    + +
    + +
    +
    + 2020 +
    + +
    + +
    + +
    +
    + + + + +
    + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git "a/tags/\351\241\271\347\233\256\351\203\250\347\275\262/index.html" "b/tags/\351\241\271\347\233\256\351\203\250\347\275\262/index.html" new file mode 100644 index 0000000..bc1e4c5 --- /dev/null +++ "b/tags/\351\241\271\347\233\256\351\203\250\347\275\262/index.html" @@ -0,0 +1,435 @@ + + + + + + + + + + + + 标签: 项目部署 | + + LINSHUKAI'S BLOG + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + +
    + + +
    + + + + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + + +
    +
    +
     项目部署 +
    +
    + +
    + +
    +
    + 2020 +
    + +
    + +
    + +
    +
    + + + + +
    + + + +
    + +
    + +
    + +
    +
    + + + +
    + 由 Hexo 驱动 & 主题 Keep +
    + + + + + + +
    + + + + + 访客数 + + + + + + + 访问量 + + + +
    +
    +
    + +
    +
    + + + + + +
    +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + + +
    • + +
    • +
    + +
      + + + + +
    • + +
    • + +
    • + + +
    • +
    +
    + +
    + + +
    + +
    + + + + +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +