Site updated: 2023-09-22 21:57:09
192
archives/2023/03/index.html
Normal file
192
archives/2023/03/page/2/index.html
Normal file
192
archives/2023/04/index.html
Normal file
192
archives/2023/04/page/2/index.html
Normal file
192
archives/2023/05/index.html
Normal file
192
archives/2023/05/page/2/index.html
Normal file
192
archives/2023/06/index.html
Normal file
192
archives/2023/07/index.html
Normal file
192
archives/2023/08/index.html
Normal file
192
archives/2023/09/index.html
Normal file
192
archives/2023/index.html
Normal file
192
archives/2023/page/2/index.html
Normal file
192
archives/2023/page/3/index.html
Normal file
192
archives/2023/page/4/index.html
Normal file
192
archives/2023/page/5/index.html
Normal file
192
archives/2023/page/6/index.html
Normal file
192
archives/2023/page/7/index.html
Normal file
192
archives/index.html
Normal file
192
archives/page/2/index.html
Normal file
192
archives/page/3/index.html
Normal file
192
archives/page/4/index.html
Normal file
192
archives/page/5/index.html
Normal file
192
archives/page/6/index.html
Normal file
192
archives/page/7/index.html
Normal file
283
baidusitemap.xml
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/32696.html</loc>
|
||||||
|
<lastmod>2023-09-22</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/24183.html</loc>
|
||||||
|
<lastmod>2023-09-22</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/8957.html</loc>
|
||||||
|
<lastmod>2023-09-21</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/29985.html</loc>
|
||||||
|
<lastmod>2023-09-21</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/25154.html</loc>
|
||||||
|
<lastmod>2023-09-19</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/39654.html</loc>
|
||||||
|
<lastmod>2023-09-18</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/53088.html</loc>
|
||||||
|
<lastmod>2023-09-14</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/64695.html</loc>
|
||||||
|
<lastmod>2023-09-14</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/5727.html</loc>
|
||||||
|
<lastmod>2023-09-12</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/19306.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/63724.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/60685.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/18459.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/28118.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/63587.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/20683.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/48020.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/21883.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/17259.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/24637.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/53306.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/50465.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/24606.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/73.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/19270.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/29367.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/50908.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/62439.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/26768.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/22654.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/56742.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/32679.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/32246.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/855.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/12929.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/38823.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/64205.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/47407.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/13813.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/54835.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/60780.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/30127.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/6932.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/1530.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/1416.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/13579.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/47003.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/51007.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/14438.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/17772.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/63333.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/1727.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/36397.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/45572.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/432.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/28687.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/60684.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/3661.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/35630.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/27166.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/46306.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/22202.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/7353.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/29250.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/31385.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/11844.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/6319.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/46317.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/40445.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://jasonsgong.gitee.io/posts/45726.html</loc>
|
||||||
|
<lastmod>2023-09-11</lastmod>
|
||||||
|
</url>
|
||||||
|
</urlset>
|
194
categories/index.html
Normal file
192
categories/个人/index.html
Normal file
192
categories/前端/index.html
Normal file
192
categories/后端/index.html
Normal file
192
categories/后端/page/2/index.html
Normal file
192
categories/后端/page/3/index.html
Normal file
192
categories/后端/page/4/index.html
Normal file
192
categories/后端/page/5/index.html
Normal file
192
categories/后端/page/6/index.html
Normal file
192
categories/测试/index.html
Normal file
192
categories/运维/index.html
Normal file
192
categories/面试/index.html
Normal file
749
css/hbe.style.css
Normal file
@ -0,0 +1,749 @@
|
|||||||
|
.hbe,
|
||||||
|
.hbe:after,
|
||||||
|
.hbe:before {
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-container{
|
||||||
|
margin: 0 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.hbe-content {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 150%;
|
||||||
|
padding: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 1em;
|
||||||
|
width: 80%;
|
||||||
|
min-width: 200px;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field {
|
||||||
|
line-height: normal;
|
||||||
|
font-size: 100%;
|
||||||
|
margin: 0;
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
float: right;
|
||||||
|
padding: 0.8em;
|
||||||
|
width: 60%;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
background: #f0f0f0;
|
||||||
|
color: #aaa;
|
||||||
|
font-weight: 400;
|
||||||
|
font-family: "Avenir Next", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
-webkit-appearance: none; /* for box shadows to show on iOS */
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label {
|
||||||
|
display: inline-block;
|
||||||
|
float: right;
|
||||||
|
padding: 0 1em;
|
||||||
|
width: 40%;
|
||||||
|
color: #696969;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 70.25%;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-khtml-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-content {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
padding: 1.6em 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-graphic {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
fill: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hbe button in post page */
|
||||||
|
.hbe-button {
|
||||||
|
width: 130px;
|
||||||
|
height: 40px;
|
||||||
|
background: linear-gradient(to bottom, #4eb5e5 0%,#389ed5 100%); /* W3C */
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
position: relative;
|
||||||
|
border-bottom: 4px solid #2b8bc6;
|
||||||
|
color: #fbfbfb;
|
||||||
|
font-weight: 600;
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
text-shadow: 1px 1px 1px rgba(0,0,0,.4);
|
||||||
|
font-size: 15px;
|
||||||
|
text-align: left;
|
||||||
|
text-indent: 5px;
|
||||||
|
box-shadow: 0px 3px 0px 0px rgba(0,0,0,.2);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-button:active {
|
||||||
|
box-shadow: 0px 2px 0px 0px rgba(0,0,0,.2);
|
||||||
|
top: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-button:after {
|
||||||
|
content: "";
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
display: block;
|
||||||
|
border-top: 20px solid #187dbc;
|
||||||
|
border-bottom: 20px solid #187dbc;
|
||||||
|
border-left: 16px solid transparent;
|
||||||
|
border-right: 20px solid #187dbc;
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0.6;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
border-radius: 0 5px 5px 0;
|
||||||
|
}
|
||||||
|
/* hbe button in post page */
|
||||||
|
|
||||||
|
/* default theme {{{ */
|
||||||
|
.hbe-input-default {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-default {
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
padding: 0.5em;
|
||||||
|
margin-bottom: 2em;
|
||||||
|
color: #f9f7f6;
|
||||||
|
z-index: 100;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-default {
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
text-align: left;
|
||||||
|
padding: 0.5em 0;
|
||||||
|
pointer-events: none;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-default::before,
|
||||||
|
.hbe-input-label-default::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-default::before {
|
||||||
|
height: 100%;
|
||||||
|
background: #666666;
|
||||||
|
top: 0;
|
||||||
|
-webkit-transform: translate3d(0, -100%, 0);
|
||||||
|
transform: translate3d(0, -100%, 0);
|
||||||
|
-webkit-transition: -webkit-transform 0.2s;
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-default::after {
|
||||||
|
height: 2px;
|
||||||
|
background: #666666;
|
||||||
|
top: 100%;
|
||||||
|
-webkit-transition: opacity 0.2s;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-content-default {
|
||||||
|
padding: 0;
|
||||||
|
-webkit-transform-origin: 0 0;
|
||||||
|
transform-origin: 0 0;
|
||||||
|
-webkit-transition: -webkit-transform 0.2s, color 0.2s;
|
||||||
|
transition: transform 0.2s, color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-default:focus,
|
||||||
|
.hbe-input--filled .hbe-input-field-default {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transition: opacity 0s 0.2s;
|
||||||
|
transition: opacity 0s 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-default::before,
|
||||||
|
.hbe-input-label-default::after,
|
||||||
|
.hbe-input-label-content-default,
|
||||||
|
.hbe-input-field-default:focus,
|
||||||
|
.hbe-input--filled .hbe-input-field-default {
|
||||||
|
-webkit-transition-timing-function: cubic-bezier(0, 0.25, 0.5, 1);
|
||||||
|
transition-timing-function: cubic-bezier(0, 0.25, 0.5, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-default:focus + .hbe-input-label-default::before,
|
||||||
|
.hbe-input--filled .hbe-input-label-default::before {
|
||||||
|
-webkit-transform: translate3d(0, 0, 0);
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-default:focus + .hbe-input-label-default::after,
|
||||||
|
.hbe-input--filled .hbe-input-label-default::after {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-default:focus + .hbe-input-label-default .hbe-input-label-content-default,
|
||||||
|
.hbe-input--filled .hbe-input-label-default .hbe-input-label-content-default {
|
||||||
|
color: #555555;
|
||||||
|
-webkit-transform: translate3d(0, 2.1em, 0) scale3d(0.65, 0.65, 1);
|
||||||
|
transform: translate3d(0, 2.1em, 0) scale3d(0.65, 0.65, 1);
|
||||||
|
}
|
||||||
|
/* default theme }}} */
|
||||||
|
|
||||||
|
/* up theme {{{ */
|
||||||
|
.hbe-input-up {
|
||||||
|
overflow: hidden;
|
||||||
|
padding-top: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-up {
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
opacity: 0;
|
||||||
|
padding: 0.35em;
|
||||||
|
z-index: 100;
|
||||||
|
color: #837482;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-up {
|
||||||
|
width: 100%;
|
||||||
|
bottom: 0;
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
text-align: left;
|
||||||
|
color: #8E9191;
|
||||||
|
padding: 0 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-up::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 4em;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
background: #fff;
|
||||||
|
border-top: 4px solid #9B9F9F;
|
||||||
|
-webkit-transform: translate3d(0, -3px, 0);
|
||||||
|
transform: translate3d(0, -3px, 0);
|
||||||
|
-webkit-transition: -webkit-transform 0.4s;
|
||||||
|
transition: transform 0.4s;
|
||||||
|
-webkit-transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1);
|
||||||
|
transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-content-up {
|
||||||
|
padding: 0.5em 0;
|
||||||
|
-webkit-transform-origin: 0% 100%;
|
||||||
|
transform-origin: 0% 100%;
|
||||||
|
-webkit-transition: -webkit-transform 0.4s, color 0.4s;
|
||||||
|
transition: transform 0.4s, color 0.4s;
|
||||||
|
-webkit-transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1);
|
||||||
|
transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-up:focus,
|
||||||
|
.input--filled .hbe-input-field-up {
|
||||||
|
cursor: text;
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transition: opacity 0s 0.4s;
|
||||||
|
transition: opacity 0s 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-up:focus + .hbe-input-label-up::before,
|
||||||
|
.input--filled .hbe-input-label-up::before {
|
||||||
|
-webkit-transition-delay: 0.05s;
|
||||||
|
transition-delay: 0.05s;
|
||||||
|
-webkit-transform: translate3d(0, -3.3em, 0);
|
||||||
|
transform: translate3d(0, -3.3em, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-up:focus + .hbe-input-label-up .hbe-input-label-content-up,
|
||||||
|
.input--filled .hbe-input-label-content-up {
|
||||||
|
color: #6B6E6E;
|
||||||
|
-webkit-transform: translate3d(0, -3.3em, 0) scale3d(0.81, 0.81, 1);
|
||||||
|
transform: translate3d(0, -3.3em, 0) scale3d(0.81, 0.81, 1);
|
||||||
|
}
|
||||||
|
/* up theme }}} */
|
||||||
|
|
||||||
|
/* wave theme {{{ */
|
||||||
|
.hbe-input-wave {
|
||||||
|
overflow: hidden;
|
||||||
|
padding-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-wave {
|
||||||
|
padding: 0.5em 0em 0.25em;
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
color: #9da8b2;
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-wave {
|
||||||
|
position: absolute;
|
||||||
|
top: 0.95em;
|
||||||
|
font-size: 0.85em;
|
||||||
|
left: 0;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
padding: 0em;
|
||||||
|
pointer-events: none;
|
||||||
|
-webkit-transform-origin: 0 0;
|
||||||
|
transform-origin: 0 0;
|
||||||
|
-webkit-transition: -webkit-transform 0.2s 0.15s, color 1s;
|
||||||
|
transition: transform 0.2s 0.15s, color 1s;
|
||||||
|
-webkit-transition-timing-function: ease-out;
|
||||||
|
transition-timing-function: ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-graphic-wave {
|
||||||
|
stroke: #92989e;
|
||||||
|
pointer-events: none;
|
||||||
|
-webkit-transition: -webkit-transform 0.7s, stroke 0.7s;
|
||||||
|
transition: transform 0.7s, stroke 0.7s;
|
||||||
|
-webkit-transition-timing-function: cubic-bezier(0, 0.25, 0.5, 1);
|
||||||
|
transition-timing-function: cubic-bezier(0, 0.25, 0.5, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-wave:focus + .hbe-input-label-wave,
|
||||||
|
.input--filled .hbe-input-label-wave {
|
||||||
|
color: #333;
|
||||||
|
-webkit-transform: translate3d(0, -1.25em, 0) scale3d(0.75, 0.75, 1);
|
||||||
|
transform: translate3d(0, -1.25em, 0) scale3d(0.75, 0.75, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-wave:focus ~ .hbe-graphic-wave,
|
||||||
|
.input--filled .graphic-wave {
|
||||||
|
stroke: #333;
|
||||||
|
-webkit-transform: translate3d(-66.6%, 0, 0);
|
||||||
|
transform: translate3d(-66.6%, 0, 0);
|
||||||
|
}
|
||||||
|
/* wave theme }}} */
|
||||||
|
|
||||||
|
/* flip theme {{{ */
|
||||||
|
.hbe-input-field-flip {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #d0d1d0;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
-webkit-transition: background-color 0.25s, border-color 0.25s;
|
||||||
|
transition: background-color 0.25s, border-color 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-flip {
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0 1.25em;
|
||||||
|
-webkit-transform: translate3d(0, 3em, 0);
|
||||||
|
transform: translate3d(0, 3em, 0);
|
||||||
|
-webkit-transition: -webkit-transform 0.25s;
|
||||||
|
transition: transform 0.25s ;
|
||||||
|
-webkit-transition-timing-function: ease-in-out;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-content-flip {
|
||||||
|
color: #8B8C8B;
|
||||||
|
padding: 0.25em 0;
|
||||||
|
-webkit-transition: -webkit-transform 0.25s;
|
||||||
|
transition: transform 0.25s;
|
||||||
|
-webkit-transition-timing-function: ease-in-out;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-content-flip::after {
|
||||||
|
content: attr(data-content);
|
||||||
|
position: absolute;
|
||||||
|
font-weight: 800;
|
||||||
|
bottom: 100%;
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
color: #666666;
|
||||||
|
padding: 0.25em 0;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-flip:focus + .hbe-input-label-flip,
|
||||||
|
.input--filled .hbe-input-label-flip {
|
||||||
|
-webkit-transform: translate3d(0, 0, 0);
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-flip:focus + .hbe-input-label-flip .hbe-input-label-content-flip,
|
||||||
|
.input--filled .hbe-input-label-content-flip {
|
||||||
|
-webkit-transform: translate3d(0, 100%, 0);
|
||||||
|
transform: translate3d(0, 100%, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-flip:focus + .hbe-input-field-flip,
|
||||||
|
.input--filled .hbe-input-field-flip {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: #666666;
|
||||||
|
}
|
||||||
|
/* flip theme }}} */
|
||||||
|
|
||||||
|
/* xray theme {{{ */
|
||||||
|
.hbe-input-xray {
|
||||||
|
overflow: hidden;
|
||||||
|
padding-bottom: 2.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-xray {
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 1.2em;
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
color: #84AF9B ;
|
||||||
|
font-size: 1.55em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-xray {
|
||||||
|
position: absolute;
|
||||||
|
top: 2em;
|
||||||
|
left: 0;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
padding: 0em;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
color: #84AF9B ;
|
||||||
|
pointer-events: none;
|
||||||
|
-webkit-transform-origin: 0 0;
|
||||||
|
transform-origin: 0 0;
|
||||||
|
-webkit-transition: -webkit-transform 0.2s 0.1s, color 0.3s;
|
||||||
|
transition: transform 0.2s 0.1s, color 0.3s;
|
||||||
|
-webkit-transition-timing-function: ease-out;
|
||||||
|
transition-timing-function: ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-graphic-xray {
|
||||||
|
stroke: #84AF9B ;
|
||||||
|
pointer-events: none;
|
||||||
|
stroke-width: 2px;
|
||||||
|
top: 1.25em;
|
||||||
|
bottom: 0px;
|
||||||
|
height: 3.275em;
|
||||||
|
-webkit-transition: -webkit-transform 0.7s, stroke 0.7s;
|
||||||
|
transition: transform 0.7s, stroke 0.7s;
|
||||||
|
-webkit-transition-timing-function: cubic-bezier(0, 0.25, 0.5, 1);
|
||||||
|
transition-timing-function: cubic-bezier(0, 0.25, 0.5, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-xray:focus + .hbe-input-label-xray,
|
||||||
|
.input--filled .hbe-input-label-xray {
|
||||||
|
color: #84AF9B ;
|
||||||
|
-webkit-transform: translate3d(0, 3.5em, 0) scale3d(0.85, 0.85, 1);
|
||||||
|
transform: translate3d(0, 3.5em, 0) scale3d(0.85, 0.85, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-xray:focus ~ .hbe-graphic-xray,
|
||||||
|
.input--filled .graphic-xray {
|
||||||
|
stroke: #84AF9B ;
|
||||||
|
-webkit-transform: translate3d(-66.6%, 0, 0);
|
||||||
|
transform: translate3d(-66.6%, 0, 0);
|
||||||
|
}
|
||||||
|
/* xray theme }}} */
|
||||||
|
|
||||||
|
/* blink theme {{{ */
|
||||||
|
.hbe-input-blink {
|
||||||
|
padding-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-blink {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.8em 0.5em;
|
||||||
|
background: transparent;
|
||||||
|
border: 2px solid;
|
||||||
|
color: #8781bd;
|
||||||
|
-webkit-transition: border-color 0.25s;
|
||||||
|
transition: border-color 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-blink {
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
text-align: left;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
-webkit-transform: translate3d(0, 3em, 0);
|
||||||
|
transform: translate3d(0, 3em, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-content-blink {
|
||||||
|
padding: 0 1em;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #b5b5b5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-content-blink::after {
|
||||||
|
content: attr(data-content);
|
||||||
|
position: absolute;
|
||||||
|
top: -200%;
|
||||||
|
left: 0;
|
||||||
|
color: #8781bd ;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-blink:focus,
|
||||||
|
.input--filled .hbe-input-field-blink {
|
||||||
|
border-color: #8781bd ;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-blink:focus + .hbe-input-label-blink,
|
||||||
|
.input--filled .hbe-input-label-blink {
|
||||||
|
-webkit-animation: anim-blink-1 0.25s forwards;
|
||||||
|
animation: anim-blink-1 0.25s forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-blink:focus + .hbe-input-label-blink .hbe-input-label-content-blink,
|
||||||
|
.input--filled .hbe-input-label-content-blink {
|
||||||
|
-webkit-animation: anim-blink-2 0.25s forwards ease-in;
|
||||||
|
animation: anim-blink-2 0.25s forwards ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes anim-blink-1 {
|
||||||
|
0%, 70% {
|
||||||
|
-webkit-transform: translate3d(0, 3em, 0);
|
||||||
|
transform: translate3d(0, 3em, 0);
|
||||||
|
}
|
||||||
|
71%, 100% {
|
||||||
|
-webkit-transform: translate3d(0, 0, 0);
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes anim-blink-2 {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: translate3d(0, 0, 0);
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
70%, 71% {
|
||||||
|
-webkit-transform: translate3d(0, 125%, 0);
|
||||||
|
transform: translate3d(0, 125%, 0);
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-animation-timing-function: ease-out;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
color: transparent;
|
||||||
|
-webkit-transform: translate3d(0, 200%, 0);
|
||||||
|
transform: translate3d(0, 200%, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-blink-1 {
|
||||||
|
0%, 70% {
|
||||||
|
-webkit-transform: translate3d(0, 3em, 0);
|
||||||
|
transform: translate3d(0, 3em, 0);
|
||||||
|
}
|
||||||
|
71%, 100% {
|
||||||
|
-webkit-transform: translate3d(0, 0, 0);
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-blink-2 {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: translate3d(0, 0, 0);
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
70%, 71% {
|
||||||
|
-webkit-transform: translate3d(0, 125%, 0);
|
||||||
|
transform: translate3d(0, 125%, 0);
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-animation-timing-function: ease-out;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
color: transparent;
|
||||||
|
-webkit-transform: translate3d(0, 200%, 0);
|
||||||
|
transform: translate3d(0, 200%, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* blink theme }}} */
|
||||||
|
|
||||||
|
/* surge theme {{{ */
|
||||||
|
.hbe-input-surge {
|
||||||
|
overflow: hidden;
|
||||||
|
padding-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-surge {
|
||||||
|
padding: 0.25em 0.5em;
|
||||||
|
margin-top: 1.25em;
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
color: #D0D0D0;
|
||||||
|
font-size: 1.55em;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-surge {
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
position: absolute;
|
||||||
|
top: 1em;
|
||||||
|
pointer-events: none;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0 0.25em;
|
||||||
|
-webkit-transform: translate3d(1em, 2.75em, 0);
|
||||||
|
transform: translate3d(1em, 2.75em, 0);
|
||||||
|
-webkit-transition: -webkit-transform 0.3s;
|
||||||
|
transition: transform 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-content-surge {
|
||||||
|
color: #A4A5A6;
|
||||||
|
padding: 0.4em 0 0.25em;
|
||||||
|
-webkit-transition: -webkit-transform 0.3s;
|
||||||
|
transition: transform 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-content-surge::after {
|
||||||
|
content: attr(data-content);
|
||||||
|
position: absolute;
|
||||||
|
font-weight: 800;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
color: #2C3E50;
|
||||||
|
padding: 0.25em 0;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
font-size: 0.85em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-graphic-surge {
|
||||||
|
fill: #2C3E50;
|
||||||
|
pointer-events: none;
|
||||||
|
top: 1em;
|
||||||
|
bottom: 0px;
|
||||||
|
height: 4.5em;
|
||||||
|
z-index: -1;
|
||||||
|
-webkit-transition: -webkit-transform 0.7s, fill 0.7s;
|
||||||
|
transition: transform 0.7s, fill 0.7s;
|
||||||
|
-webkit-transition-timing-function: cubic-bezier(0, 0.25, 0.5, 1);
|
||||||
|
transition-timing-function: cubic-bezier(0, 0.25, 0.5, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-surge:focus,
|
||||||
|
.input--filled .hbe-input-field-surge {
|
||||||
|
-webkit-transition: opacity 0s 0.35s;
|
||||||
|
transition: opacity 0s 0.35s;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-surge:focus + .hbe-input-label-surge,
|
||||||
|
.input--filled .hbe-input-label-surge {
|
||||||
|
-webkit-transition-delay: 0.15s;
|
||||||
|
transition-delay: 0.15s;
|
||||||
|
-webkit-transform: translate3d(0, 0, 0);
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-surge:focus + .hbe-input-label-surge .hbe-input-label-content-surge,
|
||||||
|
.input--filled .hbe-input-label-content-surge {
|
||||||
|
-webkit-transition-delay: 0.15s;
|
||||||
|
transition-delay: 0.15s;
|
||||||
|
-webkit-transform: translate3d(0, -100%, 0);
|
||||||
|
transform: translate3d(0, -100%, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-surge:focus ~ .hbe-graphic-surge,
|
||||||
|
.input--filled .graphic-surge {
|
||||||
|
fill: #2C3E50;
|
||||||
|
-webkit-transform: translate3d(-66.6%, 0, 0);
|
||||||
|
transform: translate3d(-66.6%, 0, 0);
|
||||||
|
}
|
||||||
|
/* surge theme }}} */
|
||||||
|
|
||||||
|
/* shrink theme {{{ */
|
||||||
|
.hbe-input-field-shrink {
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
padding: 0.5em 0;
|
||||||
|
margin-bottom: 2em;
|
||||||
|
color: #2C3E50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-shrink {
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 1em;
|
||||||
|
padding: 10px 0 5px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-shrink::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 7px;
|
||||||
|
background: #B7C3AC;
|
||||||
|
left: 0;
|
||||||
|
top: 100%;
|
||||||
|
-webkit-transform-origin: 50% 100%;
|
||||||
|
transform-origin: 50% 100%;
|
||||||
|
-webkit-transition: -webkit-transform 0.3s, background-color 0.3s;
|
||||||
|
transition: transform 0.3s, background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-label-content-shrink {
|
||||||
|
padding: 0;
|
||||||
|
-webkit-transform-origin: 0 0;
|
||||||
|
transform-origin: 0 0;
|
||||||
|
-webkit-transition: -webkit-transform 0.3s, color 0.3s;
|
||||||
|
transition: transform 0.3s, color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-shrink:focus + .hbe-input-label-shrink::after,
|
||||||
|
.input--filled .hbe-input-label-shrink::after {
|
||||||
|
background: #84AF9B;
|
||||||
|
-webkit-transform: scale3d(1, 0.25, 1);
|
||||||
|
transform: scale3d(1, 0.25, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hbe-input-field-shrink:focus + .hbe-input-label-shrink .hbe-input-label-content-shrink,
|
||||||
|
.input--filled .hbe-input-label-shrink .hbe-input-label-content-shrink {
|
||||||
|
color: #84AF9B;
|
||||||
|
-webkit-transform: translate3d(0, 2em, 0) scale3d(0.655, 0.655, 1);
|
||||||
|
transform: translate3d(0, 2em, 0) scale3d(0.655, 0.655, 1);
|
||||||
|
}
|
||||||
|
/* shrink theme }}} */
|
5924
css/index.css
Normal file
BIN
img/404.jpg
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
img/avatar.jpg
Normal file
After Width: | Height: | Size: 720 KiB |
BIN
img/favicon.png
Normal file
After Width: | Height: | Size: 323 B |
BIN
img/loading.gif
Normal file
After Width: | Height: | Size: 342 KiB |
BIN
img/图标.png
Normal file
After Width: | Height: | Size: 100 KiB |
355
index.html
Normal file
827
js/main.js
Normal file
@ -0,0 +1,827 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
let headerContentWidth, $nav
|
||||||
|
let mobileSidebarOpen = false
|
||||||
|
|
||||||
|
const adjustMenu = init => {
|
||||||
|
const getAllWidth = ele => {
|
||||||
|
let width = 0
|
||||||
|
ele.length && Array.from(ele).forEach(i => { width += i.offsetWidth })
|
||||||
|
return width
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init) {
|
||||||
|
const blogInfoWidth = getAllWidth(document.querySelector('#blog-info > a').children)
|
||||||
|
const menusWidth = getAllWidth(document.getElementById('menus').children)
|
||||||
|
headerContentWidth = blogInfoWidth + menusWidth
|
||||||
|
$nav = document.getElementById('nav')
|
||||||
|
}
|
||||||
|
|
||||||
|
let hideMenuIndex = ''
|
||||||
|
if (window.innerWidth <= 768) hideMenuIndex = true
|
||||||
|
else hideMenuIndex = headerContentWidth > $nav.offsetWidth - 120
|
||||||
|
|
||||||
|
if (hideMenuIndex) {
|
||||||
|
$nav.classList.add('hide-menu')
|
||||||
|
} else {
|
||||||
|
$nav.classList.remove('hide-menu')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化header
|
||||||
|
const initAdjust = () => {
|
||||||
|
adjustMenu(true)
|
||||||
|
$nav.classList.add('show')
|
||||||
|
}
|
||||||
|
|
||||||
|
// sidebar menus
|
||||||
|
const sidebarFn = {
|
||||||
|
open: () => {
|
||||||
|
btf.sidebarPaddingR()
|
||||||
|
document.body.style.overflow = 'hidden'
|
||||||
|
btf.animateIn(document.getElementById('menu-mask'), 'to_show 0.5s')
|
||||||
|
document.getElementById('sidebar-menus').classList.add('open')
|
||||||
|
mobileSidebarOpen = true
|
||||||
|
},
|
||||||
|
close: () => {
|
||||||
|
const $body = document.body
|
||||||
|
$body.style.overflow = ''
|
||||||
|
$body.style.paddingRight = ''
|
||||||
|
btf.animateOut(document.getElementById('menu-mask'), 'to_hide 0.5s')
|
||||||
|
document.getElementById('sidebar-menus').classList.remove('open')
|
||||||
|
mobileSidebarOpen = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 首頁top_img底下的箭頭
|
||||||
|
*/
|
||||||
|
const scrollDownInIndex = () => {
|
||||||
|
const $scrollDownEle = document.getElementById('scroll-down')
|
||||||
|
$scrollDownEle && $scrollDownEle.addEventListener('click', function () {
|
||||||
|
btf.scrollToDest(document.getElementById('content-inner').offsetTop, 300)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代碼
|
||||||
|
* 只適用於Hexo默認的代碼渲染
|
||||||
|
*/
|
||||||
|
const addHighlightTool = function () {
|
||||||
|
const highLight = GLOBAL_CONFIG.highlight
|
||||||
|
if (!highLight) return
|
||||||
|
|
||||||
|
const { highlightCopy, highlightLang, highlightHeightLimit, plugin } = highLight
|
||||||
|
const isHighlightShrink = GLOBAL_CONFIG_SITE.isHighlightShrink
|
||||||
|
const isShowTool = highlightCopy || highlightLang || isHighlightShrink !== undefined
|
||||||
|
const $figureHighlight = plugin === 'highlighjs' ? document.querySelectorAll('figure.highlight') : document.querySelectorAll('pre[class*="language-"]')
|
||||||
|
|
||||||
|
if (!((isShowTool || highlightHeightLimit) && $figureHighlight.length)) return
|
||||||
|
|
||||||
|
const isPrismjs = plugin === 'prismjs'
|
||||||
|
const highlightShrinkClass = isHighlightShrink === true ? 'closed' : ''
|
||||||
|
const highlightShrinkEle = isHighlightShrink !== undefined ? `<i class="fas fa-angle-down expand ${highlightShrinkClass}"></i>` : ''
|
||||||
|
const highlightCopyEle = highlightCopy ? '<div class="copy-notice"></div><i class="fas fa-paste copy-button"></i>' : ''
|
||||||
|
|
||||||
|
const copy = (text, ctx) => {
|
||||||
|
if (document.queryCommandSupported && document.queryCommandSupported('copy')) {
|
||||||
|
document.execCommand('copy')
|
||||||
|
if (GLOBAL_CONFIG.Snackbar !== undefined) {
|
||||||
|
btf.snackbarShow(GLOBAL_CONFIG.copy.success)
|
||||||
|
} else {
|
||||||
|
const prevEle = ctx.previousElementSibling
|
||||||
|
prevEle.textContent = GLOBAL_CONFIG.copy.success
|
||||||
|
prevEle.style.opacity = 1
|
||||||
|
setTimeout(() => { prevEle.style.opacity = 0 }, 700)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (GLOBAL_CONFIG.Snackbar !== undefined) {
|
||||||
|
btf.snackbarShow(GLOBAL_CONFIG.copy.noSupport)
|
||||||
|
} else {
|
||||||
|
ctx.previousElementSibling.textContent = GLOBAL_CONFIG.copy.noSupport
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// click events
|
||||||
|
const highlightCopyFn = (ele) => {
|
||||||
|
const $buttonParent = ele.parentNode
|
||||||
|
$buttonParent.classList.add('copy-true')
|
||||||
|
const selection = window.getSelection()
|
||||||
|
const range = document.createRange()
|
||||||
|
const preCodeSelector = isPrismjs ? 'pre code' : 'table .code pre'
|
||||||
|
range.selectNodeContents($buttonParent.querySelectorAll(`${preCodeSelector}`)[0])
|
||||||
|
selection.removeAllRanges()
|
||||||
|
selection.addRange(range)
|
||||||
|
const text = selection.toString()
|
||||||
|
copy(text, ele.lastChild)
|
||||||
|
selection.removeAllRanges()
|
||||||
|
$buttonParent.classList.remove('copy-true')
|
||||||
|
}
|
||||||
|
|
||||||
|
const highlightShrinkFn = (ele) => {
|
||||||
|
const $nextEle = [...ele.parentNode.children].slice(1)
|
||||||
|
ele.firstChild.classList.toggle('closed')
|
||||||
|
if (btf.isHidden($nextEle[$nextEle.length - 1])) {
|
||||||
|
$nextEle.forEach(e => { e.style.display = 'block' })
|
||||||
|
} else {
|
||||||
|
$nextEle.forEach(e => { e.style.display = 'none' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const highlightToolsFn = function (e) {
|
||||||
|
const $target = e.target.classList
|
||||||
|
if ($target.contains('expand')) highlightShrinkFn(this)
|
||||||
|
else if ($target.contains('copy-button')) highlightCopyFn(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
const expandCode = function () {
|
||||||
|
this.classList.toggle('expand-done')
|
||||||
|
}
|
||||||
|
|
||||||
|
function createEle (lang, item, service) {
|
||||||
|
const fragment = document.createDocumentFragment()
|
||||||
|
|
||||||
|
if (isShowTool) {
|
||||||
|
const hlTools = document.createElement('div')
|
||||||
|
hlTools.className = `highlight-tools ${highlightShrinkClass}`
|
||||||
|
hlTools.innerHTML = highlightShrinkEle + lang + highlightCopyEle
|
||||||
|
hlTools.addEventListener('click', highlightToolsFn)
|
||||||
|
fragment.appendChild(hlTools)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (highlightHeightLimit && item.offsetHeight > highlightHeightLimit + 30) {
|
||||||
|
const ele = document.createElement('div')
|
||||||
|
ele.className = 'code-expand-btn'
|
||||||
|
ele.innerHTML = '<i class="fas fa-angle-double-down"></i>'
|
||||||
|
ele.addEventListener('click', expandCode)
|
||||||
|
fragment.appendChild(ele)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service === 'hl') {
|
||||||
|
item.insertBefore(fragment, item.firstChild)
|
||||||
|
} else {
|
||||||
|
item.parentNode.insertBefore(fragment, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPrismjs) {
|
||||||
|
$figureHighlight.forEach(item => {
|
||||||
|
if (highlightLang) {
|
||||||
|
const langName = item.getAttribute('data-language') || 'Code'
|
||||||
|
const highlightLangEle = `<div class="code-lang">${langName}</div>`
|
||||||
|
btf.wrap(item, 'figure', { class: 'highlight' })
|
||||||
|
createEle(highlightLangEle, item)
|
||||||
|
} else {
|
||||||
|
btf.wrap(item, 'figure', { class: 'highlight' })
|
||||||
|
createEle('', item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
$figureHighlight.forEach(function (item) {
|
||||||
|
if (highlightLang) {
|
||||||
|
let langName = item.getAttribute('class').split(' ')[1]
|
||||||
|
if (langName === 'plain' || langName === undefined) langName = 'Code'
|
||||||
|
const highlightLangEle = `<div class="code-lang">${langName}</div>`
|
||||||
|
createEle(highlightLangEle, item, 'hl')
|
||||||
|
} else {
|
||||||
|
createEle('', item, 'hl')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PhotoFigcaption
|
||||||
|
*/
|
||||||
|
function addPhotoFigcaption () {
|
||||||
|
document.querySelectorAll('#article-container img').forEach(function (item) {
|
||||||
|
const parentEle = item.parentNode
|
||||||
|
const altValue = item.title || item.alt
|
||||||
|
if (altValue && !parentEle.parentNode.classList.contains('justified-gallery')) {
|
||||||
|
const ele = document.createElement('div')
|
||||||
|
ele.className = 'img-alt is-center'
|
||||||
|
ele.textContent = altValue
|
||||||
|
parentEle.insertBefore(ele, item.nextSibling)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lightbox
|
||||||
|
*/
|
||||||
|
const runLightbox = () => {
|
||||||
|
btf.loadLightbox(document.querySelectorAll('#article-container img:not(.no-lightbox)'))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* justified-gallery 圖庫排版
|
||||||
|
*/
|
||||||
|
const runJustifiedGallery = function (ele) {
|
||||||
|
const htmlStr = arr => {
|
||||||
|
let str = ''
|
||||||
|
const replaceDq = str => str.replace(/"/g, '"') // replace double quotes to "
|
||||||
|
arr.forEach(i => {
|
||||||
|
const alt = i.alt ? `alt="${replaceDq(i.alt)}"` : ''
|
||||||
|
const title = i.title ? `title="${replaceDq(i.title)}"` : ''
|
||||||
|
str += `<div class="fj-gallery-item"><img src="${i.url}" ${alt + title}"></div>`
|
||||||
|
})
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
const lazyloadFn = (i, arr, limit) => {
|
||||||
|
const loadItem = limit
|
||||||
|
const arrLength = arr.length
|
||||||
|
if (arrLength > loadItem) i.insertAdjacentHTML('beforeend', htmlStr(arr.splice(0, loadItem)))
|
||||||
|
else {
|
||||||
|
i.insertAdjacentHTML('beforeend', htmlStr(arr))
|
||||||
|
i.classList.remove('lazyload')
|
||||||
|
}
|
||||||
|
return arrLength > loadItem ? loadItem : arrLength
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchUrl = async (url) => {
|
||||||
|
const response = await fetch(url)
|
||||||
|
return await response.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
const runJustifiedGallery = (item, arr) => {
|
||||||
|
if (!item.classList.contains('lazyload')) item.innerHTML = htmlStr(arr)
|
||||||
|
else {
|
||||||
|
const limit = item.getAttribute('data-limit')
|
||||||
|
lazyloadFn(item, arr, limit)
|
||||||
|
const clickBtnFn = () => {
|
||||||
|
const lastItemLength = lazyloadFn(item, arr, limit)
|
||||||
|
fjGallery(item, 'appendImages', item.querySelectorAll(`.fj-gallery-item:nth-last-child(-n+${lastItemLength})`))
|
||||||
|
btf.loadLightbox(item.querySelectorAll('img'))
|
||||||
|
lastItemLength < limit && item.nextElementSibling.removeEventListener('click', clickBtnFn)
|
||||||
|
}
|
||||||
|
item.nextElementSibling.addEventListener('click', clickBtnFn)
|
||||||
|
}
|
||||||
|
btf.initJustifiedGallery(item)
|
||||||
|
btf.loadLightbox(item.querySelectorAll('img'))
|
||||||
|
}
|
||||||
|
|
||||||
|
const addJustifiedGallery = () => {
|
||||||
|
ele.forEach(item => {
|
||||||
|
item.classList.contains('url')
|
||||||
|
? fetchUrl(item.textContent).then(res => { runJustifiedGallery(item, res) })
|
||||||
|
: runJustifiedGallery(item, JSON.parse(item.textContent))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.fjGallery) {
|
||||||
|
addJustifiedGallery()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
getCSS(`${GLOBAL_CONFIG.source.justifiedGallery.css}`)
|
||||||
|
getScript(`${GLOBAL_CONFIG.source.justifiedGallery.js}`).then(addJustifiedGallery)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rightside scroll percent
|
||||||
|
*/
|
||||||
|
const rightsideScrollPercent = currentTop => {
|
||||||
|
const perNum = btf.getScrollPercent(currentTop, document.body)
|
||||||
|
const $goUp = document.getElementById('go-up')
|
||||||
|
if (perNum < 95) {
|
||||||
|
$goUp.classList.add('show-percent')
|
||||||
|
$goUp.querySelector('.scroll-percent').textContent = perNum
|
||||||
|
} else {
|
||||||
|
$goUp.classList.remove('show-percent')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 滾動處理
|
||||||
|
*/
|
||||||
|
const scrollFn = function () {
|
||||||
|
const $rightside = document.getElementById('rightside')
|
||||||
|
const innerHeight = window.innerHeight + 56
|
||||||
|
let initTop = 0
|
||||||
|
let isChatShow = true
|
||||||
|
const $header = document.getElementById('page-header')
|
||||||
|
const isChatBtn = typeof chatBtn !== 'undefined'
|
||||||
|
const isShowPercent = GLOBAL_CONFIG.percent.rightside
|
||||||
|
|
||||||
|
// 當滾動條小于 56 的時候
|
||||||
|
if (document.body.scrollHeight <= innerHeight) {
|
||||||
|
$rightside.style.cssText = 'opacity: 1; transform: translateX(-58px)'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the scroll direction
|
||||||
|
const scrollDirection = currentTop => {
|
||||||
|
const result = currentTop > initTop // true is down & false is up
|
||||||
|
initTop = currentTop
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
const scrollTask = btf.throttle(() => {
|
||||||
|
const currentTop = window.scrollY || document.documentElement.scrollTop
|
||||||
|
const isDown = scrollDirection(currentTop)
|
||||||
|
if (currentTop > 56) {
|
||||||
|
if (isDown) {
|
||||||
|
if ($header.classList.contains('nav-visible')) $header.classList.remove('nav-visible')
|
||||||
|
if (isChatBtn && isChatShow === true) {
|
||||||
|
window.chatBtn.hide()
|
||||||
|
isChatShow = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!$header.classList.contains('nav-visible')) $header.classList.add('nav-visible')
|
||||||
|
if (isChatBtn && isChatShow === false) {
|
||||||
|
window.chatBtn.show()
|
||||||
|
isChatShow = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$header.classList.add('nav-fixed')
|
||||||
|
if (window.getComputedStyle($rightside).getPropertyValue('opacity') === '0') {
|
||||||
|
$rightside.style.cssText = 'opacity: 0.8; transform: translateX(-58px)'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (currentTop === 0) {
|
||||||
|
$header.classList.remove('nav-fixed', 'nav-visible')
|
||||||
|
}
|
||||||
|
$rightside.style.cssText = "opacity: ''; transform: ''"
|
||||||
|
}
|
||||||
|
|
||||||
|
isShowPercent && rightsideScrollPercent(currentTop)
|
||||||
|
|
||||||
|
if (document.body.scrollHeight <= innerHeight) {
|
||||||
|
$rightside.style.cssText = 'opacity: 0.8; transform: translateX(-58px)'
|
||||||
|
}
|
||||||
|
}, 200)
|
||||||
|
|
||||||
|
window.scrollCollect = scrollTask
|
||||||
|
|
||||||
|
window.addEventListener('scroll', scrollCollect)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* toc,anchor
|
||||||
|
*/
|
||||||
|
const scrollFnToDo = function () {
|
||||||
|
const isToc = GLOBAL_CONFIG_SITE.isToc
|
||||||
|
const isAnchor = GLOBAL_CONFIG.isAnchor
|
||||||
|
const $article = document.getElementById('article-container')
|
||||||
|
|
||||||
|
if (!($article && (isToc || isAnchor))) return
|
||||||
|
|
||||||
|
let $tocLink, $cardToc, autoScrollToc, $tocPercentage, isExpand
|
||||||
|
|
||||||
|
if (isToc) {
|
||||||
|
const $cardTocLayout = document.getElementById('card-toc')
|
||||||
|
$cardToc = $cardTocLayout.getElementsByClassName('toc-content')[0]
|
||||||
|
$tocLink = $cardToc.querySelectorAll('.toc-link')
|
||||||
|
$tocPercentage = $cardTocLayout.querySelector('.toc-percentage')
|
||||||
|
isExpand = $cardToc.classList.contains('is-expand')
|
||||||
|
|
||||||
|
window.mobileToc = {
|
||||||
|
open: () => {
|
||||||
|
$cardTocLayout.style.cssText = 'animation: toc-open .3s; opacity: 1; right: 55px'
|
||||||
|
},
|
||||||
|
|
||||||
|
close: () => {
|
||||||
|
$cardTocLayout.style.animation = 'toc-close .2s'
|
||||||
|
setTimeout(() => {
|
||||||
|
$cardTocLayout.style.cssText = "opacity:''; animation: ''; right: ''"
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// toc元素點擊
|
||||||
|
$cardToc.addEventListener('click', e => {
|
||||||
|
e.preventDefault()
|
||||||
|
const target = e.target.classList
|
||||||
|
if (target.contains('toc-content')) return
|
||||||
|
const $target = target.contains('toc-link')
|
||||||
|
? e.target
|
||||||
|
: e.target.parentElement
|
||||||
|
btf.scrollToDest(btf.getEleTop(document.getElementById(decodeURI($target.getAttribute('href')).replace('#', ''))), 300)
|
||||||
|
if (window.innerWidth < 900) {
|
||||||
|
window.mobileToc.close()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
autoScrollToc = item => {
|
||||||
|
const activePosition = item.getBoundingClientRect().top
|
||||||
|
const sidebarScrollTop = $cardToc.scrollTop
|
||||||
|
if (activePosition > (document.documentElement.clientHeight - 100)) {
|
||||||
|
$cardToc.scrollTop = sidebarScrollTop + 150
|
||||||
|
}
|
||||||
|
if (activePosition < 100) {
|
||||||
|
$cardToc.scrollTop = sidebarScrollTop - 150
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// find head position & add active class
|
||||||
|
const list = $article.querySelectorAll('h1,h2,h3,h4,h5,h6')
|
||||||
|
let detectItem = ''
|
||||||
|
const findHeadPosition = function (top) {
|
||||||
|
if (top === 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentId = ''
|
||||||
|
let currentIndex = ''
|
||||||
|
|
||||||
|
list.forEach(function (ele, index) {
|
||||||
|
if (top > btf.getEleTop(ele) - 80) {
|
||||||
|
const id = ele.id
|
||||||
|
currentId = id ? '#' + encodeURI(id) : ''
|
||||||
|
currentIndex = index
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (detectItem === currentIndex) return
|
||||||
|
|
||||||
|
if (isAnchor) btf.updateAnchor(currentId)
|
||||||
|
|
||||||
|
detectItem = currentIndex
|
||||||
|
|
||||||
|
if (isToc) {
|
||||||
|
$cardToc.querySelectorAll('.active').forEach(i => { i.classList.remove('active') })
|
||||||
|
|
||||||
|
if (currentId === '') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentActive = $tocLink[currentIndex]
|
||||||
|
currentActive.classList.add('active')
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
autoScrollToc(currentActive)
|
||||||
|
}, 0)
|
||||||
|
|
||||||
|
if (isExpand) return
|
||||||
|
let parent = currentActive.parentNode
|
||||||
|
|
||||||
|
for (; !parent.matches('.toc'); parent = parent.parentNode) {
|
||||||
|
if (parent.matches('li')) parent.classList.add('active')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// main of scroll
|
||||||
|
window.tocScrollFn = btf.throttle(() => {
|
||||||
|
const currentTop = window.scrollY || document.documentElement.scrollTop
|
||||||
|
if (isToc && GLOBAL_CONFIG.percent.toc) {
|
||||||
|
$tocPercentage.textContent = btf.getScrollPercent(currentTop, $article)
|
||||||
|
}
|
||||||
|
findHeadPosition(currentTop)
|
||||||
|
}, 100)
|
||||||
|
|
||||||
|
window.addEventListener('scroll', tocScrollFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
const modeChangeFn = mode => {
|
||||||
|
if (!window.themeChange) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const turnMode = item => window.themeChange[item](mode)
|
||||||
|
|
||||||
|
Object.keys(window.themeChange).forEach(item => {
|
||||||
|
if (['disqus', 'disqusjs'].includes(item)) {
|
||||||
|
setTimeout(() => turnMode(item), 300)
|
||||||
|
} else {
|
||||||
|
turnMode(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rightside
|
||||||
|
*/
|
||||||
|
const rightSideFn = {
|
||||||
|
switchReadMode: () => { // read-mode
|
||||||
|
const $body = document.body
|
||||||
|
$body.classList.add('read-mode')
|
||||||
|
const newEle = document.createElement('button')
|
||||||
|
newEle.type = 'button'
|
||||||
|
newEle.className = 'fas fa-sign-out-alt exit-readmode'
|
||||||
|
$body.appendChild(newEle)
|
||||||
|
|
||||||
|
const clickFn = () => {
|
||||||
|
$body.classList.remove('read-mode')
|
||||||
|
newEle.remove()
|
||||||
|
newEle.removeEventListener('click', clickFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
newEle.addEventListener('click', clickFn)
|
||||||
|
},
|
||||||
|
switchDarkMode: () => { // Switch Between Light And Dark Mode
|
||||||
|
const willChangeMode = document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'
|
||||||
|
if (willChangeMode === 'dark') {
|
||||||
|
activateDarkMode()
|
||||||
|
saveToLocal.set('theme', 'dark', 2)
|
||||||
|
GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.day_to_night)
|
||||||
|
} else {
|
||||||
|
activateLightMode()
|
||||||
|
saveToLocal.set('theme', 'light', 2)
|
||||||
|
GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.night_to_day)
|
||||||
|
}
|
||||||
|
modeChangeFn(willChangeMode)
|
||||||
|
},
|
||||||
|
showOrHideBtn: (e) => { // rightside 點擊設置 按鈕 展開
|
||||||
|
const rightsideHideClassList = document.getElementById('rightside-config-hide').classList
|
||||||
|
rightsideHideClassList.toggle('show')
|
||||||
|
if (e.classList.contains('show')) {
|
||||||
|
rightsideHideClassList.add('status')
|
||||||
|
setTimeout(() => {
|
||||||
|
rightsideHideClassList.remove('status')
|
||||||
|
}, 300)
|
||||||
|
}
|
||||||
|
e.classList.toggle('show')
|
||||||
|
},
|
||||||
|
scrollToTop: () => { // Back to top
|
||||||
|
btf.scrollToDest(0, 500)
|
||||||
|
},
|
||||||
|
hideAsideBtn: () => { // Hide aside
|
||||||
|
const $htmlDom = document.documentElement.classList
|
||||||
|
const saveStatus = $htmlDom.contains('hide-aside') ? 'show' : 'hide'
|
||||||
|
saveToLocal.set('aside-status', saveStatus, 2)
|
||||||
|
$htmlDom.toggle('hide-aside')
|
||||||
|
},
|
||||||
|
runMobileToc: () => {
|
||||||
|
if (window.getComputedStyle(document.getElementById('card-toc')).getPropertyValue('opacity') === '0') window.mobileToc.open()
|
||||||
|
else window.mobileToc.close()
|
||||||
|
},
|
||||||
|
toggleChatDisplay: () => {
|
||||||
|
window.chatBtnFn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('rightside').addEventListener('click', function (e) {
|
||||||
|
const $target = e.target.id ? e.target : e.target.parentNode
|
||||||
|
switch ($target.id) {
|
||||||
|
case 'go-up':
|
||||||
|
rightSideFn.scrollToTop()
|
||||||
|
break
|
||||||
|
case 'rightside_config':
|
||||||
|
rightSideFn.showOrHideBtn($target)
|
||||||
|
break
|
||||||
|
case 'mobile-toc-button':
|
||||||
|
rightSideFn.runMobileToc()
|
||||||
|
break
|
||||||
|
case 'readmode':
|
||||||
|
rightSideFn.switchReadMode()
|
||||||
|
break
|
||||||
|
case 'darkmode':
|
||||||
|
rightSideFn.switchDarkMode()
|
||||||
|
break
|
||||||
|
case 'hide-aside-btn':
|
||||||
|
rightSideFn.hideAsideBtn()
|
||||||
|
break
|
||||||
|
case 'chat-btn':
|
||||||
|
rightSideFn.toggleChatDisplay()
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* menu
|
||||||
|
* 側邊欄sub-menu 展開/收縮
|
||||||
|
*/
|
||||||
|
const clickFnOfSubMenu = () => {
|
||||||
|
document.querySelectorAll('#sidebar-menus .site-page.group').forEach(function (item) {
|
||||||
|
item.addEventListener('click', function () {
|
||||||
|
this.classList.toggle('hide')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 複製時加上版權信息
|
||||||
|
*/
|
||||||
|
const addCopyright = () => {
|
||||||
|
const copyright = GLOBAL_CONFIG.copyright
|
||||||
|
document.body.oncopy = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
const copyFont = window.getSelection(0).toString()
|
||||||
|
let textFont = copyFont
|
||||||
|
if (copyFont.length > copyright.limitCount) {
|
||||||
|
textFont = `${copyFont}\n\n\n${copyright.languages.author}\n${copyright.languages.link}${window.location.href}\n${copyright.languages.source}\n${copyright.languages.info}`
|
||||||
|
}
|
||||||
|
if (e.clipboardData) {
|
||||||
|
return e.clipboardData.setData('text', textFont)
|
||||||
|
} else {
|
||||||
|
return window.clipboardData.setData('text', textFont)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 網頁運行時間
|
||||||
|
*/
|
||||||
|
const addRuntime = () => {
|
||||||
|
const $runtimeCount = document.getElementById('runtimeshow')
|
||||||
|
if ($runtimeCount) {
|
||||||
|
const publishDate = $runtimeCount.getAttribute('data-publishDate')
|
||||||
|
$runtimeCount.textContent = `${btf.diffDate(publishDate)} ${GLOBAL_CONFIG.runtime}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最後一次更新時間
|
||||||
|
*/
|
||||||
|
const addLastPushDate = () => {
|
||||||
|
const $lastPushDateItem = document.getElementById('last-push-date')
|
||||||
|
if ($lastPushDateItem) {
|
||||||
|
const lastPushDate = $lastPushDateItem.getAttribute('data-lastPushDate')
|
||||||
|
$lastPushDateItem.textContent = btf.diffDate(lastPushDate, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* table overflow
|
||||||
|
*/
|
||||||
|
const addTableWrap = () => {
|
||||||
|
const $table = document.querySelectorAll('#article-container :not(.highlight) > table, #article-container > table')
|
||||||
|
if ($table.length) {
|
||||||
|
$table.forEach(item => {
|
||||||
|
btf.wrap(item, 'div', { class: 'table-wrap' })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tag-hide
|
||||||
|
*/
|
||||||
|
const clickFnOfTagHide = function () {
|
||||||
|
const $hideInline = document.querySelectorAll('#article-container .hide-button')
|
||||||
|
if ($hideInline.length) {
|
||||||
|
$hideInline.forEach(function (item) {
|
||||||
|
item.addEventListener('click', function (e) {
|
||||||
|
const $this = this
|
||||||
|
$this.classList.add('open')
|
||||||
|
const $fjGallery = $this.nextElementSibling.querySelectorAll('.fj-gallery')
|
||||||
|
$fjGallery.length && btf.initJustifiedGallery($fjGallery)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabsFn = {
|
||||||
|
clickFnOfTabs: function () {
|
||||||
|
document.querySelectorAll('#article-container .tab > button').forEach(function (item) {
|
||||||
|
item.addEventListener('click', function (e) {
|
||||||
|
const $this = this
|
||||||
|
const $tabItem = $this.parentNode
|
||||||
|
|
||||||
|
if (!$tabItem.classList.contains('active')) {
|
||||||
|
const $tabContent = $tabItem.parentNode.nextElementSibling
|
||||||
|
const $siblings = btf.siblings($tabItem, '.active')[0]
|
||||||
|
$siblings && $siblings.classList.remove('active')
|
||||||
|
$tabItem.classList.add('active')
|
||||||
|
const tabId = $this.getAttribute('data-href').replace('#', '')
|
||||||
|
const childList = [...$tabContent.children]
|
||||||
|
childList.forEach(item => {
|
||||||
|
if (item.id === tabId) item.classList.add('active')
|
||||||
|
else item.classList.remove('active')
|
||||||
|
})
|
||||||
|
const $isTabJustifiedGallery = $tabContent.querySelectorAll(`#${tabId} .fj-gallery`)
|
||||||
|
if ($isTabJustifiedGallery.length > 0) {
|
||||||
|
btf.initJustifiedGallery($isTabJustifiedGallery)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
backToTop: () => {
|
||||||
|
document.querySelectorAll('#article-container .tabs .tab-to-top').forEach(function (item) {
|
||||||
|
item.addEventListener('click', function () {
|
||||||
|
btf.scrollToDest(btf.getEleTop(btf.getParents(this, '.tabs')), 300)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleCardCategory = function () {
|
||||||
|
const $cardCategory = document.querySelectorAll('#aside-cat-list .card-category-list-item.parent i')
|
||||||
|
if ($cardCategory.length) {
|
||||||
|
$cardCategory.forEach(function (item) {
|
||||||
|
item.addEventListener('click', function (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
const $this = this
|
||||||
|
$this.classList.toggle('expand')
|
||||||
|
const $parentEle = $this.parentNode.nextElementSibling
|
||||||
|
if (btf.isHidden($parentEle)) {
|
||||||
|
$parentEle.style.display = 'block'
|
||||||
|
} else {
|
||||||
|
$parentEle.style.display = 'none'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const switchComments = function () {
|
||||||
|
let switchDone = false
|
||||||
|
const $switchBtn = document.querySelector('#comment-switch > .switch-btn')
|
||||||
|
$switchBtn && $switchBtn.addEventListener('click', function () {
|
||||||
|
this.classList.toggle('move')
|
||||||
|
document.querySelectorAll('#post-comment > .comment-wrap > div').forEach(function (item) {
|
||||||
|
if (btf.isHidden(item)) {
|
||||||
|
item.style.cssText = 'display: block;animation: tabshow .5s'
|
||||||
|
} else {
|
||||||
|
item.style.cssText = "display: none;animation: ''"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!switchDone && typeof loadOtherComment === 'function') {
|
||||||
|
switchDone = true
|
||||||
|
loadOtherComment()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const addPostOutdateNotice = function () {
|
||||||
|
const data = GLOBAL_CONFIG.noticeOutdate
|
||||||
|
const diffDay = btf.diffDate(GLOBAL_CONFIG_SITE.postUpdate)
|
||||||
|
if (diffDay >= data.limitDay) {
|
||||||
|
const ele = document.createElement('div')
|
||||||
|
ele.className = 'post-outdate-notice'
|
||||||
|
ele.textContent = data.messagePrev + ' ' + diffDay + ' ' + data.messageNext
|
||||||
|
const $targetEle = document.getElementById('article-container')
|
||||||
|
if (data.position === 'top') {
|
||||||
|
$targetEle.insertBefore(ele, $targetEle.firstChild)
|
||||||
|
} else {
|
||||||
|
$targetEle.appendChild(ele)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const lazyloadImg = () => {
|
||||||
|
window.lazyLoadInstance = new LazyLoad({
|
||||||
|
elements_selector: 'img',
|
||||||
|
threshold: 0,
|
||||||
|
data_src: 'lazy-src'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const relativeDate = function (selector) {
|
||||||
|
selector.forEach(item => {
|
||||||
|
const timeVal = item.getAttribute('datetime')
|
||||||
|
item.textContent = btf.diffDate(timeVal, true)
|
||||||
|
item.style.display = 'inline'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const unRefreshFn = function () {
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
adjustMenu(false)
|
||||||
|
btf.isHidden(document.getElementById('toggle-menu')) && mobileSidebarOpen && sidebarFn.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
document.getElementById('menu-mask').addEventListener('click', e => { sidebarFn.close() })
|
||||||
|
|
||||||
|
clickFnOfSubMenu()
|
||||||
|
GLOBAL_CONFIG.islazyload && lazyloadImg()
|
||||||
|
GLOBAL_CONFIG.copyright !== undefined && addCopyright()
|
||||||
|
|
||||||
|
if (GLOBAL_CONFIG.autoDarkmode) {
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
|
||||||
|
if (saveToLocal.get('theme') !== undefined) return
|
||||||
|
e.matches ? modeChangeFn('dark') : modeChangeFn('light')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.refreshFn = function () {
|
||||||
|
initAdjust()
|
||||||
|
|
||||||
|
if (GLOBAL_CONFIG_SITE.isPost) {
|
||||||
|
GLOBAL_CONFIG.noticeOutdate !== undefined && addPostOutdateNotice()
|
||||||
|
GLOBAL_CONFIG.relativeDate.post && relativeDate(document.querySelectorAll('#post-meta time'))
|
||||||
|
} else {
|
||||||
|
GLOBAL_CONFIG.relativeDate.homepage && relativeDate(document.querySelectorAll('#recent-posts time'))
|
||||||
|
GLOBAL_CONFIG.runtime && addRuntime()
|
||||||
|
addLastPushDate()
|
||||||
|
toggleCardCategory()
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollFnToDo()
|
||||||
|
GLOBAL_CONFIG_SITE.isHome && scrollDownInIndex()
|
||||||
|
addHighlightTool()
|
||||||
|
GLOBAL_CONFIG.isPhotoFigcaption && addPhotoFigcaption()
|
||||||
|
scrollFn()
|
||||||
|
|
||||||
|
const $jgEle = document.querySelectorAll('#article-container .fj-gallery')
|
||||||
|
$jgEle.length && runJustifiedGallery($jgEle)
|
||||||
|
|
||||||
|
runLightbox()
|
||||||
|
addTableWrap()
|
||||||
|
clickFnOfTagHide()
|
||||||
|
tabsFn.clickFnOfTabs()
|
||||||
|
tabsFn.backToTop()
|
||||||
|
switchComments()
|
||||||
|
document.getElementById('toggle-menu').addEventListener('click', () => { sidebarFn.open() })
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshFn()
|
||||||
|
unRefreshFn()
|
||||||
|
})
|
177
js/search/algolia.js
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
window.addEventListener('load', () => {
|
||||||
|
const $searchMask = document.getElementById('search-mask')
|
||||||
|
const $searchDialog = document.querySelector('#algolia-search .search-dialog')
|
||||||
|
|
||||||
|
const openSearch = () => {
|
||||||
|
const bodyStyle = document.body.style
|
||||||
|
bodyStyle.width = '100%'
|
||||||
|
bodyStyle.overflow = 'hidden'
|
||||||
|
btf.animateIn($searchMask, 'to_show 0.5s')
|
||||||
|
btf.animateIn($searchDialog, 'titleScale 0.5s')
|
||||||
|
setTimeout(() => { document.querySelector('#algolia-search .ais-SearchBox-input').focus() }, 100)
|
||||||
|
|
||||||
|
// shortcut: ESC
|
||||||
|
document.addEventListener('keydown', function f (event) {
|
||||||
|
if (event.code === 'Escape') {
|
||||||
|
closeSearch()
|
||||||
|
document.removeEventListener('keydown', f)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
fixSafariHeight()
|
||||||
|
window.addEventListener('resize', fixSafariHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeSearch = () => {
|
||||||
|
const bodyStyle = document.body.style
|
||||||
|
bodyStyle.width = ''
|
||||||
|
bodyStyle.overflow = ''
|
||||||
|
btf.animateOut($searchDialog, 'search_close .5s')
|
||||||
|
btf.animateOut($searchMask, 'to_hide 0.5s')
|
||||||
|
window.removeEventListener('resize', fixSafariHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fix safari
|
||||||
|
const fixSafariHeight = () => {
|
||||||
|
if (window.innerWidth < 768) {
|
||||||
|
$searchDialog.style.setProperty('--search-height', window.innerHeight + 'px')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchClickFn = () => {
|
||||||
|
document.querySelector('#search-button > .search').addEventListener('click', openSearch)
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchFnOnce = () => {
|
||||||
|
$searchMask.addEventListener('click', closeSearch)
|
||||||
|
document.querySelector('#algolia-search .search-close-button').addEventListener('click', closeSearch)
|
||||||
|
}
|
||||||
|
|
||||||
|
const cutContent = content => {
|
||||||
|
if (content === '') return ''
|
||||||
|
|
||||||
|
const firstOccur = content.indexOf('<mark>')
|
||||||
|
|
||||||
|
let start = firstOccur - 30
|
||||||
|
let end = firstOccur + 120
|
||||||
|
let pre = ''
|
||||||
|
let post = ''
|
||||||
|
|
||||||
|
if (start <= 0) {
|
||||||
|
start = 0
|
||||||
|
end = 140
|
||||||
|
} else {
|
||||||
|
pre = '...'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end > content.length) {
|
||||||
|
end = content.length
|
||||||
|
} else {
|
||||||
|
post = '...'
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchContent = pre + content.substring(start, end) + post
|
||||||
|
return matchContent
|
||||||
|
}
|
||||||
|
|
||||||
|
const algolia = GLOBAL_CONFIG.algolia
|
||||||
|
const isAlgoliaValid = algolia.appId && algolia.apiKey && algolia.indexName
|
||||||
|
if (!isAlgoliaValid) {
|
||||||
|
return console.error('Algolia setting is invalid!')
|
||||||
|
}
|
||||||
|
|
||||||
|
const search = instantsearch({
|
||||||
|
indexName: algolia.indexName,
|
||||||
|
/* global algoliasearch */
|
||||||
|
searchClient: algoliasearch(algolia.appId, algolia.apiKey),
|
||||||
|
searchFunction (helper) {
|
||||||
|
helper.state.query && helper.search()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const configure = instantsearch.widgets.configure({
|
||||||
|
hitsPerPage: 5
|
||||||
|
})
|
||||||
|
|
||||||
|
const searchBox = instantsearch.widgets.searchBox({
|
||||||
|
container: '#algolia-search-input',
|
||||||
|
showReset: false,
|
||||||
|
showSubmit: false,
|
||||||
|
placeholder: GLOBAL_CONFIG.algolia.languages.input_placeholder,
|
||||||
|
showLoadingIndicator: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const hits = instantsearch.widgets.hits({
|
||||||
|
container: '#algolia-hits',
|
||||||
|
templates: {
|
||||||
|
item (data) {
|
||||||
|
const link = data.permalink ? data.permalink : (GLOBAL_CONFIG.root + data.path)
|
||||||
|
const result = data._highlightResult
|
||||||
|
const content = result.contentStripTruncate
|
||||||
|
? cutContent(result.contentStripTruncate.value)
|
||||||
|
: result.contentStrip
|
||||||
|
? cutContent(result.contentStrip.value)
|
||||||
|
: result.content
|
||||||
|
? cutContent(result.content.value)
|
||||||
|
: ''
|
||||||
|
return `
|
||||||
|
<a href="${link}" class="algolia-hit-item-link">
|
||||||
|
<span class="algolia-hits-item-title">${result.title.value || 'no-title'}</span>
|
||||||
|
<p class="algolia-hit-item-content">${content}</p>
|
||||||
|
</a>`
|
||||||
|
},
|
||||||
|
empty: function (data) {
|
||||||
|
return (
|
||||||
|
'<div id="algolia-hits-empty">' +
|
||||||
|
GLOBAL_CONFIG.algolia.languages.hits_empty.replace(/\$\{query}/, data.query) +
|
||||||
|
'</div>'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const stats = instantsearch.widgets.stats({
|
||||||
|
container: '#algolia-info > .algolia-stats',
|
||||||
|
templates: {
|
||||||
|
text: function (data) {
|
||||||
|
const stats = GLOBAL_CONFIG.algolia.languages.hits_stats
|
||||||
|
.replace(/\$\{hits}/, data.nbHits)
|
||||||
|
.replace(/\$\{time}/, data.processingTimeMS)
|
||||||
|
return (
|
||||||
|
`<hr>${stats}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const powerBy = instantsearch.widgets.poweredBy({
|
||||||
|
container: '#algolia-info > .algolia-poweredBy'
|
||||||
|
})
|
||||||
|
|
||||||
|
const pagination = instantsearch.widgets.pagination({
|
||||||
|
container: '#algolia-pagination',
|
||||||
|
totalPages: 5,
|
||||||
|
templates: {
|
||||||
|
first: '<i class="fas fa-angle-double-left"></i>',
|
||||||
|
last: '<i class="fas fa-angle-double-right"></i>',
|
||||||
|
previous: '<i class="fas fa-angle-left"></i>',
|
||||||
|
next: '<i class="fas fa-angle-right"></i>'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
search.addWidgets([configure, searchBox, hits, stats, powerBy, pagination]) // add the widgets to the instantsearch instance
|
||||||
|
|
||||||
|
search.start()
|
||||||
|
|
||||||
|
searchClickFn()
|
||||||
|
searchFnOnce()
|
||||||
|
|
||||||
|
window.addEventListener('pjax:complete', () => {
|
||||||
|
!btf.isHidden($searchMask) && closeSearch()
|
||||||
|
searchClickFn()
|
||||||
|
})
|
||||||
|
|
||||||
|
window.pjax && search.on('render', () => {
|
||||||
|
window.pjax.refresh(document.getElementById('algolia-hits'))
|
||||||
|
})
|
||||||
|
})
|
360
js/search/local-search.js
Normal file
@ -0,0 +1,360 @@
|
|||||||
|
/**
|
||||||
|
* Refer to hexo-generator-searchdb
|
||||||
|
* https://github.com/next-theme/hexo-generator-searchdb/blob/main/dist/search.js
|
||||||
|
* Modified by hexo-theme-butterfly
|
||||||
|
*/
|
||||||
|
|
||||||
|
class LocalSearch {
|
||||||
|
constructor ({
|
||||||
|
path = '',
|
||||||
|
unescape = false,
|
||||||
|
top_n_per_article = 1
|
||||||
|
}) {
|
||||||
|
this.path = path
|
||||||
|
this.unescape = unescape
|
||||||
|
this.top_n_per_article = top_n_per_article
|
||||||
|
this.isfetched = false
|
||||||
|
this.datas = null
|
||||||
|
}
|
||||||
|
|
||||||
|
getIndexByWord (words, text, caseSensitive = false) {
|
||||||
|
const index = []
|
||||||
|
const included = new Set()
|
||||||
|
|
||||||
|
if (!caseSensitive) {
|
||||||
|
text = text.toLowerCase()
|
||||||
|
}
|
||||||
|
words.forEach(word => {
|
||||||
|
if (this.unescape) {
|
||||||
|
const div = document.createElement('div')
|
||||||
|
div.innerText = word
|
||||||
|
word = div.innerHTML
|
||||||
|
}
|
||||||
|
const wordLen = word.length
|
||||||
|
if (wordLen === 0) return
|
||||||
|
let startPosition = 0
|
||||||
|
let position = -1
|
||||||
|
if (!caseSensitive) {
|
||||||
|
word = word.toLowerCase()
|
||||||
|
}
|
||||||
|
while ((position = text.indexOf(word, startPosition)) > -1) {
|
||||||
|
index.push({ position, word })
|
||||||
|
included.add(word)
|
||||||
|
startPosition = position + wordLen
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// Sort index by position of keyword
|
||||||
|
index.sort((left, right) => {
|
||||||
|
if (left.position !== right.position) {
|
||||||
|
return left.position - right.position
|
||||||
|
}
|
||||||
|
return right.word.length - left.word.length
|
||||||
|
})
|
||||||
|
return [index, included]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge hits into slices
|
||||||
|
mergeIntoSlice (start, end, index) {
|
||||||
|
let item = index[0]
|
||||||
|
let { position, word } = item
|
||||||
|
const hits = []
|
||||||
|
const count = new Set()
|
||||||
|
while (position + word.length <= end && index.length !== 0) {
|
||||||
|
count.add(word)
|
||||||
|
hits.push({
|
||||||
|
position,
|
||||||
|
length: word.length
|
||||||
|
})
|
||||||
|
const wordEnd = position + word.length
|
||||||
|
|
||||||
|
// Move to next position of hit
|
||||||
|
index.shift()
|
||||||
|
while (index.length !== 0) {
|
||||||
|
item = index[0]
|
||||||
|
position = item.position
|
||||||
|
word = item.word
|
||||||
|
if (wordEnd > position) {
|
||||||
|
index.shift()
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
hits,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
count: count.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Highlight title and content
|
||||||
|
highlightKeyword (val, slice) {
|
||||||
|
let result = ''
|
||||||
|
let index = slice.start
|
||||||
|
for (const { position, length } of slice.hits) {
|
||||||
|
result += val.substring(index, position)
|
||||||
|
index = position + length
|
||||||
|
result += `<mark class="search-keyword">${val.substr(position, length)}</mark>`
|
||||||
|
}
|
||||||
|
result += val.substring(index, slice.end)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
getResultItems (keywords) {
|
||||||
|
const resultItems = []
|
||||||
|
this.datas.forEach(({ title, content, url }) => {
|
||||||
|
// The number of different keywords included in the article.
|
||||||
|
const [indexOfTitle, keysOfTitle] = this.getIndexByWord(keywords, title)
|
||||||
|
const [indexOfContent, keysOfContent] = this.getIndexByWord(keywords, content)
|
||||||
|
const includedCount = new Set([...keysOfTitle, ...keysOfContent]).size
|
||||||
|
|
||||||
|
// Show search results
|
||||||
|
const hitCount = indexOfTitle.length + indexOfContent.length
|
||||||
|
if (hitCount === 0) return
|
||||||
|
|
||||||
|
const slicesOfTitle = []
|
||||||
|
if (indexOfTitle.length !== 0) {
|
||||||
|
slicesOfTitle.push(this.mergeIntoSlice(0, title.length, indexOfTitle))
|
||||||
|
}
|
||||||
|
|
||||||
|
let slicesOfContent = []
|
||||||
|
while (indexOfContent.length !== 0) {
|
||||||
|
const item = indexOfContent[0]
|
||||||
|
const { position } = item
|
||||||
|
// Cut out 120 characters. The maxlength of .search-input is 80.
|
||||||
|
const start = Math.max(0, position - 20)
|
||||||
|
const end = Math.min(content.length, position + 100)
|
||||||
|
slicesOfContent.push(this.mergeIntoSlice(start, end, indexOfContent))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort slices in content by included keywords' count and hits' count
|
||||||
|
slicesOfContent.sort((left, right) => {
|
||||||
|
if (left.count !== right.count) {
|
||||||
|
return right.count - left.count
|
||||||
|
} else if (left.hits.length !== right.hits.length) {
|
||||||
|
return right.hits.length - left.hits.length
|
||||||
|
}
|
||||||
|
return left.start - right.start
|
||||||
|
})
|
||||||
|
|
||||||
|
// Select top N slices in content
|
||||||
|
const upperBound = parseInt(this.top_n_per_article, 10)
|
||||||
|
if (upperBound >= 0) {
|
||||||
|
slicesOfContent = slicesOfContent.slice(0, upperBound)
|
||||||
|
}
|
||||||
|
|
||||||
|
let resultItem = ''
|
||||||
|
|
||||||
|
url = new URL(url, location.origin)
|
||||||
|
url.searchParams.append('highlight', keywords.join(' '))
|
||||||
|
|
||||||
|
if (slicesOfTitle.length !== 0) {
|
||||||
|
resultItem += `<div class="local-search-hit-item"><a href="${url.href}"><span class="search-result-title">${this.highlightKeyword(title, slicesOfTitle[0])}</span>`
|
||||||
|
} else {
|
||||||
|
resultItem += `<div class="local-search-hit-item"><a href="${url.href}"><span class="search-result-title">${title}</span>`
|
||||||
|
}
|
||||||
|
|
||||||
|
slicesOfContent.forEach(slice => {
|
||||||
|
resultItem += `<p class="search-result">${this.highlightKeyword(content, slice)}...</p></a>`
|
||||||
|
})
|
||||||
|
|
||||||
|
resultItem += '</div>'
|
||||||
|
resultItems.push({
|
||||||
|
item: resultItem,
|
||||||
|
id: resultItems.length,
|
||||||
|
hitCount,
|
||||||
|
includedCount
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return resultItems
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchData () {
|
||||||
|
const isXml = !this.path.endsWith('json')
|
||||||
|
fetch(this.path)
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(res => {
|
||||||
|
// Get the contents from search data
|
||||||
|
this.isfetched = true
|
||||||
|
this.datas = isXml
|
||||||
|
? [...new DOMParser().parseFromString(res, 'text/xml').querySelectorAll('entry')].map(element => ({
|
||||||
|
title: element.querySelector('title').textContent,
|
||||||
|
content: element.querySelector('content').textContent,
|
||||||
|
url: element.querySelector('url').textContent
|
||||||
|
}))
|
||||||
|
: JSON.parse(res)
|
||||||
|
// Only match articles with non-empty titles
|
||||||
|
this.datas = this.datas.filter(data => data.title).map(data => {
|
||||||
|
data.title = data.title.trim()
|
||||||
|
data.content = data.content ? data.content.trim().replace(/<[^>]+>/g, '') : ''
|
||||||
|
data.url = decodeURIComponent(data.url).replace(/\/{2,}/g, '/')
|
||||||
|
return data
|
||||||
|
})
|
||||||
|
// Remove loading animation
|
||||||
|
window.dispatchEvent(new Event('search:loaded'))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Highlight by wrapping node in mark elements with the given class name
|
||||||
|
highlightText (node, slice, className) {
|
||||||
|
const val = node.nodeValue
|
||||||
|
let index = slice.start
|
||||||
|
const children = []
|
||||||
|
for (const { position, length } of slice.hits) {
|
||||||
|
const text = document.createTextNode(val.substring(index, position))
|
||||||
|
index = position + length
|
||||||
|
const mark = document.createElement('mark')
|
||||||
|
mark.className = className
|
||||||
|
mark.appendChild(document.createTextNode(val.substr(position, length)))
|
||||||
|
children.push(text, mark)
|
||||||
|
}
|
||||||
|
node.nodeValue = val.substring(index, slice.end)
|
||||||
|
children.forEach(element => {
|
||||||
|
node.parentNode.insertBefore(element, node)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Highlight the search words provided in the url in the text
|
||||||
|
highlightSearchWords (body) {
|
||||||
|
const params = new URL(location.href).searchParams.get('highlight')
|
||||||
|
const keywords = params ? params.split(' ') : []
|
||||||
|
if (!keywords.length || !body) return
|
||||||
|
const walk = document.createTreeWalker(body, NodeFilter.SHOW_TEXT, null)
|
||||||
|
const allNodes = []
|
||||||
|
while (walk.nextNode()) {
|
||||||
|
if (!walk.currentNode.parentNode.matches('button, select, textarea, .mermaid')) allNodes.push(walk.currentNode)
|
||||||
|
}
|
||||||
|
allNodes.forEach(node => {
|
||||||
|
const [indexOfNode] = this.getIndexByWord(keywords, node.nodeValue)
|
||||||
|
if (!indexOfNode.length) return
|
||||||
|
const slice = this.mergeIntoSlice(0, node.nodeValue.length, indexOfNode)
|
||||||
|
this.highlightText(node, slice, 'search-keyword')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
// Search
|
||||||
|
const { path, top_n_per_article, unescape, languages } = GLOBAL_CONFIG.localSearch
|
||||||
|
const localSearch = new LocalSearch({
|
||||||
|
path,
|
||||||
|
top_n_per_article,
|
||||||
|
unescape
|
||||||
|
})
|
||||||
|
|
||||||
|
const input = document.querySelector('#local-search-input input')
|
||||||
|
const statsItem = document.getElementById('local-search-stats-wrap')
|
||||||
|
const $loadingStatus = document.getElementById('loading-status')
|
||||||
|
|
||||||
|
const inputEventFunction = () => {
|
||||||
|
if (!localSearch.isfetched) return
|
||||||
|
const searchText = input.value.trim().toLowerCase()
|
||||||
|
if (searchText !== '') $loadingStatus.innerHTML = '<i class="fas fa-spinner fa-pulse"></i>'
|
||||||
|
const keywords = searchText.split(/[-\s]+/)
|
||||||
|
const container = document.getElementById('local-search-results')
|
||||||
|
let resultItems = []
|
||||||
|
if (searchText.length > 0) {
|
||||||
|
// Perform local searching
|
||||||
|
resultItems = localSearch.getResultItems(keywords)
|
||||||
|
}
|
||||||
|
if (keywords.length === 1 && keywords[0] === '') {
|
||||||
|
container.classList.add('no-result')
|
||||||
|
container.textContent = ''
|
||||||
|
} else if (resultItems.length === 0) {
|
||||||
|
container.textContent = ''
|
||||||
|
statsItem.innerHTML = `<div class="search-result-stats">${languages.hits_empty.replace(/\$\{query}/, searchText)}</div>`
|
||||||
|
} else {
|
||||||
|
resultItems.sort((left, right) => {
|
||||||
|
if (left.includedCount !== right.includedCount) {
|
||||||
|
return right.includedCount - left.includedCount
|
||||||
|
} else if (left.hitCount !== right.hitCount) {
|
||||||
|
return right.hitCount - left.hitCount
|
||||||
|
}
|
||||||
|
return right.id - left.id
|
||||||
|
})
|
||||||
|
|
||||||
|
const stats = languages.hits_stats.replace(/\$\{hits}/, resultItems.length)
|
||||||
|
|
||||||
|
container.classList.remove('no-result')
|
||||||
|
container.innerHTML = `<div class="search-result-list">${resultItems.map(result => result.item).join('')}</div>`
|
||||||
|
statsItem.innerHTML = `<br><div class="search-result-stats">${stats}</div>`
|
||||||
|
window.pjax && window.pjax.refresh(container)
|
||||||
|
}
|
||||||
|
|
||||||
|
$loadingStatus.textContent = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
let loadFlag = false
|
||||||
|
const $searchMask = document.getElementById('search-mask')
|
||||||
|
const $searchDialog = document.querySelector('#local-search .search-dialog')
|
||||||
|
|
||||||
|
// fix safari
|
||||||
|
const fixSafariHeight = () => {
|
||||||
|
if (window.innerWidth < 768) {
|
||||||
|
$searchDialog.style.setProperty('--search-height', window.innerHeight + 'px')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const openSearch = () => {
|
||||||
|
const bodyStyle = document.body.style
|
||||||
|
bodyStyle.width = '100%'
|
||||||
|
bodyStyle.overflow = 'hidden'
|
||||||
|
btf.animateIn($searchMask, 'to_show 0.5s')
|
||||||
|
btf.animateIn($searchDialog, 'titleScale 0.5s')
|
||||||
|
setTimeout(() => { input.focus() }, 300)
|
||||||
|
if (!loadFlag) {
|
||||||
|
!localSearch.isfetched && localSearch.fetchData()
|
||||||
|
input.addEventListener('input', inputEventFunction)
|
||||||
|
loadFlag = true
|
||||||
|
}
|
||||||
|
// shortcut: ESC
|
||||||
|
document.addEventListener('keydown', function f (event) {
|
||||||
|
if (event.code === 'Escape') {
|
||||||
|
closeSearch()
|
||||||
|
document.removeEventListener('keydown', f)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
fixSafariHeight()
|
||||||
|
window.addEventListener('resize', fixSafariHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeSearch = () => {
|
||||||
|
const bodyStyle = document.body.style
|
||||||
|
bodyStyle.width = ''
|
||||||
|
bodyStyle.overflow = ''
|
||||||
|
btf.animateOut($searchDialog, 'search_close .5s')
|
||||||
|
btf.animateOut($searchMask, 'to_hide 0.5s')
|
||||||
|
window.removeEventListener('resize', fixSafariHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchClickFn = () => {
|
||||||
|
document.querySelector('#search-button > .search').addEventListener('click', openSearch)
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchFnOnce = () => {
|
||||||
|
document.querySelector('#local-search .search-close-button').addEventListener('click', closeSearch)
|
||||||
|
$searchMask.addEventListener('click', closeSearch)
|
||||||
|
if (GLOBAL_CONFIG.localSearch.preload) {
|
||||||
|
localSearch.fetchData()
|
||||||
|
}
|
||||||
|
localSearch.highlightSearchWords(document.getElementById('article-container'))
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('search:loaded', () => {
|
||||||
|
const $loadDataItem = document.getElementById('loading-database')
|
||||||
|
$loadDataItem.nextElementSibling.style.display = 'block'
|
||||||
|
$loadDataItem.remove()
|
||||||
|
})
|
||||||
|
|
||||||
|
searchClickFn()
|
||||||
|
searchFnOnce()
|
||||||
|
|
||||||
|
// pjax
|
||||||
|
window.addEventListener('pjax:complete', () => {
|
||||||
|
!btf.isHidden($searchMask) && closeSearch()
|
||||||
|
localSearch.highlightSearchWords(document.getElementById('article-container'))
|
||||||
|
searchClickFn()
|
||||||
|
})
|
||||||
|
})
|
115
js/tw_cn.js
Normal file
307
js/utils.js
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
const btf = {
|
||||||
|
debounce: function (func, wait, immediate) {
|
||||||
|
let timeout
|
||||||
|
return function () {
|
||||||
|
const context = this
|
||||||
|
const args = arguments
|
||||||
|
const later = function () {
|
||||||
|
timeout = null
|
||||||
|
if (!immediate) func.apply(context, args)
|
||||||
|
}
|
||||||
|
const callNow = immediate && !timeout
|
||||||
|
clearTimeout(timeout)
|
||||||
|
timeout = setTimeout(later, wait)
|
||||||
|
if (callNow) func.apply(context, args)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
throttle: function (func, wait, options) {
|
||||||
|
let timeout, context, args
|
||||||
|
let previous = 0
|
||||||
|
if (!options) options = {}
|
||||||
|
|
||||||
|
const later = function () {
|
||||||
|
previous = options.leading === false ? 0 : new Date().getTime()
|
||||||
|
timeout = null
|
||||||
|
func.apply(context, args)
|
||||||
|
if (!timeout) context = args = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const throttled = function () {
|
||||||
|
const now = new Date().getTime()
|
||||||
|
if (!previous && options.leading === false) previous = now
|
||||||
|
const remaining = wait - (now - previous)
|
||||||
|
context = this
|
||||||
|
args = arguments
|
||||||
|
if (remaining <= 0 || remaining > wait) {
|
||||||
|
if (timeout) {
|
||||||
|
clearTimeout(timeout)
|
||||||
|
timeout = null
|
||||||
|
}
|
||||||
|
previous = now
|
||||||
|
func.apply(context, args)
|
||||||
|
if (!timeout) context = args = null
|
||||||
|
} else if (!timeout && options.trailing !== false) {
|
||||||
|
timeout = setTimeout(later, remaining)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return throttled
|
||||||
|
},
|
||||||
|
|
||||||
|
sidebarPaddingR: () => {
|
||||||
|
const innerWidth = window.innerWidth
|
||||||
|
const clientWidth = document.body.clientWidth
|
||||||
|
const paddingRight = innerWidth - clientWidth
|
||||||
|
if (innerWidth !== clientWidth) {
|
||||||
|
document.body.style.paddingRight = paddingRight + 'px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
snackbarShow: (text, showAction = false, duration = 2000) => {
|
||||||
|
const { position, bgLight, bgDark } = GLOBAL_CONFIG.Snackbar
|
||||||
|
const bg = document.documentElement.getAttribute('data-theme') === 'light' ? bgLight : bgDark
|
||||||
|
Snackbar.show({
|
||||||
|
text,
|
||||||
|
backgroundColor: bg,
|
||||||
|
showAction,
|
||||||
|
duration,
|
||||||
|
pos: position,
|
||||||
|
customClass: 'snackbar-css'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
diffDate: (d, more = false) => {
|
||||||
|
const dateNow = new Date()
|
||||||
|
const datePost = new Date(d)
|
||||||
|
const dateDiff = dateNow.getTime() - datePost.getTime()
|
||||||
|
const minute = 1000 * 60
|
||||||
|
const hour = minute * 60
|
||||||
|
const day = hour * 24
|
||||||
|
const month = day * 30
|
||||||
|
const { dateSuffix } = GLOBAL_CONFIG
|
||||||
|
|
||||||
|
if (!more) return parseInt(dateDiff / day)
|
||||||
|
|
||||||
|
const monthCount = dateDiff / month
|
||||||
|
const dayCount = dateDiff / day
|
||||||
|
const hourCount = dateDiff / hour
|
||||||
|
const minuteCount = dateDiff / minute
|
||||||
|
|
||||||
|
if (monthCount > 12) return datePost.toISOString().slice(0, 10)
|
||||||
|
if (monthCount >= 1) return `${parseInt(monthCount)} ${dateSuffix.month}`
|
||||||
|
if (dayCount >= 1) return `${parseInt(dayCount)} ${dateSuffix.day}`
|
||||||
|
if (hourCount >= 1) return `${parseInt(hourCount)} ${dateSuffix.hour}`
|
||||||
|
if (minuteCount >= 1) return `${parseInt(minuteCount)} ${dateSuffix.min}`
|
||||||
|
return dateSuffix.just
|
||||||
|
},
|
||||||
|
|
||||||
|
loadComment: (dom, callback) => {
|
||||||
|
if ('IntersectionObserver' in window) {
|
||||||
|
const observerItem = new IntersectionObserver((entries) => {
|
||||||
|
if (entries[0].isIntersecting) {
|
||||||
|
callback()
|
||||||
|
observerItem.disconnect()
|
||||||
|
}
|
||||||
|
}, { threshold: [0] })
|
||||||
|
observerItem.observe(dom)
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
scrollToDest: (pos, time = 500) => {
|
||||||
|
const currentPos = window.pageYOffset
|
||||||
|
const isNavFixed = document.getElementById('page-header').classList.contains('fixed')
|
||||||
|
if (currentPos > pos || isNavFixed) pos = pos - 70
|
||||||
|
|
||||||
|
if ('scrollBehavior' in document.documentElement.style) {
|
||||||
|
window.scrollTo({
|
||||||
|
top: pos,
|
||||||
|
behavior: 'smooth'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let start = null
|
||||||
|
pos = +pos
|
||||||
|
window.requestAnimationFrame(function step (currentTime) {
|
||||||
|
start = !start ? currentTime : start
|
||||||
|
const progress = currentTime - start
|
||||||
|
if (currentPos < pos) {
|
||||||
|
window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos)
|
||||||
|
} else {
|
||||||
|
window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time))
|
||||||
|
}
|
||||||
|
if (progress < time) {
|
||||||
|
window.requestAnimationFrame(step)
|
||||||
|
} else {
|
||||||
|
window.scrollTo(0, pos)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
animateIn: (ele, text) => {
|
||||||
|
ele.style.display = 'block'
|
||||||
|
ele.style.animation = text
|
||||||
|
},
|
||||||
|
|
||||||
|
animateOut: (ele, text) => {
|
||||||
|
ele.addEventListener('animationend', function f () {
|
||||||
|
ele.style.display = ''
|
||||||
|
ele.style.animation = ''
|
||||||
|
ele.removeEventListener('animationend', f)
|
||||||
|
})
|
||||||
|
ele.style.animation = text
|
||||||
|
},
|
||||||
|
|
||||||
|
getParents: (elem, selector) => {
|
||||||
|
for (; elem && elem !== document; elem = elem.parentNode) {
|
||||||
|
if (elem.matches(selector)) return elem
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
|
||||||
|
siblings: (ele, selector) => {
|
||||||
|
return [...ele.parentNode.children].filter((child) => {
|
||||||
|
if (selector) {
|
||||||
|
return child !== ele && child.matches(selector)
|
||||||
|
}
|
||||||
|
return child !== ele
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {*} selector
|
||||||
|
* @param {*} eleType the type of create element
|
||||||
|
* @param {*} options object key: value
|
||||||
|
*/
|
||||||
|
wrap: (selector, eleType, options) => {
|
||||||
|
const createEle = document.createElement(eleType)
|
||||||
|
for (const [key, value] of Object.entries(options)) {
|
||||||
|
createEle.setAttribute(key, value)
|
||||||
|
}
|
||||||
|
selector.parentNode.insertBefore(createEle, selector)
|
||||||
|
createEle.appendChild(selector)
|
||||||
|
},
|
||||||
|
|
||||||
|
unwrap: el => {
|
||||||
|
const parent = el.parentNode
|
||||||
|
if (parent && parent !== document.body) {
|
||||||
|
parent.replaceChild(el, parent)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
isHidden: ele => ele.offsetHeight === 0 && ele.offsetWidth === 0,
|
||||||
|
|
||||||
|
getEleTop: ele => {
|
||||||
|
let actualTop = ele.offsetTop
|
||||||
|
let current = ele.offsetParent
|
||||||
|
|
||||||
|
while (current !== null) {
|
||||||
|
actualTop += current.offsetTop
|
||||||
|
current = current.offsetParent
|
||||||
|
}
|
||||||
|
|
||||||
|
return actualTop
|
||||||
|
},
|
||||||
|
|
||||||
|
loadLightbox: ele => {
|
||||||
|
const service = GLOBAL_CONFIG.lightbox
|
||||||
|
|
||||||
|
if (service === 'mediumZoom') {
|
||||||
|
mediumZoom(ele, { background: 'var(--zoom-bg)' })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service === 'fancybox') {
|
||||||
|
ele.forEach(i => {
|
||||||
|
if (i.parentNode.tagName !== 'A') {
|
||||||
|
const dataSrc = i.dataset.lazySrc || i.src
|
||||||
|
const dataCaption = i.title || i.alt || ''
|
||||||
|
btf.wrap(i, 'a', { href: dataSrc, 'data-fancybox': 'gallery', 'data-caption': dataCaption, 'data-thumb': dataSrc })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!window.fancyboxRun) {
|
||||||
|
Fancybox.bind('[data-fancybox]', {
|
||||||
|
Hash: false,
|
||||||
|
Thumbs: {
|
||||||
|
showOnStart: false
|
||||||
|
},
|
||||||
|
Images: {
|
||||||
|
Panzoom: {
|
||||||
|
maxScale: 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Carousel: {
|
||||||
|
transition: 'slide'
|
||||||
|
},
|
||||||
|
Toolbar: {
|
||||||
|
display: {
|
||||||
|
left: ['infobar'],
|
||||||
|
middle: [
|
||||||
|
'zoomIn',
|
||||||
|
'zoomOut',
|
||||||
|
'toggle1to1',
|
||||||
|
'rotateCCW',
|
||||||
|
'rotateCW',
|
||||||
|
'flipX',
|
||||||
|
'flipY'
|
||||||
|
],
|
||||||
|
right: ['slideshow', 'thumbs', 'close']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
window.fancyboxRun = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
initJustifiedGallery: function (selector) {
|
||||||
|
const runJustifiedGallery = i => {
|
||||||
|
if (!btf.isHidden(i)) {
|
||||||
|
fjGallery(i, {
|
||||||
|
itemSelector: '.fj-gallery-item',
|
||||||
|
rowHeight: i.getAttribute('data-rowHeight'),
|
||||||
|
gutter: 4,
|
||||||
|
onJustify: function () {
|
||||||
|
this.$container.style.opacity = '1'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.from(selector).length === 0) runJustifiedGallery(selector)
|
||||||
|
else selector.forEach(i => { runJustifiedGallery(i) })
|
||||||
|
},
|
||||||
|
|
||||||
|
updateAnchor: (anchor) => {
|
||||||
|
if (anchor !== window.location.hash) {
|
||||||
|
if (!anchor) anchor = location.pathname
|
||||||
|
const title = GLOBAL_CONFIG_SITE.title
|
||||||
|
window.history.replaceState({
|
||||||
|
url: location.href,
|
||||||
|
title
|
||||||
|
}, title, anchor)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getScrollPercent: (currentTop, ele) => {
|
||||||
|
const docHeight = ele.clientHeight
|
||||||
|
const winHeight = document.documentElement.clientHeight
|
||||||
|
const headerHeight = ele.offsetTop
|
||||||
|
const contentMath = (docHeight > winHeight) ? (docHeight - winHeight) : (document.documentElement.scrollHeight - winHeight)
|
||||||
|
const scrollPercent = (currentTop - headerHeight) / (contentMath)
|
||||||
|
const scrollPercentRounded = Math.round(scrollPercent * 100)
|
||||||
|
const percentage = (scrollPercentRounded > 100) ? 100 : (scrollPercentRounded <= 0) ? 0 : scrollPercentRounded
|
||||||
|
return percentage
|
||||||
|
},
|
||||||
|
|
||||||
|
addModeChange: (name, fn) => {
|
||||||
|
if (window.themeChange && window.themeChange[name]) return
|
||||||
|
window.themeChange = {
|
||||||
|
...window.themeChange,
|
||||||
|
[name]: fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
297
lib/hbe.js
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
(() => {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const cryptoObj = window.crypto || window.msCrypto;
|
||||||
|
const storage = window.localStorage;
|
||||||
|
|
||||||
|
const storageName = 'hexo-blog-encrypt:#' + window.location.pathname;
|
||||||
|
const keySalt = textToArray('hexo-blog-encrypt的作者们都是大帅比!');
|
||||||
|
const ivSalt = textToArray('hexo-blog-encrypt是地表最强Hexo加密插件!');
|
||||||
|
|
||||||
|
// As we can't detect the wrong password with AES-CBC,
|
||||||
|
// so adding an empty div and check it when decrption.
|
||||||
|
const knownPrefix = "<hbe-prefix></hbe-prefix>";
|
||||||
|
|
||||||
|
const mainElement = document.getElementById('hexo-blog-encrypt');
|
||||||
|
const wrongPassMessage = mainElement.dataset['wpm'];
|
||||||
|
const wrongHashMessage = mainElement.dataset['whm'];
|
||||||
|
const dataElement = mainElement.getElementsByTagName('script')['hbeData'];
|
||||||
|
const encryptedData = dataElement.innerText;
|
||||||
|
const HmacDigist = dataElement.dataset['hmacdigest'];
|
||||||
|
|
||||||
|
function hexToArray(s) {
|
||||||
|
return new Uint8Array(s.match(/[\da-f]{2}/gi).map((h => {
|
||||||
|
return parseInt(h, 16);
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
function textToArray(s) {
|
||||||
|
var i = s.length;
|
||||||
|
var n = 0;
|
||||||
|
var ba = new Array()
|
||||||
|
|
||||||
|
for (var j = 0; j < i;) {
|
||||||
|
var c = s.codePointAt(j);
|
||||||
|
if (c < 128) {
|
||||||
|
ba[n++] = c;
|
||||||
|
j++;
|
||||||
|
} else if ((c > 127) && (c < 2048)) {
|
||||||
|
ba[n++] = (c >> 6) | 192;
|
||||||
|
ba[n++] = (c & 63) | 128;
|
||||||
|
j++;
|
||||||
|
} else if ((c > 2047) && (c < 65536)) {
|
||||||
|
ba[n++] = (c >> 12) | 224;
|
||||||
|
ba[n++] = ((c >> 6) & 63) | 128;
|
||||||
|
ba[n++] = (c & 63) | 128;
|
||||||
|
j++;
|
||||||
|
} else {
|
||||||
|
ba[n++] = (c >> 18) | 240;
|
||||||
|
ba[n++] = ((c >> 12) & 63) | 128;
|
||||||
|
ba[n++] = ((c >> 6) & 63) | 128;
|
||||||
|
ba[n++] = (c & 63) | 128;
|
||||||
|
j += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Uint8Array(ba);
|
||||||
|
}
|
||||||
|
|
||||||
|
function arrayBufferToHex(arrayBuffer) {
|
||||||
|
if (typeof arrayBuffer !== 'object' || arrayBuffer === null || typeof arrayBuffer.byteLength !== 'number') {
|
||||||
|
throw new TypeError('Expected input to be an ArrayBuffer')
|
||||||
|
}
|
||||||
|
|
||||||
|
var view = new Uint8Array(arrayBuffer)
|
||||||
|
var result = ''
|
||||||
|
var value
|
||||||
|
|
||||||
|
for (var i = 0; i < view.length; i++) {
|
||||||
|
value = view[i].toString(16)
|
||||||
|
result += (value.length === 1 ? '0' + value : value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getExecutableScript(oldElem) {
|
||||||
|
let out = document.createElement('script');
|
||||||
|
const attList = ['type', 'text', 'src', 'crossorigin', 'defer', 'referrerpolicy'];
|
||||||
|
attList.forEach((att) => {
|
||||||
|
if (oldElem[att])
|
||||||
|
out[att] = oldElem[att];
|
||||||
|
})
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function convertHTMLToElement(content) {
|
||||||
|
let out = document.createElement('div');
|
||||||
|
out.innerHTML = content;
|
||||||
|
out.querySelectorAll('script').forEach(async (elem) => {
|
||||||
|
elem.replaceWith(await getExecutableScript(elem));
|
||||||
|
});
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getKeyMaterial(password) {
|
||||||
|
let encoder = new TextEncoder();
|
||||||
|
return cryptoObj.subtle.importKey(
|
||||||
|
'raw',
|
||||||
|
encoder.encode(password),
|
||||||
|
{
|
||||||
|
'name': 'PBKDF2',
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
[
|
||||||
|
'deriveKey',
|
||||||
|
'deriveBits',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHmacKey(keyMaterial) {
|
||||||
|
return cryptoObj.subtle.deriveKey({
|
||||||
|
'name': 'PBKDF2',
|
||||||
|
'hash': 'SHA-256',
|
||||||
|
'salt': keySalt.buffer,
|
||||||
|
'iterations': 1024
|
||||||
|
}, keyMaterial, {
|
||||||
|
'name': 'HMAC',
|
||||||
|
'hash': 'SHA-256',
|
||||||
|
'length': 256,
|
||||||
|
}, true, [
|
||||||
|
'verify',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDecryptKey(keyMaterial) {
|
||||||
|
return cryptoObj.subtle.deriveKey({
|
||||||
|
'name': 'PBKDF2',
|
||||||
|
'hash': 'SHA-256',
|
||||||
|
'salt': keySalt.buffer,
|
||||||
|
'iterations': 1024,
|
||||||
|
}, keyMaterial, {
|
||||||
|
'name': 'AES-CBC',
|
||||||
|
'length': 256,
|
||||||
|
}, true, [
|
||||||
|
'decrypt',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIv(keyMaterial) {
|
||||||
|
return cryptoObj.subtle.deriveBits({
|
||||||
|
'name': 'PBKDF2',
|
||||||
|
'hash': 'SHA-256',
|
||||||
|
'salt': ivSalt.buffer,
|
||||||
|
'iterations': 512,
|
||||||
|
}, keyMaterial, 16 * 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function verifyContent(key, content) {
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const encoded = encoder.encode(content);
|
||||||
|
|
||||||
|
let signature = hexToArray(HmacDigist);
|
||||||
|
|
||||||
|
const result = await cryptoObj.subtle.verify({
|
||||||
|
'name': 'HMAC',
|
||||||
|
'hash': 'SHA-256',
|
||||||
|
}, key, signature, encoded);
|
||||||
|
console.log(`Verification result: ${result}`);
|
||||||
|
if (!result) {
|
||||||
|
alert(wrongHashMessage);
|
||||||
|
console.log(`${wrongHashMessage}, got `, signature, ` but proved wrong.`);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function decrypt(decryptKey, iv, hmacKey) {
|
||||||
|
let typedArray = hexToArray(encryptedData);
|
||||||
|
|
||||||
|
const result = await cryptoObj.subtle.decrypt({
|
||||||
|
'name': 'AES-CBC',
|
||||||
|
'iv': iv,
|
||||||
|
}, decryptKey, typedArray.buffer).then(async (result) => {
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
const decoded = decoder.decode(result);
|
||||||
|
|
||||||
|
// check the prefix, if not then we can sure here is wrong password.
|
||||||
|
if (!decoded.startsWith(knownPrefix)) {
|
||||||
|
throw "Decode successfully but not start with KnownPrefix.";
|
||||||
|
}
|
||||||
|
|
||||||
|
const hideButton = document.createElement('button');
|
||||||
|
hideButton.textContent = 'Encrypt again';
|
||||||
|
hideButton.type = 'button';
|
||||||
|
hideButton.classList.add("hbe-button");
|
||||||
|
hideButton.addEventListener('click', () => {
|
||||||
|
window.localStorage.removeItem(storageName);
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('hexo-blog-encrypt').style.display = 'inline';
|
||||||
|
document.getElementById('hexo-blog-encrypt').innerHTML = '';
|
||||||
|
document.getElementById('hexo-blog-encrypt').appendChild(await convertHTMLToElement(decoded));
|
||||||
|
document.getElementById('hexo-blog-encrypt').appendChild(hideButton);
|
||||||
|
|
||||||
|
// support html5 lazyload functionality.
|
||||||
|
document.querySelectorAll('img').forEach((elem) => {
|
||||||
|
if (elem.getAttribute("data-src") && !elem.src) {
|
||||||
|
elem.src = elem.getAttribute('data-src');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// support theme-next refresh
|
||||||
|
window.NexT && NexT.boot && typeof NexT.boot.refresh === 'function' && NexT.boot.refresh();
|
||||||
|
|
||||||
|
// TOC part
|
||||||
|
var tocDiv = document.getElementById("toc-div");
|
||||||
|
if (tocDiv) {
|
||||||
|
tocDiv.style.display = 'inline';
|
||||||
|
}
|
||||||
|
|
||||||
|
var tocDivs = document.getElementsByClassName('toc-div-class');
|
||||||
|
if (tocDivs && tocDivs.length > 0) {
|
||||||
|
for (var idx = 0; idx < tocDivs.length; idx++) {
|
||||||
|
tocDivs[idx].style.display = 'inline';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// trigger event
|
||||||
|
var event = new Event('hexo-blog-decrypt');
|
||||||
|
window.dispatchEvent(event);
|
||||||
|
|
||||||
|
return await verifyContent(hmacKey, decoded);
|
||||||
|
}).catch((e) => {
|
||||||
|
alert(wrongPassMessage);
|
||||||
|
console.log(e);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function hbeLoader() {
|
||||||
|
|
||||||
|
const oldStorageData = JSON.parse(storage.getItem(storageName));
|
||||||
|
|
||||||
|
if (oldStorageData) {
|
||||||
|
console.log(`Password got from localStorage(${storageName}): `, oldStorageData);
|
||||||
|
|
||||||
|
const sIv = hexToArray(oldStorageData.iv).buffer;
|
||||||
|
const sDk = oldStorageData.dk;
|
||||||
|
const sHmk = oldStorageData.hmk;
|
||||||
|
|
||||||
|
cryptoObj.subtle.importKey('jwk', sDk, {
|
||||||
|
'name': 'AES-CBC',
|
||||||
|
'length': 256,
|
||||||
|
}, true, [
|
||||||
|
'decrypt',
|
||||||
|
]).then((dkCK) => {
|
||||||
|
cryptoObj.subtle.importKey('jwk', sHmk, {
|
||||||
|
'name': 'HMAC',
|
||||||
|
'hash': 'SHA-256',
|
||||||
|
'length': 256,
|
||||||
|
}, true, [
|
||||||
|
'verify',
|
||||||
|
]).then((hmkCK) => {
|
||||||
|
decrypt(dkCK, sIv, hmkCK).then((result) => {
|
||||||
|
if (!result) {
|
||||||
|
storage.removeItem(storageName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mainElement.addEventListener('keydown', async (event) => {
|
||||||
|
if (event.isComposing || event.keyCode === 13) {
|
||||||
|
const password = document.getElementById('hbePass').value;
|
||||||
|
const keyMaterial = await getKeyMaterial(password);
|
||||||
|
const hmacKey = await getHmacKey(keyMaterial);
|
||||||
|
const decryptKey = await getDecryptKey(keyMaterial);
|
||||||
|
const iv = await getIv(keyMaterial);
|
||||||
|
|
||||||
|
decrypt(decryptKey, iv, hmacKey).then((result) => {
|
||||||
|
console.log(`Decrypt result: ${result}`);
|
||||||
|
if (result) {
|
||||||
|
cryptoObj.subtle.exportKey('jwk', decryptKey).then((dk) => {
|
||||||
|
cryptoObj.subtle.exportKey('jwk', hmacKey).then((hmk) => {
|
||||||
|
const newStorageData = {
|
||||||
|
'dk': dk,
|
||||||
|
'iv': arrayBufferToHex(iv),
|
||||||
|
'hmk': hmk,
|
||||||
|
};
|
||||||
|
storage.setItem(storageName, JSON.stringify(newStorageData));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
hbeLoader();
|
||||||
|
|
||||||
|
})();
|
204
notice/index.html
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0,viewport-fit=cover"><title>公告 | The Blog</title><meta name="author" content="Jason"><meta name="copyright" content="Jason"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="2023 03-1 建立博客 09-10 改进博客中图片的存储方案 09-11 博客增加百度访问统计功能 09-12 博客中增加公告页">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:title" content="公告">
|
||||||
|
<meta property="og:url" content="https://jasonsgong.gitee.io/notice/index.html">
|
||||||
|
<meta property="og:site_name" content="The Blog">
|
||||||
|
<meta property="og:description" content="2023 03-1 建立博客 09-10 改进博客中图片的存储方案 09-11 博客增加百度访问统计功能 09-12 博客中增加公告页">
|
||||||
|
<meta property="og:locale" content="zh_CN">
|
||||||
|
<meta property="og:image" content="https://jasonsgong.gitee.io/img/5.jpg">
|
||||||
|
<meta property="article:published_time" content="2023-09-12T05:52:08.000Z">
|
||||||
|
<meta property="article:modified_time" content="2023-09-12T03:17:10.000Z">
|
||||||
|
<meta property="article:author" content="Jason">
|
||||||
|
<meta name="twitter:card" content="summary">
|
||||||
|
<meta name="twitter:image" content="https://jasonsgong.gitee.io/img/5.jpg"><link rel="shortcut icon" href="/img/%E5%9B%BE%E6%A0%87.png"><link rel="canonical" href="https://jasonsgong.gitee.io/notice/index.html"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//hm.baidu.com"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox/fancybox.min.css" media="print" onload="this.media='all'"><script>var _hmt = _hmt || [];
|
||||||
|
(function() {
|
||||||
|
var hm = document.createElement("script");
|
||||||
|
hm.src = "https://hm.baidu.com/hm.js?863f84ff1342665d6b55193272aea7b2";
|
||||||
|
var s = document.getElementsByTagName("script")[0];
|
||||||
|
s.parentNode.insertBefore(hm, s);
|
||||||
|
})();
|
||||||
|
</script><script>const GLOBAL_CONFIG = {
|
||||||
|
root: '/',
|
||||||
|
algolia: undefined,
|
||||||
|
localSearch: {"path":"/search.xml","preload":true,"top_n_per_article":1,"unescape":false,"languages":{"hits_empty":"找不到您查询的内容:${query}","hits_stats":"共找到 ${hits} 篇文章"}},
|
||||||
|
translate: undefined,
|
||||||
|
noticeOutdate: undefined,
|
||||||
|
highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":400},
|
||||||
|
copy: {
|
||||||
|
success: '复制成功',
|
||||||
|
error: '复制错误',
|
||||||
|
noSupport: '浏览器不支持'
|
||||||
|
},
|
||||||
|
relativeDate: {
|
||||||
|
homepage: true,
|
||||||
|
post: true
|
||||||
|
},
|
||||||
|
runtime: '天',
|
||||||
|
dateSuffix: {
|
||||||
|
just: '刚刚',
|
||||||
|
min: '分钟前',
|
||||||
|
hour: '小时前',
|
||||||
|
day: '天前',
|
||||||
|
month: '个月前'
|
||||||
|
},
|
||||||
|
copyright: undefined,
|
||||||
|
lightbox: 'mediumZoom',
|
||||||
|
Snackbar: {"chs_to_cht":"你已切换为繁体","cht_to_chs":"你已切换为简体","day_to_night":"你已切换为深色模式","night_to_day":"你已切换为浅色模式","bgLight":"#008080","bgDark":"#008080","position":"top-center"},
|
||||||
|
source: {
|
||||||
|
justifiedGallery: {
|
||||||
|
js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.js',
|
||||||
|
css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.css'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isPhotoFigcaption: false,
|
||||||
|
islazyload: false,
|
||||||
|
isAnchor: false,
|
||||||
|
percent: {
|
||||||
|
toc: true,
|
||||||
|
rightside: false,
|
||||||
|
},
|
||||||
|
autoDarkmode: false
|
||||||
|
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
|
||||||
|
title: '公告',
|
||||||
|
isPost: false,
|
||||||
|
isHome: false,
|
||||||
|
isHighlightShrink: false,
|
||||||
|
isToc: false,
|
||||||
|
postUpdate: '2023-09-12 11:17:10'
|
||||||
|
}</script><noscript><style type="text/css">
|
||||||
|
#nav {
|
||||||
|
opacity: 1
|
||||||
|
}
|
||||||
|
.justified-gallery img {
|
||||||
|
opacity: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#recent-posts time,
|
||||||
|
#post-meta time {
|
||||||
|
display: inline !important
|
||||||
|
}
|
||||||
|
</style></noscript><script>(win=>{
|
||||||
|
win.saveToLocal = {
|
||||||
|
set: function setWithExpiry(key, value, ttl) {
|
||||||
|
if (ttl === 0) return
|
||||||
|
const now = new Date()
|
||||||
|
const expiryDay = ttl * 86400000
|
||||||
|
const item = {
|
||||||
|
value: value,
|
||||||
|
expiry: now.getTime() + expiryDay,
|
||||||
|
}
|
||||||
|
localStorage.setItem(key, JSON.stringify(item))
|
||||||
|
},
|
||||||
|
|
||||||
|
get: function getWithExpiry(key) {
|
||||||
|
const itemStr = localStorage.getItem(key)
|
||||||
|
|
||||||
|
if (!itemStr) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
const item = JSON.parse(itemStr)
|
||||||
|
const now = new Date()
|
||||||
|
|
||||||
|
if (now.getTime() > item.expiry) {
|
||||||
|
localStorage.removeItem(key)
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return item.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
win.getScript = url => new Promise((resolve, reject) => {
|
||||||
|
const script = document.createElement('script')
|
||||||
|
script.src = url
|
||||||
|
script.async = true
|
||||||
|
script.onerror = reject
|
||||||
|
script.onload = script.onreadystatechange = function() {
|
||||||
|
const loadState = this.readyState
|
||||||
|
if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
|
||||||
|
script.onload = script.onreadystatechange = null
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
document.head.appendChild(script)
|
||||||
|
})
|
||||||
|
|
||||||
|
win.getCSS = (url,id = false) => new Promise((resolve, reject) => {
|
||||||
|
const link = document.createElement('link')
|
||||||
|
link.rel = 'stylesheet'
|
||||||
|
link.href = url
|
||||||
|
if (id) link.id = id
|
||||||
|
link.onerror = reject
|
||||||
|
link.onload = link.onreadystatechange = function() {
|
||||||
|
const loadState = this.readyState
|
||||||
|
if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
|
||||||
|
link.onload = link.onreadystatechange = null
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
document.head.appendChild(link)
|
||||||
|
})
|
||||||
|
|
||||||
|
win.activateDarkMode = function () {
|
||||||
|
document.documentElement.setAttribute('data-theme', 'dark')
|
||||||
|
if (document.querySelector('meta[name="theme-color"]') !== null) {
|
||||||
|
document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
win.activateLightMode = function () {
|
||||||
|
document.documentElement.setAttribute('data-theme', 'light')
|
||||||
|
if (document.querySelector('meta[name="theme-color"]') !== null) {
|
||||||
|
document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const t = saveToLocal.get('theme')
|
||||||
|
|
||||||
|
if (t === 'dark') activateDarkMode()
|
||||||
|
else if (t === 'light') activateLightMode()
|
||||||
|
|
||||||
|
const asideStatus = saveToLocal.get('aside-status')
|
||||||
|
if (asideStatus !== undefined) {
|
||||||
|
if (asideStatus === 'hide') {
|
||||||
|
document.documentElement.classList.add('hide-aside')
|
||||||
|
} else {
|
||||||
|
document.documentElement.classList.remove('hide-aside')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const detectApple = () => {
|
||||||
|
if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
|
||||||
|
document.documentElement.classList.add('apple')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
detectApple()
|
||||||
|
})(window)</script><!-- hexo injector head_end start --><link rel="stylesheet" href="https://npm.elemecdn.com/hexo-butterfly-swiper/lib/swiper.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://npm.elemecdn.com/hexo-butterfly-swiper/lib/swiperstyle.css" media="print" onload="this.media='all'"><!-- hexo injector head_end end --><meta name="generator" content="Hexo 6.3.0"></head><body><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="/img/avatar.jpg" onerror="onerror=null;src='/img/loading.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">70</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">37</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">6</div></a></div><br/><div class="menus_items"><div class="menus_item"><a class="site-page" target="_blank" rel="noopener" href="https://gitee.com/JasonsGong/jasonsgong/pages"><i class="fa-fw fas fa-sync-alt"></i><span> 更新</span></a></div><div class="menus_item"><a class="site-page" target="_blank" rel="noopener" href="https://www.tutorialspoint.com/compile_java8_online.php"><i class="fa-fw fas fa-code"></i><span> 代码</span></a></div><div class="menus_item"><a class="site-page" href="/notice/"><i class="fa-fw far fa-paper-plane"></i><span> 公告</span></a></div><div class="menus_item"><a class="site-page" href="/website/bookmarks.html"><i class="fa-fw fas fa-desktop"></i><span> 网址</span></a></div><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div></div></div></div><div class="page" id="body-wrap"><header class="not-top-img" id="page-header"><nav id="nav"><span id="blog-info"><a href="/" title="The Blog"><span class="site-name">The Blog</span></a></span><div id="menus"><div id="search-button"><a class="site-page social-icon search" href="javascript:void(0);"><i class="fas fa-search fa-fw"></i><span> 搜索</span></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" target="_blank" rel="noopener" href="https://gitee.com/JasonsGong/jasonsgong/pages"><i class="fa-fw fas fa-sync-alt"></i><span> 更新</span></a></div><div class="menus_item"><a class="site-page" target="_blank" rel="noopener" href="https://www.tutorialspoint.com/compile_java8_online.php"><i class="fa-fw fas fa-code"></i><span> 代码</span></a></div><div class="menus_item"><a class="site-page" href="/notice/"><i class="fa-fw far fa-paper-plane"></i><span> 公告</span></a></div><div class="menus_item"><a class="site-page" href="/website/bookmarks.html"><i class="fa-fw fas fa-desktop"></i><span> 网址</span></a></div><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div></div><div id="toggle-menu"><a class="site-page" href="javascript:void(0);"><i class="fas fa-bars fa-fw"></i></a></div></div></nav></header><main class="layout hide-aside" id="content-inner"><div id="page"><h1 class="page-title">公告</h1><div id="article-container"><div class="timeline green"><div class='timeline-item headline'><div class='timeline-item-title'><div class='item-circle'><p>2023</p>
|
||||||
|
</div></div></div><div class='timeline-item'><div class='timeline-item-title'><div class='item-circle'><p>03-1</p>
|
||||||
|
</div></div><div class='timeline-item-content'><p>建立博客</p>
|
||||||
|
</div></div><div class='timeline-item'><div class='timeline-item-title'><div class='item-circle'><p>09-10</p>
|
||||||
|
</div></div><div class='timeline-item-content'><p>改进博客中图片的存储方案</p>
|
||||||
|
</div></div><div class='timeline-item'><div class='timeline-item-title'><div class='item-circle'><p>09-11</p>
|
||||||
|
</div></div><div class='timeline-item-content'><p>博客增加百度访问统计功能</p>
|
||||||
|
</div></div><div class='timeline-item'><div class='timeline-item-title'><div class='item-circle'><p>09-12</p>
|
||||||
|
</div></div><div class='timeline-item-content'><p>博客中增加公告页</p>
|
||||||
|
</div></div></div>
|
||||||
|
</div></div></main><footer id="footer"><div id="footer-wrap"><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button id="go-up" type="button" title="回到顶部"><span class="scroll-percent"></span><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="https://cdn.jsdelivr.net/npm/medium-zoom/dist/medium-zoom.min.js"></script><script src="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.js"></script><div class="js-pjax"></div><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><div id="local-search"><div class="search-dialog"><nav class="search-nav"><span class="search-dialog-title">搜索</span><span id="loading-status"></span><button class="search-close-button"><i class="fas fa-times"></i></button></nav><div class="is-center" id="loading-database"><i class="fas fa-spinner fa-pulse"></i><span> 数据库加载中</span></div><div class="search-wrap"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div><br/><div class="no-result" id="local-search-results"></div><div id="local-search-stats-wrap"></div></div></div><div id="search-mask"></div><script src="/js/search/local-search.js"></script></div></div><!-- hexo injector body_end start --><script data-pjax>
|
||||||
|
function butterfly_swiper_injector_config(){
|
||||||
|
var parent_div_git = document.getElementById('recent-posts');
|
||||||
|
var item_html = '<div class="recent-post-item" style="height: auto;width: 100%"><div class="blog-slider swiper-container-fade swiper-container-horizontal" id="swiper_container"><div class="blog-slider__wrp swiper-wrapper" style="transition-duration: 0ms;"><div class="blog-slider__item swiper-slide" style="width: 750px; opacity: 1; transform: translate3d(0px, 0px, 0px); transition-duration: 0ms;"><a class="blog-slider__img" href="posts/19306.html" alt=""><img width="48" height="48" src="/img/1.jpg" alt="" onerror="this.src=https://unpkg.zhimg.com/akilar-candyassets/image/loading.gif; this.onerror = null;"/></a><div class="blog-slider__content"><span class="blog-slider__code">2023-04-21</span><a class="blog-slider__title" href="posts/19306.html" alt="">Docker容器化技术</a><div class="blog-slider__text">Docker</div><a class="blog-slider__button" href="posts/19306.html" alt="">详情 </a></div></div><div class="blog-slider__item swiper-slide" style="width: 750px; opacity: 1; transform: translate3d(0px, 0px, 0px); transition-duration: 0ms;"><a class="blog-slider__img" href="posts/47003.html" alt=""><img width="48" height="48" src="/img/1.jpg" alt="" onerror="this.src=https://unpkg.zhimg.com/akilar-candyassets/image/loading.gif; this.onerror = null;"/></a><div class="blog-slider__content"><span class="blog-slider__code">2023-03-10</span><a class="blog-slider__title" href="posts/47003.html" alt="">常用正则表达式大全</a><div class="blog-slider__text">正则表达式</div><a class="blog-slider__button" href="posts/47003.html" alt="">详情 </a></div></div><div class="blog-slider__item swiper-slide" style="width: 750px; opacity: 1; transform: translate3d(0px, 0px, 0px); transition-duration: 0ms;"><a class="blog-slider__img" href="posts/20683.html" alt=""><img width="48" height="48" src="/img/3.jpg" alt="" onerror="this.src=https://unpkg.zhimg.com/akilar-candyassets/image/loading.gif; this.onerror = null;"/></a><div class="blog-slider__content"><span class="blog-slider__code">2023-06-05</span><a class="blog-slider__title" href="posts/20683.html" alt="">Linux中开发环境的搭建</a><div class="blog-slider__text">环境搭建</div><a class="blog-slider__button" href="posts/20683.html" alt="">详情 </a></div></div><div class="blog-slider__item swiper-slide" style="width: 750px; opacity: 1; transform: translate3d(0px, 0px, 0px); transition-duration: 0ms;"><a class="blog-slider__img" href="posts/63333.html" alt=""><img width="48" height="48" src="/img/1.jpg" alt="" onerror="this.src=https://unpkg.zhimg.com/akilar-candyassets/image/loading.gif; this.onerror = null;"/></a><div class="blog-slider__content"><span class="blog-slider__code">2023-06-03</span><a class="blog-slider__title" href="posts/63333.html" alt="">开发环境的搭建</a><div class="blog-slider__text">环境搭建</div><a class="blog-slider__button" href="posts/63333.html" alt="">详情 </a></div></div></div><div class="blog-slider__pagination swiper-pagination-clickable swiper-pagination-bullets"></div></div></div>';
|
||||||
|
parent_div_git.insertAdjacentHTML("afterbegin",item_html)
|
||||||
|
}
|
||||||
|
var elist = 'undefined'.split(',');
|
||||||
|
var cpage = location.pathname;
|
||||||
|
var epage = '/';
|
||||||
|
var flag = 0;
|
||||||
|
|
||||||
|
for (var i=0;i<elist.length;i++){
|
||||||
|
if (cpage.includes(elist[i])){
|
||||||
|
flag++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((epage ==='all')&&(flag == 0)){
|
||||||
|
butterfly_swiper_injector_config();
|
||||||
|
}
|
||||||
|
else if (epage === cpage){
|
||||||
|
butterfly_swiper_injector_config();
|
||||||
|
}
|
||||||
|
</script><script defer src="https://npm.elemecdn.com/hexo-butterfly-swiper/lib/swiper.min.js"></script><script defer data-pjax src="https://npm.elemecdn.com/hexo-butterfly-swiper/lib/swiper_init.js"></script><!-- hexo injector body_end end --></body></html>
|
315
page/2/index.html
Normal file
403
page/3/index.html
Normal file
276
page/4/index.html
Normal file
271
page/5/index.html
Normal file
307
page/6/index.html
Normal file
235
page/7/index.html
Normal file
BIN
pdf/2023最新Java面试题全集.pdf
Normal file
BIN
pdf/JAVA开发_李传播_5年.pdf
Normal file
BIN
pdf/Java8实战.pdf
Normal file
BIN
pdf/Java开发_AAA_N年.pdf
Normal file
BIN
pdf/Linux命令行与shell脚本编程大全.pdf
Normal file
BIN
pdf/MyBatis.pdf
Normal file
BIN
pdf/MySQL-基础篇.pdf
Normal file
BIN
pdf/MySQL-运维篇.pdf
Normal file
BIN
pdf/MySQL-进阶篇.pdf
Normal file
BIN
pdf/MySQL5.7.19安装.pdf
Normal file
BIN
pdf/MySQL面试题-参考回答.pdf
Normal file
BIN
pdf/Redis面试题-参考回答.pdf
Normal file
BIN
pdf/SSM整合.pdf
Normal file
BIN
pdf/Thymeleaf中文参考文档.pdf
Normal file
BIN
pdf/【参考】1年_本科.pdf
Normal file
BIN
pdf/【必看】面试参考话术.pdf
Normal file
BIN
pdf/个人简历(大四实习).pdf
Normal file
BIN
pdf/尚硅谷图解Java数据结构和算法.pdf
Normal file
BIN
pdf/微服务面试题-参考回答.pdf
Normal file
BIN
pdf/时尚线条简历模板.pdf
Normal file
BIN
pdf/框架篇面试题-参考回答.pdf
Normal file
BIN
pdf/消息中间件面试题-参考回答.pdf
Normal file
BIN
pdf/灰色大气简约简历模板.pdf
Normal file
BIN
pdf/灰蓝色色时尚简历模板.pdf
Normal file
BIN
pdf/科技版简历模板.pdf
Normal file
BIN
pdf/简历.pdf
Normal file
BIN
pdf/简历模板_CN.pdf
Normal file
BIN
pdf/简约大气橙色简历模板.pdf
Normal file
BIN
pdf/精通Linux.第2版.pdf
Normal file
BIN
pdf/经典风格简历模板.pdf
Normal file
BIN
pdf/韩顺平_循序渐进学Java零基础【完整笔记】.pdf
Normal file
BIN
pdf/韩顺平教育安装CentOS7.6.pdf
Normal file
BIN
pdf/韩顺平教育安装CentOS8.1.pdf
Normal file
BIN
pictures/1573636765632.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
pictures/1573649804623.png
Normal file
After Width: | Height: | Size: 12 KiB |