hexo-theme-shokax 0.3.13 → 0.4.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_config.yml +16 -9
- package/layout/_mixin/comment.pug +4 -4
- package/layout/_partials/footer.pug +1 -1
- package/layout/_partials/head/head.pug +7 -1
- package/layout/_partials/header.pug +1 -1
- package/layout/_partials/layout.pug +0 -10
- package/layout/_partials/post/footer.pug +1 -2
- package/package.json +10 -5
- package/scripts/generaters/archive.js +1 -1
- package/scripts/generaters/script.js +64 -42
- package/scripts/helpers/asset.js +1 -1
- package/scripts/helpers/list_categories.js +0 -4
- package/scripts/plugin/lib/injects.js +1 -1
- package/source/js/_app/components/comments.ts +33 -0
- package/source/js/_app/components/sidebar.ts +239 -0
- package/source/js/_app/globals/globalVars.ts +99 -0
- package/source/js/_app/globals/handles.ts +123 -0
- package/source/js/_app/globals/themeColor.ts +64 -0
- package/source/js/_app/globals/thirdparty.ts +63 -0
- package/source/js/_app/globals/tools.ts +75 -0
- package/source/js/_app/library/anime.ts +110 -0
- package/source/js/_app/library/declare.d.ts +126 -0
- package/source/js/_app/library/dom.ts +26 -0
- package/source/js/_app/library/loadFile.ts +43 -0
- package/source/js/_app/library/proto.ts +141 -0
- package/source/js/_app/library/scriptPjax.ts +72 -0
- package/source/js/_app/library/storage.ts +12 -0
- package/source/js/_app/library/vue.ts +50 -0
- package/source/js/_app/page/common.ts +42 -0
- package/source/js/_app/page/fancybox.ts +71 -0
- package/source/js/_app/page/post.ts +266 -0
- package/source/js/_app/page/search.ts +135 -0
- package/source/js/_app/page/tab.ts +60 -0
- package/source/js/_app/pjax/domInit.ts +96 -0
- package/source/js/_app/pjax/refresh.ts +78 -0
- package/source/js/_app/pjax/siteInit.ts +72 -0
- package/source/js/_app/player.ts +799 -0
- package/source/assets/beian.webp +0 -0
- package/source/js/_app/components/sidebar.js +0 -209
- package/source/js/_app/fireworks.js +0 -10
- package/source/js/_app/globals/globalVars.js +0 -80
- package/source/js/_app/globals/handles.js +0 -98
- package/source/js/_app/globals/themeColor.js +0 -62
- package/source/js/_app/globals/thirdparty.js +0 -62
- package/source/js/_app/globals/tools.js +0 -66
- package/source/js/_app/library/anime.js +0 -106
- package/source/js/_app/library/dom.js +0 -34
- package/source/js/_app/library/loadFile.js +0 -36
- package/source/js/_app/library/proto.js +0 -163
- package/source/js/_app/library/scriptPjax.js +0 -70
- package/source/js/_app/library/storage.js +0 -12
- package/source/js/_app/library/vue.js +0 -53
- package/source/js/_app/page/comment.js +0 -23
- package/source/js/_app/page/common.js +0 -41
- package/source/js/_app/page/fancybox.js +0 -65
- package/source/js/_app/page/post.js +0 -244
- package/source/js/_app/page/search.js +0 -118
- package/source/js/_app/page/tab.js +0 -53
- package/source/js/_app/pjax/domInit.js +0 -76
- package/source/js/_app/pjax/refresh.js +0 -52
- package/source/js/_app/pjax/siteInit.js +0 -51
- package/source/js/_app/player.js +0 -771
@@ -0,0 +1,110 @@
|
|
1
|
+
import anime from 'theme-shokax-anime'
|
2
|
+
import { siteNavHeight } from '../globals/globalVars'
|
3
|
+
import type { AnimeOptions } from 'theme-shokax-anime/dist/types'
|
4
|
+
import {getTop, setDisplay} from './proto'
|
5
|
+
|
6
|
+
/**
|
7
|
+
* 参数 动画效果
|
8
|
+
* 0 元素逐渐消失
|
9
|
+
* 1 元素逐渐出现
|
10
|
+
* bounceUpIn 元素从下方弹跳出现
|
11
|
+
* shrinkIn 元素从放大到正常大小出现
|
12
|
+
* slideRightIn 元素从右侧滑入
|
13
|
+
* slideRightOut 元素向右侧滑出
|
14
|
+
* TODO 函数功能过于复杂,需要拆分
|
15
|
+
*/
|
16
|
+
export const transition = (target: HTMLElement, type: number|string|Function, complete?: Function, begin?: Function): void => {
|
17
|
+
let animation:Partial<AnimeOptions>
|
18
|
+
let display = 'none'
|
19
|
+
switch (type) {
|
20
|
+
case 0:
|
21
|
+
animation = { opacity: [1, 0] }
|
22
|
+
break
|
23
|
+
case 1:
|
24
|
+
animation = { opacity: [0, 1] }
|
25
|
+
display = 'block'
|
26
|
+
break
|
27
|
+
case 'bounceUpIn':
|
28
|
+
animation = {
|
29
|
+
begin (anim) {
|
30
|
+
setDisplay(target, 'block')
|
31
|
+
},
|
32
|
+
translateY: [
|
33
|
+
{ value: -60, duration: 200 },
|
34
|
+
{ value: 10, duration: 200 },
|
35
|
+
{ value: -5, duration: 200 },
|
36
|
+
{ value: 0, duration: 200 }
|
37
|
+
],
|
38
|
+
opacity: [0, 1]
|
39
|
+
}
|
40
|
+
display = 'block'
|
41
|
+
break
|
42
|
+
case 'shrinkIn':
|
43
|
+
animation = {
|
44
|
+
begin (anim) {
|
45
|
+
setDisplay(target, 'block')
|
46
|
+
},
|
47
|
+
scale: [
|
48
|
+
{ value: 1.1, duration: 300 },
|
49
|
+
{ value: 1, duration: 200 }
|
50
|
+
],
|
51
|
+
opacity: 1
|
52
|
+
}
|
53
|
+
display = 'block'
|
54
|
+
break
|
55
|
+
case 'slideRightIn':
|
56
|
+
animation = {
|
57
|
+
begin (anim) {
|
58
|
+
setDisplay(target, 'block')
|
59
|
+
},
|
60
|
+
translateX: ['100%', '0%'],
|
61
|
+
opacity: [0, 1]
|
62
|
+
}
|
63
|
+
display = 'block'
|
64
|
+
break
|
65
|
+
case 'slideRightOut':
|
66
|
+
animation = {
|
67
|
+
translateX: ['0%', '100%'],
|
68
|
+
opacity: [1, 0]
|
69
|
+
}
|
70
|
+
break
|
71
|
+
default:
|
72
|
+
// @ts-ignore
|
73
|
+
animation = type
|
74
|
+
// @ts-ignore
|
75
|
+
display = type.display
|
76
|
+
break
|
77
|
+
}
|
78
|
+
anime(Object.assign({
|
79
|
+
targets: target,
|
80
|
+
duration: 200,
|
81
|
+
easing: 'linear',
|
82
|
+
begin () {
|
83
|
+
begin && begin()
|
84
|
+
},
|
85
|
+
complete () {
|
86
|
+
setDisplay(target, display)
|
87
|
+
complete && complete()
|
88
|
+
}
|
89
|
+
}, animation)).play()
|
90
|
+
}
|
91
|
+
|
92
|
+
export const pageScroll = (target: HTMLElement|number, offset?: number, complete?: Function) => {
|
93
|
+
// target: 滚动到的目标元素或坐标(number)
|
94
|
+
// offset: 可选的偏移量
|
95
|
+
// complete: 可选的回调函数,在动画完成时调用
|
96
|
+
anime({
|
97
|
+
// 动画目标
|
98
|
+
targets: typeof offset === 'number' && typeof target !== 'number' ? target.parentNode : document.scrollingElement,
|
99
|
+
// 动画持续时间
|
100
|
+
duration: 500,
|
101
|
+
// 动画缓动函数
|
102
|
+
easing: 'easeInOutQuad',
|
103
|
+
// 如果 offset 存在,则滚动到 offset,如果 target 是数字,则滚动到 target,如果 target 是 DOM 元素,则滚动到下述表达式
|
104
|
+
scrollTop: offset || (typeof target === 'number' ? target : (target ? getTop(target) + document.documentElement.scrollTop - siteNavHeight : 0)),
|
105
|
+
// 完成回调函数
|
106
|
+
complete () {
|
107
|
+
complete && complete()
|
108
|
+
}
|
109
|
+
}).play()
|
110
|
+
}
|
@@ -0,0 +1,126 @@
|
|
1
|
+
/*
|
2
|
+
对注释的说明: 部分注释为openai-chatgpt生成的注释,可能存在描述或语义的问题
|
3
|
+
*/
|
4
|
+
interface AudioItem {
|
5
|
+
title: string;
|
6
|
+
list: string[];
|
7
|
+
}
|
8
|
+
|
9
|
+
declare interface EventTarget {
|
10
|
+
createChild(tag: string, obj: Object, positon?: string): HTMLElement;
|
11
|
+
wrapObject(obj: Object): void;
|
12
|
+
changeOrGetHeight(h: number | string): void;
|
13
|
+
changeOrGetHeight(): number;
|
14
|
+
changeOrGetWidth(w: number | string): void;
|
15
|
+
changeOrGetWidth(): number;
|
16
|
+
getTop(): number;
|
17
|
+
left(): number;
|
18
|
+
insertAfter(element: HTMLElement): void;
|
19
|
+
display(d: string): EventTarget;
|
20
|
+
display():string
|
21
|
+
child(selector: string): HTMLElement;
|
22
|
+
find(selector: string): NodeListOf<HTMLElement>;
|
23
|
+
_class(type: string, className: string, display?: boolean): void;
|
24
|
+
addClass(className: string): EventTarget;
|
25
|
+
removeClass(className: string): EventTarget;
|
26
|
+
toggleClass(className: string, display?: boolean): EventTarget;
|
27
|
+
hasClass(className: string): boolean;
|
28
|
+
}
|
29
|
+
|
30
|
+
type walineMeta = 'nick'|'mail'|'link'
|
31
|
+
|
32
|
+
declare const LOCAL: {
|
33
|
+
path: string;
|
34
|
+
ignores: Array<(uri:string)=>boolean>;
|
35
|
+
audio: string[];
|
36
|
+
search: {
|
37
|
+
placeholder: string,
|
38
|
+
empty: string,
|
39
|
+
stats: string
|
40
|
+
};
|
41
|
+
quiz: {
|
42
|
+
choice: string,
|
43
|
+
multiple: string,
|
44
|
+
true_false: string,
|
45
|
+
essay: string,
|
46
|
+
gap_fill: string,
|
47
|
+
mistake: string
|
48
|
+
};
|
49
|
+
nocopy: boolean;
|
50
|
+
copyright: string;
|
51
|
+
outime: boolean
|
52
|
+
template: string
|
53
|
+
favicon: {
|
54
|
+
hide: string
|
55
|
+
show: string
|
56
|
+
}
|
57
|
+
}
|
58
|
+
interface configType {
|
59
|
+
hostname: string;
|
60
|
+
fireworks: any;
|
61
|
+
audio: AudioItem[];
|
62
|
+
version: number
|
63
|
+
root: string
|
64
|
+
statics: string
|
65
|
+
outime: {
|
66
|
+
enable: boolean
|
67
|
+
days: number
|
68
|
+
}
|
69
|
+
favicon: {
|
70
|
+
normal: string,
|
71
|
+
hidden: string
|
72
|
+
}
|
73
|
+
darkmode: boolean
|
74
|
+
auto_dark: {
|
75
|
+
enable: boolean
|
76
|
+
start: number
|
77
|
+
end: number
|
78
|
+
}
|
79
|
+
auto_scroll: boolean
|
80
|
+
loader: {
|
81
|
+
start: boolean
|
82
|
+
switch: boolean
|
83
|
+
}
|
84
|
+
js: {
|
85
|
+
chart: string
|
86
|
+
copy_tex: string
|
87
|
+
fancybox: string
|
88
|
+
echarts: string
|
89
|
+
}
|
90
|
+
css: {
|
91
|
+
valine: string
|
92
|
+
katex: string
|
93
|
+
mermaid: string
|
94
|
+
fancybox: string
|
95
|
+
}
|
96
|
+
search: any,
|
97
|
+
waline: {
|
98
|
+
serverURL: string
|
99
|
+
lang: string
|
100
|
+
locale: object
|
101
|
+
emoji: boolean
|
102
|
+
meta: walineMeta[]
|
103
|
+
requiredMeta: walineMeta[]
|
104
|
+
wordLimit: number
|
105
|
+
pageSize: number
|
106
|
+
pageview: boolean
|
107
|
+
}
|
108
|
+
walinePageView: boolean
|
109
|
+
quicklink: {
|
110
|
+
ignores: any
|
111
|
+
timeout: number
|
112
|
+
priority: boolean
|
113
|
+
}
|
114
|
+
playerAPI: string
|
115
|
+
}
|
116
|
+
// esbuild 静态常量
|
117
|
+
declare const __shokax_player__:boolean
|
118
|
+
declare const __shokax_fireworks__:boolean
|
119
|
+
declare const __shokax_search__:boolean
|
120
|
+
declare const __shokax_VL__:boolean
|
121
|
+
declare const __shokax_outime__:boolean
|
122
|
+
declare const __shokax_tabs__: boolean
|
123
|
+
declare const __shokax_quiz__: boolean
|
124
|
+
declare const __shokax_fancybox__: boolean
|
125
|
+
declare const __shokax_waline__:boolean
|
126
|
+
declare const shokax_CONFIG:configType
|
@@ -0,0 +1,26 @@
|
|
1
|
+
const getDocHeight = () => $dom('main > .inner').offsetHeight
|
2
|
+
/**
|
3
|
+
* 获取一个dom选择器对应的元素
|
4
|
+
*/
|
5
|
+
const $dom = (selector: string, element: Document = document): HTMLElement => {
|
6
|
+
// 在测试环境中这能优化0.01-0.02ms左右
|
7
|
+
if (selector[0] === '#') {
|
8
|
+
return <HTMLElement> element.getElementById(selector.substring(1))
|
9
|
+
}
|
10
|
+
return <HTMLElement> element.querySelector(selector)
|
11
|
+
}
|
12
|
+
|
13
|
+
/**
|
14
|
+
* 获取具有此选择器的所有dom节点
|
15
|
+
*/
|
16
|
+
$dom.all = (selector: string, element: Document = document): NodeListOf<HTMLElement> => {
|
17
|
+
return element.querySelectorAll(selector)
|
18
|
+
}
|
19
|
+
/**
|
20
|
+
* 获取具有此选择器的所有dom节点,并依次执行callback函数
|
21
|
+
*/
|
22
|
+
$dom.each = (selector: string, callback: (value: HTMLElement, key: number, parent: NodeListOf<Element>) => void, element?: Document): void => {
|
23
|
+
$dom.all(selector, element).forEach(callback)
|
24
|
+
}
|
25
|
+
|
26
|
+
export { $dom, getDocHeight }
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import { getScript } from './scriptPjax'
|
2
|
+
import { CONFIG } from '../globals/globalVars'
|
3
|
+
import { createChild } from './proto'
|
4
|
+
|
5
|
+
/**
|
6
|
+
* 用途是根据不同的资源名称和类型生成相应的资源 URL。
|
7
|
+
*/
|
8
|
+
const assetUrl = (asset: string, type: string): string => {
|
9
|
+
const str = CONFIG[asset][type]
|
10
|
+
if (str.includes('http')) {
|
11
|
+
return str
|
12
|
+
}
|
13
|
+
if (str.includes('gh') || str.includes('combine')) {
|
14
|
+
return `https://cdn.jsdelivr.net/${str}`
|
15
|
+
}
|
16
|
+
if (str.includes('npm')) {
|
17
|
+
return `https://cdn.jsdelivr.net/${str}`
|
18
|
+
}
|
19
|
+
return `/${str}`
|
20
|
+
}
|
21
|
+
|
22
|
+
export const vendorJs = (type: string, callback?: Function, condition?: string) => {
|
23
|
+
if (LOCAL[type]) {
|
24
|
+
getScript(assetUrl('js', type), callback || function () {
|
25
|
+
window[type] = true
|
26
|
+
}, condition || window[type])
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
export const vendorCss = (type: string, condition?: string): void => {
|
31
|
+
if (window['css' + type]) {
|
32
|
+
return
|
33
|
+
}
|
34
|
+
|
35
|
+
if (LOCAL[type]) {
|
36
|
+
createChild(document.head, 'link', {
|
37
|
+
rel: 'stylesheet',
|
38
|
+
href: assetUrl('css', type)
|
39
|
+
})
|
40
|
+
|
41
|
+
window['css' + type] = true
|
42
|
+
}
|
43
|
+
}
|
@@ -0,0 +1,141 @@
|
|
1
|
+
import { $dom } from './dom'
|
2
|
+
|
3
|
+
export const insertAfter = function (el: HTMLElement, element: HTMLElement): void {
|
4
|
+
const parent = el.parentNode
|
5
|
+
if (parent.lastChild === el) {
|
6
|
+
parent.appendChild(element)
|
7
|
+
} else {
|
8
|
+
parent.insertBefore(element, el.nextSibling)
|
9
|
+
}
|
10
|
+
}
|
11
|
+
|
12
|
+
/**
|
13
|
+
* 创建一个子节点并放置
|
14
|
+
*/
|
15
|
+
export const createChild = function (parent: HTMLElement, tag: string, obj: object, positon?: string): HTMLElement {
|
16
|
+
const child = document.createElement(tag)
|
17
|
+
Object.assign(child, obj)
|
18
|
+
switch (positon) {
|
19
|
+
case 'after':
|
20
|
+
insertAfter(parent, child)
|
21
|
+
break
|
22
|
+
case 'replace':
|
23
|
+
parent.innerHTML = ''
|
24
|
+
parent.appendChild(child)
|
25
|
+
break
|
26
|
+
default:
|
27
|
+
parent.appendChild(child)
|
28
|
+
}
|
29
|
+
return child
|
30
|
+
}
|
31
|
+
|
32
|
+
/**
|
33
|
+
* 此方法使用`<div>`包装一个 DOM 元素
|
34
|
+
* @param parent
|
35
|
+
* @param obj 需要被包装的对象
|
36
|
+
*/
|
37
|
+
export const wrapObject = function (parent: HTMLElement, obj: any): void {
|
38
|
+
const box = document.createElement('div')
|
39
|
+
Object.assign(box, obj)
|
40
|
+
parent.insertBefore(box, obj)
|
41
|
+
parent.removeChild(obj)
|
42
|
+
box.appendChild(obj)
|
43
|
+
}
|
44
|
+
|
45
|
+
export const getHeight = function (el: HTMLElement): number {
|
46
|
+
return el.getBoundingClientRect().height
|
47
|
+
}
|
48
|
+
|
49
|
+
export const setWidth = function (el: HTMLElement, w: number|string): void {
|
50
|
+
el.style.width = typeof w === 'number' ? w + 'rem' : w
|
51
|
+
}
|
52
|
+
|
53
|
+
export const getWidth = function (el: HTMLElement): number {
|
54
|
+
return el.getBoundingClientRect().width
|
55
|
+
}
|
56
|
+
|
57
|
+
export const getTop = function (el: HTMLElement): number {
|
58
|
+
return el.getBoundingClientRect().top
|
59
|
+
}
|
60
|
+
|
61
|
+
export const getLeft = function (el: HTMLElement): number {
|
62
|
+
return el.getBoundingClientRect().left
|
63
|
+
}
|
64
|
+
|
65
|
+
export const getDisplay = function (el: HTMLElement): string {
|
66
|
+
return el.style.display
|
67
|
+
}
|
68
|
+
|
69
|
+
export const setDisplay = function (el: HTMLElement, d: string): HTMLElement {
|
70
|
+
el.style.display = d
|
71
|
+
return el
|
72
|
+
}
|
73
|
+
|
74
|
+
// TODO 未完成迁移
|
75
|
+
export const child = function (el: HTMLElement, selector: string): HTMLElement {
|
76
|
+
return $dom(selector, (el as unknown as Document))
|
77
|
+
}
|
78
|
+
export default function initProto () {
|
79
|
+
Object.assign(HTMLElement.prototype, {
|
80
|
+
/**
|
81
|
+
* 找到此节点第一个符合selector选择器的子节点
|
82
|
+
*/
|
83
|
+
child (selector: string): HTMLElement {
|
84
|
+
return $dom(selector, this)
|
85
|
+
},
|
86
|
+
/**
|
87
|
+
* 找到此节点所有符合selector选择器的子节点
|
88
|
+
*/
|
89
|
+
find (selector: string): NodeListOf<HTMLElement> {
|
90
|
+
return $dom.all(selector, this)
|
91
|
+
},
|
92
|
+
/**
|
93
|
+
* 这个方法接受三个参数:
|
94
|
+
* type 表示操作类型('add'、'remove'、'toggle'),
|
95
|
+
* className 是一个或多个要操作的类名,
|
96
|
+
* display 是一个可选的布尔值,用于在执行切换操作时指定类名是否应显示或隐藏。
|
97
|
+
* 该方法会根据操作类型执行相应的类名操作。
|
98
|
+
*/
|
99
|
+
_class (type: string, className: string, display?: boolean): void {
|
100
|
+
const classNames = className.indexOf(' ') ? className.split(' ') : [className]
|
101
|
+
classNames.forEach((name) => {
|
102
|
+
if (type === 'toggle') {
|
103
|
+
this.classList.toggle(name, display)
|
104
|
+
} else {
|
105
|
+
this.classList[type](name)
|
106
|
+
}
|
107
|
+
})
|
108
|
+
},
|
109
|
+
/**
|
110
|
+
* 这个方法是对 _class 方法的封装,调用时会将操作类型设为 'add',然后执行添加类名的操作。
|
111
|
+
* 最后,它返回当前的 EventTarget,通常是 DOM 元素本身,以支持链式调用。
|
112
|
+
*/
|
113
|
+
addClass (className: string): EventTarget {
|
114
|
+
this._class('add', className)
|
115
|
+
return this
|
116
|
+
},
|
117
|
+
/**
|
118
|
+
* 这个方法是对 _class 方法的封装,调用时会将操作类型设为 'remove',然后执行移除类名的操作。
|
119
|
+
* 最后,它返回当前的 EventTarget,通常是 DOM 元素本身,以支持链式调用。
|
120
|
+
*/
|
121
|
+
removeClass (className: string): EventTarget {
|
122
|
+
this._class('remove', className)
|
123
|
+
return this
|
124
|
+
},
|
125
|
+
/**
|
126
|
+
* 这个方法是对 _class 方法的封装,调用时会将操作类型设为 'toggle',然后执行切换类名的操作。
|
127
|
+
* 如果提供了 display 参数,它将根据布尔值决定是否显示或隐藏类名。
|
128
|
+
* 最后,它返回当前的 EventTarget,通常是 DOM 元素本身,以支持链式调用。
|
129
|
+
*/
|
130
|
+
toggleClass (className: string, display?: boolean): EventTarget {
|
131
|
+
this._class('toggle', className, display)
|
132
|
+
return this
|
133
|
+
},
|
134
|
+
/**
|
135
|
+
* 这个方法返回一个布尔值,表示元素是否包含指定的类名。
|
136
|
+
*/
|
137
|
+
hasClass (className: string): boolean {
|
138
|
+
return this.classList.contains(className)
|
139
|
+
}
|
140
|
+
})
|
141
|
+
}
|
@@ -0,0 +1,72 @@
|
|
1
|
+
// rocket-loader & Auto minify(cloudflare) 补丁
|
2
|
+
// cloudflare 的上述功能会导致DOMContentLoaded事件无法触发,此补丁会将DOMContentLoaded重定向为load事件
|
3
|
+
export function cloudflareInit () {
|
4
|
+
let inCloudFlare = true
|
5
|
+
window.addEventListener('DOMContentLoaded', function () {
|
6
|
+
inCloudFlare = false
|
7
|
+
})
|
8
|
+
|
9
|
+
if (document.readyState === 'loading') {
|
10
|
+
window.addEventListener('load', function () {
|
11
|
+
if (inCloudFlare) {
|
12
|
+
window.dispatchEvent(new Event('DOMContentLoaded'))
|
13
|
+
console.log('%c ☁️cloudflare patch ' + '%c running(rocket & minify)', 'color: white; background: #ff8c00; padding: 5px 3px;', 'padding: 4px;border:1px solid #ff8c00')
|
14
|
+
}
|
15
|
+
})
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
export const getScript = (url: string, callback?: Function, condition?: string): void => {
|
20
|
+
// url: 脚本文件的URL地址
|
21
|
+
// callback: 当脚本加载完成时要执行的回调函数
|
22
|
+
// condition: 可选的条件参数,如果存在,则执行callback
|
23
|
+
if (condition) {
|
24
|
+
// 如果条件存在,则执行回调函数
|
25
|
+
callback()
|
26
|
+
} else {
|
27
|
+
let script = document.createElement('script')
|
28
|
+
|
29
|
+
// @ts-ignore
|
30
|
+
script.onload = function (_, isAbort: boolean) {
|
31
|
+
// _: 事件对象
|
32
|
+
// isAbort: 是否中止
|
33
|
+
// @ts-ignore
|
34
|
+
if (isAbort || !script.readyState) {
|
35
|
+
console.log('abort!')
|
36
|
+
script.onload = null
|
37
|
+
script = undefined
|
38
|
+
if (!isAbort && callback) setTimeout(callback, 0)
|
39
|
+
}
|
40
|
+
}
|
41
|
+
script.src = url
|
42
|
+
document.head.appendChild(script)
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
export const pjaxScript = (element: HTMLScriptElement) => {
|
47
|
+
const { text, parentNode, id, className, type, src, dataset } = element
|
48
|
+
const code = text || element.textContent || element.innerHTML || ''
|
49
|
+
parentNode.removeChild(element)
|
50
|
+
const script = document.createElement('script')
|
51
|
+
if (id) {
|
52
|
+
script.id = id
|
53
|
+
}
|
54
|
+
if (className) {
|
55
|
+
script.className = className
|
56
|
+
}
|
57
|
+
if (type) {
|
58
|
+
script.type = type
|
59
|
+
}
|
60
|
+
if (src) {
|
61
|
+
// Force synchronous loading of peripheral JS.
|
62
|
+
script.src = src
|
63
|
+
script.async = false
|
64
|
+
}
|
65
|
+
if (dataset.pjax !== undefined) {
|
66
|
+
script.dataset.pjax = ''
|
67
|
+
}
|
68
|
+
if (code !== '') {
|
69
|
+
script.appendChild(document.createTextNode(code))
|
70
|
+
}
|
71
|
+
parentNode.appendChild(script)
|
72
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
// Html5LocalStorage的一个API
|
2
|
+
export const $storage = {
|
3
|
+
set (key: string, value: string): void {
|
4
|
+
localStorage.setItem(key, value)
|
5
|
+
},
|
6
|
+
get (key: string): string {
|
7
|
+
return localStorage.getItem(key)
|
8
|
+
},
|
9
|
+
del (key: string): void {
|
10
|
+
localStorage.removeItem(key)
|
11
|
+
}
|
12
|
+
}
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import { $storage } from './storage'
|
2
|
+
import { transition } from './anime'
|
3
|
+
import { $dom } from './dom'
|
4
|
+
import { BODY } from '../globals/globalVars'
|
5
|
+
import { changeTheme } from '../globals/themeColor'
|
6
|
+
import { child, createChild, setDisplay } from './proto'
|
7
|
+
export function initVue () {
|
8
|
+
function changeThemeByBtn () {
|
9
|
+
let c: { (): void; (): void; (): void }
|
10
|
+
const btn = child($dom('.theme'), '.ic')
|
11
|
+
|
12
|
+
const neko = createChild(BODY, 'div', {
|
13
|
+
id: 'neko',
|
14
|
+
innerHTML: '<div class="planet"><div class="sun"></div><div class="moon"></div></div><div class="body"><div class="face"><section class="eyes left"><span class="pupil"></span></section><section class="eyes right"><span class="pupil"></span></section><span class="nose"></span></div></div>'
|
15
|
+
})
|
16
|
+
|
17
|
+
const hideNeko = () => {
|
18
|
+
transition(neko, {
|
19
|
+
// @ts-ignore
|
20
|
+
delay: 2500,
|
21
|
+
opacity: 0
|
22
|
+
}, () => {
|
23
|
+
BODY.removeChild(neko)
|
24
|
+
})
|
25
|
+
}
|
26
|
+
|
27
|
+
if (btn.hasClass('i-sun')) {
|
28
|
+
c = () => {
|
29
|
+
neko.addClass('dark')
|
30
|
+
changeTheme('dark')
|
31
|
+
$storage.set('theme', 'dark')
|
32
|
+
hideNeko()
|
33
|
+
}
|
34
|
+
} else {
|
35
|
+
neko.addClass('dark')
|
36
|
+
c = () => {
|
37
|
+
neko.removeClass('dark')
|
38
|
+
changeTheme()
|
39
|
+
$storage.set('theme', 'light')
|
40
|
+
hideNeko()
|
41
|
+
}
|
42
|
+
}
|
43
|
+
transition(neko, 1, () => {
|
44
|
+
setTimeout(c, 210)
|
45
|
+
}, () => {
|
46
|
+
setDisplay(neko, 'block')
|
47
|
+
})
|
48
|
+
}
|
49
|
+
child($dom('#rightNav'), '.theme .ic').addEventListener('click', changeThemeByBtn)
|
50
|
+
}
|
@@ -0,0 +1,42 @@
|
|
1
|
+
import { $dom } from '../library/dom'
|
2
|
+
|
3
|
+
export const cardActive = () => {
|
4
|
+
if (!$dom('.index.wrap')) { return }
|
5
|
+
const io = new IntersectionObserver((entries) => {
|
6
|
+
entries.forEach((article) => {
|
7
|
+
if (article.target.hasClass('show')) {
|
8
|
+
io.unobserve(article.target)
|
9
|
+
} else {
|
10
|
+
if (article.isIntersecting || article.intersectionRatio > 0) {
|
11
|
+
article.target.addClass('show')
|
12
|
+
io.unobserve(article.target)
|
13
|
+
}
|
14
|
+
}
|
15
|
+
})
|
16
|
+
}, {
|
17
|
+
root: null,
|
18
|
+
threshold: [0.3]
|
19
|
+
})
|
20
|
+
|
21
|
+
$dom.each('.index.wrap article.item, .index.wrap section.item', (article) => {
|
22
|
+
io.observe(article)
|
23
|
+
})
|
24
|
+
|
25
|
+
$dom('.index.wrap .item:first-child').addClass('show')
|
26
|
+
|
27
|
+
$dom.each('.cards .item', (element) => {
|
28
|
+
['mouseenter', 'touchstart'].forEach((item) => {
|
29
|
+
element.addEventListener(item, () => {
|
30
|
+
if ($dom('.cards .item.active')) {
|
31
|
+
$dom('.cards .item.active').removeClass('active')
|
32
|
+
}
|
33
|
+
element.addClass('active')
|
34
|
+
}, { passive: true })
|
35
|
+
});
|
36
|
+
['mouseleave'].forEach((item) => {
|
37
|
+
element.addEventListener(item, () => {
|
38
|
+
element.removeClass('active')
|
39
|
+
}, { passive: true })
|
40
|
+
})
|
41
|
+
})
|
42
|
+
}
|
@@ -0,0 +1,71 @@
|
|
1
|
+
import { $dom } from '../library/dom'
|
2
|
+
import { vendorCss, vendorJs } from '../library/loadFile'
|
3
|
+
import { insertAfter } from '../library/proto'
|
4
|
+
|
5
|
+
export const postFancybox = (p:string) => {
|
6
|
+
if ($dom(p + ' .md img')) {
|
7
|
+
vendorCss('fancybox')
|
8
|
+
vendorCss('justifiedGallery')
|
9
|
+
vendorJs('fancybox', () => {
|
10
|
+
const q = jQuery.noConflict()
|
11
|
+
|
12
|
+
$dom.each(p + ' p.gallery', (element) => {
|
13
|
+
const box = document.createElement('div')
|
14
|
+
box.className = 'gallery'
|
15
|
+
box.setAttribute('data-height', String(element.getAttribute('data-height') || 220))
|
16
|
+
|
17
|
+
box.innerHTML = element.innerHTML.replace(/<br>/g, '')
|
18
|
+
|
19
|
+
element.parentNode.insertBefore(box, element)
|
20
|
+
element.remove()
|
21
|
+
})
|
22
|
+
|
23
|
+
$dom.each(p + ' .md img:not(.emoji):not(.vemoji)', (element) => {
|
24
|
+
const $image = q(element)
|
25
|
+
const imageLink = $image.attr('data-src') || $image.attr('src') // 替换
|
26
|
+
const $imageWrapLink = $image.wrap('<a class="fancybox" href="' + imageLink + '" itemscope itemtype="https://schema.org/ImageObject" itemprop="url"></a>').parent('a')
|
27
|
+
let info; let captionClass = 'image-info'
|
28
|
+
if (!$image.is('a img')) {
|
29
|
+
$image.data('safe-src', imageLink)
|
30
|
+
if (!$image.is('.gallery img')) {
|
31
|
+
$imageWrapLink.attr('data-fancybox', 'default').attr('rel', 'default')
|
32
|
+
} else {
|
33
|
+
captionClass = 'jg-caption'
|
34
|
+
}
|
35
|
+
}
|
36
|
+
if ((info = element.getAttribute('title'))) {
|
37
|
+
$imageWrapLink.attr('data-caption', info)
|
38
|
+
const para = document.createElement('span')
|
39
|
+
const txt = document.createTextNode(info)
|
40
|
+
para.appendChild(txt)
|
41
|
+
para.addClass(captionClass)
|
42
|
+
insertAfter(element, para)
|
43
|
+
}
|
44
|
+
})
|
45
|
+
|
46
|
+
$dom.each(p + ' div.gallery', (el, i) => {
|
47
|
+
// @ts-ignore
|
48
|
+
q(el).justifiedGallery({
|
49
|
+
rowHeight: q(el).data('height') || 120,
|
50
|
+
rel: 'gallery-' + i
|
51
|
+
}).on('jg.complete', function () {
|
52
|
+
q(this).find('a').each((k, ele) => {
|
53
|
+
ele.setAttribute('data-fancybox', 'gallery-' + i)
|
54
|
+
})
|
55
|
+
})
|
56
|
+
})
|
57
|
+
|
58
|
+
q.fancybox.defaults.hash = false
|
59
|
+
q(p + ' .fancybox').fancybox({
|
60
|
+
loop: true,
|
61
|
+
// @ts-ignore
|
62
|
+
helpers: {
|
63
|
+
overlay: {
|
64
|
+
locked: false
|
65
|
+
}
|
66
|
+
}
|
67
|
+
})
|
68
|
+
// @ts-ignore
|
69
|
+
}, window.jQuery)
|
70
|
+
}
|
71
|
+
}
|