93 |
--------------------------------------------------------------------------------
/docs/个人申请总结/环境科学与工程学院/环境科学与工程/README.md:
--------------------------------------------------------------------------------
1 | ## 环境科学与工程
2 |
3 | #### PhD Programs:
4 |
5 | To be continued.
6 |
7 | #### Master Programs:
8 |
9 | To be continued.
10 |
11 | ##### 联培:
12 |
13 | To be continued.
14 |
15 | ##### 直博:
16 |
17 | To be continued.
18 |
19 | ##### 保研:
20 |
21 | To be continued.
22 |
23 | ##### 考研:
24 |
25 | To be continued.
26 |
27 | ##### 就业:
28 |
29 | To be continued.
30 |
--------------------------------------------------------------------------------
/docs/个人申请总结/生物医学工程系/README.md:
--------------------------------------------------------------------------------
1 | ## 生物医学工程
2 |
3 | #### PhD Programs:
4 |
5 | To be continued.
6 |
7 | #### Master Programs:
8 |
9 | - [15级-隋昌祥 Duke](个人申请总结/生物医学工程系/[US]-15-隋昌祥.md)
10 |
11 | ##### 联培:
12 |
13 | To be continued.
14 |
15 | ##### 直博:
16 |
17 | To be continued.
18 |
19 | ##### 保研:
20 |
21 | To be continued.
22 |
23 | ##### 考研:
24 |
25 | To be continued.
26 |
27 | ##### 就业:
28 |
29 | To be continued.
30 |
--------------------------------------------------------------------------------
/docs/个人申请总结/生物医学工程系/[US]-15-隋昌祥.md:
--------------------------------------------------------------------------------
1 | # \[US\]-15-隋昌祥 MS in Biomedical Engineering @ Duke University
2 |
3 | ## 基本背景
4 |
5 | > 三维: GPA 3.58/4, Ranking: 5/20;
6 | >
7 | > TOEFL: 94 (24 23 22 25), 97 (29 28 18 22);
8 | >
9 | > GRE: 154V + 167Q + 3AW
10 |
11 | ## 申请结果
12 |
13 | 最终录取学校:MS in Biomedical Engineering @ Duke University
14 |
15 | 其他录取结果:
16 |
17 | * **Offer\(0\):**
18 | * 暂无。
19 | * **AD\(2\):**
20 | * \[MS @ CMU\]
21 | * \[MS @ Washington in St. Louis\]
22 | * **Rej\(0\)**:
23 | * 暂无。
24 |
25 | ## 申请心得
26 |
27 | 1. 本科GPA
28 | 刚上大一时,很难适应,第一学期gpa 2.86,心态蛮崩的,跟学长和辅导员聊了很多,学长都觉得可以后期拉上来但需要很大努力,毕竟大一学分很多且都是必修课,不过在辅导员告诉我进步空间也是高校看重的一部分时还是给了我很大鼓励。而对我帮助最大的还是学习伙伴的出现,无论是从大一下认识创建这个网站的阎大佬到与桥神以及姜神四人建立第一个学习小组,还是大三与生医工系的天一海哥子潇一起复习,都对我整个大学生涯有着非常深远的影响。也是他们的出现,让我重拾信心,将大二大三的gpa一度拉到3.8,拿到了致仁书院大会的最佳进步奖和最佳学习小组奖以及两次校二等奖学金。3.58的总gpa也许对于phd申请还是比较困难了点,但MS的申请尤其是生医工领域其实也算绰绰有余了。
29 | 2. 交流经历
30 | 共参加了两次境外学习交流,都是学校的合作项目,一次是大一暑假的Gatech为期一月的文化课交流,一次是大三暑假的UCInspire暑研项目。个人觉得大一暑假的交流对申请并没有什么用处,但对自己了解美国生活,决定未来是否去美国深造有很大的意义。大三暑假的UCInspire暑研无疑是我申请的最关键一笔,首先在高中大学直系学长朱志凯的推荐下,我有幸参加到美国UCI生医工系大牛陈忠平教授的实验室,因为教授碰巧暑假休年假回国了,在只是开营典礼见过一面的情况下,我每一到两周就发一封工作汇报让教授对我留下了不错的印象,而师姐的帮助更是让我最后拿到了教授的推荐信,没有这封推荐信,凭我自己的科研经历和学习成绩也许美国top50大学都很难进入。说到这就必须提下我的大学科研部分,说实话,我一共进入两个实验室,时间都不长,为了gpa我一定程度上放弃了科研,paper也只有一篇八作的挂名都不忍心放到简历上。这一点希望以后的学弟学妹千万不要向我学习,科研经历还是非常重要的,薄弱的科研和gpa也是我不考虑本科申phd的原因之一。
31 | 注:暑研及推荐人的重要程度比较在[CS-15-阎相易](海外交流/暑研/在计系,暑研对北美申请重要性的讨论.md)的介绍中非常清楚,我也就不需要多提了
32 |
33 | 3. 语言成绩
34 | 在潜移默化灌输了美国top20必须100+320的思想后,很多人对我仅仅97的t都感到疑惑,其实我本来也是没报希望进top20的,只是为了买彩票报着试试看的,因为Duke对国际生的t标准只有90,但我97的托福口语又只有18,所以我索性将两次托福的分一起送了过去进行互补,有没有作用我不太清楚,但我在3个月三战GRE后到了申请的最后两个月,也实在没有了三战t的勇气。而抱着能再回UCI与师姐共事的念头也让我心理上不感到丝毫遗憾,甚至还觉得这样的t起码不会给学校觉得我把他们作为保底而拒绝我的契机。
35 |
36 | 4. 关于中介
37 | 很多学弟学妹也来咨询我要不要报中介的问题,其实我个人是觉得没有必要的,但我还是报了,也把我的中介介绍给了他们,其中一个主要原因是,省事:其一中介可以根据历届案例对你进行好的选校定位,选择对的学校对最后的offer有着决定性的影响,当然这一点本系的学长也可以做到。其二中介的一体化服务以及资源确实帮你省去了不少时间精力也避免了很多错误,尤其对于父母关心你升学的家庭来说,中介的实时汇报无疑让他们感到非常舒心。当然如果只是为了文书的话,就没有什么必要了,中介的文书只能说是到位但不出彩,而且每一所学校框架也都是完全一样的,帮你申一所与帮你申100所并没有任何区别。
38 |
39 | 5. 关于生医工
40 | 很多人会说这个专业什么都会,又什么都不会,由于它的过分交叉全面性,但如果真的没有用,创建它还有什么意义呢。本科本来就是一个发现自己兴趣的地方,真正想钻研留给研究生博士生就好了,生医工集生物电子材料计算机医学于一身,首先是一个发现兴趣的好地方,其次每个生医工人都有自己的一个研究方向,比如纳米探针、医学成像、生物材料、心血管支架等等,说学的宽泛只是没有真正找到自己的热爱的领域,那不管是在什么专业都是很可悲的。在美国,生医工是一个非常热门且有价值的专业,Duke是世界第一个创建生物医学工程系的学校,在遍阅历届生医工教授对人类的贡献后,我能深切体会到这一点。宽泛的说多了也没用,说点现实的,由于cs、ee、me、化工等专业在美国ms,phd申请中的饱和,一半的这些专业申请者开始转入bme的行列,好申只是其一,生医工广博的包容性更是令人青睐的因素,可以预见几年之后,这个专业的申请难度将不比cs低,所以现在还有机会申请的小伙伴一定要抓紧机会哦~
41 |
--------------------------------------------------------------------------------
/docs/个人申请总结/生物系/README.md:
--------------------------------------------------------------------------------
1 | ## 生物系
2 |
3 | - [生物信息学](个人申请总结/生物系/生物信息学/README.md)
4 |
5 | - [生物技术](个人申请总结/生物系/生物技术/README.md)
6 |
7 | - [生物科学](个人申请总结/生物系/生物科学/README.md)
8 |
--------------------------------------------------------------------------------
/docs/个人申请总结/生物系/生物信息学/README.md:
--------------------------------------------------------------------------------
1 | ## 生物信息学
2 |
3 | #### PhD Programs:
4 |
5 | - [15级-孙力 UPitt](个人申请总结/生物系/生物信息学/[US]-15-孙力.md)
6 |
7 | #### Master Programs:
8 |
9 | To be continued.
10 |
11 | ##### 联培:
12 |
13 | To be continued.
14 |
15 | ##### 直博:
16 |
17 | To be continued.
18 |
19 | ##### 保研:
20 |
21 | To be continued.
22 |
23 | ##### 考研:
24 |
25 | To be continued.
26 |
27 | ##### 就业:
28 |
29 | To be continued.
30 |
--------------------------------------------------------------------------------
/docs/个人申请总结/生物系/生物信息学/[US]-15-孙力.md:
--------------------------------------------------------------------------------
1 | # \[US\]-15-孙力 PhD in Intelligent Systems @ UPitt
2 |
3 | ## 基本背景
4 |
5 | > GPA 3.86/4.0, Ranking: 2/22;
6 | >
7 | > TOEFL: 104\(Speaking 22\);
8 | >
9 | > GRE: 156V + 170Q + 3.5AW
10 | >
11 | > 其它: 国家奖学金,两篇水会一作,一篇期刊挂名
12 |
13 | ## 申请结果
14 |
15 | * **Offer\(5\):**
16 | * \[Intelligent Systems PhD @ University of Pittsburgh\]
17 | * \[Biomedical Informatics PhD @ Stony Brook\]
18 | * \[Biomedical Informatics PhD @ ASU\]
19 | * \[CS PhD @ KAUST\]
20 | * \[CS PhD @ UT Arlington\]
21 |
22 | * **Rej\(5\)**:
23 | * \[CS PhD @ UVa\]
24 | * \[CS PhD @ CUHK\]
25 | * \[CS Master @ UBC\]
26 | * \[CS Master @ University of Toronto\]
27 | * \[BME PhD @ Cornell\]
28 |
29 | ## 经历简介
30 |
31 | #### 大一大二: 上一站清晨
32 |
33 | 大一暑假时我加入了陈炜教授的实验室,做了一点微小的工作,积累了一些数据分析与Coding方向的经验。我选择生物信息专业,是因为我对生命健康领域比较感兴趣,同时觉得交叉领域比较容易做出成绩。但后来我发现生命系统是非常复杂的,没有比较好的数理基础和前沿的技术手段分析数据很可能只是scratch the scrface。 (当然也可能是由于我理解不深刻,比较naive) 同时我觉得我的Coding水平还很有限。 在陈炜老师实验室我结识了李仲潇学长,他给我安利了网课CS231n,还给我实力劝退了一波。 我当时觉得怎么有这么牛逼的技术,同时也受到万里大佬的长期熏陶,就被CV方向圈粉了。
34 |
35 | #### 大三: 新月
36 | 大三时有幸能和[松涛](个人申请总结/计算机科学与工程系/[CN]-15-章松涛.md)一起做一些Biomedical Imaging方向的项目,运气还行中了两篇MICCAI Workshops。 同时我还参加了MICCAI BraTS Challenge, 算是Biomedical Imaging领域里Top Challenges之一了。 这段时间又做了一些微小的工作,运气爆表在Survival Prediction Task上拿了2nd Place (also Top 5 in Tumor Segmentaton Task)。 然后获邀去西班牙参加MICCAI作Spotlight Presentation, 同时拿了Intel AI的Award。 在会上结识了一帮顶会大佬,感觉自己还是需要继续努力。
37 |
38 | #### 申请季: 我不曾懂得的苍凉
39 | 我深知CV/AI方向申请竞争激烈,哪怕是Biomedical Imaging子方向,同时由于暑研失误没有去到好的实验室,感觉申请前景很不乐观。 我觉得以我的辣鸡条件更需要找到match的教授,主动套磁。 所以我查过了所有在MICCAI 2018发过文章的教授,同时我希望他曾经发过CVPR等顶会(至于学校排名不敢期望太高)。 在确定了一个hunting list之后,我就开始一一发邮件套磁。 那段时间感觉很焦虑,每天都在一亩三分地论坛寻求安慰。 一方面由于CS基础薄弱面跪了很想去的CUHK某教授,另一方面因为催的太急有封推荐信内容可能一般。 好在最终还是撑过了那段时间,另请其他人写了推荐信,同时在一些面试中得到了积极回复。 (感谢所有给我Offer的教授)
40 |
41 | #### 晨光: 我们的新开始
42 | 三月中旬的时候,终于收到了期盼已久的匹兹堡大学的Offer,申请季进入了尾声。 故事结束之际,我想圆一个梦想: 去MSRA。 通过联系背景比较match的mentor, 加上强势动情走心高效的内推,最后竟然让我混进去了。 在这里,再次感受到了扎实的数理基础的重要性,还有双商被按在地上摩擦的快感哈哈。
43 |
44 | ## 申请心得
45 |
46 | #### 关于英语
47 | 对于想出国的同学,早考TOEFL/GRE还是很重要的。 周围有些同学其他条件都很好,却被英语卡住了,感觉很可惜。 考英语也要留足时间,我自己考了一次雅思两次托福口语才过线呜呜。
48 |
49 | #### 关于暑研
50 | 我觉得暑研要选和自己方向match,自己想去的实验室。 我当时尽管暑研去了JHU, 但是感觉方向不太喜欢,所以申请时少了一个保底的选项,压力也大了好多。
51 |
52 | #### 关于套磁
53 | 我觉得申请PhD的时候,套磁还是很重要的。 教授招PhD的时候肯定优先考虑自己熟悉的Candidate。 Faculty Recommendation 是让自己从 Candidate Pool上岸的重要因素。
54 |
55 | ## 鸣谢
56 | 在学习与生活中,我得到了很多老师和同学的帮助,伴随我度过了充实的大学四年。 十分感谢!
57 |
58 | 如果有其他问题,欢迎联系: v-sunli@microsoft.com
59 |
--------------------------------------------------------------------------------
/docs/个人申请总结/生物系/生物技术/README.md:
--------------------------------------------------------------------------------
1 | ## 生物技术
2 |
3 | #### PhD Programs:
4 |
5 | To be continued.
6 |
7 | #### Master Programs:
8 |
9 | To be continued.
10 |
11 | ##### 联培:
12 |
13 | To be continued.
14 |
15 | ##### 直博:
16 |
17 | To be continued.
18 |
19 | ##### 保研:
20 |
21 | To be continued.
22 |
23 | ##### 考研:
24 |
25 | To be continued.
26 |
27 | ##### 就业:
28 |
29 | To be continued.
30 |
--------------------------------------------------------------------------------
/docs/个人申请总结/生物系/生物科学/README.md:
--------------------------------------------------------------------------------
1 | ## 生物科学
2 |
3 | #### PhD Programs:
4 |
5 | To be continued.
6 |
7 | #### Master Programs:
8 |
9 | To be continued.
10 |
11 | ##### 联培:
12 |
13 | To be continued.
14 |
15 | ##### 直博:
16 |
17 | To be continued.
18 |
19 | ##### 保研:
20 |
21 | To be continued.
22 |
23 | ##### 考研:
24 |
25 | To be continued.
26 |
27 | ##### 就业:
28 |
29 | To be continued.
30 |
--------------------------------------------------------------------------------
/docs/个人申请总结/电子与电气工程系/README.md:
--------------------------------------------------------------------------------
1 | ## 电子与电气工程系
2 |
3 | - [信息工程](个人申请总结/电子与电气工程系/信息工程/README.md)
4 |
5 | - [通信工程](个人申请总结/电子与电气工程系/通信工程/README.md)
6 |
7 | - [光电信息科学与工程](个人申请总结/电子与电气工程系/光电信息科学与工程/README.md)
8 |
9 | - [微电子科学与工程](个人申请总结/电子与电气工程系/微电子科学与工程/README.md)
10 |
--------------------------------------------------------------------------------
/docs/个人申请总结/电子与电气工程系/信息工程/README.md:
--------------------------------------------------------------------------------
1 | ## 信息工程
2 |
3 | #### PhD Programs:
4 |
5 | To be continued.
6 |
7 | #### Master Programs:
8 |
9 | To be continued.
10 |
11 | ##### 联培:
12 |
13 | To be continued.
14 |
15 | ##### 直博:
16 |
17 | To be continued.
18 |
19 | ##### 保研:
20 |
21 | To be continued.
22 |
23 | ##### 考研:
24 |
25 | To be continued.
26 |
27 | ##### 就业:
28 |
29 | To be continued.
30 |
--------------------------------------------------------------------------------
/docs/个人申请总结/电子与电气工程系/光电信息科学与工程/README.md:
--------------------------------------------------------------------------------
1 | ## 光电信息科学与工程
2 |
3 | #### PhD Programs:
4 |
5 | To be continued.
6 |
7 | #### Master Programs:
8 |
9 | To be continued.
10 |
11 | ##### 联培:
12 |
13 | To be continued.
14 |
15 | ##### 直博:
16 |
17 | To be continued.
18 |
19 | ##### 保研:
20 |
21 | To be continued.
22 |
23 | ##### 考研:
24 |
25 | To be continued.
26 |
27 | ##### 就业:
28 |
29 | To be continued.
30 |
--------------------------------------------------------------------------------
/docs/个人申请总结/电子与电气工程系/微电子科学与工程/README.md:
--------------------------------------------------------------------------------
1 | ## 微电子科学与工程
2 |
3 | #### PhD Programs:
4 |
5 | - [15级-郭欣格 NUS/东京大学](个人申请总结/电子与电气工程系/微电子科学与工程/[SG_JP]-15-郭欣格.md)
6 | - [15级-袁锦东 UC Irvine](个人申请总结/电子与电气工程系/微电子科学与工程/[US]-15-袁锦东.md)
7 |
8 | #### Master Programs:
9 |
10 | To be continued.
11 |
12 | ##### 联培:
13 |
14 | To be continued.
15 |
16 | ##### 直博:
17 |
18 | To be continued.
19 |
20 | ##### 保研:
21 |
22 | To be continued.
23 |
24 | ##### 考研:
25 |
26 | To be continued.
27 |
28 | ##### 就业:
29 |
30 | To be continued.
31 |
--------------------------------------------------------------------------------
/docs/个人申请总结/电子与电气工程系/微电子科学与工程/[SG_JP]-15-郭欣格.md:
--------------------------------------------------------------------------------
1 | # \[SG/JP\]15-郭欣格-NUS-Ph.D / 东京大学-修士
2 |
3 | ## 基本背景
4 |
5 | > 三维: GPA 3.83/4, Ranking 1/43 ;
6 | >
7 | > TOEFL: 107 (RLSW 29-28-23-27\);
8 | >
9 | > GRE: No ⊙▽⊙
10 |
11 | ## 申请结果
12 |
13 | 录取学校:
14 |
15 | >[Ph.D] NUS ECE
16 | >
17 | >[修士] 东京大学 ME (Utokyo Fellowship)
18 |
19 |
20 | ## 申请心得
21 |
22 | * 一些小Tips(血泪史):
23 |
24 | * (申请前) 抓住机会在教授面前刷脸:
25 |
26 | > 千万别把所有的宝都压在申请季来临后的邮件套磁上...
27 |
28 | 我自己在大三下的时候有一次出国开会的机会。当时老板特意叮嘱我要多和教授聊聊天,在他们pre完之后积极提问。然而我……很自闭(怂  ̄□ ̄||)。一连四天都在会场上当小透明,想着到了申请季直接给他们发邮件也不是没有机会……然而…等到了申请季,教授邮箱里的套磁信真的数都数不过来,可能自己发的邮件连标题都没能被看到就被埋没在了角落中……在申请季开始前,如果发现有机会可以和自己感兴趣的教授直接接触,一定要做好充足的准备(多看看他近期的论文等),然后抓住和他面对面交流,提问,展示自己的机会,并于后期用邮件积极保持联系,自认为能给申请不小的帮助。
29 |
30 |
31 |
32 | * (申请时) 多运用网络资源:
33 |
34 | > 在我自己看来,申请季就是一个和世界各地的学生拼**资源**的过程,包括个人资源,老板资源,还有从别的地方获取来的属于他人的资源,等。虽然前两种最为重要,但是在申请季难以短时提高。第三种却仍然能带来很多的可能性。
35 |
36 | 相对于其他老牌学校而言,我科目前的相关资源还是较少,所以除了加入我校的各大申请群外,大家还可以积极的去网上的相关论坛检索些有用的信息,例如寄托天下,一亩三分地等。了解下前人的经验和他们走过的弯路,来提高自己的申请效率与成功率。除此之外,别的大学的《飞跃手册》在网上也是直接百度就能下到的资源,我自己当时看的就是中科大的《飞跃手册》。PS:一些时候可能还会有些意外收获,我就阴差阳错的加上了新加坡和东京两边套磁的教授组里的学长学姐,提前询问了教授的相关信息,从而能更有针对性的发Email和面试。
37 |
38 | * 自主申请方面:
39 |
40 | * 申请材料:
41 |
42 | > 申请材料记得要**提前**准备,**仔细**核对。
43 |
44 | 虽然是很简单的道理,但是真的有必要拿出来强调(因为自己就是个反例……)。各个学校要求的材料往往各不相同,种类繁多,甚至还有些学校会需要些很“诡异”的材料(高中毕业证明,高考成绩证明等……)。越早准备,了解,才不至于到时候手忙脚乱,提心吊胆,生怕自己又哪边忘了啥。除此之外,PS,Research Proposal等文书写作所花的时间往往也比我们预料的要长很多,所以尽量尽量去**提前**准备,留更多的时间去逐条核对,精心润色。我自己去年因为拖延症就是在24:00 DDL前才交的东京大学的申请材料,结果申请的系都填错了(下意识的填了自己的电子系)。得亏对面的秘书十分的尽职+友善,主动发邮件联系我更改…………不然现在村委楼都没了,也不知道该找个啥地方跳……
45 |
46 | * NUS方面:
47 |
48 | > 这个……还真的没什么好说的……就是……如果没有申别的学校的话,等结果会等的很慌……。
49 |
50 | NUS ECE需要官方的checklist上要求提交的材料很少,不过我还是自己多提交了论文,RS,PS,CV等方面的内容。NUS ECE今年出结果真的奇慢,往年十二月,一月就有消息的面试,今年三月才开始。申了美帝那边的可能有些都已经接收offer了……相关申请流程,表格填写在网上也都有十分详细的资料,想不出来什么需要特别提示的地方,就不多说了。
51 |
52 | * 东京大学方面:
53 |
54 | > 我申的是东大的IME项目,申请**不需要日语**
55 | >
56 | > PS:得从修士开始念,本科不能直接申Ph.D。
57 |
58 | 东大的流程和别的学校不太一样,官方是禁止学生提前套磁教授的。大致流程如下:
59 | * 首先要在他们的官方申请页面上提交所需的申请资料,待过审以后,会收到东大方面的“申请成功”的邮件通知。申请时填写两名自己心仪的教授。
60 |
61 | * 之后,官方会把你提交的材料直接送给你志愿一填写的教授,让他审阅,并需要在两周内给出回复。在此阶段,教授一般会主动联系你面试。(不过当然还是主动联系他比较好)
62 |
63 | * 若志愿一教授同意你的申请,则需要把在步骤一中填写的资料打印出来,寄至东京大学,以完成整个申请流程。若志愿一教授拒绝,则会转交给志愿二教授。
64 |
65 | * 在学校收到你寄的材料并确认无误后,会收到相应的邮件,此后需等待委员会的进一步审核,至次年二月公布结果。
66 |
67 | * 奖学金结果和录取结果会分开公布。(也可发邮件给秘书询问)
68 |
69 | * 非免试系科在入学前还会有个入学考试,没考过的话身份会由Graduate Student变成Research Student……
70 |
71 | 可能有人会对我跨专业申了东大的机械系比较感兴趣,这里也简单提一下:因为套磁的教授在机械系,我就顺便去机械系了……
72 |
73 | > 暂时想不起来别的比较想要分享的内容啦,以后想起来还会再继续更新,大家有别的问题的话也可以直接加我QQ聊,祝申请顺利!ᕕ(ᐛ)ᕗ
74 |
--------------------------------------------------------------------------------
/docs/个人申请总结/电子与电气工程系/通信工程/README.md:
--------------------------------------------------------------------------------
1 | ## 通信工程
2 |
3 | #### PhD Programs:
4 |
5 | - [15级-钟文韶 UIC](个人申请总结/电子与电气工程系/通信工程/[US]-15-钟文韶.md)
6 |
7 | #### Master Programs:
8 |
9 | To be continued.
10 |
11 | ##### 联培:
12 |
13 | To be continued.
14 |
15 | ##### 直博:
16 |
17 | To be continued.
18 |
19 | ##### 保研:
20 |
21 | To be continued.
22 |
23 | ##### 考研:
24 |
25 | To be continued.
26 |
27 | ##### 就业:
28 |
29 | To be continued.
30 |
--------------------------------------------------------------------------------
/docs/个人申请总结/计算机科学与工程系/README.md:
--------------------------------------------------------------------------------
1 | ## 计算机科学与技术
2 |
3 | #### PhD Programs:
4 |
5 | - [15级-阎相易 UC Irvine](个人申请总结/计算机科学与工程系/[US]-15-阎相易.md)
6 | - [15级-谢丹宁 Purdue](个人申请总结/计算机科学与工程系/[US]-15-谢丹宁.md) 内含PhD面经!
7 |
8 | #### Master Programs:
9 |
10 | - [15级-方一栋 UW-Madison](个人申请总结/计算机科学与工程系/[US]-15-方一栋.md)
11 |
12 | ##### 联培:
13 |
14 | To be continued.
15 |
16 | ##### 直博:
17 |
18 | To be continued.
19 |
20 | ##### 保研:
21 |
22 | - [15级-章松涛 PKU](个人申请总结/计算机科学与工程系/[CN]-15-章松涛.md)
23 | - [15级-李子强 SUSTech](个人申请总结/计算机科学与工程系/[CN]-15-李子强.md)
24 |
25 | ##### 考研:
26 |
27 | To be continued.
28 |
29 | ##### 就业:
30 |
31 | - [15级-郑艺林 蚂蚁金服](个人申请总结/计算机科学与工程系/[CN]-15-郑艺林.md)
32 |
--------------------------------------------------------------------------------
/docs/个人申请总结/计算机科学与工程系/[CN]-15-李子强.md:
--------------------------------------------------------------------------------
1 | # \[CN\]-15-李子强--MS in SUSTech
2 |
3 | ## 基本背景
4 |
5 | > 三维: GPA 3.63/4 ; Ranking:19/98;
6 |
7 | ## 申请结果
8 |
9 | 最终录取学校:南方科技大学-保研
10 |
11 | 有问题可以邮件联系我✉️ : 11510352 AT mail.sustech.edu.cn
12 |
13 | ## 一点制度更新提醒
14 |
15 | 1. 2019届起,没有联合培养的境内硕士,而且由 **2年** 改为,只有 **3年** 学术型硕士。
16 | 2. 目前录取的硕士研究生 **计算机科学与工程系**(作为学院) 在 **电子科学与技术** 专业下招生,并非是 **计算机科学与技术** 专业下。
17 |
18 |
19 | ## 保研流程
20 |
21 | 按照时间从先到后的顺序
22 |
23 | 1. 【4-6月】准备申请材料,申请参加夏令营
24 | 2. 【7-8月】参加夏令营(人话就是参观学校环境,以及提前考核和面试)
25 | 3. 【7-9月】通过夏令营筛选 -- 即获取 目标研究生学校的接收资格(**A**)
26 | 4. 【9-10月】获取 本科学校的“保研”(推荐优秀应届本科毕业生免试攻读硕士学位研究生)资格 -- 即获取 本科学校的推送资格(**B**)
27 |
28 | 当且仅当同时获取 **A** 和 **B** 2个资格,才可以完成保研流程,缺一不可。由此可以得出,学生可以获得多个 **A** 资格,然而 **B** 资格仅能由本科学校给出。
29 |
30 | 然而本校保研,即目标研究生学校和本科学校是同一所学校。
31 |
32 | ## 心路历程
33 |
34 | 大三上我基本上给自己选了2条路:境外升学 / 就业。第一条路由于种种个人原因,耽误地比较多,同时 TOEFL 和 GRE 没有准备充分;在准备的同时,我准备进入进入企业看一看,所以大三-大四暑假和大四秋季学期,我在 NVIDIA 实习 6个月 ,虽然做的是测试工程师,但是干的却是全栈的活。公司氛围很融洽,劳工福利也照顾的不错,学习到了一个正规企业应该是怎么样子:如何互相合作,相互沟通,工作规范,对待工作的态度等等。
35 |
36 | 为了给我自己一个机会,暑假抽空请假回来参加了夏令营,夏令营一般是3 - 4 天,重要的只有 笔试,面试,听介绍套磁。本校的套磁方便很多,就是直接发邮件,约去教授办公室聊最方便,笔试和面试大约占据 2 天,其他活动看需求可以请假。不像隔壁电子系,本校免笔试直接按照之前的成绩计算,计算机系还是有笔试的,考查范围基本上是4大专业课:数据结构与算法,数据库,计算机网络,操作系统;面试的话其实就聊学术经历和实习经历,以及自己的想法,这个因人而异,把握积极向上的主旨,表现出严谨专业的学术态度。
37 |
38 | 大四开学之后,我要在保研 / 联培 / 就业 / 自主申请 中选择,一是不太想马上本科就业,二以我学业成绩申请MSc(Master in Science)应该是没问题的,但是联培 / Phd / MS(MPhil) 还是压力比较大,经过各方考量选择了3年学硕。
39 |
40 | #### 一些教训
41 | * 如果想境外升学,早考英语,推迟实习。实习相对占用时间比较多。
42 |
--------------------------------------------------------------------------------
/docs/个人申请总结/计算机科学与工程系/[CN]-15-章松涛.md:
--------------------------------------------------------------------------------
1 | # \[CN\]-15-章松涛--MS in PKU
2 |
3 | ## 基本背景
4 |
5 | > 三维: GPA 3.67/4 ; Ranking:15/98;
6 | >
7 | > 两篇共同一作论文(医学影像辅助诊断方向)
8 | >
9 | > 没有TOFEL/GRE 成绩
10 | >
11 |
12 | ## 申请结果
13 |
14 | 最终录取学校:北京大学+创新菁英计划+硕士
15 |
16 | 其他录取结果:
17 |
18 | * **Offer\(1\):**
19 | * \[清华大学深研院+精准医学与公共健康+硕士\]
20 | * **AD\(0\):**
21 | * \[学校名称+项目名称\] 若干
22 | * **Rej\(1\)**:
23 | * \[清华大学+叉院+硕士]
24 |
25 | ## 申请心得
26 | #### 夏令营申请
27 | 国内保研夏令营最看重的是排名(学校属性+个人排名),夏令营初审最省事的方式就是按照排名筛选,每个学校会根据当年的申请数和学校定位确立个标准。深研院一般初审的标准大概是 985的top20%,211的top10%,其他的学校的top3,北京本部的标准会相应更高。另外会看其他一些基本要求比如CET-4(525+) ,CET-6(越高越好),最后才会看一些论文和竞赛,一方面初审材料太多一些秘书懒得看,另一方面他们也很难分辨其中的含金量,无法评判。按招生老师的话说我的排名不高,达不到他们的标准,但论文方面有加分让我了通过了初审。
28 |
29 | #### 夏令营面试
30 | 面试的时候老师会看重一些软实力,一般都会围绕着简历中的发表的论文和一些项目来问问题。个人感受是如果有论文的话在面试时是很加分的,基本上所有老师都会围绕着论文解决了哪些问题,有什么创新之处来深问下去,但一定要对自己写在简历上的所有细节十分熟悉。并且计系的学生应该在之前会经历过很多暑期实习的面试,所以面试应该很有经验。面试前也会有些英语提问的一些测试,整个面试时间一般在20min内,不会允许任何超时,尽量回答言简意赅,但也不要而语速过快而紧张。
31 |
32 | #### 一些教训
33 | 1. 如果打算升学的话,一定把专业课程学习放在第一位,学有余力再去做其他的事情。我自己就在大三因为搞些竞赛和论文,很多课程就仅停留在完成作业的状态,GPA一年也掉了0.1,这可能让很多申请连初审都过不了,会很被动。
34 | 2. 早做规划,早考英语。当大三突然想申请国外学校发现还没考TOFEL成绩。CET4/6也没有很重视,国内申请时才发现CET6的分数达不到个别专业的最低要求。
35 | 3. 回头看己申请时是很仓促,认识和准备也是不足的。倒也没有什么心得,写下一些自己的经历供大家参考。
36 | 4. 以后想到再补充
37 |
--------------------------------------------------------------------------------
/docs/个人申请总结/计算机科学与工程系/[CN]-15-郑艺林.md:
--------------------------------------------------------------------------------
1 | ## 计算机科学与技术
2 |
3 | #### 基本背景
4 |
5 | > 学业成绩还是过得去的,只是对求职没有太大意义
6 | > 有过一些项目经历
7 | > 有过一段腾讯实习
8 |
9 | ## 求职结果
10 |
11 | > 腾讯实习转正 offer 由于个人原因没有接受
12 | > 最终去向:蚂蚁金服
13 |
14 | ## 求职心得
15 |
16 | #### 求职时间线
17 |
18 | 这个时间线并不是完全准确的,但是大概落在了对应的区间内,要求职的同学还是需要认真查看网络、官网、学校推送等各渠道的消息,把握好时机。
19 |
20 | 1. 3-5 月:实习招聘
21 | 2. **7-11 月:秋招**
22 | 3. 次年 1-3 月:春招补录
23 |
24 | #### 简历编写
25 |
26 | 简历要针对岗位编写,我比较感兴趣后台开发,综合我的过往项目经历,简历上体现比较多的还是后端开发的能力。一些个人觉得比较好的有一些技术含量的课程项目也可以作为项目经历写在简历上。简历在最开始需要包含基本的个人信息、联系方式、GitHub 地址(除非你的 GitHub 上没有啥有可以提现自己能力的)、院校以及毕业时间。项目经历、与应聘岗位相关的竞赛、科研经历也可以写上去。不过个人参加的娱乐性质社团、与技术不相关的个人爱好都不是什么有价值的东西,不仅对应聘没有加分意义,反而还会占用简历篇幅,个人建议这类信息不要往简历上写。另外,项目经历需要体现个人的角色、负责的内容,可以适当罗列一下项目中用到的一些开源技术或者框架,这都是面试官比较在意的东西。
27 |
28 | #### 基础准备
29 |
30 | 技术类的基础主要是专业的一些基础课程、核心课程,也可能涉及一些选修课程:
31 |
32 | - **编程语言**
33 | - **数据结构与算法**
34 | - **操作系统**
35 | - **数据库**
36 | - **计算机网路**
37 | - **编译原理**
38 | - 分布式系统
39 |
40 | #### 笔试准备
41 |
42 | 技术类岗位的笔试形式主要有在线笔试和线下笔试。
43 |
44 | 在线笔试一般是有一个链接进去然后在页面内进行选择题、问答题和代码题的考察,中间一般不允许被弹窗打扰、不允许或者有限次数的切出页面。笔试前的准备,除了基础方面的准备,主要还有场地、时间和纸笔等草稿工具的准备。笔试过程中要注意关闭不必要的软件例如通讯应用,以防止弹窗打扰导致被误判为作弊。如果有的笔试允许在本地编辑器或者 IDE 中编写代码题,则可以考虑提前准备好顺手的编辑器或者 IDE。笔试的网络环境最好要稳定一点,周围的环境要尽量安静和谐,这些都是可能影响笔试正常发挥的因素。
45 |
46 | 线下笔试一般就是做一份卷子,然后面试官根据卷子进行一个简单的面试。个人感觉遇到的次数不多,腾讯实习提前批和某小游戏公司应聘过程中遇到过。一般不需要怎么准备吧,带上脑子,不要迟到就是了。
47 |
48 | #### 面试准备
49 |
50 | 笔试环节有时候筛人的比例比较低,因为主要筛人的部分在面试环节。面试就是要跟面试官一对一或者多对一(当然是多个面试官啦)交流的环节。面试的形式可能是线上也可能线下。不管线上线下,面试都有一些套路化了。最常问的问题第一个问题是自我介绍。一上来的自我介绍不要太长,1 min 左右足够了。紧接着可能就是专业基础的问答或者项目经历的询问。面试环节一般都不止一次,比如腾旭有两次技术面,而阿里可能有三次技术面,还有字节就都是三次技术面。面试前要做好专业基础的复习、。项目经历问答也是可以准备的,自己对自己做过的项目要多多了解,比如对用到的技术的原理、为啥用这个技术之类,这都是应该要能够答出来的,否则就很减分。面试最后一般面试官会让面试者提问,这时候可以问问面试的相关岗位的工作内容、面试反馈等等。其中面试反馈很重要,有些面试官愿意提供反馈,是可以根据反馈改进自己的面试准备的,有助于面试结束后的查漏补缺。
51 |
52 | #### 实习
53 |
54 | 秋招之前的实习是非常好的机会。很多正式工作 offer 都是从实习转正过程中拿到的。腾讯、阿里、字节、百度等各大互联网公司都是有实习生招聘的,一般在毕业前一年的春季开始,暑期是很多同学实习的时间。因为当年的秋季就是秋招了,所以很多直接从实习转正获得正式工作 offer 可以为自己省去秋招的功夫,另外秋招要想去其他公司,实习经历也可以为自己的背景添砖加瓦。去大公司实习的好处是可以看大一个大型企业的运转流程、规范化的管理方式和比较好的福利吧,但是坏处可能是拧螺丝感比较重,毕竟分工非常细化了。去小公司可能就是苦一点累一些,但是学到的东西可能比较多,因为可能什么都要做。平台大小可以根据自己的偏好选择。
55 |
56 | 个人暑期在腾讯实习过,公司氛围很融洽,福利也很好,不过拧螺丝的枯燥感还是有一些的,不过也还是可以学到很多东西。从团队的融入、工作流程、对待技术和工作的态度等,多方面都会能够有所收获,重要的是培养自己能够适应并且做好一份工作。
57 |
58 | #### 秋招
59 |
60 | 秋招是拿工作 offer 最合适的时间。如果从实习转正过来的话,秋招就可以省些事情。即使有转正拿到的 offer,个人依旧建议秋招季多投一些公司,给自己一些选择的余地,有能力的甚至可以根据与公司 argue 薪资。我本人错过了秋招季所以后面秋招找工作确实遇到不少困难,hc 少了,面试门槛也就提高了,通过概率自然就下降了,还会与毕设进程卡在一起,着实不让人轻松。如果没有实习经历,可能秋招会遇到更大的竞争压力,但是有出色的背景和扎实的专业技能的话,秋招依旧不是大问题。如果短时间内接到多个公司的 offer,可以根据岗位、工作内容、薪资、公司福利、地点等多个因素考虑做出最终选择。我个人比较偏好大公司,喜欢一个大的平台,因此除了几大互联网公司以外,很小的公司我不怎么投,投的录了最后也没去。
61 |
--------------------------------------------------------------------------------
/docs/个人申请总结/计算机科学与工程系/[US]-15-方一栋.md:
--------------------------------------------------------------------------------
1 | # [US]-15-方一栋 M.S. in Computer Sciences @ UW-Madison
2 |
3 | ## 基本背景
4 |
5 | > 三维: GPA 3.87/4, Ranking: 1/98;
6 | >
7 | > TOEFL: 99 (Speaking 20);
8 | >
9 | > GRE: 155V + 169Q + 3.0AW
10 |
11 | ## 申请结果
12 |
13 | 最终录取学校:MS in Computer Science @ University of Wisconsin-Madison
14 |
15 | 其他录取结果:
16 |
17 | * **Offer\(1\):**
18 | * PhD @ Stony Brook University, New York
19 |
20 | * **AD\(2\):**
21 | * M.S. @ University of Wisconsin-Madison
22 | * M.S. @ University of Massachusetts Amherst
23 |
24 | ## 申请心得
25 |
26 | ### 干货区
27 |
28 | - 主要 Timeline:
29 |
30 | 美国 CS Top 30 左右的学校,最早截至学校 12/15 (UCLA), 大部分学校申请截止时间 01/15 or 03/15。申请截至前确保 TOEFL、GRE 过关,请预留至少一个月选校准备文书简历推荐信。
31 |
32 | - 文书简历推荐信
33 |
34 | 文书主要指的是 Statement of Purpose / Personal Statement,这个是除了推荐信之外最重要的,要突出个人特点,尽量不要与 CV 简历重复,要突出你与你申请项目的 match 程度,也可在其中点明你感兴趣的导师。Personal Histroy Statement 需要的学校不多,主要用于突出 diversity, 如果你觉得你有什么特别的地方,可以写上去。最后文书和简历建议找 native 再润色一番,有很多途径,网上和语言中心都可以,请用好学校给你们提供的资源。
35 |
36 | - 选校
37 |
38 | 其实我不太有经验,建议参考一亩三分地往年申请情况,结合自身条件,进行选择。考虑的因素有学费,地理位置,导师等等。若申请 Ph.D. 请慎重选择导师。
39 |
40 | ### 忠告
41 |
42 | 希望大家不要为了出国而出国,还是要想清楚你希望得到什么,多收集信息,从各方面做好 trade off,留学不是万能的。多听听别人的故事,多问问自己的内心。另外,没有谁的成功经验能被完美复刻,请结合自己的实际情况进行调整。
43 |
44 | 联系方式 :fangyd1997@gmail.com
45 |
46 | 个人主页:https://cv.edydfang.com
47 |
48 | ---
49 |
50 | 以下算是废话了
51 |
52 | ### GPA
53 | 我刚进大学时计算机系并没有正式成立,教授也只有寥寥几个电子系的代课教授。所以我一直会提醒我把自己该做的事都先做好,然后慢慢等待机会到来。因此我从大一开始就十分注重课程的学习,选修课也不会只是为了毕业选水课,而是选择那写在我的认知范围内最有用的课程,一个最典型的例子就是一门我导师教的高级算法的课,这门课只有很少的人选,因为负担会比较重,老师每节课都会让同学提出思路实现代码并在后一堂课做 pre,但是我觉得他会很有用,因为那里面有很多近似求解NP问题的算法,而且让我有机会多上到台前去向别人讲解自己做的东西,是对能力很大的提升。另一个深刻的体会就是,对于我这种夜猫子,如果早上没有课,我通常无法起床,但是如果有课,我从不会缺席。因为一门高质量的课程,一定能让你的课后时间事半功倍,让自己更高效的完成作业并腾出时间探索自己感兴趣的领域。再大学四年里,我伴随着计算机系的成立与成长,看到了课程质量的不断提高,因为稳定的通识课成绩和过硬的专业基础,使得我的GPA始终位列系里第一。
54 |
55 |
56 | ### 托福一战
57 | 根据学长学姐的建议,我在大二暑假开始时便进行了托福的一战,花了 15 天准备,考到了 97 分,但是其中的 speaking 部分只有 17 分,这 17 分的口语为我的之后的申请埋下了深深的隐患。没有过多考虑,我直到大三暑假的暑研开始之前都没有担心托福的问题。
58 |
59 | ### UCLA-CSST 暑研
60 | 在之后的UCLA-CSST的暑研,让我改变了对太多事情的看法,让我更加清晰的认识到自己在全球众多优秀的申请者中所处的位置。这次难得的暑研机会来自于同学的一句鼓励,真的很感谢当时那位和我一同申请该项目的一位同学,没有他的鼓励,我本不打算将那份申请提交出去,因为往年该项目学校只去了2人甚至更少的学长学姐。多多尝试,你会遇到好事的。 在 UCLA,因为文书没有写好,针对性不强,我申请的导师的研究方向其实并不是我的favorite,从这开始我才认识到文书的重要性。同时也在 UCLA,我遇到了中国大陆最优秀的学校的最顶尖学生,从他们身上我看到了一个优秀者应该具备的品质:自信、勤奋、独立。尽管导师选择不敬人意,但是UCLA的实验室师兄和导师依然给我了莫大帮助,在那里,我开启了对我而言一个全新领域研究的技能树。同时,在那里的两个月,我考出了不错的GRE成绩。
61 |
62 | ### 腾讯实习
63 | 怀着对未来可能性探索的倔强,UCLA 暑研归来后我便马不停蹄的申请进入了腾讯 AI Lab 的实习。我想知道工业界的研究与学术界的区别所在,也想让自己知道自己到底是否热衷于学术研究。这段实习可以说穿插了我的整个申请季节,难忘而与众不同。**探索总会付出代价,但是不探索导致的结果也许代价更大。**
64 |
65 | ### 划重点:遗憾和对学弟学妹的建议
66 |
67 | 我的申请季最大的硬伤当属托福,当我 UCLA 归来再考的二战三战依然没有达到100分时,那种焦虑与无奈可想而知。这种标准化考试不仅要早准备,还要早点分手才好。
68 |
--------------------------------------------------------------------------------
/docs/个人申请总结/计算机科学与工程系/[US]-15-阎相易.md:
--------------------------------------------------------------------------------
1 | # \[US\]-15-阎相易 PhD in Computer Science @ UC Irvine
2 |
3 | ## 基本背景
4 |
5 | > 三维: GPA 3.85/4, Ranking: 3/98;
6 | >
7 | > TOEFL: 102\(Speaking 23\);
8 | >
9 | > GRE: 149V + 169Q + 3.5AW
10 |
11 | ## 申请结果
12 |
13 | 最终录取学校:PhD in Computer Science @ UC Irvine
14 |
15 | 其他录取结果:
16 |
17 | * **Offer\(2\):**
18 | * \[PhD @ CUHK (提前批)\]
19 | * \[PhD @ HKU (口头offer)\]
20 | * **AD\(0\):**
21 | * 暂无。
22 | * **Rej\(0\)**:
23 | * 暂无。
24 |
25 | ## 申请心得
26 |
27 | #### 一些经历的总结
28 |
29 | 1. 机械系暑期认知实习:
30 |
31 | 大一暑假被机械系融亦鸣老师推到了清华付成龙老师(现在已经是南科大付成龙老师了hhh)的实验室,主要是了解科研是怎么回事,并确定了自己对机械的执念也仅限于执念而已,很庆幸当时有这个机会,也谢谢当时的自己把握住了,不然不知道之后大二在选专业这件事上还要back and forth多久。
32 |
33 | 谢谢融老师、付老师。
34 |
35 | 2. 南科大郝祁老师组:
36 |
37 | 暑假结束后就加入了郝祁老师的组,当时计系还是刚从二科搬到智园,认识了本科对我影响最深的人——孙佳明学长。我的整个大二都是在这个lab里度过的,在佳明学长的指引下,大二这年是我成长最快的一年,学长不断强调,一定要有**Geeky**的精神,什么东西都要想搞一搞,所以大二这年总结一下就是追求了广度,打基础、养习惯,一个字总结也就是**搞**,什么也搞点,炸机、焊板子、学cmd、装黑果……
38 |
39 | 在和佳明学长的相处中,我培养起了自己对CS相关的中文资料的bias,慢慢的我也尽量克制自己,不用百度和QQ群debug,一切与专业相关的东西全部向英文靠拢,有什么不会的就强迫自己**用英文**google(在google输中文还不如用百度),久而久之就习惯了,现在出去之后发现比大部分其他学校的同学搜索、debug都快,也是得益于这些习惯。直到目前,我还是坚定地认为任何计算机相关的中文资料都是pure trash,当然微信小程序这种没有英文开发社区的除外LOL。所以之后有学弟学妹找我,我前几句话一定包括:把所有的操作系统、各种账户设置能设成英文的全设成英文,学新的概念的时候中文和英文都一样是新的,那就选英文,因为语言的鸿沟和知识的天堑相比来说,实在是太微不足道了。算是对“佳明精神”的传承吧hhh。
40 |
41 | 同时,我养成了猛烈看网课的习惯,由于我系的大部分课程的课件都是基本照搬海外,我就把大二大三大部分专业课的网课也全程同步跟了一遍,从一开始需要聚精会神的看带字幕版本,到比较放松的看带字幕版本,再到聚精会神的看不带字幕版本,最后到非常放松的看不带字幕版本。手机里放满网课(专业扒视频请认准`youtube-dl`/`coursera-dl`),地铁公交火车飞机上,吃饭走路水课开会,只要有心情就拿出来看一波。好处是第一可以巩固课上的知识。第二可以练听力,托福听力29基本没练过TPO,应该都是得益于平时看网课和看油管上的沙雕视频hhh。第三可以培养对美帝学校的执念和对教学方式的偏见,为什么他们的教授能 1️⃣每节课都照着自己预先备好的一沓手稿讲课,而不是PPT阅读机;2️⃣课堂上备课手写板书,只有在总结的时候才祭出为数不多的、画龙点睛用的PPT;3️⃣发supplementary materials,这个有点小贪心,MIT的notes详细到我完全不知道还需要自己再记什么笔记;4️⃣能对每个概念都从 example (what) → motivation (why) → intuition (how) → principle (exactly how) → application (when & where) 这一条线穿起来,讲的这么通透。我以后也要去这种学校,如果我以后有机会做老师,我也要对教学这么用心。
42 |
43 | 谢谢Srini Devadas、Erik Demaine、Krste Asanović、Vladimir Stojanovic、John Kubiatowicz、Patrick H. Winston、Andrew Ng、Andrej Karpathy陪伴我走过无数个人满为患的地铁站、航空管制的的机场、清晨无人的饭堂、蛤声震耳的夜路和心悸难眠的夜晚。
44 |
45 | 非常谢谢孙老师和实验室的其他小伙伴给了我一个快乐而充实的大二。
46 |
47 | 3. 大数据中心:
48 |
49 | 大二暑假,入门CV参加了一个Kaggle的比赛,并没有成长,个人认为是一段浪费时间的经历。
50 |
51 | 这里branch出来说说本科期间其他容易分散注意力的事情,请打算出国的同学不要参加除robomaster外,任何主办方是大陆单位的比赛(天池、腾讯、华为、蓝桥杯...);与此相似的是,请不要带着申请功利的想法参加任何暑期上课的项目(UBC、Gatech...),我对于以上两种行为的认知是:可以体验、可以玩、学校给了机会、会有收获,但更多是体验上、视野上、人生维度上长远来看的收获,但是如果拿着有助于申请的幌子 ~~骗~~ 安慰自己就不对了。
52 |
53 | 私以为我校目前对申请有价值的项目只有:
54 | - [学期交换]牛津大学LMH优秀本科生一年期访问学生海外学习项目
55 | - [学期交换]MIT学期交换
56 | - [暑研]CSST
57 | - [暑研]UCInspire
58 |
59 | 4. 先进院MMLAB(乔宇老师组):
60 |
61 | 课题不是很感兴趣,带我的是王亚立老师,负责也通情达理,并没有很认真的做事,所以没出成果,很惭愧。
62 |
63 | 谢谢王老师。
64 |
65 | 5. UCInspire
66 |
67 | CSST面试没过来的这个项目,当时还沮丧了一阵,来之后真香,尔湾真好啊!下午五点科研完开车10分钟到海边,沙滩上吃个饭看落日吹海风,浪完了10分钟回实验室继续搞到十一二点,走回宿舍。舒服。
68 |
69 | 导师是Xiaohui Xie,一起在实验室的有谢丹宁和东南大学的一个老哥,宁神去purdue了,我和东南老哥phd就留在UCI继续~~搞基~~了。老板可能对本科生的期待不是很高,所以很容易impress吧,感觉他比较看好南科大这个学校。
70 |
71 | Xiaohui的推荐信还是很管用的,这封推荐把我和东南老哥从UCI AI PhD applicant pool捞出来,也把宁神从各种学校的SE PhD applicant pool捞了出来。
72 |
73 | 谢谢带我们的PhD学长德莹和王喆,谢谢Xiaohui。
74 |
75 | 6. 腾讯医疗AI Lab
76 |
77 | 大四上学期开始在北京腾讯医疗AI Lab继续做UCI的项目,Xiaohui之前的一个PhD在这,于是推过来,算是把我放在这先pre doctorial training了,刚开始10 10 5,现在paper快投了赶实验大概11 12 7吧。
78 |
79 | 谢谢逸飞学长。
80 |
81 | 7. 一直以来的榜样
82 |
83 | 选导师的时候没有选郝祁老师,因为佳明学长毕业了,我没有理由再留在郝老师组里。于是加入了王琦老师的组,我本也无意做密码学的相关工作,王老师非常通情达理,对我也出奇的放心,只要他知道我在做有意义的事,我就不需要worry系里繁冗的章程。说王老师是计算机系教学第一人绝对不为过,教学在我系好像一直不被重视,我鲜有看到其他老师在一门课上花这么大的心力,tex课件自己写、卷子自己阅、作业自己讲、密切关注课程群并回答同学们的问题,我想我从王老师身上学到的不只是离散数学一门课的内容(这门课我还是B+,实在是羞愧),他对基础知识的完完全全扎实的掌握,对教学一丝不苟的态度,对学生的关心和支持,还有在对我们软实力(Pre、写作、阅读)的教育方面做的工作,都是我在做决定进组的时候从未预料过的,如果没有王老师的鼎力支持,我觉得我不会有这么多自由的空间“瞎折腾”。
84 |
85 | 非常谢谢王老师。
86 |
87 | 8. 最后再感谢一下一起学习一起吹逼的姜天恺、蓝桥和[隋昌祥](个人申请总结/生物医学工程系/[US]-15-隋昌祥),说不出什么具体要感谢的,祝大家都前程似锦啦,就这样吧!(呲牙
88 |
89 | (写完了发现更像回忆录。。不过也没事。。吧。。(~~骗自己🙂~~)这部分就先这样啦!
90 |
91 | 联系方式:x.yan@uci.edu。
92 |
--------------------------------------------------------------------------------
/docs/个人申请总结/金融系/README.md:
--------------------------------------------------------------------------------
1 | ## 金融系
2 |
3 | - [金融学](个人申请总结/金融系/金融学/README.md)
4 |
5 | - [金融工程](个人申请总结/金融系/金融工程/README.md)
6 |
--------------------------------------------------------------------------------
/docs/个人申请总结/金融系/金融学/README.md:
--------------------------------------------------------------------------------
1 | ## 金融学
2 |
3 | #### PhD Programs:
4 |
5 | To be continued.
6 |
7 | #### Master Programs:
8 |
9 | - [15级-王咏睿 CUHK(SZ)](个人申请总结/金融系/金融学/[CN]-15-王咏睿.md)
10 |
11 | ##### 联培:
12 |
13 | To be continued.
14 |
15 | ##### 直博:
16 |
17 | To be continued.
18 |
19 | ##### 保研:
20 |
21 | - [15级-蔡多 中山大学](个人申请总结/金融系/金融学/[CN]-15-蔡多.md)
22 |
23 | ##### 考研:
24 |
25 | To be continued.
26 |
27 | ##### 就业:
28 |
29 | To be continued.
30 |
--------------------------------------------------------------------------------
/docs/个人申请总结/金融系/金融学/[CN]-15-王咏睿.md:
--------------------------------------------------------------------------------
1 | # \[CN\]-15级-王咏睿 Master of Accounting @ CUHK(SZ)
2 |
3 | ## 基本背景
4 |
5 | > 三维: GPA 3.59/4, Ranking: 15+/60+;
6 | >
7 | > TOEFL: 90;
8 |
9 | ## 申请结果
10 |
11 | 最终录取学校:Master of Accounting @ CUHK(SZ)
12 |
13 | 其他录取结果:
14 |
15 | * **Offer\(0\):**
16 | * 暂无。
17 | * **AD\(0\):**
18 | * 暂无。
19 | * **Rej\(0\)**:
20 | * 暂无。
21 |
22 | ## 申请心得
23 |
24 | #### 夏令营/成绩
25 |
26 | 港中文深圳的申请相比港校本部要好申请一些,最低要求只要托福80,雅思好像是6.5,不要求论文。夏令营在7月份,从4-5月份开始申请,分批次录取,越早申请录取的几率越大。不过夏令营的时候大部分名校学生会采取海投的方法,许多名校尖子生也会申请港中深,导致我校学生竞争压力会非常大。对于想要升学的同学,学业成绩的重要性要高于实习,GPA是硬指标,是筛选时的主要因素。建议不要在大三课程多的学期中去实习,努力把专业课学好,GPA提上去。我当时由于时间安排不合理,在课最多的大三上还出去实习,导致课程没跟上,GPA直降0.1。实习的机会以后非常多,学校资源也很多,但GPA被拉低就很难补救。
27 |
28 | #### 专业/面试
29 |
30 | 港中文深圳金融相关专业有四个,经济,金融,金工,会计。其中会计学最好申,面试只有单面,没有笔试。其他三个专业均有笔试和群面。可同时申请多个专业。由于对自己的成绩没有信心,导师建议我申请会计学,理由是经管类学科课程设置差距不大,港中深会计学的学科资源也很丰富,就业面很广并没有局限性。当然成绩较好的同学可以挑选自己感兴趣的专业申请,会计学可以作为保底。
31 | 各个专业的笔试题不一样,包含数学和金融经济理论的题目,总体难度不大,题目较为基础,基础知识不扎实的同学应该适当进行复习。群面类似于企业面试的小组讨论,围绕热点话题。知乎或者论坛有一些面试者分享的经验,面试之前可以提前准备。会计学单面全英文回答,自我介绍,对于成绩单和简历进行提问题,并会问一些金融知识点,考察面试者的学业水平是否扎实,成绩有无虚假。最后是在限定时间内阅读材料,并进行开放式分析和回答,需要脑筋转快一些。
32 |
33 | 第一批录取概率最大,建议尽早申请,9月-10月份开始。越往后申请的人越多,包括考研失败的很多学生,但是录取名额却比前面批次还少。因此一定要提早申请,不能拖沓。
34 |
--------------------------------------------------------------------------------
/docs/个人申请总结/金融系/金融学/[CN]-15-蔡多.md:
--------------------------------------------------------------------------------
1 | # \[CN\]-15级-蔡多 财经传媒专业硕士 @ 中山大学
2 |
3 | ## 基本背景
4 |
5 | > 三维: GPA 3.65/4, Ranking: 10/68;
6 | >
7 | > IELTS: 6.5 (R 7 L 7.5 S 6 W 6);
8 |
9 | ## 申请结果
10 |
11 | 最终录取学校:财经传媒专业 @ 中山大学
12 |
13 | 其他录取结果:
14 |
15 | * **Offer\(0\):**
16 | * 暂无。
17 | * **AD\(2\):**
18 | * 财经传媒专业 @ 哈尔滨工业大学(深圳)
19 | * 金融学专业 @ 南方科技大学
20 | * **Rej\(0\)**:
21 | * 北大汇丰财经传媒专业(保研阶段的dream school,复试的时候,它拒了我)
22 | * 中国传媒大学国际新闻专业(时间和中山复试重合,我拒了它)
23 |
24 | ## 申请心得
25 |
26 | #### 实习经历
27 |
28 | - 2017年暑假 中国银河金融控股有限责任公司(风控部)1个月
29 | - 2018年学期间 21世纪经济报道(海外市场部)3个月(6月开始) 远程实习
30 | - 2018年学期间 万科(战略规划部) 3个月(11月开始)
31 |
32 |
33 | #### 写在前面的话
34 |
35 | 小学萌我在大一上期的时候属于极度慢热的同学,以及天生对物理不OK,所以大一上学期十分痛苦,但是给学弟学妹的建议还是:**痛苦归痛苦,捋起袖子还得干。**大一的成绩在一定程度上是后面GPA的基数,如果大一上GPA不太好,后面需要付出更多。努力还是有用滴!
36 | 先简单谈一谈个人对金融专业的一些想法,本科阶段学习的知识基本都是浅尝辄止,对于金融学,除了成绩,最大的一块是实践,也就是我们通常所说的实习或者是一些商赛。这一点无论是就业、国内升学还是国外申请都是很重要的一步,也是金融类专业简历中最核心的一块。实习的含金量有些时候会决定你offer的含金量。但请切记:**实习千万个,学习第一位!千万不要为了实习翘课哦~**
37 | 国内升学的大环境决定了和“财经金融”这几个字联系的专业都是“僧多粥少”,你面对的对手不光是同专业的人还有许多跨专业的人。此外,这个专业是拥有“资源文化”和“圈子文化”的,我校“双非”的状况注定会受一些影响,但这并不影响top的小朋友(所以努力才是最要紧的)。然后很多人好奇我为什么突然正经地变成了“文科生”,接下来我就以我的例子给准备保研(尤其是想要跨专业保研)的同学们讲一下血泪史背后的一些准备和注意事项。
38 |
39 | #### 为什么保研
40 |
41 | 2018年初的时候我还在一门心思想申请,为什么突然想保研出于几方面原因:
42 |
43 | 1. 和10+位国外升学的学长学姐交流+金融业界前辈说到的2018年经济形势不好,因此不管是就业、国外申请还是考研的形势都非常严峻。我的人生规划是风险规避型。
44 |
45 | 2. 无意中了解到财经传媒这个专业,综合分析了一下觉得是把爱好和本科专业结合起来的方向。但同专业如果国外申请,对语言成绩要求非常非常高(比如可能会告诉你雅思总分8,单项不低于7.5)
46 |
47 | 3. 对自己在系里的排名大概有个估算,拿到推免资格把握较大(金融系2015级是按照6:4综合计算排名进行推免资格选取,即60%的成绩+40%的奖项。据非官方消息,2016级可能会调整到7:3)。保研最大的标准就是成绩,所以有心保研的同学对成绩(尤其是专业成绩)要有所努力。
48 |
49 |
50 | #### 怎么选择Target School
51 |
52 | 总体来说,这和国外申请差不多,但国内还有**夏令营、九推(这个很重要,但我在这里不详细展开,强烈建议学弟学妹仔细了解保研流程,不要临时抱佛脚)**之类的东西,所以大概会更加繁琐一点。你大概得有以下几步:
53 |
54 | 1. 确定你想读的专业+青睐的地区
55 |
56 | 2. 按照好、较好、一般的三类选取大概5-6所院校(如果有精力,肯定多一点会好)。但是这几所院校不是完全凭心意选取出来,我这里以财经相关专业为例,如果你研究生后倾向就业,你首先要考虑的是地区(好的地区实习机会会很多)。紧接着你需要去联系相关院校的前辈或是业内人士稍微打听一下学校这个专业的认可度(可能存在某些院校名气大,但是专业认可度低)。最后,你要一一去确认这几所院校夏令营以及九推的情况(包括时间、地点、往年招录人数、录取比等),一定要确认几个院校间是否有时间重合的情况,最大效率参与其中。此外还要注意录取比,有些院校喜欢拉很多人入夏令营,结果最后只录取几个人(比如央财)。
57 |
58 |
59 | #### 跨保怎么准备
60 |
61 | 回答这个问题前,我想先说一个有趣的事情。在我完成保研之后的时间,我发现原来学校有很多学弟学妹想转专业。我觉得这也很正常,不用觉得这是“背弃”了你的本科专业,其一你的本科专业给了你不一样的思维模式,你得感谢;其二人生太长了,一定要拥有一个你喜欢的傍身之技,所以专业的改变也是在造福你自己的过程。
62 | 关于跨专业,**最核心的问题在于:你为什么想要跨专业?**
63 |
64 | 这个问题对于跨专业的同学来说,有助于你厘清自己的思路。一旦你选择研究生改变专业,这个问题就会反复出现在各个院校的各个面试当中,你唯有先将自己说通,才能说服别人。教授也只有感受到你强烈的心情,才能试图去了解你这个人。所以我强烈建议你们在准备跨专业的时候想清楚这件事,一定不能是一时兴起的,而是有一个背景加触发点(这一点在你的自荐信里也要着重强调)。拿我个人举例,我在大三上学期的时候完全没有想过这件事,因为一直以来我只把写作、采访当做兴趣爱好在培养,我只是很清楚自己在这上面有天赋,但还没有把它当成主业。到后来听说了这个专业,再加上去财经媒体实习,就突然意识到一条新的路。所以从我个人角度来说,背景就是我从很小的时候就喜欢这个,我认为有能力去做好这个事情,触发点就是无意中听说这个专业以及媒体实习经历。
65 |
66 | 当你想通了这件事以后,其他事情就相对简单了,因为你知道了目标,所以你就会明白你缺什么。在去21世纪经济报道实习之前我其实已经80%确定我要去这个专业了,所以实际上这个实习是为了增加我媒体背景方面的空缺。说到这里要感谢我的导师何佳老师,正如跨专业保研不容易,跨专业找实习也是不容易的,在何老师的大力支持下我才能如愿去到媒体实习(所以人生烦恼还是要多和导师聊聊,这也是提高效率的办法,毕竟是前辈)。
67 |
68 | 除了媒体这个实习,我没有太多其他实践方面的准备,但这并不意味着零。原因在于,因为我个人的兴趣,在学校新闻社做了大大小小的采访(在这里当然还要再感谢一下新闻社各位老师和同学对我的支持),推出了《南科新知》杂志的正式版。但人生比较奇妙,你可能完全没想到,你曾经做的事情对你未来发展有这么大的推动力。所以,真实喜欢某种东西其实也算是一种准备,因为你会潜在的去做很多事情。也因此,如果学弟学妹实践方面比较空缺,一定要花时间去填补它。
69 |
70 | 关于提交的资料我个人以为最重要的是自荐信和简历(这两个如果有需要的学弟学妹,可以私戳我)。在没有面对面和长期深入交流的前提下,教授只能通过这两个东西了解你,所以面试前一定要非常熟悉这两样东西,甚至你应该预想到他会针对这上面的哪一块内容问你什么。关于语言成绩,四六级成绩需要,但似乎最近的趋势也是希望你有雅思托福(?存疑),甚至清华深研院鼓励GRE成绩(320以上免笔试)。
71 |
72 |
73 | #### 几个项目的一些详细内容
74 |
75 | 不太清楚境外升学的情况,但国内每年的面试基本是有规律可循的。因此简单将几个项目的一些具体细节分享给大家
76 |
77 | 1. 中山大学
78 |
79 | 财经传媒这个专业原来是挂在岭南商学院下的,后来传播与设计学院建好以后就到这边了。课程安排有金融类课程,也有媒体类课程。就业去向有媒体、也有金融行业,学制是两年。据我的师兄说,第一年在广州中山大学学习,第二年实则在北大汇丰(绕不出的缘分)和北大同专业的同学一起上课。
80 |
81 | 这个项目夏令营非常早,2015级是大概4月份就有了(我当时不知道就错过了!),夏令营录取比我没有了解过,有兴趣的同学可以去官网看看。同时这个项目没有九推,因此夏令营过了就是9.28号的最后录取时间了(国内还有一部分学院是这样的,要注意)。9.28的那一批名额已经比较少了(因为会留给考研一些名额,所以推免录取的人数是有严格限制的)。
82 |
83 | 面试的时候大概有8、9位老师,进去会先让你读一段英文新闻,然后给你很短时间准备,之后让你翻译。翻译不用逐字逐句,核心内容以及一些细节点说到就可以。之后会拿着你的简历以及你准备的一些附件对你进行询问。首先问了我为什么转专业(**这个问题真的很重要!拜托大家好好准备!**)和你觉得你有什么优势,然后对我的媒体实习进行了详细的询问。我当时提供了我实习期间写的新闻链接,所以还问了很多时事热点(我估计这个是常规内容)。最后还问了一下我对新闻的看法,以及我认为财经记者和金融分析师写出来的东西有什么区别。整体氛围很nice,老师也会很认真听你说,当时感觉就像一个小型交流会。当天上午面试完,下午一点就收到录取的电话啦\~后来在官网看到是专业第一录取滴还是很开心惹\~
84 |
85 | 2. 北大汇丰
86 |
87 | 说起这个也还是有点难过,虽然初试过了还是技不如人挂在了复试上,我的复试过程属于不愠不火,挑不出毛病也没有亮点的那种。复试全部都是去北京本校面试,老师比中山要少一些。在面试之前会让你抽一个小纸条,上面有一个题目,我当时抽到的是“你怎么看《如懿传》和《延禧攻略》”。北大汇丰的题目更加多元化一点,然后偏时事多一些。之后老师开始问我对新闻的一些看法(我感觉这里也许还要再多看一点专业书,从我的感觉,我觉得我准备得还不够到位)。北大汇丰这个专业虽然挂在商学院名下,和中山大学一样仍然是颁发的新闻传播学学位,但是这个学制是三年。
88 |
89 | 3. 哈工深
90 |
91 | 我是夏令营去的哈工深,然后和前两个不一样,这个是金融学。夏令营我这个专业招了10个人入营,我记得8个录取(记忆有点模糊了)。面试流程首先是英文介绍自己,然后老师围绕你的简历问问题。哈工深这边倾向于学术多一点,简历上写的时间序列pro是被详细问到的,实习之类的经历只是随口问了问。据当时夏令营的其他小伙伴讲,还问到了计量经济学、微观经济学等一些很细节的知识点。
92 |
93 | 个人不太清楚哈工深金融的实力,就我个人而言,我觉得和本校差别不大。
94 |
95 | #### 最后的最后
96 |
97 | 还是要感谢男朋友小李同学的持续资瓷,在最焦躁的时候安慰和鼓励我。
98 |
99 | 感谢我的导师以及金融系对我施以援手的各位老师。
100 |
101 | 感谢学校新闻社各位催稿的老师同学
102 |
103 | 感谢周围一群没心没肺的心肝朋友
104 |
105 | 感谢有你们的出现,让我终于踏上了这条未知的但是星光熠熠的道路!
106 |
107 | 最后,祝所有小朋友都能快快乐乐地收割offer~!
108 |
109 | 那么,如果还有其他问题欢迎联系我啦:)
110 |
111 | > <11510751@mail.sustech.edu.cn>
112 |
113 | > <237635190@qq.com>
114 |
--------------------------------------------------------------------------------
/docs/个人申请总结/金融系/金融工程/README.md:
--------------------------------------------------------------------------------
1 | ## 金融工程
2 |
3 | #### PhD Programs:
4 |
5 | To be continued.
6 |
7 | #### Master Programs:
8 |
9 | To be continued.
10 |
11 | ##### 联培:
12 |
13 | To be continued.
14 |
15 | ##### 直博:
16 |
17 | To be continued.
18 |
19 | ##### 保研:
20 |
21 | To be continued.
22 |
23 | ##### 考研:
24 |
25 | To be continued.
26 |
27 | ##### 就业:
28 |
29 | To be continued.
30 |
--------------------------------------------------------------------------------
/docs/国内申请/保研/README.md:
--------------------------------------------------------------------------------
1 | ## Under Construction. 🏗
2 |
--------------------------------------------------------------------------------
/docs/国内申请/直博/README.md:
--------------------------------------------------------------------------------
1 | ## Under Construction. 🏗
2 |
--------------------------------------------------------------------------------
/docs/国内申请/考研/README.md:
--------------------------------------------------------------------------------
1 | ## Under Construction. 🏗
2 |
--------------------------------------------------------------------------------
/docs/如何进行经验分享.md:
--------------------------------------------------------------------------------
1 | ## 如何进行经验分享
2 |
3 | 1. 如果已有`Github`账号可以跳过此步。打开[Github](https://github.com),点击`Sign Up`按钮注册一个`Github`账号,选择`Free Plan`即可。
4 |
5 | 2. 登陆后进入[这个页面](https://github.com/SUSTech-Application/2019-Fall/tree/master/docs/),打开一个你希望分享的页面,如``个人申请总结``->``计算机科学与工程系``,这篇文章将会一直以此为例进行说明。
6 |
7 | 
8 |
9 | 
10 |
11 | 3. 点击`Create new file`按钮。
12 |
13 | 
14 |
15 | 命名方式为:`[国家]-入学年级-姓名.md`。
16 | 如:`[US]-15-阎相易.md`。
17 |
18 | 
19 |
20 | 将你想要分享的内容填写在下方。我们在此提供模板,同学们只需要改动其中的汉字部分即可,点击`Preview`按钮可以进行预览。
21 |
22 | ```markdown
23 | # \[US\]15-阎相易-学校名称-项目名称
24 |
25 | ## 基本背景
26 |
27 | > 三维: GPA 0.00/4 ;
28 | >
29 | > TOEFL: 0\(Speaking 0\);
30 | >
31 | > GRE: 130V + 130Q + 0AW
32 |
33 | ## 申请结果
34 |
35 | 最终录取学校:学校名称+项目名称
36 |
37 | 其他录取结果:
38 |
39 | * **Offer\(0\):**
40 | * \[学校名称+项目名称\] 若干
41 | * **AD\(0\):**
42 | * \[学校名称+项目名称\] 若干
43 | * **Rej\(10\)**:
44 | * \[学校名称+项目名称\] 若干
45 |
46 | ## 申请心得
47 |
48 | 若干。
49 | ```
50 |
51 | 
52 |
53 | 4. 点击`Propose new file`按钮。
54 |
55 | 
56 |
57 | 5. 点击`Create pull request`按钮。
58 |
59 | 
60 |
61 | 6. 确保`Allow edits from maintainers`是被选中状态,点击`Create pull request`按钮。
62 |
63 | 
64 |
65 | 7.大功告成。
66 |
67 | 首先请我们代表学弟学妹们,感谢你愿意分享自己宝贵的经历,同时我们也要感谢你选择了自食其力的方式,这为我们节省了大量格式转换的时间,现在我们只需专注于修改、审核即可。同时我们要恭喜你拥有了自己的`Github`账号并且了解了`.md`也就是`Markdown`这种神奇的事物。希望这个账号不会就此荒废,因为`Github`是这个世界上最富饶的代码库,你几乎可以找到任何你想要的东西。
68 |
69 | 最后,祝前程似锦。
70 |
--------------------------------------------------------------------------------
/docs/海外交流/学期交流/README.md:
--------------------------------------------------------------------------------
1 | ## Under Construction. 🏗
2 |
--------------------------------------------------------------------------------
/docs/海外交流/暑校/README.md:
--------------------------------------------------------------------------------
1 | ## Under Construction. 🏗
2 |
--------------------------------------------------------------------------------
/docs/海外交流/暑研/README.md:
--------------------------------------------------------------------------------
1 | ## 暑研
2 |
3 | - [《在计系,暑研对北美申请重要性的讨论》](海外交流/暑研/在计系,暑研对北美申请重要性的讨论.md) by [15级-计算机-阎相易](个人申请总结/计算机科学与工程系/[US]-15-阎相易.md)
4 |
5 | - [《哥伦比亚大学环境学院LDEO暑期实习申请攻略+体验》](海外交流/暑研/哥伦比亚大学环境学院LDEO暑期实习申请攻略+体验.md) by 15级-环境-刘静宇
6 |
--------------------------------------------------------------------------------
/docs/海外交流/暑研/在计系,暑研对北美申请重要性的讨论.md:
--------------------------------------------------------------------------------
1 | # 在计系,暑研对北美申请重要性的讨论
2 |
3 | by [15级-计算机-阎相易](个人申请总结/计算机科学与工程系/[US]-15-阎相易)
4 |
5 | 在计系,如果没有北美暑研的经历,基本不可能申请到好学校的PhD项目。
6 |
7 | - Why?
8 |
9 | 1. 大背景:
10 | CS大热,申请北美的学校时,大家不仅要和其他学校计算机系同级的同学竞争,还要面临转专业大军和大批美本、美硕的涌入,CS的application pool永远处于饱和状态,说CS是最难申请的专业一点都不夸张。
11 |
12 | 2. PhD申请要素:
13 | - 英语(相对不重要):TOEFL(一般过百即可) + GRE (一般V过150即可)
14 | - GPA(相对不重要):3.7+
15 | - Paper(相对重要)
16 | - 推荐信(非常重要):
17 | - 规律一:国内无效
18 | 由于多年国内糟糕的推荐信风气(让学生写了之后发给老师改改就提交,或者老师直接把网申系统填推荐信的链接发给学生自由处置),大陆教授的推荐信越发不值钱。
19 |
20 | - 规律二:亲近、了解大于牛
21 | 假如你的导师人品合格、学风端正,决定亲自来写推荐信,则此规律生效,即:跟你亲近的、了解你日常生活、学习、科研的导师的“了解推”,要比只是title上高(xxx院士、xxx fellow、xxx chair)但是并不了解你的导师的“普通推”有效很多。
22 |
23 | 3. 我系现状:
24 | 我们系的绝大部分教授博士毕业于香港、英国、日本和新加坡,我了解到的深度合作院校是悉尼科技大学,最近加入我系的图灵奖得主之前工作的学校是EPFL,我们就索性把欧陆也加进去吧。由此可见,我系的connection主要还是和这些地方多,目前我了解到的只有郝祁、张煜群老师博士毕业于北美高校。
25 |
26 | 我系学生申请这些地方的学校要比北美容易很多(北美本身就是最难+没有connection)。因此如果要申请北美的PhD,基本就只能靠自己折腾了。
27 |
28 | - How?
29 |
30 | 按成功概率排名:
31 |
32 | 1. 暑研项目:
33 | 随着大家越来越意识到暑研的重要性,暑研项目也变成了“小申请”,每年的难度都在增加。
34 |
35 | 1)我校有两个优秀的暑研项目:
36 | - [CSST @ UCLA](https://csst.ucla.edu/):
37 | 难,算上我校优秀的补贴,自己不花钱。
38 |
39 | (我系最终入选人数/我系进入面试人数)
40 | - 13级:1/2人。
41 | - 14级:0/0人。
42 | - 15级:1/2人。
43 | - 16级:0/0人。
44 |
45 | - [UCInspire @ UCI](https://sites.uci.edu/ucinspire/):
46 |
47 | 一年比一年难,算上我校优秀的补贴,自己大约3-4万,我系15级入选8人左右。
48 |
49 | 2)可以自己申请的:
50 | - [MITACS @ 加拿大](https://www.mitacs.ca/en)
51 | - [ICT Summer Research Program @ USC](http://ict.usc.edu/academics/internships/)
52 | - [RISS @ CMU](https://riss.ri.cmu.edu/)(不要抱~~太大~~任何希望)
53 |
54 | 3)欧洲:
55 | - [Summer @ EPFL](https://summer.epfl.ch/)(不要抱~~太大~~任何希望)
56 |
57 | 如有其他优秀项目,欢迎大家提PR补充。
58 |
59 | 2. 如果之前有过科研实习,实习单位导师可以推。
60 |
61 | 3. 导师通过connection推。
62 |
63 | 4. 自己发邮件套。
64 |
65 | 通过暑研项目找到暑研的概率要比后面三种的概率大很多,先不谈有多少竞争者,或者对方老板的位置早就被熟人推过去的学生填满了。
66 |
67 | 我们来看一个实际的问题:签证。
68 |
69 | 通过暑研项目,走的是F1签证-学生签,也就是officially你是被算作去“上课”的,只不过是实验室自习课hhh,这种签证比较好过,而且上上下下都有项目小秘打点,教授不用跑这跑那操心你的签证,也不用给你发stipend。
70 |
71 | 后三种方式,走的是J1签证-访问学者签,对面老板不仅要帮你四处奔波办手续,要面临你被拒签的风险,还可能要给你发工资。
72 |
73 | 所以除非是非常铁的熟人推,或者是你特别特别牛,后三种方式,都很困难。
74 |
75 |
76 |
77 | 附表:对于推荐信的理解
78 |
79 | | 描述 | 有效性 |
80 | | :---: | :---: |
81 | | 北美圈子里有名的教授强推 | 五星 |
82 | | 北美圈子里有名的教授普通推 | 四星 |
83 | | 圈子里有名的外国教授的强推 | 四星 |
84 | | 圈子里有名的外国教授的普通推 | 三星 |
85 | | 非常了解你的、不一定要非常牛的大陆教授的走心推荐 | 三星 |
86 | | 只是很牛但不了解你的大陆教授的推荐 | 没用 |
87 | | 其他 | 减分 |
88 |
89 | 联系方式:x.yan@uci.edu。
90 |
--------------------------------------------------------------------------------
/docs/申请注意事项/README.md:
--------------------------------------------------------------------------------
1 | ## 申请注意事项
2 |
3 | #### General
4 |
5 | - 欢迎投稿!
6 |
7 | #### Department-specific
8 |
9 | - [《MathFin/MFE的一点微小的经验》](申请注意事项/[US]15-MathFin&MFE的一点微小的经验.md) by 15级-金融数学-胡筱一
10 |
--------------------------------------------------------------------------------
/docs/英语学习/GMAT/README.md:
--------------------------------------------------------------------------------
1 | ## Under Construction. 🏗
2 |
--------------------------------------------------------------------------------
/docs/英语学习/GRE/README.md:
--------------------------------------------------------------------------------
1 | ## GRE
2 |
3 | - [《GRE修仙指南》](英语学习/GRE/GRE修仙指南-15级-张孜晟.md) by 15级-化学-张孜晟
4 |
--------------------------------------------------------------------------------
/docs/英语学习/IELTS/README.md:
--------------------------------------------------------------------------------
1 | ## Under Construction. 🏗
2 |
--------------------------------------------------------------------------------
/docs/英语学习/TOEFL/README.md:
--------------------------------------------------------------------------------
1 | ## Under Construction. 🏗
2 |
--------------------------------------------------------------------------------
/docs/英语学习/四六级/README.md:
--------------------------------------------------------------------------------
1 | ## Under Construction. 🏗
2 |
--------------------------------------------------------------------------------
/docs/选校经验/README.md:
--------------------------------------------------------------------------------
1 | ## Under Construction. 🏗
2 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | docsify
7 |
8 |
9 |
10 |
11 |
12 |
17 |
18 |
19 |
20 |
21 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "lerna": "2.0.0-rc.5",
3 | "packages": [
4 | "packages/*"
5 | ],
6 | "version": "0.0.0"
7 | }
8 |
--------------------------------------------------------------------------------
/lib/plugins/disqus.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var fixedPath = location.href.replace('/-/', '/#/');
3 | if (fixedPath !== location.href) {
4 | location.href = fixedPath;
5 | }
6 |
7 | function install(hook, vm) {
8 | var dom = Docsify.dom;
9 | var disqus = vm.config.disqus;
10 | if (!disqus) {
11 | throw Error('$docsify.disqus is required')
12 | }
13 |
14 | hook.init(function (_) {
15 | var script = dom.create('script');
16 |
17 | script.async = true;
18 | script.src = "https://" + disqus + ".disqus.com/embed.js";
19 | script.setAttribute('data-timestamp', Number(new Date()));
20 | dom.appendTo(dom.body, script);
21 | });
22 |
23 | hook.mounted(function (_) {
24 | var div = dom.create('div');
25 | div.id = 'disqus_thread';
26 | var main = dom.getNode('#main');
27 | div.style = "width: " + (main.clientWidth) + "px; margin: 0 auto 20px;";
28 | dom.appendTo(dom.find('.content'), div);
29 |
30 | // eslint-disable-next-line
31 | window.disqus_config = function() {
32 | this.page.url = location.origin + '/-' + vm.route.path;
33 | this.page.identifier = vm.route.path;
34 | this.page.title = document.title;
35 | };
36 | });
37 |
38 | hook.doneEach(function (_) {
39 | if (typeof window.DISQUS !== 'undefined') {
40 | window.DISQUS.reset({
41 | reload: true,
42 | config: function () {
43 | this.page.url = location.origin + '/-' + vm.route.path;
44 | this.page.identifier = vm.route.path;
45 | this.page.title = document.title;
46 | }
47 | });
48 | }
49 | });
50 | }
51 |
52 | $docsify.plugins = [].concat(install, $docsify.plugins);
53 |
54 | }());
55 |
--------------------------------------------------------------------------------
/lib/plugins/disqus.min.js:
--------------------------------------------------------------------------------
1 | !function(){var i=location.href.replace("/-/","/#/");i!==location.href&&(location.href=i),$docsify.plugins=[].concat(function(i,o){var n=Docsify.dom,e=o.config.disqus;if(!e)throw Error("$docsify.disqus is required");i.init(function(i){var t=n.create("script");t.async=!0,t.src="https://"+e+".disqus.com/embed.js",t.setAttribute("data-timestamp",Number(new Date)),n.appendTo(n.body,t)}),i.mounted(function(i){var t=n.create("div");t.id="disqus_thread";var e=n.getNode("#main");t.style="width: "+e.clientWidth+"px; margin: 0 auto 20px;",n.appendTo(n.find(".content"),t),window.disqus_config=function(){this.page.url=location.origin+"/-"+o.route.path,this.page.identifier=o.route.path,this.page.title=document.title}}),i.doneEach(function(i){void 0!==window.DISQUS&&window.DISQUS.reset({reload:!0,config:function(){this.page.url=location.origin+"/-"+o.route.path,this.page.identifier=o.route.path,this.page.title=document.title}})})},$docsify.plugins)}();
2 |
--------------------------------------------------------------------------------
/lib/plugins/external-script.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | function handleExternalScript() {
3 | var container = Docsify.dom.getNode('#main');
4 | var scripts = Docsify.dom.findAll(container, 'script');
5 |
6 | for (var i = scripts.length; i--;) {
7 | var script = scripts[i];
8 |
9 | if (script && script.src) {
10 | var newScript = document.createElement('script');
11 |
12 | Array.prototype.slice.call(script.attributes).forEach(function (attribute) {
13 | newScript[attribute.name] = attribute.value;
14 | });
15 |
16 | script.parentNode.insertBefore(newScript, script);
17 | script.parentNode.removeChild(script);
18 | }
19 | }
20 | }
21 |
22 | var install = function (hook) {
23 | hook.doneEach(handleExternalScript);
24 | };
25 |
26 | window.$docsify.plugins = [].concat(install, window.$docsify.plugins);
27 |
28 | }());
29 |
--------------------------------------------------------------------------------
/lib/plugins/external-script.min.js:
--------------------------------------------------------------------------------
1 | !function(){function e(){for(var o=Docsify.dom.getNode("#main"),e=Docsify.dom.findAll(o,"script"),n=e.length;n--;){var i=e[n];if(i&&i.src){var t=document.createElement("script");Array.prototype.slice.call(i.attributes).forEach(function(o){t[o.name]=o.value}),i.parentNode.insertBefore(t,i),i.parentNode.removeChild(i)}}}window.$docsify.plugins=[].concat(function(o){o.doneEach(e)},window.$docsify.plugins)}();
2 |
--------------------------------------------------------------------------------
/lib/plugins/front-matter.min.js:
--------------------------------------------------------------------------------
1 | !function(){var b=[],y=[],t=0,R={regLevel:new RegExp("^([\\s\\-]+)"),invalidLine:new RegExp("^\\-\\-\\-|^\\.\\.\\.|^\\s*#.*|^\\s*$"),dashesString:new RegExp('^\\s*\\"([^\\"]*)\\"\\s*$'),quotesString:new RegExp("^\\s*\\'([^\\']*)\\'\\s*$"),float:new RegExp("^[+-]?[0-9]+\\.[0-9]+(e[+-]?[0-9]+(\\.[0-9]+)?)?$"),integer:new RegExp("^[+-]?[0-9]+$"),array:new RegExp("\\[\\s*(.*)\\s*\\]"),map:new RegExp("\\{\\s*(.*)\\s*\\}"),key_value:new RegExp("([a-z0-9_-][ a-z0-9_-]*):( .+)","i"),single_key_value:new RegExp("^([a-z0-9_-][ a-z0-9_-]*):( .+?)$","i"),key:new RegExp("([a-z0-9_-][ a-z0-9_-]+):( .+)?","i"),item:new RegExp("^-\\s+"),trim:new RegExp("^\\s+|\\s+$"),comment:new RegExp("([^\\'\\\"#]+([\\'\\\"][^\\'\\\"]*[\\'\\\"])*)*(#.*)?")};function m(e){return{parent:null,length:0,level:e,lines:[],children:[],addChild:function(e){this.children.push(e),++(e.parent=this).length}}}function N(e){var n=null;if("true"==(e=e.replace(R.trim,"")))return!0;if("false"==e)return!1;if(".NaN"==e)return Number.NaN;if("null"==e)return null;if(".inf"==e)return Number.POSITIVE_INFINITY;if("-.inf"==e)return Number.NEGATIVE_INFINITY;if(n=e.match(R.dashesString))return n[1];if(n=e.match(R.quotesString))return n[1];if(n=e.match(R.float))return parseFloat(n[0]);if(n=e.match(R.integer))return parseInt(n[0]);if(isNaN(n=Date.parse(e))){if(n=e.match(R.single_key_value))return(i={})[n[1]]=N(n[2]),i;if(n=e.match(R.array)){for(var t=0,r=" ",i=[],l="",u=!1,a=0,s=n[1].length;a"==d[0]?null!=u?u[v]=_(l.shift()):r[v]=_(l.shift()):null!=u?u[v]=N(d):r[v]=N(d)}else null!=u?u[v]=e(l):r[v]=e(l)}else{if(g.match(/^-\s*$/)){f&&(f=!1,void 0===r.length&&(r=[])),null!=u&&r.push(u),u={},f=!0;continue}if(t=g.match(/^-\s*(.*)/)){null!=u?u.push(N(t[1])):(f&&(f=!1,void 0===r.length&&(r=[])),r.push(N(t[1])));continue}}}null!=u&&(f&&(f=!1,void 0===r.length&&(r=[])),r.push(u))}for(h=s.length-1;0<=h;--h)n.splice.call(n,s[h],1);return r}(e.children)}function l(e){b=[],y=[],t=(new Date).getTime();var n=r(function(e){var n,t=R.regLevel,r=R.invalidLine,i=e.split("\n"),l=0,u=0,a=[],s=new m(-1),f=new m(0);s.addChild(f);var h=[],o="";a.push(f),h.push(l);for(var c=0,p=i.length;c":">",'"':""","'":"'","/":"/"};return String(e).replace(/[&<>"'/]/g,function(e){return n[e]})}function o(i,a){var e="auto"===i.paths,n=localStorage.getItem("docsify.search.expires")l.length&&(o=l.length);var r="..."+h(l).substring(a,o).replace(t,''+e+"")+"...";c+=r}}),s)){var i={title:h(d),content:c,url:t};a.push(i)}},t=0;t\n\n'+e.title+"
\n"+e.content+"
\n\n"}),t.classList.add("show"),i.classList.add("show"),t.innerHTML=s||''+l+"
",c.hideOtherSidebarContent&&(a.classList.add("hide"),o.classList.add("hide"))}function p(e){c=e}function r(e,n){var t,i,a,o,r=n.router.parse().query.s;p(e),Docsify.dom.style("\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0 7px;\n line-height: 36px;\n font-size: 14px;\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n.search .clear-button {\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}"),function(e){void 0===e&&(e="");var n='\n \n ',t=Docsify.dom.create("div",n),i=Docsify.dom.find("aside");Docsify.dom.toggleClass(t,"search"),Docsify.dom.before(i,t)}(r),i=Docsify.dom.find("div.search"),a=Docsify.dom.find(i,"input"),o=Docsify.dom.find(i,".input-wrap"),Docsify.dom.on(i,"click",function(e){return"A"!==e.target.tagName&&e.stopPropagation()}),Docsify.dom.on(a,"input",function(n){clearTimeout(t),t=setTimeout(function(e){return s(n.target.value.trim())},100)}),Docsify.dom.on(o,"click",function(e){"INPUT"!==e.target.tagName&&(a.value="",s())}),r&&setTimeout(function(e){return s(r)},500)}function f(e,n){p(e),function(e,n){var t=Docsify.dom.getNode('.search input[type="search"]');if(t)if("string"==typeof e)t.placeholder=e;else{var i=Object.keys(e).filter(function(e){return-1 {})
26 | .catch(err => {})
27 | ```
28 |
29 | *index.template.html*
30 |
31 | ```html
32 |
33 |
34 |
35 |
36 | docsify
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | ```
47 |
--------------------------------------------------------------------------------
/packages/docsify-server-renderer/index.js:
--------------------------------------------------------------------------------
1 | import * as tpl from '../../src/core/render/tpl'
2 | import fetch from 'node-fetch'
3 | import {AbstractHistory} from '../../src/core/router/history/abstract'
4 | import {Compiler} from '../../src/core/render/compiler'
5 | import {isAbsolutePath} from '../../src/core/router/util'
6 | import {readFileSync} from 'fs'
7 | import {resolve, basename} from 'path'
8 | import resolvePathname from 'resolve-pathname'
9 | import debug from 'debug'
10 | import {prerenderEmbed} from '../../src/core/render/embed'
11 |
12 | function cwd(...args) {
13 | return resolve(process.cwd(), ...args)
14 | }
15 |
16 | function mainTpl(config) {
17 | let html = ``
20 |
21 | if (config.repo) {
22 | html += tpl.corner(config.repo)
23 | }
24 | if (config.coverpage) {
25 | html += tpl.cover()
26 | }
27 |
28 | html += tpl.main(config)
29 |
30 | return html
31 | }
32 |
33 | export default class Renderer {
34 | constructor({template, config, cache}) {
35 | this.html = template
36 | this.config = config = Object.assign({}, config, {
37 | routerMode: 'history'
38 | })
39 | this.cache = cache
40 |
41 | this.router = new AbstractHistory(config)
42 | this.compiler = new Compiler(config, this.router)
43 |
44 | this.router.getCurrentPath = () => this.url
45 | this._renderHtml(
46 | 'inject-config',
47 | ``
48 | )
49 | this._renderHtml('inject-app', mainTpl(config))
50 |
51 | this.template = this.html
52 | }
53 |
54 | _getPath(url) {
55 | const file = this.router.getFile(url)
56 |
57 | return isAbsolutePath(file) ? file : cwd(`./${file}`)
58 | }
59 |
60 | async renderToString(url) {
61 | this.url = url = this.router.parse(url).path
62 | const {loadSidebar, loadNavbar, coverpage} = this.config
63 |
64 | const mainFile = this._getPath(url)
65 | this._renderHtml('main', await this._render(mainFile, 'main'))
66 |
67 | if (loadSidebar) {
68 | const name = loadSidebar === true ? '_sidebar.md' : loadSidebar
69 | const sidebarFile = this._getPath(resolve(url, `./${name}`))
70 | this._renderHtml('sidebar', await this._render(sidebarFile, 'sidebar'))
71 | }
72 |
73 | if (loadNavbar) {
74 | const name = loadNavbar === true ? '_navbar.md' : loadNavbar
75 | const navbarFile = this._getPath(resolve(url, `./${name}`))
76 | this._renderHtml('navbar', await this._render(navbarFile, 'navbar'))
77 | }
78 |
79 | if (coverpage) {
80 | let path = null
81 | if (typeof coverpage === 'string') {
82 | if (url === '/') {
83 | path = coverpage
84 | }
85 | } else if (Array.isArray(coverpage)) {
86 | path = coverpage.indexOf(url) > -1 && '_coverpage.md'
87 | } else {
88 | const cover = coverpage[url]
89 | path = cover === true ? '_coverpage.md' : cover
90 | }
91 |
92 | const coverFile = this._getPath(resolve(url, `./${path}`))
93 |
94 | this._renderHtml('cover', await this._render(coverFile), 'cover')
95 | }
96 |
97 | const html = this.html
98 | this.html = this.template
99 |
100 | return html
101 | }
102 |
103 | _renderHtml(match, content) {
104 | this.html = this.html.replace(new RegExp(``, 'g'), content)
105 |
106 | return this.html
107 | }
108 |
109 | async _render(path, type) {
110 | let html = await this._loadFile(path)
111 | const {subMaxLevel, maxLevel} = this.config
112 | let tokens
113 |
114 | switch (type) {
115 | case 'sidebar':
116 | html =
117 | this.compiler.sidebar(html, maxLevel) +
118 | ``
121 | break
122 | case 'cover':
123 | html = this.compiler.cover(html)
124 | break
125 | case 'main':
126 | tokens = await new Promise(r => {
127 | prerenderEmbed(
128 | {
129 | fetch: url => this._loadFile(this._getPath(url)),
130 | compiler: this.compiler,
131 | raw: html
132 | },
133 | r
134 | )
135 | })
136 | html = this.compiler.compile(tokens)
137 | break
138 | case 'navbar':
139 | case 'article':
140 | default:
141 | html = this.compiler.compile(html)
142 | break
143 | }
144 |
145 | return html
146 | }
147 |
148 | async _loadFile(filePath) {
149 | debug('docsify')(`load > ${filePath}`)
150 | let content
151 | try {
152 | if (isAbsolutePath(filePath)) {
153 | const res = await fetch(filePath)
154 | if (!res.ok) {
155 | throw Error()
156 | }
157 | content = await res.text()
158 | this.lock = 0
159 | } else {
160 | content = await readFileSync(filePath, 'utf8')
161 | this.lock = 0
162 | }
163 | return content
164 | } catch (e) {
165 | this.lock = this.lock || 0
166 | if (++this.lock > 10) {
167 | this.lock = 0
168 | return
169 | }
170 |
171 | const fileName = basename(filePath)
172 | const result = await this._loadFile(
173 | resolvePathname(`../${fileName}`, filePath)
174 | )
175 |
176 | return result
177 | }
178 | }
179 | }
180 |
181 | Renderer.version = '__VERSION__'
182 |
--------------------------------------------------------------------------------
/packages/docsify-server-renderer/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "lockfileVersion": 1,
3 | "dependencies": {
4 | "debug": {
5 | "version": "2.6.8",
6 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
7 | "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw="
8 | },
9 | "encoding": {
10 | "version": "0.1.12",
11 | "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
12 | "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s="
13 | },
14 | "iconv-lite": {
15 | "version": "0.4.18",
16 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz",
17 | "integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA=="
18 | },
19 | "is-stream": {
20 | "version": "1.1.0",
21 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
22 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
23 | },
24 | "ms": {
25 | "version": "2.0.0",
26 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
27 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
28 | },
29 | "node-fetch": {
30 | "version": "1.7.1",
31 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.1.tgz",
32 | "integrity": "sha512-j8XsFGCLw79vWXkZtMSmmLaOk9z5SQ9bV/tkbZVCqvgwzrjAGq66igobLofHtF63NvMTp2WjytpsNTGKa+XRIQ=="
33 | },
34 | "resolve-pathname": {
35 | "version": "2.1.0",
36 | "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.1.0.tgz",
37 | "integrity": "sha1-6DWIAbhrg7F1YNTjw4LXrvIQCUQ="
38 | }
39 | },
40 | "version": "4.8.6"
41 | }
42 |
--------------------------------------------------------------------------------
/packages/docsify-server-renderer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docsify-server-renderer",
3 | "version": "4.8.6",
4 | "description": "docsify server renderer",
5 | "author": {
6 | "name": "qingwei-li",
7 | "email": "cinwell.li@gmail.com",
8 | "url": "https://github.com/QingWei-Li"
9 | },
10 | "homepage": "https://docsify.js.org",
11 | "license": "MIT",
12 | "repository": "docsifyjs/docsify",
13 | "main": "build.js",
14 | "scripts": {
15 | "test": "echo 'hello'"
16 | },
17 | "dependencies": {
18 | "debug": "^2.6.8",
19 | "docsify": "^4.8.0",
20 | "node-fetch": "^1.7.0",
21 | "resolve-pathname": "^2.1.0"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | const liveServer = require('live-server')
2 | const isSSR = !!process.env.SSR
3 | const middleware = []
4 |
5 | if (isSSR) {
6 | const Renderer = require('./packages/docsify-server-renderer/build.js')
7 | const renderer = new Renderer({
8 | template: `
9 |
10 |
11 |
12 |
13 | docsify
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | `,
23 | config: {
24 | name: 'docsify',
25 | repo: 'docsifyjs/docsify',
26 | basePath: 'https://docsify.js.org/',
27 | loadNavbar: true,
28 | loadSidebar: true,
29 | subMaxLevel: 3,
30 | auto2top: true,
31 | alias: {
32 | '/de-de/changelog': '/changelog',
33 | '/zh-cn/changelog': '/changelog',
34 | '/changelog':
35 | 'https://raw.githubusercontent.com/docsifyjs/docsify/master/CHANGELOG'
36 | }
37 | },
38 | path: './'
39 | })
40 |
41 | middleware.push(function(req, res, next) {
42 | if (/\.(css|js)$/.test(req.url)) {
43 | return next()
44 | }
45 | renderer.renderToString(req.url).then(html => res.end(html))
46 | })
47 | }
48 |
49 | const params = {
50 | port: 3000,
51 | watch: ['lib', 'docs', 'themes'],
52 | middleware
53 | }
54 |
55 | liveServer.start(params)
56 |
--------------------------------------------------------------------------------
/src/core/config.js:
--------------------------------------------------------------------------------
1 | import {merge, hyphenate, isPrimitive, hasOwn} from './util/core'
2 |
3 | export default function () {
4 | const config = merge(
5 | {
6 | el: '#app',
7 | repo: '',
8 | maxLevel: 6,
9 | subMaxLevel: 0,
10 | loadSidebar: null,
11 | loadNavbar: null,
12 | homepage: 'README.md',
13 | coverpage: '',
14 | basePath: '',
15 | auto2top: false,
16 | name: '',
17 | themeColor: '',
18 | nameLink: window.location.pathname,
19 | autoHeader: false,
20 | executeScript: null,
21 | noEmoji: false,
22 | ga: '',
23 | ext: '.md',
24 | mergeNavbar: false,
25 | formatUpdated: '',
26 | externalLinkTarget: '_blank',
27 | routerMode: 'hash',
28 | noCompileLinks: []
29 | },
30 | window.$docsify
31 | )
32 |
33 | const script =
34 | document.currentScript ||
35 | [].slice
36 | .call(document.getElementsByTagName('script'))
37 | .filter(n => /docsify\./.test(n.src))[0]
38 |
39 | if (script) {
40 | for (const prop in config) {
41 | if (hasOwn.call(config, prop)) {
42 | const val = script.getAttribute('data-' + hyphenate(prop))
43 |
44 | if (isPrimitive(val)) {
45 | config[prop] = val === '' ? true : val
46 | }
47 | }
48 | }
49 |
50 | if (config.loadSidebar === true) {
51 | config.loadSidebar = '_sidebar' + config.ext
52 | }
53 | if (config.loadNavbar === true) {
54 | config.loadNavbar = '_navbar' + config.ext
55 | }
56 | if (config.coverpage === true) {
57 | config.coverpage = '_coverpage' + config.ext
58 | }
59 | if (config.repo === true) {
60 | config.repo = ''
61 | }
62 | if (config.name === true) {
63 | config.name = ''
64 | }
65 | }
66 |
67 | window.$docsify = config
68 |
69 | return config
70 | }
71 |
--------------------------------------------------------------------------------
/src/core/event/index.js:
--------------------------------------------------------------------------------
1 | import {isMobile} from '../util/env'
2 | import {body, on} from '../util/dom'
3 | import * as sidebar from './sidebar'
4 | import {scrollIntoView} from './scroll'
5 |
6 | export function eventMixin(proto) {
7 | proto.$resetEvents = function () {
8 | scrollIntoView(this.route.path, this.route.query.id)
9 |
10 | if (this.config.loadNavbar) {
11 | sidebar.getAndActive(this.router, 'nav')
12 | }
13 | }
14 | }
15 |
16 | export function initEvent(vm) {
17 | // Bind toggle button
18 | sidebar.btn('button.sidebar-toggle', vm.router)
19 | sidebar.collapse('.sidebar', vm.router)
20 | // Bind sticky effect
21 | if (vm.config.coverpage) {
22 | !isMobile && on('scroll', sidebar.sticky)
23 | } else {
24 | body.classList.add('sticky')
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/core/event/scroll.js:
--------------------------------------------------------------------------------
1 | import {isMobile} from '../util/env'
2 | import * as dom from '../util/dom'
3 | import Tweezer from 'tweezer.js'
4 |
5 | const nav = {}
6 | let hoverOver = false
7 | let scroller = null
8 | let enableScrollEvent = true
9 | let coverHeight = 0
10 |
11 | function scrollTo(el) {
12 | if (scroller) {
13 | scroller.stop()
14 | }
15 | enableScrollEvent = false
16 | scroller = new Tweezer({
17 | start: window.pageYOffset,
18 | end: el.getBoundingClientRect().top + window.pageYOffset,
19 | duration: 500
20 | })
21 | .on('tick', v => window.scrollTo(0, v))
22 | .on('done', () => {
23 | enableScrollEvent = true
24 | scroller = null
25 | })
26 | .begin()
27 | }
28 |
29 | function highlight(path) {
30 | if (!enableScrollEvent) {
31 | return
32 | }
33 | const sidebar = dom.getNode('.sidebar')
34 | const anchors = dom.findAll('.anchor')
35 | const wrap = dom.find(sidebar, '.sidebar-nav')
36 | let active = dom.find(sidebar, 'li.active')
37 | const doc = document.documentElement
38 | const top = ((doc && doc.scrollTop) || document.body.scrollTop) - coverHeight
39 | let last
40 |
41 | for (let i = 0, len = anchors.length; i < len; i += 1) {
42 | const node = anchors[i]
43 |
44 | if (node.offsetTop > top) {
45 | if (!last) {
46 | last = node
47 | }
48 | break
49 | } else {
50 | last = node
51 | }
52 | }
53 | if (!last) {
54 | return
55 | }
56 | const li = nav[getNavKey(decodeURIComponent(path), last.getAttribute('data-id'))]
57 |
58 | if (!li || li === active) {
59 | return
60 | }
61 |
62 | active && active.classList.remove('active')
63 | li.classList.add('active')
64 | active = li
65 |
66 | // Scroll into view
67 | // https://github.com/vuejs/vuejs.org/blob/master/themes/vue/source/js/common.js#L282-L297
68 | if (!hoverOver && dom.body.classList.contains('sticky')) {
69 | const height = sidebar.clientHeight
70 | const curOffset = 0
71 | const cur = active.offsetTop + active.clientHeight + 40
72 | const isInView =
73 | active.offsetTop >= wrap.scrollTop && cur <= wrap.scrollTop + height
74 | const notThan = cur - curOffset < height
75 | const top = isInView ? wrap.scrollTop : notThan ? curOffset : cur - height
76 |
77 | sidebar.scrollTop = top
78 | }
79 | }
80 |
81 | function getNavKey(path, id) {
82 | return `${path}?id=${id}`
83 | }
84 |
85 | export function scrollActiveSidebar(router) {
86 | const cover = dom.find('.cover.show')
87 | coverHeight = cover ? cover.offsetHeight : 0
88 |
89 | const sidebar = dom.getNode('.sidebar')
90 | const lis = dom.findAll(sidebar, 'li')
91 |
92 | for (let i = 0, len = lis.length; i < len; i += 1) {
93 | const li = lis[i]
94 | const a = li.querySelector('a')
95 | if (!a) {
96 | continue
97 | }
98 | let href = a.getAttribute('href')
99 |
100 | if (href !== '/') {
101 | const {query: {id}, path} = router.parse(href)
102 | if (id) {
103 | href = getNavKey(path, id)
104 | }
105 | }
106 |
107 | if (href) {
108 | nav[decodeURIComponent(href)] = li
109 | }
110 | }
111 |
112 | if (isMobile) {
113 | return
114 | }
115 | const path = router.getCurrentPath()
116 | dom.off('scroll', () => highlight(path))
117 | dom.on('scroll', () => highlight(path))
118 | dom.on(sidebar, 'mouseover', () => {
119 | hoverOver = true
120 | })
121 | dom.on(sidebar, 'mouseleave', () => {
122 | hoverOver = false
123 | })
124 | }
125 |
126 | export function scrollIntoView(path, id) {
127 | if (!id) {
128 | return
129 | }
130 |
131 | const section = dom.find('#' + id)
132 | section && scrollTo(section)
133 |
134 | const li = nav[getNavKey(path, id)]
135 | const sidebar = dom.getNode('.sidebar')
136 | const active = dom.find(sidebar, 'li.active')
137 | active && active.classList.remove('active')
138 | li && li.classList.add('active')
139 | }
140 |
141 | const scrollEl = dom.$.scrollingElement || dom.$.documentElement
142 |
143 | export function scroll2Top(offset = 0) {
144 | scrollEl.scrollTop = offset === true ? 0 : Number(offset)
145 | }
146 |
--------------------------------------------------------------------------------
/src/core/event/sidebar.js:
--------------------------------------------------------------------------------
1 | import {isMobile} from '../util/env'
2 | import * as dom from '../util/dom'
3 |
4 | const title = dom.$.title
5 | /**
6 | * Toggle button
7 | */
8 | export function btn(el) {
9 | const toggle = _ => dom.body.classList.toggle('close')
10 |
11 | el = dom.getNode(el)
12 | dom.on(el, 'click', e => {
13 | e.stopPropagation()
14 | toggle()
15 | })
16 |
17 | isMobile &&
18 | dom.on(
19 | dom.body,
20 | 'click',
21 | _ => dom.body.classList.contains('close') && toggle()
22 | )
23 | }
24 |
25 | export function collapse(el) {
26 | el = dom.getNode(el)
27 |
28 | dom.on(el, 'click', ({target}) => {
29 | if (
30 | target.nodeName === 'A' &&
31 | target.nextSibling &&
32 | target.nextSibling.classList.contains('app-sub-sidebar')
33 | ) {
34 | dom.toggleClass(target.parentNode, 'collapse')
35 | }
36 | })
37 | }
38 |
39 | export function sticky() {
40 | const cover = dom.getNode('section.cover')
41 | if (!cover) {
42 | return
43 | }
44 | const coverHeight = cover.getBoundingClientRect().height
45 |
46 | if (window.pageYOffset >= coverHeight || cover.classList.contains('hidden')) {
47 | dom.toggleClass(dom.body, 'add', 'sticky')
48 | } else {
49 | dom.toggleClass(dom.body, 'remove', 'sticky')
50 | }
51 | }
52 |
53 | /**
54 | * Get and active link
55 | * @param {object} router
56 | * @param {string|element} el
57 | * @param {Boolean} isParent acitve parent
58 | * @param {Boolean} autoTitle auto set title
59 | * @return {element}
60 | */
61 | export function getAndActive(router, el, isParent, autoTitle) {
62 | el = dom.getNode(el)
63 |
64 | const links = dom.findAll(el, 'a')
65 | const hash = decodeURI(router.toURL(router.getCurrentPath()))
66 | let target
67 |
68 | links.sort((a, b) => b.href.length - a.href.length).forEach(a => {
69 | const href = a.getAttribute('href')
70 | const node = isParent ? a.parentNode : a
71 |
72 | if (hash.indexOf(href) === 0 && !target) {
73 | target = a
74 | dom.toggleClass(node, 'add', 'active')
75 | } else {
76 | dom.toggleClass(node, 'remove', 'active')
77 | }
78 | })
79 |
80 | if (autoTitle) {
81 | dom.$.title = target ? (target.title || `${target.innerText} - ${title}`) : title
82 | }
83 |
84 | return target
85 | }
86 |
--------------------------------------------------------------------------------
/src/core/fetch/ajax.js:
--------------------------------------------------------------------------------
1 | import progressbar from '../render/progressbar'
2 | import {noop, hasOwn} from '../util/core'
3 |
4 | const cache = {}
5 |
6 | /**
7 | * Simple ajax get
8 | * @param {string} url
9 | * @param {boolean} [hasBar=false] has progress bar
10 | * @return { then(resolve, reject), abort }
11 | */
12 | export function get(url, hasBar = false, headers = {}) {
13 | const xhr = new XMLHttpRequest()
14 | const on = function () {
15 | xhr.addEventListener.apply(xhr, arguments)
16 | }
17 | const cached = cache[url]
18 |
19 | if (cached) {
20 | return {then: cb => cb(cached.content, cached.opt), abort: noop}
21 | }
22 |
23 | xhr.open('GET', url)
24 | for (const i in headers) {
25 | if (hasOwn.call(headers, i)) {
26 | xhr.setRequestHeader(i, headers[i])
27 | }
28 | }
29 | xhr.send()
30 |
31 | return {
32 | then: function (success, error = noop) {
33 | if (hasBar) {
34 | const id = setInterval(
35 | _ =>
36 | progressbar({
37 | step: Math.floor(Math.random() * 5 + 1)
38 | }),
39 | 500
40 | )
41 |
42 | on('progress', progressbar)
43 | on('loadend', evt => {
44 | progressbar(evt)
45 | clearInterval(id)
46 | })
47 | }
48 |
49 | on('error', error)
50 | on('load', ({target}) => {
51 | if (target.status >= 400) {
52 | error(target)
53 | } else {
54 | const result = (cache[url] = {
55 | content: target.response,
56 | opt: {
57 | updatedAt: xhr.getResponseHeader('last-modified')
58 | }
59 | })
60 |
61 | success(result.content, result.opt)
62 | }
63 | })
64 | },
65 | abort: _ => xhr.readyState !== 4 && xhr.abort()
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/core/fetch/index.js:
--------------------------------------------------------------------------------
1 | import {get} from './ajax'
2 | import {callHook} from '../init/lifecycle'
3 | import {getParentPath, stringifyQuery} from '../router/util'
4 | import {noop} from '../util/core'
5 | import {getAndActive} from '../event/sidebar'
6 |
7 | function loadNested(path, qs, file, next, vm, first) {
8 | path = first ? path : path.replace(/\/$/, '')
9 | path = getParentPath(path)
10 |
11 | if (!path) {
12 | return
13 | }
14 |
15 | get(
16 | vm.router.getFile(path + file) + qs,
17 | false,
18 | vm.config.requestHeaders
19 | ).then(next, _ => loadNested(path, qs, file, next, vm))
20 | }
21 |
22 | export function fetchMixin(proto) {
23 | let last
24 |
25 | const abort = () => last && last.abort && last.abort()
26 | const request = (url, hasbar, requestHeaders) => {
27 | abort()
28 | last = get(url, true, requestHeaders)
29 | return last
30 | }
31 |
32 | const get404Path = (path, config) => {
33 | const {notFoundPage, ext} = config
34 | const defaultPath = '_404' + (ext || '.md')
35 | let key
36 | let path404
37 |
38 | switch (typeof notFoundPage) {
39 | case 'boolean':
40 | path404 = defaultPath
41 | break
42 | case 'string':
43 | path404 = notFoundPage
44 | break
45 |
46 | case 'object':
47 | key = Object.keys(notFoundPage)
48 | .sort((a, b) => b.length - a.length)
49 | .find(key => path.match(new RegExp('^' + key)))
50 |
51 | path404 = (key && notFoundPage[key]) || defaultPath
52 | break
53 |
54 | default:
55 | break
56 | }
57 |
58 | return path404
59 | }
60 |
61 | proto._loadSideAndNav = function (path, qs, loadSidebar, cb) {
62 | return () => {
63 | if (!loadSidebar) {
64 | return cb()
65 | }
66 |
67 | const fn = result => {
68 | this._renderSidebar(result)
69 | cb()
70 | }
71 |
72 | // Load sidebar
73 | loadNested(path, qs, loadSidebar, fn, this, true)
74 | }
75 | }
76 |
77 | proto._fetch = function (cb = noop) {
78 | const {path, query} = this.route
79 | const qs = stringifyQuery(query, ['id'])
80 | const {loadNavbar, requestHeaders, loadSidebar} = this.config
81 | // Abort last request
82 |
83 | const file = this.router.getFile(path)
84 | const req = request(file + qs, true, requestHeaders)
85 |
86 | // Current page is html
87 | this.isHTML = /\.html$/g.test(file)
88 |
89 | // Load main content
90 | req.then(
91 | (text, opt) =>
92 | this._renderMain(
93 | text,
94 | opt,
95 | this._loadSideAndNav(path, qs, loadSidebar, cb)
96 | ),
97 | _ => {
98 | this._fetchFallbackPage(file, qs, cb) || this._fetch404(file, qs, cb)
99 | }
100 | )
101 |
102 | // Load nav
103 | loadNavbar &&
104 | loadNested(
105 | path,
106 | qs,
107 | loadNavbar,
108 | text => this._renderNav(text),
109 | this,
110 | true
111 | )
112 | }
113 |
114 | proto._fetchCover = function () {
115 | const {coverpage, requestHeaders} = this.config
116 | const query = this.route.query
117 | const root = getParentPath(this.route.path)
118 |
119 | if (coverpage) {
120 | let path = null
121 | const routePath = this.route.path
122 | if (typeof coverpage === 'string') {
123 | if (routePath === '/') {
124 | path = coverpage
125 | }
126 | } else if (Array.isArray(coverpage)) {
127 | path = coverpage.indexOf(routePath) > -1 && '_coverpage'
128 | } else {
129 | const cover = coverpage[routePath]
130 | path = cover === true ? '_coverpage' : cover
131 | }
132 |
133 | const coverOnly = Boolean(path) && this.config.onlyCover
134 | if (path) {
135 | path = this.router.getFile(root + path)
136 | this.coverIsHTML = /\.html$/g.test(path)
137 | get(path + stringifyQuery(query, ['id']), false, requestHeaders).then(
138 | text => this._renderCover(text, coverOnly)
139 | )
140 | } else {
141 | this._renderCover(null, coverOnly)
142 | }
143 | return coverOnly
144 | }
145 | }
146 |
147 | proto.$fetch = function (cb = noop) {
148 | const done = () => {
149 | callHook(this, 'doneEach')
150 | cb()
151 | }
152 |
153 | const onlyCover = this._fetchCover()
154 |
155 | if (onlyCover) {
156 | done()
157 | } else {
158 | this._fetch(() => {
159 | this.$resetEvents()
160 | done()
161 | })
162 | }
163 | }
164 |
165 | proto._fetchFallbackPage = function (path, qs, cb = noop) {
166 | const {requestHeaders, fallbackLanguages, loadSidebar} = this.config
167 |
168 | if (!fallbackLanguages) {
169 | return false
170 | }
171 |
172 | const local = path.split('/')[1]
173 |
174 | if (fallbackLanguages.indexOf(local) === -1) {
175 | return false
176 | }
177 | const newPath = path.replace(new RegExp(`^/${local}`), '')
178 | const req = request(newPath + qs, true, requestHeaders)
179 |
180 | req.then(
181 | (text, opt) =>
182 | this._renderMain(
183 | text,
184 | opt,
185 | this._loadSideAndNav(path, qs, loadSidebar, cb)
186 | ),
187 | () => this._fetch404(path, qs, cb)
188 | )
189 |
190 | return true
191 | }
192 | /**
193 | * Load the 404 page
194 | * @param path
195 | * @param qs
196 | * @param cb
197 | * @returns {*}
198 | * @private
199 | */
200 | proto._fetch404 = function (path, qs, cb = noop) {
201 | const {loadSidebar, requestHeaders, notFoundPage} = this.config
202 |
203 | const fnLoadSideAndNav = this._loadSideAndNav(path, qs, loadSidebar, cb)
204 | if (notFoundPage) {
205 | const path404 = get404Path(path, this.config)
206 |
207 | request(this.router.getFile(path404), true, requestHeaders).then(
208 | (text, opt) => this._renderMain(text, opt, fnLoadSideAndNav),
209 | () => this._renderMain(null, {}, fnLoadSideAndNav)
210 | )
211 | return true
212 | }
213 |
214 | this._renderMain(null, {}, fnLoadSideAndNav)
215 | return false
216 | }
217 | }
218 |
219 | export function initFetch(vm) {
220 | const {loadSidebar} = vm.config
221 |
222 | // Server-Side Rendering
223 | if (vm.rendered) {
224 | const activeEl = getAndActive(vm.router, '.sidebar-nav', true, true)
225 | if (loadSidebar && activeEl) {
226 | activeEl.parentNode.innerHTML += window.__SUB_SIDEBAR__
227 | }
228 | vm._bindEventOnRendered(activeEl)
229 | vm.$resetEvents()
230 | callHook(vm, 'doneEach')
231 | callHook(vm, 'ready')
232 | } else {
233 | vm.$fetch(_ => callHook(vm, 'ready'))
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/src/core/global-api.js:
--------------------------------------------------------------------------------
1 | import * as util from './util'
2 | import * as dom from './util/dom'
3 | import {Compiler} from './render/compiler'
4 | import {slugify} from './render/slugify'
5 | import {get} from './fetch/ajax'
6 | import marked from 'marked'
7 | import prism from 'prismjs'
8 |
9 | export default function () {
10 | window.Docsify = {
11 | util,
12 | dom,
13 | get,
14 | slugify,
15 | version: '__VERSION__'
16 | }
17 | window.DocsifyCompiler = Compiler
18 | window.marked = marked
19 | window.Prism = prism
20 | }
21 |
--------------------------------------------------------------------------------
/src/core/index.js:
--------------------------------------------------------------------------------
1 | import {initMixin} from './init'
2 | import {routerMixin} from './router'
3 | import {renderMixin} from './render'
4 | import {fetchMixin} from './fetch'
5 | import {eventMixin} from './event'
6 | import initGlobalAPI from './global-api'
7 |
8 | /**
9 | * Fork https://github.com/bendrucker/document-ready/blob/master/index.js
10 | */
11 | function ready(callback) {
12 | const state = document.readyState
13 |
14 | if (state === 'complete' || state === 'interactive') {
15 | return setTimeout(callback, 0)
16 | }
17 |
18 | document.addEventListener('DOMContentLoaded', callback)
19 | }
20 |
21 | function Docsify() {
22 | this._init()
23 | }
24 |
25 | const proto = Docsify.prototype
26 |
27 | initMixin(proto)
28 | routerMixin(proto)
29 | renderMixin(proto)
30 | fetchMixin(proto)
31 | eventMixin(proto)
32 |
33 | /**
34 | * Global API
35 | */
36 | initGlobalAPI()
37 |
38 | /**
39 | * Run Docsify
40 | */
41 | ready(_ => new Docsify())
42 |
--------------------------------------------------------------------------------
/src/core/init/index.js:
--------------------------------------------------------------------------------
1 | import config from '../config'
2 | import {initLifecycle, callHook} from './lifecycle'
3 | import {initRender} from '../render'
4 | import {initRouter} from '../router'
5 | import {initEvent} from '../event'
6 | import {initFetch} from '../fetch'
7 | import {isFn} from '../util/core'
8 |
9 | export function initMixin(proto) {
10 | proto._init = function () {
11 | const vm = this
12 | vm.config = config()
13 |
14 | initLifecycle(vm) // Init hooks
15 | initPlugin(vm) // Install plugins
16 | callHook(vm, 'init')
17 | initRouter(vm) // Add router
18 | initRender(vm) // Render base DOM
19 | initEvent(vm) // Bind events
20 | initFetch(vm) // Fetch data
21 | callHook(vm, 'mounted')
22 | }
23 | }
24 |
25 | function initPlugin(vm) {
26 | [].concat(vm.config.plugins).forEach(fn => isFn(fn) && fn(vm._lifecycle, vm))
27 | }
28 |
--------------------------------------------------------------------------------
/src/core/init/lifecycle.js:
--------------------------------------------------------------------------------
1 | import {noop} from '../util/core'
2 |
3 | export function initLifecycle(vm) {
4 | const hooks = [
5 | 'init',
6 | 'mounted',
7 | 'beforeEach',
8 | 'afterEach',
9 | 'doneEach',
10 | 'ready'
11 | ]
12 |
13 | vm._hooks = {}
14 | vm._lifecycle = {}
15 | hooks.forEach(hook => {
16 | const arr = (vm._hooks[hook] = [])
17 | vm._lifecycle[hook] = fn => arr.push(fn)
18 | })
19 | }
20 |
21 | export function callHook(vm, hook, data, next = noop) {
22 | const queue = vm._hooks[hook]
23 |
24 | const step = function (index) {
25 | const hook = queue[index]
26 | if (index >= queue.length) {
27 | next(data)
28 | } else if (typeof hook === 'function') {
29 | if (hook.length === 2) {
30 | hook(data, result => {
31 | data = result
32 | step(index + 1)
33 | })
34 | } else {
35 | const result = hook(data)
36 | data = result === undefined ? data : result
37 | step(index + 1)
38 | }
39 | } else {
40 | step(index + 1)
41 | }
42 | }
43 |
44 | step(0)
45 | }
46 |
--------------------------------------------------------------------------------
/src/core/render/embed.js:
--------------------------------------------------------------------------------
1 | import {get} from '../fetch/ajax'
2 | import {merge} from '../util/core'
3 |
4 | const cached = {}
5 |
6 | function walkFetchEmbed({embedTokens, compile, fetch}, cb) {
7 | let token
8 | let step = 0
9 | let count = 1
10 |
11 | if (!embedTokens.length) {
12 | return cb({})
13 | }
14 |
15 | while ((token = embedTokens[step++])) {
16 | const next = (function (token) {
17 | return text => {
18 | let embedToken
19 | if (text) {
20 | if (token.embed.type === 'markdown') {
21 | embedToken = compile.lexer(text)
22 | } else if (token.embed.type === 'code') {
23 | embedToken = compile.lexer(
24 | '```' +
25 | token.embed.lang +
26 | '\n' +
27 | text.replace(/`/g, '@DOCSIFY_QM@') +
28 | '\n```\n'
29 | )
30 | } else if (token.embed.type === 'mermaid') {
31 | embedToken = [
32 | {type: 'html', text: `\n${text}\n
`}
33 | ]
34 | embedToken.links = {}
35 | } else {
36 | embedToken = [{type: 'html', text}]
37 | embedToken.links = {}
38 | }
39 | }
40 | cb({token, embedToken})
41 | if (++count >= step) {
42 | cb({})
43 | }
44 | }
45 | })(token)
46 |
47 | if (token.embed.url) {
48 | if (process.env.SSR) {
49 | fetch(token.embed.url).then(next)
50 | } else {
51 | get(token.embed.url).then(next)
52 | }
53 | } else {
54 | next(token.embed.html)
55 | }
56 | }
57 | }
58 |
59 | export function prerenderEmbed({compiler, raw = '', fetch}, done) {
60 | let hit = cached[raw]
61 | if (hit) {
62 | const copy = hit.slice()
63 | copy.links = hit.links
64 | return done(copy)
65 | }
66 |
67 | const compile = compiler._marked
68 | let tokens = compile.lexer(raw)
69 | const embedTokens = []
70 | const linkRE = compile.InlineLexer.rules.link
71 | const links = tokens.links
72 |
73 | tokens.forEach((token, index) => {
74 | if (token.type === 'paragraph') {
75 | token.text = token.text.replace(
76 | new RegExp(linkRE.source, 'g'),
77 | (src, filename, href, title) => {
78 | const embed = compiler.compileEmbed(href, title)
79 |
80 | if (embed) {
81 | embedTokens.push({
82 | index,
83 | embed
84 | })
85 | }
86 |
87 | return src
88 | }
89 | )
90 | }
91 | })
92 |
93 | let moveIndex = 0
94 | walkFetchEmbed({compile, embedTokens, fetch}, ({embedToken, token}) => {
95 | if (token) {
96 | const index = token.index + moveIndex
97 |
98 | merge(links, embedToken.links)
99 |
100 | tokens = tokens
101 | .slice(0, index)
102 | .concat(embedToken, tokens.slice(index + 1))
103 | moveIndex += embedToken.length - 1
104 | } else {
105 | cached[raw] = tokens.concat()
106 | tokens.links = cached[raw].links = links
107 | done(tokens)
108 | }
109 | })
110 | }
111 |
--------------------------------------------------------------------------------
/src/core/render/emojify.js:
--------------------------------------------------------------------------------
1 | import {inBrowser} from '../util/env'
2 |
3 | function replace(m, $1) {
4 | return '
'
5 | }
6 |
7 | export function emojify(text) {
8 | return text
9 | .replace(/<(pre|template|code)[^>]*?>[\s\S]+?<\/(pre|template|code)>/g, m => m.replace(/:/g, '__colon__'))
10 | .replace(/:(\w+?):/ig, (inBrowser && window.emojify) || replace)
11 | .replace(/__colon__/g, ':')
12 | }
13 |
--------------------------------------------------------------------------------
/src/core/render/gen-tree.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Gen toc tree
3 | * @link https://github.com/killercup/grock/blob/5280ae63e16c5739e9233d9009bc235ed7d79a50/styles/solarized/assets/js/behavior.coffee#L54-L81
4 | * @param {Array} toc
5 | * @param {Number} maxLevel
6 | * @return {Array}
7 | */
8 | export function genTree(toc, maxLevel) {
9 | const headlines = []
10 | const last = {}
11 |
12 | toc.forEach(headline => {
13 | const level = headline.level || 1
14 | const len = level - 1
15 |
16 | if (level > maxLevel) {
17 | return
18 | }
19 | if (last[len]) {
20 | last[len].children = (last[len].children || []).concat(headline)
21 | } else {
22 | headlines.push(headline)
23 | }
24 | last[level] = headline
25 | })
26 |
27 | return headlines
28 | }
29 |
--------------------------------------------------------------------------------
/src/core/render/progressbar.js:
--------------------------------------------------------------------------------
1 | import * as dom from '../util/dom'
2 |
3 | let barEl
4 | let timeId
5 |
6 | /**
7 | * Init progress component
8 | */
9 | function init() {
10 | const div = dom.create('div')
11 |
12 | div.classList.add('progress')
13 | dom.appendTo(dom.body, div)
14 | barEl = div
15 | }
16 | /**
17 | * Render progress bar
18 | */
19 | export default function ({loaded, total, step}) {
20 | let num
21 |
22 | !barEl && init()
23 |
24 | if (step) {
25 | num = parseInt(barEl.style.width || 0, 10) + step
26 | num = num > 80 ? 80 : num
27 | } else {
28 | num = Math.floor(loaded / total * 100)
29 | }
30 |
31 | barEl.style.opacity = 1
32 | barEl.style.width = num >= 95 ? '100%' : num + '%'
33 |
34 | if (num >= 95) {
35 | clearTimeout(timeId)
36 | timeId = setTimeout(_ => {
37 | barEl.style.opacity = 0
38 | barEl.style.width = '0%'
39 | }, 200)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/core/render/slugify.js:
--------------------------------------------------------------------------------
1 | import {hasOwn} from '../util/core'
2 |
3 | let cache = {}
4 | const re = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g
5 |
6 | function lower(string) {
7 | return string.toLowerCase()
8 | }
9 |
10 | export function slugify(str) {
11 | if (typeof str !== 'string') {
12 | return ''
13 | }
14 |
15 | let slug = str
16 | .trim()
17 | .replace(/[A-Z]+/g, lower)
18 | .replace(/<[^>\d]+>/g, '')
19 | .replace(re, '')
20 | .replace(/\s/g, '-')
21 | .replace(/-+/g, '-')
22 | .replace(/^(\d)/, '_$1')
23 | let count = cache[slug]
24 |
25 | count = hasOwn.call(cache, slug) ? count + 1 : 0
26 | cache[slug] = count
27 |
28 | if (count) {
29 | slug = slug + '-' + count
30 | }
31 |
32 | return slug
33 | }
34 |
35 | slugify.clear = function () {
36 | cache = {}
37 | }
38 |
--------------------------------------------------------------------------------
/src/core/render/tpl.js:
--------------------------------------------------------------------------------
1 | import {isMobile} from '../util/env'
2 | /**
3 | * Render github corner
4 | * @param {Object} data
5 | * @return {String}
6 | */
7 | export function corner(data) {
8 | if (!data) {
9 | return ''
10 | }
11 | if (!/\/\//.test(data)) {
12 | data = 'https://github.com/' + data
13 | }
14 | data = data.replace(/^git\+/, '')
15 |
16 | return (
17 | `` +
18 | '' +
23 | ''
24 | )
25 | }
26 |
27 | /**
28 | * Render main content
29 | */
30 | export function main(config) {
31 | const aside =
32 | '' +
37 | ''
47 |
48 | return (
49 | (isMobile ? `${aside}` : `${aside}`) +
50 | '' +
53 | ''
54 | )
55 | }
56 |
57 | /**
58 | * Cover Page
59 | */
60 | export function cover() {
61 | const SL = ', 100%, 85%'
62 | const bgc =
63 | 'linear-gradient(to left bottom, ' +
64 | `hsl(${Math.floor(Math.random() * 255) + SL}) 0%,` +
65 | `hsl(${Math.floor(Math.random() * 255) + SL}) 100%)`
66 |
67 | return (
68 | `` +
69 | '' +
70 | '' +
71 | ''
72 | )
73 | }
74 |
75 | /**
76 | * Render tree
77 | * @param {Array} tree
78 | * @param {String} tpl
79 | * @return {String}
80 | */
81 | export function tree(toc, tpl = '') {
82 | if (!toc || !toc.length) {
83 | return ''
84 | }
85 | let innerHTML = ''
86 | toc.forEach(node => {
87 | innerHTML += `${node.title}`
88 | if (node.children) {
89 | innerHTML += tree(node.children, tpl)
90 | }
91 | })
92 | return tpl.replace('{inner}', innerHTML)
93 | }
94 |
95 | export function helper(className, content) {
96 | return `${content.slice(5).trim()}
`
97 | }
98 |
99 | export function theme(color) {
100 | return ``
101 | }
102 |
--------------------------------------------------------------------------------
/src/core/router/history/abstract.js:
--------------------------------------------------------------------------------
1 | import {History} from './base'
2 | import {parseQuery} from '../util'
3 |
4 | export class AbstractHistory extends History {
5 | constructor(config) {
6 | super(config)
7 | this.mode = 'abstract'
8 | }
9 |
10 | parse(path) {
11 | let query = ''
12 |
13 | const queryIndex = path.indexOf('?')
14 | if (queryIndex >= 0) {
15 | query = path.slice(queryIndex + 1)
16 | path = path.slice(0, queryIndex)
17 | }
18 |
19 | return {
20 | path,
21 | file: this.getFile(path),
22 | query: parseQuery(query)
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/core/router/history/base.js:
--------------------------------------------------------------------------------
1 | import {
2 | getPath,
3 | isAbsolutePath,
4 | stringifyQuery,
5 | cleanPath,
6 | replaceSlug
7 | } from '../util'
8 | import {noop, merge} from '../../util/core'
9 |
10 | const cached = {}
11 |
12 | function getAlias(path, alias, last) {
13 | const match = Object.keys(alias).filter(key => {
14 | const re = cached[key] || (cached[key] = new RegExp(`^${key}$`))
15 | return re.test(path) && path !== last
16 | })[0]
17 |
18 | return match ?
19 | getAlias(path.replace(cached[match], alias[match]), alias, path) :
20 | path
21 | }
22 |
23 | function getFileName(path, ext) {
24 | return new RegExp(`\\.(${ext.replace(/^\./, '')}|html)$`, 'g').test(path) ?
25 | path :
26 | /\/$/g.test(path) ? `${path}README${ext}` : `${path}${ext}`
27 | }
28 |
29 | export class History {
30 | constructor(config) {
31 | this.config = config
32 | }
33 |
34 | getBasePath() {
35 | return this.config.basePath
36 | }
37 |
38 | getFile(path = this.getCurrentPath(), isRelative) {
39 | const {config} = this
40 | const base = this.getBasePath()
41 | const ext = typeof config.ext === 'string' ? config.ext : '.md'
42 |
43 | path = config.alias ? getAlias(path, config.alias) : path
44 | path = getFileName(path, ext)
45 | path = path === `/README${ext}` ? config.homepage || path : path
46 | path = isAbsolutePath(path) ? path : getPath(base, path)
47 |
48 | if (isRelative) {
49 | path = path.replace(new RegExp(`^${base}`), '')
50 | }
51 |
52 | return path
53 | }
54 |
55 | onchange(cb = noop) {
56 | cb()
57 | }
58 |
59 | getCurrentPath() {}
60 |
61 | normalize() {}
62 |
63 | parse() {}
64 |
65 | toURL(path, params, currentRoute) {
66 | const local = currentRoute && path[0] === '#'
67 | const route = this.parse(replaceSlug(path))
68 |
69 | route.query = merge({}, route.query, params)
70 | path = route.path + stringifyQuery(route.query)
71 | path = path.replace(/\.md(\?)|\.md$/, '$1')
72 |
73 | if (local) {
74 | const idIndex = currentRoute.indexOf('?')
75 | path =
76 | (idIndex > 0 ? currentRoute.substr(0, idIndex) : currentRoute) + path
77 | }
78 |
79 | return cleanPath('/' + path)
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/core/router/history/hash.js:
--------------------------------------------------------------------------------
1 | import {History} from './base'
2 | import {noop} from '../../util/core'
3 | import {on} from '../../util/dom'
4 | import {parseQuery, cleanPath, replaceSlug} from '../util'
5 |
6 | function replaceHash(path) {
7 | const i = location.href.indexOf('#')
8 | location.replace(location.href.slice(0, i >= 0 ? i : 0) + '#' + path)
9 | }
10 |
11 | export class HashHistory extends History {
12 | constructor(config) {
13 | super(config)
14 | this.mode = 'hash'
15 | }
16 |
17 | getBasePath() {
18 | const path = window.location.pathname || ''
19 | const base = this.config.basePath
20 |
21 | return /^(\/|https?:)/g.test(base) ? base : cleanPath(path + '/' + base)
22 | }
23 |
24 | getCurrentPath() {
25 | // We can't use location.hash here because it's not
26 | // consistent across browsers - Firefox will pre-decode it!
27 | const href = location.href
28 | const index = href.indexOf('#')
29 | return index === -1 ? '' : href.slice(index + 1)
30 | }
31 |
32 | onchange(cb = noop) {
33 | on('hashchange', cb)
34 | }
35 |
36 | normalize() {
37 | let path = this.getCurrentPath()
38 |
39 | path = replaceSlug(path)
40 |
41 | if (path.charAt(0) === '/') {
42 | return replaceHash(path)
43 | }
44 | replaceHash('/' + path)
45 | }
46 |
47 | /**
48 | * Parse the url
49 | * @param {string} [path=location.herf]
50 | * @return {object} { path, query }
51 | */
52 | parse(path = location.href) {
53 | let query = ''
54 |
55 | const hashIndex = path.indexOf('#')
56 | if (hashIndex >= 0) {
57 | path = path.slice(hashIndex + 1)
58 | }
59 |
60 | const queryIndex = path.indexOf('?')
61 | if (queryIndex >= 0) {
62 | query = path.slice(queryIndex + 1)
63 | path = path.slice(0, queryIndex)
64 | }
65 |
66 | return {
67 | path,
68 | file: this.getFile(path, true),
69 | query: parseQuery(query)
70 | }
71 | }
72 |
73 | toURL(path, params, currentRoute) {
74 | return '#' + super.toURL(path, params, currentRoute)
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/core/router/history/html5.js:
--------------------------------------------------------------------------------
1 | import {History} from './base'
2 | import {noop} from '../../util/core'
3 | import {on} from '../../util/dom'
4 | import {parseQuery, getPath} from '../util'
5 |
6 | export class HTML5History extends History {
7 | constructor(config) {
8 | super(config)
9 | this.mode = 'history'
10 | }
11 |
12 | getCurrentPath() {
13 | const base = this.getBasePath()
14 | let path = window.location.pathname
15 |
16 | if (base && path.indexOf(base) === 0) {
17 | path = path.slice(base.length)
18 | }
19 |
20 | return (path || '/') + window.location.search + window.location.hash
21 | }
22 |
23 | onchange(cb = noop) {
24 | on('click', e => {
25 | const el = e.target.tagName === 'A' ? e.target : e.target.parentNode
26 |
27 | if (el.tagName === 'A' && !/_blank/.test(el.target)) {
28 | e.preventDefault()
29 | const url = el.href
30 | window.history.pushState({key: url}, '', url)
31 | cb()
32 | }
33 | })
34 |
35 | on('popstate', cb)
36 | }
37 |
38 | /**
39 | * Parse the url
40 | * @param {string} [path=location.href]
41 | * @return {object} { path, query }
42 | */
43 | parse(path = location.href) {
44 | let query = ''
45 |
46 | const queryIndex = path.indexOf('?')
47 | if (queryIndex >= 0) {
48 | query = path.slice(queryIndex + 1)
49 | path = path.slice(0, queryIndex)
50 | }
51 |
52 | const base = getPath(location.origin)
53 | const baseIndex = path.indexOf(base)
54 |
55 | if (baseIndex > -1) {
56 | path = path.slice(baseIndex + base.length)
57 | }
58 |
59 | return {
60 | path,
61 | file: this.getFile(path),
62 | query: parseQuery(query)
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/core/router/index.js:
--------------------------------------------------------------------------------
1 | import {HashHistory} from './history/hash'
2 | import {HTML5History} from './history/html5'
3 | import {supportsPushState} from '../util/env'
4 | import * as dom from '../util/dom'
5 |
6 | export function routerMixin(proto) {
7 | proto.route = {}
8 | }
9 |
10 | let lastRoute = {}
11 |
12 | function updateRender(vm) {
13 | vm.router.normalize()
14 | vm.route = vm.router.parse()
15 | dom.body.setAttribute('data-page', vm.route.file)
16 | }
17 |
18 | export function initRouter(vm) {
19 | const config = vm.config
20 | const mode = config.routerMode || 'hash'
21 | let router
22 |
23 | if (mode === 'history' && supportsPushState) {
24 | router = new HTML5History(config)
25 | } else {
26 | router = new HashHistory(config)
27 | }
28 |
29 | vm.router = router
30 | updateRender(vm)
31 | lastRoute = vm.route
32 |
33 | router.onchange(_ => {
34 | updateRender(vm)
35 | vm._updateRender()
36 |
37 | if (lastRoute.path === vm.route.path) {
38 | vm.$resetEvents()
39 | return
40 | }
41 |
42 | vm.$fetch()
43 | lastRoute = vm.route
44 | })
45 | }
46 |
--------------------------------------------------------------------------------
/src/core/router/util.js:
--------------------------------------------------------------------------------
1 | import {cached} from '../util/core'
2 |
3 | const decode = decodeURIComponent
4 | const encode = encodeURIComponent
5 |
6 | export function parseQuery(query) {
7 | const res = {}
8 |
9 | query = query.trim().replace(/^(\?|#|&)/, '')
10 |
11 | if (!query) {
12 | return res
13 | }
14 |
15 | // Simple parse
16 | query.split('&').forEach(function (param) {
17 | const parts = param.replace(/\+/g, ' ').split('=')
18 |
19 | res[parts[0]] = parts[1] && decode(parts[1])
20 | })
21 |
22 | return res
23 | }
24 |
25 | export function stringifyQuery(obj, ignores = []) {
26 | const qs = []
27 |
28 | for (const key in obj) {
29 | if (ignores.indexOf(key) > -1) {
30 | continue
31 | }
32 | qs.push(
33 | obj[key] ?
34 | `${encode(key)}=${encode(obj[key])}`.toLowerCase() :
35 | encode(key)
36 | )
37 | }
38 |
39 | return qs.length ? `?${qs.join('&')}` : ''
40 | }
41 |
42 | export const isAbsolutePath = cached(path => {
43 | return /(:|(\/{2}))/g.test(path)
44 | })
45 |
46 | export const getParentPath = cached(path => {
47 | return /\/$/g.test(path) ?
48 | path :
49 | (path = path.match(/(\S*\/)[^/]+$/)) ? path[1] : ''
50 | })
51 |
52 | export const cleanPath = cached(path => {
53 | return path.replace(/^\/+/, '/').replace(/([^:])\/{2,}/g, '$1/')
54 | })
55 |
56 | export function getPath(...args) {
57 | return cleanPath(args.join('/'))
58 | }
59 |
60 | export const replaceSlug = cached(path => {
61 | return path.replace('#', '?id=')
62 | })
63 |
--------------------------------------------------------------------------------
/src/core/util/core.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Create a cached version of a pure function.
3 | */
4 | export function cached(fn) {
5 | const cache = Object.create(null)
6 | return function (str) {
7 | const key = isPrimitive(str) ? str : JSON.stringify(str)
8 | const hit = cache[key]
9 | return hit || (cache[key] = fn(str))
10 | }
11 | }
12 |
13 | /**
14 | * Hyphenate a camelCase string.
15 | */
16 | export const hyphenate = cached(str => {
17 | return str.replace(/([A-Z])/g, m => '-' + m.toLowerCase())
18 | })
19 |
20 | export const hasOwn = Object.prototype.hasOwnProperty
21 |
22 | /**
23 | * Simple Object.assign polyfill
24 | */
25 | export const merge =
26 | Object.assign ||
27 | function (to) {
28 | for (let i = 1; i < arguments.length; i++) {
29 | const from = Object(arguments[i])
30 |
31 | for (const key in from) {
32 | if (hasOwn.call(from, key)) {
33 | to[key] = from[key]
34 | }
35 | }
36 | }
37 |
38 | return to
39 | }
40 |
41 | /**
42 | * Check if value is primitive
43 | */
44 | export function isPrimitive(value) {
45 | return typeof value === 'string' || typeof value === 'number'
46 | }
47 |
48 | /**
49 | * Perform no operation.
50 | */
51 | export function noop() {}
52 |
53 | /**
54 | * Check if value is function
55 | */
56 | export function isFn(obj) {
57 | return typeof obj === 'function'
58 | }
59 |
--------------------------------------------------------------------------------
/src/core/util/dom.js:
--------------------------------------------------------------------------------
1 | import {isFn} from '../util/core'
2 | import {inBrowser} from './env'
3 |
4 | const cacheNode = {}
5 |
6 | /**
7 | * Get Node
8 | * @param {String|Element} el
9 | * @param {Boolean} noCache
10 | * @return {Element}
11 | */
12 | export function getNode(el, noCache = false) {
13 | if (typeof el === 'string') {
14 | if (typeof window.Vue !== 'undefined') {
15 | return find(el)
16 | }
17 | el = noCache ? find(el) : cacheNode[el] || (cacheNode[el] = find(el))
18 | }
19 |
20 | return el
21 | }
22 |
23 | export const $ = inBrowser && document
24 |
25 | export const body = inBrowser && $.body
26 |
27 | export const head = inBrowser && $.head
28 |
29 | /**
30 | * Find element
31 | * @example
32 | * find('nav') => document.querySelector('nav')
33 | * find(nav, 'a') => nav.querySelector('a')
34 | */
35 | export function find(el, node) {
36 | return node ? el.querySelector(node) : $.querySelector(el)
37 | }
38 |
39 | /**
40 | * Find all elements
41 | * @example
42 | * findAll('a') => [].slice.call(document.querySelectorAll('a'))
43 | * findAll(nav, 'a') => [].slice.call(nav.querySelectorAll('a'))
44 | */
45 | export function findAll(el, node) {
46 | return [].slice.call(
47 | node ? el.querySelectorAll(node) : $.querySelectorAll(el)
48 | )
49 | }
50 |
51 | export function create(node, tpl) {
52 | node = $.createElement(node)
53 | if (tpl) {
54 | node.innerHTML = tpl
55 | }
56 | return node
57 | }
58 |
59 | export function appendTo(target, el) {
60 | return target.appendChild(el)
61 | }
62 |
63 | export function before(target, el) {
64 | return target.insertBefore(el, target.children[0])
65 | }
66 |
67 | export function on(el, type, handler) {
68 | isFn(type) ?
69 | window.addEventListener(el, type) :
70 | el.addEventListener(type, handler)
71 | }
72 |
73 | export function off(el, type, handler) {
74 | isFn(type) ?
75 | window.removeEventListener(el, type) :
76 | el.removeEventListener(type, handler)
77 | }
78 |
79 | /**
80 | * Toggle class
81 | *
82 | * @example
83 | * toggleClass(el, 'active') => el.classList.toggle('active')
84 | * toggleClass(el, 'add', 'active') => el.classList.add('active')
85 | */
86 | export function toggleClass(el, type, val) {
87 | el && el.classList[val ? type : 'toggle'](val || type)
88 | }
89 |
90 | export function style(content) {
91 | appendTo(head, create('style', content))
92 | }
93 |
--------------------------------------------------------------------------------
/src/core/util/env.js:
--------------------------------------------------------------------------------
1 | export const inBrowser = !process.env.SSR
2 |
3 | export const isMobile = inBrowser && document.body.clientWidth <= 600
4 |
5 | /**
6 | * @see https://github.com/MoOx/pjax/blob/master/lib/is-supported.js
7 | */
8 | export const supportsPushState =
9 | inBrowser &&
10 | (function () {
11 | // Borrowed wholesale from https://github.com/defunkt/jquery-pjax
12 | return (
13 | window.history &&
14 | window.history.pushState &&
15 | window.history.replaceState &&
16 | // PushState isn’t reliable on iOS until 5.
17 | !navigator.userAgent.match(
18 | /((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/
19 | )
20 | )
21 | })()
22 |
--------------------------------------------------------------------------------
/src/core/util/index.js:
--------------------------------------------------------------------------------
1 | export * from './core'
2 | export * from './env'
3 | export * from '../router/util'
4 |
--------------------------------------------------------------------------------
/src/core/util/polyfill/css-vars.js:
--------------------------------------------------------------------------------
1 | import * as dom from '../dom'
2 | import {get} from '../../fetch/ajax'
3 |
4 | function replaceVar(block, color) {
5 | block.innerHTML = block.innerHTML.replace(
6 | /var\(\s*--theme-color.*?\)/g,
7 | color
8 | )
9 | }
10 |
11 | export default function (color) {
12 | // Variable support
13 | if (window.CSS && window.CSS.supports && window.CSS.supports('(--v:red)')) {
14 | return
15 | }
16 |
17 | const styleBlocks = dom.findAll('style:not(.inserted),link');
18 | [].forEach.call(styleBlocks, block => {
19 | if (block.nodeName === 'STYLE') {
20 | replaceVar(block, color)
21 | } else if (block.nodeName === 'LINK') {
22 | const href = block.getAttribute('href')
23 |
24 | if (!/\.css$/.test(href)) {
25 | return
26 | }
27 |
28 | get(href).then(res => {
29 | const style = dom.create('style', res)
30 |
31 | dom.head.appendChild(style)
32 | replaceVar(style, color)
33 | })
34 | }
35 | })
36 | }
37 |
--------------------------------------------------------------------------------
/src/plugins/disqus.js:
--------------------------------------------------------------------------------
1 | const fixedPath = location.href.replace('/-/', '/#/')
2 | if (fixedPath !== location.href) {
3 | location.href = fixedPath
4 | }
5 |
6 | function install(hook, vm) {
7 | const dom = Docsify.dom
8 | const disqus = vm.config.disqus
9 | if (!disqus) {
10 | throw Error('$docsify.disqus is required')
11 | }
12 |
13 | hook.init(_ => {
14 | const script = dom.create('script')
15 |
16 | script.async = true
17 | script.src = `https://${disqus}.disqus.com/embed.js`
18 | script.setAttribute('data-timestamp', Number(new Date()))
19 | dom.appendTo(dom.body, script)
20 | })
21 |
22 | hook.mounted(_ => {
23 | const div = dom.create('div')
24 | div.id = 'disqus_thread'
25 | const main = dom.getNode('#main')
26 | div.style = `width: ${main.clientWidth}px; margin: 0 auto 20px;`
27 | dom.appendTo(dom.find('.content'), div)
28 |
29 | // eslint-disable-next-line
30 | window.disqus_config = function() {
31 | this.page.url = location.origin + '/-' + vm.route.path
32 | this.page.identifier = vm.route.path
33 | this.page.title = document.title
34 | }
35 | })
36 |
37 | hook.doneEach(_ => {
38 | if (typeof window.DISQUS !== 'undefined') {
39 | window.DISQUS.reset({
40 | reload: true,
41 | config: function () {
42 | this.page.url = location.origin + '/-' + vm.route.path
43 | this.page.identifier = vm.route.path
44 | this.page.title = document.title
45 | }
46 | })
47 | }
48 | })
49 | }
50 |
51 | $docsify.plugins = [].concat(install, $docsify.plugins)
52 |
--------------------------------------------------------------------------------
/src/plugins/external-script.js:
--------------------------------------------------------------------------------
1 | function handleExternalScript() {
2 | const container = Docsify.dom.getNode('#main')
3 | const scripts = Docsify.dom.findAll(container, 'script')
4 |
5 | for (let i = scripts.length; i--;) {
6 | const script = scripts[i]
7 |
8 | if (script && script.src) {
9 | const newScript = document.createElement('script')
10 |
11 | Array.prototype.slice.call(script.attributes).forEach(attribute => {
12 | newScript[attribute.name] = attribute.value
13 | })
14 |
15 | script.parentNode.insertBefore(newScript, script)
16 | script.parentNode.removeChild(script)
17 | }
18 | }
19 | }
20 |
21 | const install = function (hook) {
22 | hook.doneEach(handleExternalScript)
23 | }
24 |
25 | window.$docsify.plugins = [].concat(install, window.$docsify.plugins)
26 |
--------------------------------------------------------------------------------
/src/plugins/front-matter/index.js:
--------------------------------------------------------------------------------
1 | import parser from './parser'
2 |
3 | const install = function (hook, vm) {
4 | hook.beforeEach(content => {
5 | const {attributes, body} = parser(content)
6 |
7 | vm.frontmatter = attributes
8 |
9 | return body
10 | })
11 | }
12 |
13 | $docsify.plugins = [].concat(install, $docsify.plugins)
14 |
--------------------------------------------------------------------------------
/src/plugins/front-matter/parser.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Fork https://github.com/egoist/docute/blob/master/src/utils/front-matter.js
3 | */
4 | /* eslint-disable */
5 | import parser from './yaml'
6 |
7 | var optionalByteOrderMark = '\\ufeff?'
8 | var pattern =
9 | '^(' +
10 | optionalByteOrderMark +
11 | '(= yaml =|---)' +
12 | '$([\\s\\S]*?)' +
13 | '(?:\\2|\\.\\.\\.)' +
14 | '$' +
15 | '' +
16 | '(?:\\n)?)'
17 | // NOTE: If this pattern uses the 'g' flag the `regex` variable definition will
18 | // need to be moved down into the functions that use it.
19 | var regex = new RegExp(pattern, 'm')
20 |
21 | function extractor(string) {
22 | string = string || ''
23 |
24 | var lines = string.split(/(\r?\n)/)
25 | if (lines[0] && /= yaml =|---/.test(lines[0])) {
26 | return parse(string)
27 | } else {
28 | return { attributes: {}, body: string }
29 | }
30 | }
31 |
32 | function parse(string) {
33 | var match = regex.exec(string)
34 |
35 | if (!match) {
36 | return {
37 | attributes: {},
38 | body: string
39 | }
40 | }
41 |
42 | var yaml = match[match.length - 1].replace(/^\s+|\s+$/g, '')
43 | var attributes = parser(yaml) || {}
44 | var body = string.replace(match[0], '')
45 |
46 | return { attributes: attributes, body: body, frontmatter: yaml }
47 | }
48 |
49 | function test(string) {
50 | string = string || ''
51 |
52 | return regex.test(string)
53 | }
54 |
55 | export default extractor
56 |
--------------------------------------------------------------------------------
/src/plugins/ga.js:
--------------------------------------------------------------------------------
1 | // From https://github.com/egoist/vue-ga/blob/master/src/index.js
2 | function appendScript() {
3 | const script = document.createElement('script')
4 | script.async = true
5 | script.src = 'https://www.google-analytics.com/analytics.js'
6 | document.body.appendChild(script)
7 | }
8 |
9 | function init(id) {
10 | appendScript()
11 | window.ga =
12 | window.ga ||
13 | function () {
14 | (window.ga.q = window.ga.q || []).push(arguments)
15 | }
16 | window.ga.l = Number(new Date())
17 | window.ga('create', id, 'auto')
18 | }
19 |
20 | function collect() {
21 | if (!window.ga) {
22 | init($docsify.ga)
23 | }
24 |
25 | window.ga('set', 'page', location.hash)
26 | window.ga('send', 'pageview')
27 | }
28 |
29 | const install = function (hook) {
30 | if (!$docsify.ga) {
31 | console.error('[Docsify] ga is required.')
32 | return
33 | }
34 |
35 | hook.beforeEach(collect)
36 | }
37 |
38 | $docsify.plugins = [].concat(install, $docsify.plugins)
39 |
--------------------------------------------------------------------------------
/src/plugins/gitalk.js:
--------------------------------------------------------------------------------
1 | function install(hook) {
2 | const dom = Docsify.dom
3 |
4 | hook.mounted(_ => {
5 | const div = dom.create('div')
6 | div.id = 'gitalk-container'
7 | const main = dom.getNode('#main')
8 | div.style = `width: ${main.clientWidth}px; margin: 0 auto 20px;`
9 | dom.appendTo(dom.find('.content'), div)
10 | const script = dom.create('script')
11 | const content = `gitalk.render('gitalk-container')`
12 | script.textContent = content
13 | dom.appendTo(dom.body, script)
14 | })
15 | }
16 |
17 | $docsify.plugins = [].concat(install, $docsify.plugins)
18 |
--------------------------------------------------------------------------------
/src/plugins/search/component.js:
--------------------------------------------------------------------------------
1 | import {search} from './search'
2 |
3 | let NO_DATA_TEXT = ''
4 | let options
5 |
6 | function style() {
7 | const code = `
8 | .sidebar {
9 | padding-top: 0;
10 | }
11 |
12 | .search {
13 | margin-bottom: 20px;
14 | padding: 6px;
15 | border-bottom: 1px solid #eee;
16 | }
17 |
18 | .search .input-wrap {
19 | display: flex;
20 | align-items: center;
21 | }
22 |
23 | .search .results-panel {
24 | display: none;
25 | }
26 |
27 | .search .results-panel.show {
28 | display: block;
29 | }
30 |
31 | .search input {
32 | outline: none;
33 | border: none;
34 | width: 100%;
35 | padding: 0 7px;
36 | line-height: 36px;
37 | font-size: 14px;
38 | }
39 |
40 | .search input::-webkit-search-decoration,
41 | .search input::-webkit-search-cancel-button,
42 | .search input {
43 | -webkit-appearance: none;
44 | -moz-appearance: none;
45 | appearance: none;
46 | }
47 | .search .clear-button {
48 | width: 36px;
49 | text-align: right;
50 | display: none;
51 | }
52 |
53 | .search .clear-button.show {
54 | display: block;
55 | }
56 |
57 | .search .clear-button svg {
58 | transform: scale(.5);
59 | }
60 |
61 | .search h2 {
62 | font-size: 17px;
63 | margin: 10px 0;
64 | }
65 |
66 | .search a {
67 | text-decoration: none;
68 | color: inherit;
69 | }
70 |
71 | .search .matching-post {
72 | border-bottom: 1px solid #eee;
73 | }
74 |
75 | .search .matching-post:last-child {
76 | border-bottom: 0;
77 | }
78 |
79 | .search p {
80 | font-size: 14px;
81 | overflow: hidden;
82 | text-overflow: ellipsis;
83 | display: -webkit-box;
84 | -webkit-line-clamp: 2;
85 | -webkit-box-orient: vertical;
86 | }
87 |
88 | .search p.empty {
89 | text-align: center;
90 | }
91 |
92 | .app-name.hide, .sidebar-nav.hide {
93 | display: none;
94 | }`
95 |
96 | Docsify.dom.style(code)
97 | }
98 |
99 | function tpl(defaultValue = '') {
100 | const html =
101 | `
111 |
112 | `
113 | const el = Docsify.dom.create('div', html)
114 | const aside = Docsify.dom.find('aside')
115 |
116 | Docsify.dom.toggleClass(el, 'search')
117 | Docsify.dom.before(aside, el)
118 | }
119 |
120 | function doSearch(value) {
121 | const $search = Docsify.dom.find('div.search')
122 | const $panel = Docsify.dom.find($search, '.results-panel')
123 | const $clearBtn = Docsify.dom.find($search, '.clear-button')
124 | const $sidebarNav = Docsify.dom.find('.sidebar-nav')
125 | const $appName = Docsify.dom.find('.app-name')
126 |
127 | if (!value) {
128 | $panel.classList.remove('show')
129 | $clearBtn.classList.remove('show')
130 | $panel.innerHTML = ''
131 |
132 | if (options.hideOtherSidebarContent) {
133 | $sidebarNav.classList.remove('hide')
134 | $appName.classList.remove('hide')
135 | }
136 | return
137 | }
138 | const matchs = search(value)
139 |
140 | let html = ''
141 | matchs.forEach(post => {
142 | html += ``
148 | })
149 |
150 | $panel.classList.add('show')
151 | $clearBtn.classList.add('show')
152 | $panel.innerHTML = html || `${NO_DATA_TEXT}
`
153 | if (options.hideOtherSidebarContent) {
154 | $sidebarNav.classList.add('hide')
155 | $appName.classList.add('hide')
156 | }
157 | }
158 |
159 | function bindEvents() {
160 | const $search = Docsify.dom.find('div.search')
161 | const $input = Docsify.dom.find($search, 'input')
162 | const $inputWrap = Docsify.dom.find($search, '.input-wrap')
163 |
164 | let timeId
165 | // Prevent to Fold sidebar
166 | Docsify.dom.on(
167 | $search,
168 | 'click',
169 | e => e.target.tagName !== 'A' && e.stopPropagation()
170 | )
171 | Docsify.dom.on($input, 'input', e => {
172 | clearTimeout(timeId)
173 | timeId = setTimeout(_ => doSearch(e.target.value.trim()), 100)
174 | })
175 | Docsify.dom.on($inputWrap, 'click', e => {
176 | // Click input outside
177 | if (e.target.tagName !== 'INPUT') {
178 | $input.value = ''
179 | doSearch()
180 | }
181 | })
182 | }
183 |
184 | function updatePlaceholder(text, path) {
185 | const $input = Docsify.dom.getNode('.search input[type="search"]')
186 |
187 | if (!$input) {
188 | return
189 | }
190 | if (typeof text === 'string') {
191 | $input.placeholder = text
192 | } else {
193 | const match = Object.keys(text).filter(key => path.indexOf(key) > -1)[0]
194 | $input.placeholder = text[match]
195 | }
196 | }
197 |
198 | function updateNoData(text, path) {
199 | if (typeof text === 'string') {
200 | NO_DATA_TEXT = text
201 | } else {
202 | const match = Object.keys(text).filter(key => path.indexOf(key) > -1)[0]
203 | NO_DATA_TEXT = text[match]
204 | }
205 | }
206 |
207 | function updateOptions(opts) {
208 | options = opts
209 | }
210 |
211 | export function init(opts, vm) {
212 | const keywords = vm.router.parse().query.s
213 |
214 | updateOptions(opts)
215 | style()
216 | tpl(keywords)
217 | bindEvents()
218 | keywords && setTimeout(_ => doSearch(keywords), 500)
219 | }
220 |
221 | export function update(opts, vm) {
222 | updateOptions(opts)
223 | updatePlaceholder(opts.placeholder, vm.route.path)
224 | updateNoData(opts.noData, vm.route.path)
225 | }
226 |
--------------------------------------------------------------------------------
/src/plugins/search/index.js:
--------------------------------------------------------------------------------
1 | import {init as initComponet, update as updateComponent} from './component'
2 | import {init as initSearch} from './search'
3 |
4 | const CONFIG = {
5 | placeholder: 'Type to search',
6 | noData: 'No Results!',
7 | paths: 'auto',
8 | depth: 2,
9 | maxAge: 86400000, // 1 day
10 | hideOtherSidebarContent: false,
11 | namespace: undefined
12 | }
13 |
14 | const install = function (hook, vm) {
15 | const {util} = Docsify
16 | const opts = vm.config.search || CONFIG
17 |
18 | if (Array.isArray(opts)) {
19 | CONFIG.paths = opts
20 | } else if (typeof opts === 'object') {
21 | CONFIG.paths = Array.isArray(opts.paths) ? opts.paths : 'auto'
22 | CONFIG.maxAge = util.isPrimitive(opts.maxAge) ? opts.maxAge : CONFIG.maxAge
23 | CONFIG.placeholder = opts.placeholder || CONFIG.placeholder
24 | CONFIG.noData = opts.noData || CONFIG.noData
25 | CONFIG.depth = opts.depth || CONFIG.depth
26 | CONFIG.hideOtherSidebarContent = opts.hideOtherSidebarContent || CONFIG.hideOtherSidebarContent
27 | CONFIG.namespace = opts.namespace || CONFIG.namespace
28 | }
29 |
30 | const isAuto = CONFIG.paths === 'auto'
31 |
32 | hook.mounted(_ => {
33 | initComponet(CONFIG, vm)
34 | !isAuto && initSearch(CONFIG, vm)
35 | })
36 | hook.doneEach(_ => {
37 | updateComponent(CONFIG, vm)
38 | isAuto && initSearch(CONFIG, vm)
39 | })
40 | }
41 |
42 | $docsify.plugins = [].concat(install, $docsify.plugins)
43 |
--------------------------------------------------------------------------------
/src/plugins/search/search.js:
--------------------------------------------------------------------------------
1 | let INDEXS = {}
2 |
3 | const LOCAL_STORAGE = {
4 | EXPIRE_KEY: 'docsify.search.expires',
5 | INDEX_KEY: 'docsify.search.index'
6 | }
7 |
8 | function resolveExpireKey(namespace) {
9 | return namespace ? `${LOCAL_STORAGE.EXPIRE_KEY}/${namespace}` : LOCAL_STORAGE.EXPIRE_KEY
10 | }
11 | function resolveIndexKey(namespace) {
12 | return namespace ? `${LOCAL_STORAGE.INDEX_KEY}/${namespace}` : LOCAL_STORAGE.INDEX_KEY
13 | }
14 |
15 | function escapeHtml(string) {
16 | const entityMap = {
17 | '&': '&',
18 | '<': '<',
19 | '>': '>',
20 | '"': '"',
21 | '\'': ''',
22 | '/': '/'
23 | }
24 |
25 | return String(string).replace(/[&<>"'/]/g, s => entityMap[s])
26 | }
27 |
28 | function getAllPaths(router) {
29 | const paths = []
30 |
31 | Docsify.dom.findAll('.sidebar-nav a:not(.section-link):not([data-nosearch])').forEach(node => {
32 | const href = node.href
33 | const originHref = node.getAttribute('href')
34 | const path = router.parse(href).path
35 |
36 | if (
37 | path &&
38 | paths.indexOf(path) === -1 &&
39 | !Docsify.util.isAbsolutePath(originHref)
40 | ) {
41 | paths.push(path)
42 | }
43 | })
44 |
45 | return paths
46 | }
47 |
48 | function saveData(maxAge, expireKey, indexKey) {
49 | localStorage.setItem(expireKey, Date.now() + maxAge)
50 | localStorage.setItem(indexKey, JSON.stringify(INDEXS))
51 | }
52 |
53 | export function genIndex(path, content = '', router, depth) {
54 | const tokens = window.marked.lexer(content)
55 | const slugify = window.Docsify.slugify
56 | const index = {}
57 | let slug
58 |
59 | tokens.forEach(token => {
60 | if (token.type === 'heading' && token.depth <= depth) {
61 | slug = router.toURL(path, {id: slugify(token.text)})
62 | index[slug] = {slug, title: token.text, body: ''}
63 | } else {
64 | if (!slug) {
65 | return
66 | }
67 | if (!index[slug]) {
68 | index[slug] = {slug, title: '', body: ''}
69 | } else if (index[slug].body) {
70 | index[slug].body += '\n' + (token.text || '')
71 | } else {
72 | index[slug].body = token.text
73 | }
74 | }
75 | })
76 | slugify.clear()
77 | return index
78 | }
79 |
80 | /**
81 | * @param {String} query
82 | * @returns {Array}
83 | */
84 | export function search(query) {
85 | const matchingResults = []
86 | let data = []
87 | Object.keys(INDEXS).forEach(key => {
88 | data = data.concat(Object.keys(INDEXS[key]).map(page => INDEXS[key][page]))
89 | })
90 |
91 | query = query.trim()
92 | let keywords = query.split(/[\s\-,\\/]+/)
93 | if (keywords.length !== 1) {
94 | keywords = [].concat(query, keywords)
95 | }
96 |
97 | for (let i = 0; i < data.length; i++) {
98 | const post = data[i]
99 | let isMatch = false
100 | let resultStr = ''
101 | const postTitle = post.title && post.title.trim()
102 | const postContent = post.body && post.body.trim()
103 | const postUrl = post.slug || ''
104 |
105 | if (postTitle && postContent) {
106 | keywords.forEach(keyword => {
107 | // From https://github.com/sindresorhus/escape-string-regexp
108 | const regEx = new RegExp(
109 | keyword.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'),
110 | 'gi'
111 | )
112 | let indexTitle = -1
113 | let indexContent = -1
114 |
115 | indexTitle = postTitle && postTitle.search(regEx)
116 | indexContent = postContent && postContent.search(regEx)
117 |
118 | if (indexTitle < 0 && indexContent < 0) {
119 | isMatch = false
120 | } else {
121 | isMatch = true
122 | if (indexContent < 0) {
123 | indexContent = 0
124 | }
125 |
126 | let start = 0
127 | let end = 0
128 |
129 | start = indexContent < 11 ? 0 : indexContent - 10
130 | end = start === 0 ? 70 : indexContent + keyword.length + 60
131 |
132 | if (end > postContent.length) {
133 | end = postContent.length
134 | }
135 |
136 | const matchContent =
137 | '...' +
138 | escapeHtml(postContent)
139 | .substring(start, end)
140 | .replace(regEx, `${keyword}`) +
141 | '...'
142 |
143 | resultStr += matchContent
144 | }
145 | })
146 |
147 | if (isMatch) {
148 | const matchingPost = {
149 | title: escapeHtml(postTitle),
150 | content: resultStr,
151 | url: postUrl
152 | }
153 |
154 | matchingResults.push(matchingPost)
155 | }
156 | }
157 | }
158 |
159 | return matchingResults
160 | }
161 |
162 | export function init(config, vm) {
163 | const isAuto = config.paths === 'auto'
164 |
165 | const expireKey = resolveExpireKey(config.namespace)
166 | const indexKey = resolveIndexKey(config.namespace)
167 |
168 | const isExpired = localStorage.getItem(expireKey) < Date.now()
169 |
170 | INDEXS = JSON.parse(localStorage.getItem(indexKey))
171 |
172 | if (isExpired) {
173 | INDEXS = {}
174 | } else if (!isAuto) {
175 | return
176 | }
177 |
178 | const paths = isAuto ? getAllPaths(vm.router) : config.paths
179 | const len = paths.length
180 | let count = 0
181 |
182 | paths.forEach(path => {
183 | if (INDEXS[path]) {
184 | return count++
185 | }
186 |
187 | Docsify
188 | .get(vm.router.getFile(path), false, vm.config.requestHeaders)
189 | .then(result => {
190 | INDEXS[path] = genIndex(path, result, vm.router, config.depth)
191 | len === ++count && saveData(config.maxAge, expireKey, indexKey)
192 | })
193 | })
194 | }
195 |
--------------------------------------------------------------------------------
/src/plugins/zoom-image.js:
--------------------------------------------------------------------------------
1 | import mediumZoom from 'medium-zoom'
2 |
3 | const matchesSelector = Element.prototype.matches || Element.prototype.webkitMatchesSelector || Element.prototype.msMatchesSelector
4 |
5 | function install(hook) {
6 | let zoom
7 |
8 | hook.doneEach(_ => {
9 | let elms = Array.apply(null, document.querySelectorAll('.markdown-section img:not(.emoji):not([data-no-zoom])'))
10 |
11 | elms = elms.filter(elm => matchesSelector.call(elm, 'a img') === false)
12 |
13 | if (zoom) {
14 | zoom.detach()
15 | }
16 |
17 | zoom = mediumZoom(elms)
18 | })
19 | }
20 |
21 | $docsify.plugins = [].concat(install, $docsify.plugins)
22 |
--------------------------------------------------------------------------------
/src/themes/basic/_coverpage.styl:
--------------------------------------------------------------------------------
1 | section.cover
2 | align-items center
3 | background-position center center
4 | background-repeat no-repeat
5 | background-size cover
6 | height 100vh
7 | display none
8 |
9 | &.show
10 | display flex
11 |
12 | &.has-mask .mask
13 | background-color $color-bg
14 | opacity 0.8
15 | position absolute
16 | top 0
17 | height 100%
18 | width 100%
19 |
20 | .cover-main
21 | flex 1
22 | margin -20px 16px 0
23 | text-align center
24 | z-index 1
25 |
26 | a
27 | color inherit
28 | text-decoration none
29 |
30 | &:hover
31 | text-decoration none
32 |
33 | p
34 | line-height 1.5rem
35 | margin 1em 0
36 |
37 | h1
38 | color inherit
39 | font-size 2.5rem
40 | font-weight 300
41 | margin 0.625rem 0 2.5rem
42 | position relative
43 | text-align center
44 |
45 | a
46 | display block
47 |
48 | small
49 | bottom -0.4375rem
50 | font-size 1rem
51 | position absolute
52 |
53 | blockquote
54 | font-size 1.5rem
55 | text-align center
56 |
57 | ul
58 | line-height 1.8
59 | list-style-type none
60 | margin 1em auto
61 | max-width 500px
62 | padding 0
63 |
64 | .cover-main > p:last-child a
65 | border-color var(--theme-color, $color-primary)
66 | border-radius 2rem
67 | border-style solid
68 | border-width 1px
69 | box-sizing border-box
70 | color var(--theme-color, $color-primary)
71 | display inline-block
72 | font-size 1.05rem
73 | letter-spacing 0.1rem
74 | margin 0.5rem 1rem
75 | padding 0.75em 2rem
76 | text-decoration none
77 | transition all 0.15s ease
78 |
79 | &:last-child
80 | background-color var(--theme-color, $color-primary)
81 | color #fff
82 |
83 | &:hover
84 | color inherit
85 | opacity 0.8
86 |
87 | &:hover
88 | color inherit
89 |
90 | blockquote > p > a
91 | border-bottom 2px solid var(--theme-color, $color-primary)
92 | transition color 0.3s
93 |
94 | &:hover
95 | color var(--theme-color, $color-primary)
96 |
--------------------------------------------------------------------------------
/src/themes/buble.styl:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Inconsolata|Inconsolata-Bold')
2 |
3 | $color-primary = #0074d9
4 | $color-bg = #fff
5 | $color-text = #34495e
6 | $sidebar-width = 16rem
7 |
8 | @import 'basic/_layout'
9 | @import 'basic/_coverpage'
10 |
11 | /* sidebar */
12 | .sidebar
13 | color #364149
14 | background-color $color-bg
15 |
16 | a
17 | color #666
18 | text-decoration none
19 |
20 | li
21 | list-style none
22 | margin 0
23 | padding 0.2em 0 0.2em 0
24 |
25 | ul li ul
26 | padding 0
27 |
28 | li.active
29 | a
30 | color #333
31 |
32 | background-color #eee
33 |
34 | .markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section strong
35 | color #333
36 | font-weight 400
37 |
38 | .markdown-section a
39 | color var(--theme-color, $color-primary)
40 | font-weight 400
41 |
42 | .markdown-section p, .markdown-section ul, .markdown-section ol
43 | line-height 1.6rem
44 | margin 0 0 1em 0
45 | word-spacing 0.05rem
46 |
47 | .markdown-section h1
48 | font-size 2rem
49 | font-weight 500
50 | margin 0 0 1rem
51 |
52 | .markdown-section h2
53 | font-size 1.8rem
54 | font-weight 400
55 | margin 0 0 1rem 0
56 | padding 1rem 0 0 0
57 |
58 | .markdown-section h3
59 | font-size 1.5rem
60 | margin 52px 0 1.2rem
61 |
62 | .markdown-section h4
63 | font-size 1.25rem
64 |
65 | .markdown-section h5
66 | font-size 1rem
67 |
68 | .markdown-section h6
69 | color #777
70 | font-size 1rem
71 |
72 | .markdown-section figure, .markdown-section p, .markdown-section ul, .markdown-section ol
73 | margin 1.2em 0
74 |
75 | .markdown-section ul, .markdown-section ol
76 | padding-left 1.5rem
77 |
78 | .markdown-section li
79 | line-height 1.5
80 | margin 0
81 |
82 | .markdown-section blockquote
83 | border-left 4px solid var(--theme-color, $color-primary)
84 | color #858585
85 | margin 2em 0
86 | padding-left 20px
87 |
88 | .markdown-section blockquote p
89 | font-weight 600
90 | margin-left 0
91 |
92 | .markdown-section iframe
93 | margin 1em 0
94 |
95 | .markdown-section em
96 | color #7f8c8d
97 |
98 | .markdown-section code
99 | background-color #f9f9f9
100 | border-radius 3px
101 | font-family Inconsolata
102 | padding 0.2em 0.4rem
103 | white-space nowrap
104 |
105 | .markdown-section pre
106 | background-color #f9f9f9
107 | border-left 2px solid #eee
108 | font-family Inconsolata
109 | font-size 16px
110 | margin 0 0 1em 0
111 | padding 8px
112 | padding 0 10px 12px 0
113 | overflow auto
114 | word-wrap normal
115 | position relative
116 |
117 | /* code highlight */
118 | .token.cdata, .token.comment, .token.doctype, .token.prolog
119 | color #93a1a1 /* base1 */
120 |
121 | .token.punctuation
122 | color #586e75 /* base01 */
123 |
124 | .namespace
125 | opacity 0.7
126 |
127 | .token.property, .token.tag, .token.boolean, .token.number, .token.constant, .token.symbol, .token.deleted
128 | color #268bd2 /* blue */
129 |
130 | .token.selector, .token.attr-name, .token.string, .token.char, .token.builtin, .token.url, .token.inserted
131 | color #2aa198 /* cyan */
132 |
133 | .token.entity
134 | color #657b83 /* base00 */
135 | background #eee8d5 /* base2 */
136 |
137 | .token.atrule, .token.attr-value, .token.keyword
138 | color #a11 /* green */
139 |
140 | .token.function
141 | color #b58900 /* yellow */
142 |
143 | .token.regex, .token.important, .token.variable
144 | color #cb4b16 /* orange */
145 |
146 | .token.important, .token.bold
147 | font-weight bold
148 |
149 | .token.italic
150 | font-style italic
151 |
152 | .token.entity
153 | cursor help
154 |
155 | .markdown-section pre > code
156 | background-color #f8f8f8
157 | border-radius 2px
158 | display block
159 | font-family Inconsolata
160 | line-height 1.1rem
161 | max-width inherit
162 | overflow inherit
163 | padding 20px 0.8em 20px
164 | position relative
165 | white-space inherit
166 |
167 | .markdown-section code::after, .markdown-section code::before
168 | letter-spacing 0.05rem
169 |
170 | code .token
171 | -webkit-font-smoothing initial
172 | -moz-osx-font-smoothing initial
173 | min-height 1.5rem
174 |
--------------------------------------------------------------------------------
/src/themes/dark.styl:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600')
2 |
3 | $color-primary = #ea6f5a
4 | $color-bg = #3f3f3f
5 | $color-text = #c8c8c8
6 | $sidebar-width = 300px
7 |
8 | @import 'basic/_layout'
9 | @import 'basic/_coverpage'
10 |
11 | body
12 | background-color $color-bg
13 |
14 | /* sidebar */
15 | .sidebar
16 | background-color $color-bg
17 | color #c8c8c8
18 |
19 | li
20 | margin 6px 15px 6px 0
21 |
22 | ul li a
23 | color #c8c8c8
24 | font-size 14px
25 | overflow hidden
26 | text-decoration none
27 | text-overflow ellipsis
28 | white-space nowrap
29 |
30 | &:hover
31 | text-decoration underline
32 |
33 | ul li ul
34 | padding 0
35 |
36 | ul li.active > a
37 | color var(--theme-color, $color-primary)
38 | font-weight 600
39 |
40 | /* markdown content found on pages */
41 | .markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section strong
42 | color #657b83
43 | font-weight 600
44 |
45 | .markdown-section a
46 | color var(--theme-color, $color-primary)
47 | font-weight 600
48 |
49 | .markdown-section h1
50 | font-size 2rem
51 | margin 0 0 1rem
52 |
53 | .markdown-section h2
54 | font-size 1.75rem
55 | margin 45px 0 0.8rem
56 |
57 | .markdown-section h3
58 | font-size 1.5rem
59 | margin 40px 0 0.6rem
60 |
61 | .markdown-section h4
62 | font-size 1.25rem
63 |
64 | .markdown-section h5
65 | font-size 1rem
66 |
67 | .markdown-section h6
68 | color #777
69 | font-size 1rem
70 |
71 | .markdown-section figure, .markdown-section p, .markdown-section ul, .markdown-section ol
72 | margin 1.2em 0
73 |
74 | .markdown-section p, .markdown-section ul, .markdown-section ol
75 | line-height 1.6rem
76 | word-spacing 0.05rem
77 |
78 | .markdown-section ul, .markdown-section ol
79 | padding-left 1.5rem
80 |
81 | .markdown-section blockquote
82 | border-left 4px solid var(--theme-color, $color-primary)
83 | color #858585
84 | margin 2em 0
85 | padding-left 20px
86 |
87 | .markdown-section blockquote p
88 | font-weight 600
89 | margin-left 0
90 |
91 | .markdown-section iframe
92 | margin 1em 0
93 |
94 | .markdown-section em
95 | color #7f8c8d
96 |
97 | .markdown-section code
98 | background-color #282828
99 | border-radius 2px
100 | color #657b83
101 | font-family 'Roboto Mono', Monaco, courier, monospace
102 | font-size 0.8rem
103 | margin 0 2px
104 | padding 3px 5px
105 | white-space pre-wrap
106 |
107 | .markdown-section pre
108 | -moz-osx-font-smoothing initial
109 | -webkit-font-smoothing initial
110 | background-color #282828
111 | font-family 'Roboto Mono', Monaco, courier, monospace
112 | line-height 1.5rem
113 | margin 1.2em 0
114 | overflow auto
115 | padding 0 1.4rem
116 | position relative
117 | word-wrap normal
118 |
119 | /* code highlight */
120 | .token.comment, .token.prolog, .token.doctype, .token.cdata
121 | color #8e908c
122 |
123 | .token.namespace
124 | opacity 0.7
125 |
126 | .token.boolean, .token.number
127 | color #c76b29
128 |
129 | .token.punctuation
130 | color #525252
131 |
132 | .token.property
133 | color #c08b30
134 |
135 | .token.tag
136 | color #2973b7
137 |
138 | .token.string
139 | color var(--theme-color, $color-primary)
140 |
141 | .token.selector
142 | color #6679cc
143 |
144 | .token.attr-name
145 | color #2973b7
146 |
147 | .token.entity, .token.url, .language-css .token.string, .style .token.string
148 | color #22a2c9
149 |
150 | .token.attr-value, .token.control, .token.directive, .token.unit
151 | color var(--theme-color, $color-primary)
152 |
153 | .token.keyword
154 | color #e96900
155 |
156 | .token.statement, .token.regex, .token.atrule
157 | color #22a2c9
158 |
159 | .token.placeholder, .token.variable
160 | color #3d8fd1
161 |
162 | .token.deleted
163 | text-decoration line-through
164 |
165 | .token.inserted
166 | border-bottom 1px dotted #202746
167 | text-decoration none
168 |
169 | .token.italic
170 | font-style italic
171 |
172 | .token.important, .token.bold
173 | font-weight bold
174 |
175 | .token.important
176 | color #c94922
177 |
178 | .token.entity
179 | cursor help
180 |
181 | .markdown-section pre > code
182 | -moz-osx-font-smoothing initial
183 | -webkit-font-smoothing initial
184 | background-color #282828
185 | border-radius 2px
186 | color #657b83
187 | display block
188 | font-family 'Roboto Mono', Monaco, courier, monospace
189 | font-size 0.8rem
190 | line-height inherit
191 | margin 0 2px
192 | max-width inherit
193 | overflow inherit
194 | padding 2.2em 5px
195 | white-space inherit
196 |
197 | .markdown-section code::after, .markdown-section code::before
198 | letter-spacing 0.05rem
199 |
200 | code .token
201 | -moz-osx-font-smoothing initial
202 | -webkit-font-smoothing initial
203 | min-height 1.5rem
204 |
205 | pre::after
206 | color #ccc
207 | content attr(data-lang)
208 | font-size 0.6rem
209 | font-weight 600
210 | height 15px
211 | line-height 15px
212 | padding 5px 10px 0
213 | position absolute
214 | right 0
215 | text-align right
216 | top 0
217 |
218 | .markdown-section p.tip
219 | background-color #282828
220 | color #657b83
221 |
222 | input[type='search']
223 | background #4f4f4f
224 | border-color #4f4f4f
225 | color #c8c8c8
226 |
--------------------------------------------------------------------------------
/src/themes/pure.styl:
--------------------------------------------------------------------------------
1 | $color-primary = #000
2 | $color-bg = #fff
3 | $color-text = #000
4 | $sidebar-width = 300px
5 |
6 | @import 'basic/_layout'
7 | @import 'basic/_coverpage'
8 |
--------------------------------------------------------------------------------
/src/themes/vue.styl:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600')
2 |
3 | $color-primary = #42b983
4 | $color-bg = #fff
5 | $color-text = #34495e
6 | $sidebar-width = 300px
7 |
8 | @import 'basic/_layout'
9 | @import 'basic/_coverpage'
10 |
11 | body
12 | background-color $color-bg
13 |
14 | /* sidebar */
15 | .sidebar
16 | background-color $color-bg
17 | color #364149
18 |
19 | li
20 | margin 6px 0 6px 0
21 |
22 | ul li a
23 | color #505d6b
24 | font-size 14px
25 | font-weight normal
26 | overflow hidden
27 | text-decoration none
28 | text-overflow ellipsis
29 | white-space nowrap
30 |
31 | &:hover
32 | text-decoration underline
33 |
34 | ul li ul
35 | padding 0
36 |
37 | ul li.active > a
38 | border-right 2px solid
39 | color var(--theme-color, $color-primary)
40 | font-weight 600
41 |
42 | .app-sub-sidebar
43 | li
44 | &::before
45 | content '-'
46 | padding-right 4px
47 | float left
48 |
49 | /* markdown content found on pages */
50 | .markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section strong
51 | color #2c3e50
52 | font-weight 600
53 |
54 | .markdown-section a
55 | color var(--theme-color, $color-primary)
56 | font-weight 600
57 |
58 | .markdown-section h1
59 | font-size 2rem
60 | margin 0 0 1rem
61 |
62 | .markdown-section h2
63 | font-size 1.75rem
64 | margin 45px 0 0.8rem
65 |
66 | .markdown-section h3
67 | font-size 1.5rem
68 | margin 40px 0 0.6rem
69 |
70 | .markdown-section h4
71 | font-size 1.25rem
72 |
73 | .markdown-section h5
74 | font-size 1rem
75 |
76 | .markdown-section h6
77 | color #777
78 | font-size 1rem
79 |
80 | .markdown-section figure, .markdown-section p
81 | margin 1.2em 0
82 |
83 | .markdown-section p, .markdown-section ul, .markdown-section ol
84 | line-height 1.6rem
85 | word-spacing 0.05rem
86 |
87 | .markdown-section ul, .markdown-section ol
88 | padding-left 1.5rem
89 |
90 | .markdown-section blockquote
91 | border-left 4px solid var(--theme-color, $color-primary)
92 | color #858585
93 | margin 2em 0
94 | padding-left 20px
95 |
96 | .markdown-section blockquote p
97 | font-weight 600
98 | margin-left 0
99 |
100 | .markdown-section iframe
101 | margin 1em 0
102 |
103 | .markdown-section em
104 | color #7f8c8d
105 |
106 | .markdown-section code
107 | background-color #f8f8f8
108 | border-radius 2px
109 | color #e96900
110 | font-family 'Roboto Mono', Monaco, courier, monospace
111 | font-size 0.8rem
112 | margin 0 2px
113 | padding 3px 5px
114 | white-space pre-wrap
115 |
116 | .markdown-section pre
117 | -moz-osx-font-smoothing initial
118 | -webkit-font-smoothing initial
119 | background-color #f8f8f8
120 | font-family 'Roboto Mono', Monaco, courier, monospace
121 | line-height 1.5rem
122 | margin 1.2em 0
123 | overflow auto
124 | padding 0 1.4rem
125 | position relative
126 | word-wrap normal
127 |
128 | /* code highlight */
129 | .token.comment, .token.prolog, .token.doctype, .token.cdata
130 | color #8e908c
131 |
132 | .token.namespace
133 | opacity 0.7
134 |
135 | .token.boolean, .token.number
136 | color #c76b29
137 |
138 | .token.punctuation
139 | color #525252
140 |
141 | .token.property
142 | color #c08b30
143 |
144 | .token.tag
145 | color #2973b7
146 |
147 | .token.string
148 | color var(--theme-color, $color-primary)
149 |
150 | .token.selector
151 | color #6679cc
152 |
153 | .token.attr-name
154 | color #2973b7
155 |
156 | .token.entity, .token.url, .language-css .token.string, .style .token.string
157 | color #22a2c9
158 |
159 | .token.attr-value, .token.control, .token.directive, .token.unit
160 | color var(--theme-color, $color-primary)
161 |
162 | .token.keyword, .token.function
163 | color #e96900
164 |
165 | .token.statement, .token.regex, .token.atrule
166 | color #22a2c9
167 |
168 | .token.placeholder, .token.variable
169 | color #3d8fd1
170 |
171 | .token.deleted
172 | text-decoration line-through
173 |
174 | .token.inserted
175 | border-bottom 1px dotted #202746
176 | text-decoration none
177 |
178 | .token.italic
179 | font-style italic
180 |
181 | .token.important, .token.bold
182 | font-weight bold
183 |
184 | .token.important
185 | color #c94922
186 |
187 | .token.entity
188 | cursor help
189 |
190 | .markdown-section pre > code
191 | -moz-osx-font-smoothing initial
192 | -webkit-font-smoothing initial
193 | background-color #f8f8f8
194 | border-radius 2px
195 | color #525252
196 | display block
197 | font-family 'Roboto Mono', Monaco, courier, monospace
198 | font-size 0.8rem
199 | line-height inherit
200 | margin 0 2px
201 | max-width inherit
202 | overflow inherit
203 | padding 2.2em 5px
204 | white-space inherit
205 |
206 | .markdown-section code::after, .markdown-section code::before
207 | letter-spacing 0.05rem
208 |
209 | code .token
210 | -moz-osx-font-smoothing initial
211 | -webkit-font-smoothing initial
212 | min-height 1.5rem
213 |
214 | pre::after
215 | color #ccc
216 | content attr(data-lang)
217 | font-size 0.6rem
218 | font-weight 600
219 | height 15px
220 | line-height 15px
221 | padding 5px 10px 0
222 | position absolute
223 | right 0
224 | text-align right
225 | top 0
226 |
--------------------------------------------------------------------------------