artalk 2.7.1 → 2.7.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/README.md +1 -1
- package/dist/Artalk.css +1 -1
- package/dist/Artalk.es.js +155 -147
- package/dist/Artalk.es.js.map +1 -1
- package/dist/Artalk.iife.js +1 -1
- package/dist/Artalk.iife.js.map +1 -1
- package/dist/Artalk.js +1 -1
- package/dist/Artalk.js.map +1 -1
- package/dist/ArtalkLite.css +1 -1
- package/dist/ArtalkLite.es.js +155 -147
- package/dist/ArtalkLite.es.js.map +1 -1
- package/dist/ArtalkLite.iife.js +1 -1
- package/dist/ArtalkLite.iife.js.map +1 -1
- package/dist/ArtalkLite.js +1 -1
- package/dist/ArtalkLite.js.map +1 -1
- package/dist/i18n/jp.js +1 -1
- package/dist/i18n/zh-TW.js +1 -1
- package/dist/src/api/_base.d.ts +7 -0
- package/dist/src/api/_options.d.ts +5 -0
- package/dist/src/components/checker/index.d.ts +2 -0
- package/dist/src/i18n/en.d.ts +1 -0
- package/dist/src/i18n/external.d.ts +1 -0
- package/dist/src/i18n/index.d.ts +3 -0
- package/dist/src/i18n/jp.d.ts +1 -0
- package/dist/src/i18n/zh-TW.d.ts +1 -0
- package/dist/src/lib/user.d.ts +8 -6
- package/dist/src/plugins/editor/_kit.d.ts +7 -1
- package/dist/src/service.d.ts +2 -8
- package/dist/types/event.d.ts +1 -1
- package/package.json +43 -17
- package/dist/src/components/dark-mode.d.ts +0 -2
- package/src/api/_base.ts +0 -18
- package/src/api/_options.ts +0 -25
- package/src/api/_request.ts +0 -104
- package/src/api/admin.ts +0 -18
- package/src/api/captcha.ts +0 -24
- package/src/api/comment.ts +0 -84
- package/src/api/index.ts +0 -29
- package/src/api/page.ts +0 -83
- package/src/api/site.ts +0 -51
- package/src/api/system.ts +0 -49
- package/src/api/upload.ts +0 -28
- package/src/api/user.ts +0 -132
- package/src/artalk.ts +0 -115
- package/src/comment/actions.ts +0 -83
- package/src/comment/comment.html +0 -18
- package/src/comment/comment.ts +0 -192
- package/src/comment/height-limit.ts +0 -100
- package/src/comment/index.ts +0 -3
- package/src/comment/render.ts +0 -150
- package/src/comment/renders/actions.ts +0 -114
- package/src/comment/renders/avatar.ts +0 -22
- package/src/comment/renders/content.ts +0 -39
- package/src/comment/renders/header.ts +0 -84
- package/src/comment/renders/index.ts +0 -19
- package/src/comment/renders/pending.ts +0 -12
- package/src/comment/renders/reply-at.ts +0 -16
- package/src/comment/renders/reply-to.ts +0 -24
- package/src/components/action-btn.ts +0 -170
- package/src/components/checker/admin.ts +0 -37
- package/src/components/checker/captcha-renders.ts +0 -69
- package/src/components/checker/captcha.ts +0 -26
- package/src/components/checker/index.ts +0 -192
- package/src/components/dark-mode.ts +0 -30
- package/src/components/dialog.ts +0 -62
- package/src/components/error-dialog.ts +0 -35
- package/src/components/pagination.ts +0 -153
- package/src/components/read-more-btn.ts +0 -92
- package/src/config.ts +0 -119
- package/src/context.ts +0 -146
- package/src/data.ts +0 -110
- package/src/defaults.ts +0 -61
- package/src/editor/editor.html +0 -18
- package/src/editor/editor.ts +0 -114
- package/src/editor/state.ts +0 -52
- package/src/editor/ui.ts +0 -36
- package/src/i18n/en.ts +0 -84
- package/src/i18n/external.ts +0 -10
- package/src/i18n/index.ts +0 -67
- package/src/i18n/jp.ts +0 -86
- package/src/i18n/zh-CN.ts +0 -88
- package/src/i18n/zh-TW.ts +0 -86
- package/src/layer/index.ts +0 -4
- package/src/layer/layer-manager.ts +0 -32
- package/src/layer/layer.ts +0 -39
- package/src/layer/scrollbar-helper.ts +0 -24
- package/src/layer/sidebar-layer.html +0 -8
- package/src/layer/sidebar-layer.ts +0 -155
- package/src/layer/wrap.ts +0 -77
- package/src/lib/component.ts +0 -12
- package/src/lib/detect.ts +0 -222
- package/src/lib/event-manager.ts +0 -47
- package/src/lib/highlight.ts +0 -5
- package/src/lib/marked-renderer.ts +0 -32
- package/src/lib/marked.ts +0 -72
- package/src/lib/sanitizer.ts +0 -57
- package/src/lib/ui.ts +0 -193
- package/src/lib/user.ts +0 -55
- package/src/lib/utils.ts +0 -236
- package/src/list/comment.ts +0 -20
- package/src/list/layout.ts +0 -120
- package/src/list/list.html +0 -20
- package/src/list/list.ts +0 -79
- package/src/list/nest.ts +0 -66
- package/src/list/page.ts +0 -97
- package/src/list/paginator/index.ts +0 -25
- package/src/list/paginator/read-more.ts +0 -67
- package/src/list/paginator/up-down.ts +0 -51
- package/src/main.ts +0 -14
- package/src/plugins/admin-only-elem.ts +0 -40
- package/src/plugins/conf-remoter.ts +0 -54
- package/src/plugins/dark-mode.ts +0 -12
- package/src/plugins/editor/_kit.ts +0 -57
- package/src/plugins/editor/_plug.ts +0 -60
- package/src/plugins/editor/_sample.ts +0 -8
- package/src/plugins/editor/closable.ts +0 -44
- package/src/plugins/editor/emoticons.scss +0 -72
- package/src/plugins/editor/emoticons.ts +0 -293
- package/src/plugins/editor/header-event.ts +0 -35
- package/src/plugins/editor/header-link.ts +0 -31
- package/src/plugins/editor/header-user.ts +0 -98
- package/src/plugins/editor/index.ts +0 -40
- package/src/plugins/editor/local-storage.ts +0 -36
- package/src/plugins/editor/mover.ts +0 -29
- package/src/plugins/editor/preview.scss +0 -7
- package/src/plugins/editor/preview.ts +0 -41
- package/src/plugins/editor/state-edit.ts +0 -116
- package/src/plugins/editor/state-reply.ts +0 -91
- package/src/plugins/editor/submit-add.ts +0 -33
- package/src/plugins/editor/submit-btn.ts +0 -25
- package/src/plugins/editor/submit.ts +0 -73
- package/src/plugins/editor/textarea.ts +0 -55
- package/src/plugins/editor/upload.ts +0 -145
- package/src/plugins/editor-kit.ts +0 -161
- package/src/plugins/index.ts +0 -16
- package/src/plugins/list/copyright.ts +0 -16
- package/src/plugins/list/count.ts +0 -31
- package/src/plugins/list/dropdown.ts +0 -95
- package/src/plugins/list/error-dialog.ts +0 -22
- package/src/plugins/list/fetch.ts +0 -64
- package/src/plugins/list/goto-first.ts +0 -23
- package/src/plugins/list/goto.ts +0 -72
- package/src/plugins/list/index.ts +0 -25
- package/src/plugins/list/loading.ts +0 -17
- package/src/plugins/list/no-comment.ts +0 -25
- package/src/plugins/list/reach-bottom.ts +0 -46
- package/src/plugins/list/sidebar-btn.ts +0 -39
- package/src/plugins/list/time-ticking.ts +0 -22
- package/src/plugins/list/unread-badge.ts +0 -26
- package/src/plugins/list/unread.ts +0 -38
- package/src/plugins/list/with-editor.ts +0 -59
- package/src/plugins/markdown.ts +0 -10
- package/src/plugins/stat.ts +0 -82
- package/src/plugins/version-check.ts +0 -36
- package/src/service.ts +0 -91
- package/src/style/_color.scss +0 -41
- package/src/style/_extend.scss +0 -20
- package/src/style/_variables.scss +0 -3
- package/src/style/comment.scss +0 -360
- package/src/style/editor.scss +0 -194
- package/src/style/list.scss +0 -212
- package/src/style/main.scss +0 -486
- package/src/style/pagination.scss +0 -58
- package/src/style/sidebar-layer.scss +0 -92
- package/types/config.ts +0 -153
- package/types/context.ts +0 -84
- package/types/data.ts +0 -271
- package/types/editor.ts +0 -95
- package/types/event.ts +0 -40
- package/types/global.d.ts +0 -1
- package/types/index.ts +0 -7
- package/types/plugin.ts +0 -3
- package/types/sidebar.ts +0 -3
package/src/plugins/index.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { ArtalkPlugin } from '~/types'
|
|
2
|
-
import { ConfRemoter } from './conf-remoter'
|
|
3
|
-
import { Markdown } from './markdown'
|
|
4
|
-
import { EditorKit } from './editor-kit'
|
|
5
|
-
import { ListPlugins } from './list'
|
|
6
|
-
import { PvCountWidget } from './stat'
|
|
7
|
-
import { VersionCheck } from './version-check'
|
|
8
|
-
import { AdminOnlyElem } from './admin-only-elem'
|
|
9
|
-
import { DarkMode } from './dark-mode'
|
|
10
|
-
|
|
11
|
-
export const DefaultPlugins: ArtalkPlugin[] = [
|
|
12
|
-
ConfRemoter,
|
|
13
|
-
Markdown, EditorKit, AdminOnlyElem,
|
|
14
|
-
...ListPlugins,
|
|
15
|
-
PvCountWidget, VersionCheck, DarkMode,
|
|
16
|
-
]
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { ArtalkPlugin } from '~/types'
|
|
2
|
-
import { version as ARTALK_VERSION } from '~/package.json'
|
|
3
|
-
|
|
4
|
-
export const Copyright: ArtalkPlugin = (ctx) => {
|
|
5
|
-
ctx.on('conf-loaded', () => {
|
|
6
|
-
const list = ctx.get('list')
|
|
7
|
-
|
|
8
|
-
const $copyright = list.$el.querySelector<HTMLElement>('.atk-copyright')
|
|
9
|
-
if (!$copyright) return
|
|
10
|
-
|
|
11
|
-
$copyright.innerHTML = (
|
|
12
|
-
`Powered By <a href="https://artalk.js.org" ` +
|
|
13
|
-
`target="_blank" title="Artalk v${ARTALK_VERSION}">` +
|
|
14
|
-
`Artalk</a>`)
|
|
15
|
-
})
|
|
16
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { ArtalkPlugin } from '~/types'
|
|
2
|
-
import * as Utils from '@/lib/utils'
|
|
3
|
-
import $t from '@/i18n'
|
|
4
|
-
|
|
5
|
-
export const Count: ArtalkPlugin = (ctx) => {
|
|
6
|
-
const refreshCountNumEl = () => {
|
|
7
|
-
const list = ctx.get('list')
|
|
8
|
-
|
|
9
|
-
const $count = list.$el.querySelector('.atk-comment-count .atk-text')
|
|
10
|
-
if (!$count) return
|
|
11
|
-
|
|
12
|
-
const text = Utils.htmlEncode($t('counter', { count: `${Number(ctx.getData().getListLastFetch()?.data?.total) || 0}` }))
|
|
13
|
-
$count.innerHTML = text.replace(/(\d+)/, '<span class="atk-comment-count-num">$1</span>')
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
ctx.on('list-loaded', () => {
|
|
17
|
-
refreshCountNumEl()
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
ctx.on('comment-inserted', () => {
|
|
21
|
-
// 评论数增加 1
|
|
22
|
-
const last = ctx.getData().getListLastFetch()
|
|
23
|
-
if (last?.data) last.data.total += 1
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
ctx.on('comment-deleted', () => {
|
|
27
|
-
// 评论数减 1
|
|
28
|
-
const last = ctx.getData().getListLastFetch()
|
|
29
|
-
if (last?.data) last.data.total -= 1
|
|
30
|
-
})
|
|
31
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import type { ArtalkPlugin } from '~/types'
|
|
2
|
-
import * as Utils from '@/lib/utils'
|
|
3
|
-
import $t from '@/i18n'
|
|
4
|
-
|
|
5
|
-
export const Dropdown: ArtalkPlugin = (ctx) => {
|
|
6
|
-
const reloadUseParamsEditor = (func: (p: any) => void) => {
|
|
7
|
-
ctx.conf.listFetchParamsModifier = func
|
|
8
|
-
ctx.reload()
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const initDropdown = ($dropdownOn: HTMLElement) => {
|
|
12
|
-
renderDropdown({
|
|
13
|
-
$dropdownWrap: $dropdownOn,
|
|
14
|
-
dropdownList: [
|
|
15
|
-
[$t('sortLatest'), () => { reloadUseParamsEditor(p => { p.sort_by = 'date_desc' }) }],
|
|
16
|
-
[$t('sortBest'), () => { reloadUseParamsEditor(p => { p.sort_by = 'vote' }) }],
|
|
17
|
-
[$t('sortOldest'), () => { reloadUseParamsEditor(p => { p.sort_by = 'date_asc' }) }],
|
|
18
|
-
[$t('sortAuthor'), () => { reloadUseParamsEditor(p => { p.view_only_admin = true }) }],
|
|
19
|
-
]
|
|
20
|
-
})
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
ctx.on('conf-loaded', () => {
|
|
24
|
-
const list = ctx.get('list')
|
|
25
|
-
|
|
26
|
-
const $count = list.$el.querySelector<HTMLElement>('.atk-comment-count')
|
|
27
|
-
if (!$count) return
|
|
28
|
-
|
|
29
|
-
// 评论列表排序 Dropdown 下拉选择层
|
|
30
|
-
if (ctx.conf.listSort) {
|
|
31
|
-
initDropdown($count)
|
|
32
|
-
} else {
|
|
33
|
-
removeDropdown({
|
|
34
|
-
$dropdownWrap: $count
|
|
35
|
-
})
|
|
36
|
-
}
|
|
37
|
-
})
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** 评论排序方式选择下拉菜单 */
|
|
41
|
-
function renderDropdown(conf: {
|
|
42
|
-
$dropdownWrap: HTMLElement,
|
|
43
|
-
dropdownList: [string, () => void][]
|
|
44
|
-
}) {
|
|
45
|
-
const { $dropdownWrap, dropdownList } = conf
|
|
46
|
-
if ($dropdownWrap.querySelector('.atk-dropdown')) return
|
|
47
|
-
|
|
48
|
-
// 修改 class
|
|
49
|
-
$dropdownWrap.classList.add('atk-dropdown-wrap')
|
|
50
|
-
|
|
51
|
-
// 插入图标
|
|
52
|
-
$dropdownWrap.append(Utils.createElement(`<span class="atk-arrow-down-icon"></span>`))
|
|
53
|
-
|
|
54
|
-
// 列表项点击事件
|
|
55
|
-
let curtActive = 0 // 当前选中
|
|
56
|
-
const onItemClick = (i: number, $item: HTMLElement, name: string, action: Function) => {
|
|
57
|
-
action()
|
|
58
|
-
|
|
59
|
-
// set active
|
|
60
|
-
curtActive = i
|
|
61
|
-
$dropdown.querySelectorAll('.active').forEach((e) => { e.classList.remove('active') })
|
|
62
|
-
$item.classList.add('active')
|
|
63
|
-
|
|
64
|
-
// 关闭层 (临时消失,取消 :hover)
|
|
65
|
-
$dropdown.style.display = 'none'
|
|
66
|
-
setTimeout(() => { $dropdown.style.display = '' }, 80)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// 生成列表元素
|
|
70
|
-
const $dropdown = Utils.createElement(`<ul class="atk-dropdown atk-fade-in"></ul>`)
|
|
71
|
-
dropdownList.forEach((item, i) => {
|
|
72
|
-
const name = item[0] as string
|
|
73
|
-
const action = item[1] as Function
|
|
74
|
-
|
|
75
|
-
const $item = Utils.createElement(`<li class="atk-dropdown-item"><span></span></li>`)
|
|
76
|
-
const $link = $item.querySelector<HTMLElement>('span')!
|
|
77
|
-
$link.innerText = name
|
|
78
|
-
$link.onclick = () => { onItemClick(i, $item, name, action) }
|
|
79
|
-
$dropdown.append($item)
|
|
80
|
-
|
|
81
|
-
if (i === curtActive) $item.classList.add('active') // 默认选中项
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
$dropdownWrap.append($dropdown)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** 删除评论排序方式选择下拉菜单 */
|
|
88
|
-
function removeDropdown(conf: {
|
|
89
|
-
$dropdownWrap: HTMLElement
|
|
90
|
-
}) {
|
|
91
|
-
const { $dropdownWrap } = conf
|
|
92
|
-
$dropdownWrap.classList.remove('atk-dropdown-wrap')
|
|
93
|
-
$dropdownWrap.querySelector('.atk-arrow-down-icon')?.remove()
|
|
94
|
-
$dropdownWrap.querySelector('.atk-dropdown')?.remove()
|
|
95
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { ArtalkPlugin, ContextApi } from '~/types'
|
|
2
|
-
import { showErrorDialog } from '@/components/error-dialog'
|
|
3
|
-
import * as Ui from '../../lib/ui'
|
|
4
|
-
|
|
5
|
-
export const ErrorDialog: ArtalkPlugin = (ctx) => {
|
|
6
|
-
ctx.on('list-fetch', () => {
|
|
7
|
-
const list = ctx.get('list')
|
|
8
|
-
|
|
9
|
-
// clear the original error when a new fetch is triggered
|
|
10
|
-
Ui.setError(list.$el, null)
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
ctx.on('list-error', (err) => {
|
|
14
|
-
showErrorDialog({
|
|
15
|
-
$err: ctx.get('list').$el,
|
|
16
|
-
errMsg: err.msg,
|
|
17
|
-
errData: err.data,
|
|
18
|
-
retryFn: () => ctx.fetch({ offset: 0 })
|
|
19
|
-
})
|
|
20
|
-
})
|
|
21
|
-
}
|
|
22
|
-
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import type { ListFetchParams, ArtalkPlugin } from '~/types'
|
|
2
|
-
|
|
3
|
-
export const Fetch: ArtalkPlugin = (ctx) => {
|
|
4
|
-
ctx.on('list-fetch', (_params) => {
|
|
5
|
-
if (ctx.getData().getLoading()) return
|
|
6
|
-
ctx.getData().setLoading(true)
|
|
7
|
-
|
|
8
|
-
const params: ListFetchParams = {
|
|
9
|
-
// default params
|
|
10
|
-
offset: 0,
|
|
11
|
-
limit: ctx.conf.pagination.pageSize,
|
|
12
|
-
flatMode: ctx.conf.flatMode as boolean, // always be boolean because had been handled in Artalk.init
|
|
13
|
-
paramsModifier: ctx.conf.listFetchParamsModifier,
|
|
14
|
-
..._params
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// must before other function call
|
|
18
|
-
ctx.getData().setListLastFetch({
|
|
19
|
-
params
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
ctx.getApi().comment
|
|
23
|
-
.get(params.offset, params.limit, params.flatMode, params.paramsModifier)
|
|
24
|
-
.then((data) => {
|
|
25
|
-
// Must before all other function call and event trigger,
|
|
26
|
-
// because it will depend on the lastData
|
|
27
|
-
// TODO this is global variable, easy to use, but not good, consider to refactor.
|
|
28
|
-
// refactor work is hard, because it is used in many places.
|
|
29
|
-
ctx.getData().setListLastFetch({ params, data })
|
|
30
|
-
|
|
31
|
-
// 装置评论
|
|
32
|
-
ctx.getData().loadComments(data.comments)
|
|
33
|
-
|
|
34
|
-
// 更新页面数据
|
|
35
|
-
ctx.getData().updatePage(data.page)
|
|
36
|
-
|
|
37
|
-
// 未读消息提示功能
|
|
38
|
-
ctx.getData().updateUnreads(data.unread || [])
|
|
39
|
-
|
|
40
|
-
// trigger events when success
|
|
41
|
-
params.onSuccess && params.onSuccess(data)
|
|
42
|
-
|
|
43
|
-
ctx.trigger('list-fetched', { params, data })
|
|
44
|
-
})
|
|
45
|
-
.catch((e) => {
|
|
46
|
-
// 显示错误对话框
|
|
47
|
-
const error = {
|
|
48
|
-
msg: e.msg || String(e),
|
|
49
|
-
data: e.data
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
params.onError && params.onError(error)
|
|
53
|
-
|
|
54
|
-
// trigger events when error
|
|
55
|
-
ctx.trigger('list-error', error)
|
|
56
|
-
ctx.trigger('list-fetched', { params, error })
|
|
57
|
-
|
|
58
|
-
throw e
|
|
59
|
-
})
|
|
60
|
-
.finally(() => {
|
|
61
|
-
ctx.getData().setLoading(false)
|
|
62
|
-
})
|
|
63
|
-
})
|
|
64
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { ArtalkPlugin } from '~/types'
|
|
2
|
-
import * as Utils from '@/lib/utils'
|
|
3
|
-
|
|
4
|
-
/** List scroll to the first comment */
|
|
5
|
-
export const GotoFirst: ArtalkPlugin = (ctx) => {
|
|
6
|
-
const handler = () => {
|
|
7
|
-
const list = ctx.get('list')
|
|
8
|
-
|
|
9
|
-
const $relative = ctx.conf.scrollRelativeTo && ctx.conf.scrollRelativeTo()
|
|
10
|
-
;($relative || window).scroll({
|
|
11
|
-
top: Utils.getOffset(list.$el, $relative).top,
|
|
12
|
-
left: 0,
|
|
13
|
-
})
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
ctx.on('inited', () => {
|
|
17
|
-
ctx.on('list-goto-first', handler)
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
ctx.on('destroy', () => {
|
|
21
|
-
ctx.off('list-goto-first', handler)
|
|
22
|
-
})
|
|
23
|
-
}
|
package/src/plugins/list/goto.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import type { ArtalkPlugin } from '~/types'
|
|
2
|
-
import * as Utils from '@/lib/utils'
|
|
3
|
-
import * as Ui from '@/lib/ui'
|
|
4
|
-
|
|
5
|
-
export const Goto: ArtalkPlugin = (ctx) => {
|
|
6
|
-
let delayGoto = true
|
|
7
|
-
|
|
8
|
-
const check = () => {
|
|
9
|
-
const commentID = extractCommentID()
|
|
10
|
-
if (!commentID) return
|
|
11
|
-
|
|
12
|
-
// trigger event
|
|
13
|
-
ctx.trigger('list-goto', commentID)
|
|
14
|
-
|
|
15
|
-
// reset delayGoto
|
|
16
|
-
delayGoto = true
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const hashChangeHandler = () => {
|
|
20
|
-
delayGoto = false
|
|
21
|
-
check()
|
|
22
|
-
}
|
|
23
|
-
ctx.on('inited', () => {
|
|
24
|
-
window.addEventListener('hashchange', hashChangeHandler)
|
|
25
|
-
ctx.on('list-loaded', check)
|
|
26
|
-
})
|
|
27
|
-
ctx.on('destroy', () => {
|
|
28
|
-
window.removeEventListener('hashchange', hashChangeHandler)
|
|
29
|
-
ctx.off('list-loaded', check)
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
let foundID = 0
|
|
33
|
-
ctx.on('list-goto', (commentID) => {
|
|
34
|
-
if (foundID === commentID) return
|
|
35
|
-
|
|
36
|
-
const comment = ctx.get('list').getCommentNodes().find(c => c.getID() === commentID)
|
|
37
|
-
if (!comment) return
|
|
38
|
-
|
|
39
|
-
foundID = commentID
|
|
40
|
-
|
|
41
|
-
// 若父评论存在 “子评论部分” 限高,取消限高
|
|
42
|
-
comment.getParents().forEach((p) => {
|
|
43
|
-
p.getRender().heightLimitRemoveForChildren()
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
const goTo = () => {
|
|
47
|
-
Ui.scrollIntoView(comment.getEl(), false)
|
|
48
|
-
|
|
49
|
-
comment.getEl().classList.remove('atk-flash-once')
|
|
50
|
-
window.setTimeout(() => {
|
|
51
|
-
comment.getEl().classList.add('atk-flash-once')
|
|
52
|
-
}, 150)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (!delayGoto) goTo()
|
|
56
|
-
else window.setTimeout(() => goTo(), 350)
|
|
57
|
-
})
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function extractCommentID(): number|null {
|
|
61
|
-
// try get from query
|
|
62
|
-
let commentId = Number(Utils.getQueryParam('atk_comment')) // same as backend GetReplyLink()
|
|
63
|
-
|
|
64
|
-
// fail over to get from hash
|
|
65
|
-
if (!commentId) {
|
|
66
|
-
const match = window.location.hash.match(/#atk-comment-([0-9]+)/)
|
|
67
|
-
if (!match || !match[1] || Number.isNaN(Number(match[1]))) return null
|
|
68
|
-
commentId = Number(match[1])
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return commentId || null
|
|
72
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { ArtalkPlugin } from '~/types'
|
|
2
|
-
import { WithEditor } from './with-editor'
|
|
3
|
-
import { Unread } from './unread'
|
|
4
|
-
import { Count } from './count'
|
|
5
|
-
import { SidebarBtn } from './sidebar-btn'
|
|
6
|
-
import { UnreadBadge } from './unread-badge'
|
|
7
|
-
import { Goto } from './goto'
|
|
8
|
-
import { Copyright } from './copyright'
|
|
9
|
-
import { NoComment } from './no-comment'
|
|
10
|
-
import { Dropdown } from './dropdown'
|
|
11
|
-
import { TimeTicking } from './time-ticking'
|
|
12
|
-
import { ErrorDialog } from './error-dialog'
|
|
13
|
-
import { Loading } from './loading'
|
|
14
|
-
import { Fetch } from './fetch'
|
|
15
|
-
import { ReachBottom } from './reach-bottom'
|
|
16
|
-
import { GotoFirst } from './goto-first'
|
|
17
|
-
|
|
18
|
-
const ListPlugins: ArtalkPlugin[] = [
|
|
19
|
-
Fetch, Loading, Unread,
|
|
20
|
-
WithEditor, Count, SidebarBtn, UnreadBadge,
|
|
21
|
-
Dropdown, Goto, NoComment, Copyright,
|
|
22
|
-
TimeTicking, ErrorDialog, ReachBottom, GotoFirst
|
|
23
|
-
]
|
|
24
|
-
|
|
25
|
-
export { ListPlugins }
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { ArtalkPlugin } from '~/types'
|
|
2
|
-
import * as Ui from '@/lib/ui'
|
|
3
|
-
|
|
4
|
-
export const Loading: ArtalkPlugin = (ctx) => {
|
|
5
|
-
ctx.on('list-fetch', (p) => {
|
|
6
|
-
const list = ctx.get('list')
|
|
7
|
-
|
|
8
|
-
if (p.offset === 0) // only show loading when fetch first page
|
|
9
|
-
Ui.setLoading(true, list.$el)
|
|
10
|
-
// else if not first page, show loading in paginator (code not there)
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
ctx.on('list-fetched', () => {
|
|
14
|
-
const list = ctx.get('list')
|
|
15
|
-
Ui.setLoading(false, list.$el)
|
|
16
|
-
})
|
|
17
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { ArtalkPlugin } from '~/types'
|
|
2
|
-
import * as Utils from '@/lib/utils'
|
|
3
|
-
import { sanitize } from '@/lib/sanitizer'
|
|
4
|
-
|
|
5
|
-
export const NoComment: ArtalkPlugin = (ctx) => {
|
|
6
|
-
ctx.on('list-loaded', (comments) => {
|
|
7
|
-
const list = ctx.get('list')!
|
|
8
|
-
|
|
9
|
-
// 无评论
|
|
10
|
-
const isNoComment = comments.length <= 0
|
|
11
|
-
let $noComment = list.getCommentsWrapEl().querySelector<HTMLElement>('.atk-list-no-comment')
|
|
12
|
-
|
|
13
|
-
if (isNoComment) {
|
|
14
|
-
if (!$noComment) {
|
|
15
|
-
$noComment = Utils.createElement('<div class="atk-list-no-comment"></div>')
|
|
16
|
-
|
|
17
|
-
// sanitize before set innerHTML
|
|
18
|
-
$noComment.innerHTML = sanitize(list.ctx.conf.noComment || list.ctx.$t('noComment'))
|
|
19
|
-
list.getCommentsWrapEl().appendChild($noComment)
|
|
20
|
-
}
|
|
21
|
-
} else {
|
|
22
|
-
$noComment?.remove()
|
|
23
|
-
}
|
|
24
|
-
})
|
|
25
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import type { ArtalkPlugin } from '~/types'
|
|
2
|
-
|
|
3
|
-
export const ReachBottom: ArtalkPlugin = (ctx) => {
|
|
4
|
-
const scrollEvtAt = document // TODO support ref ctx.conf
|
|
5
|
-
let observer: IntersectionObserver|null = null
|
|
6
|
-
|
|
7
|
-
const setupObserver = ($target: HTMLElement) => {
|
|
8
|
-
// eslint-disable-next-line compat/compat
|
|
9
|
-
observer = new IntersectionObserver(([entries]) => {
|
|
10
|
-
if (entries.isIntersecting) {
|
|
11
|
-
clearObserver()
|
|
12
|
-
ctx.trigger('list-reach-bottom')
|
|
13
|
-
}
|
|
14
|
-
}, {
|
|
15
|
-
root: scrollEvtAt
|
|
16
|
-
})
|
|
17
|
-
observer.observe($target)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const clearObserver = () => {
|
|
21
|
-
observer?.disconnect()
|
|
22
|
-
observer = null
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
ctx.on('list-loaded', () => {
|
|
26
|
-
clearObserver()
|
|
27
|
-
|
|
28
|
-
const list = ctx.get('list')
|
|
29
|
-
|
|
30
|
-
// use IntersectionObserver to detect reach bottom
|
|
31
|
-
const $target = list.getCommentsWrapEl().querySelector<HTMLElement>('.atk-comment-wrap:nth-last-child(3)')
|
|
32
|
-
if (!$target) return
|
|
33
|
-
|
|
34
|
-
// check IntersectionObserver support
|
|
35
|
-
if (!('IntersectionObserver' in window)) {
|
|
36
|
-
console.warn('IntersectionObserver api not supported')
|
|
37
|
-
return
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
setupObserver($target)
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
ctx.on('destroy', () => {
|
|
44
|
-
clearObserver()
|
|
45
|
-
})
|
|
46
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import type { ArtalkPlugin } from '~/types'
|
|
2
|
-
import $t from '@/i18n'
|
|
3
|
-
|
|
4
|
-
export const SidebarBtn: ArtalkPlugin = (ctx) => {
|
|
5
|
-
let $openSidebarBtn: HTMLElement|null = null
|
|
6
|
-
|
|
7
|
-
const syncByUser = () => {
|
|
8
|
-
if (!$openSidebarBtn) return
|
|
9
|
-
const user = ctx.get('user').data
|
|
10
|
-
|
|
11
|
-
// 已输入个人信息
|
|
12
|
-
if (!!user.nick && !!user.email) {
|
|
13
|
-
$openSidebarBtn.classList.remove('atk-hide')
|
|
14
|
-
|
|
15
|
-
// update button text (normal user or admin)
|
|
16
|
-
const $btnText = $openSidebarBtn.querySelector<HTMLElement>('.atk-text')
|
|
17
|
-
if ($btnText) $btnText.innerText = (!user.isAdmin) ? $t('msgCenter') : $t('ctrlCenter')
|
|
18
|
-
} else {
|
|
19
|
-
$openSidebarBtn.classList.add('atk-hide')
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
ctx.on('conf-loaded', () => {
|
|
24
|
-
const list = ctx.get('list')
|
|
25
|
-
|
|
26
|
-
$openSidebarBtn = list.$el.querySelector<HTMLElement>('[data-action="open-sidebar"]')
|
|
27
|
-
if (!$openSidebarBtn) return
|
|
28
|
-
|
|
29
|
-
$openSidebarBtn.onclick = () => { // use onclick rather than addEventListener to prevent duplicate event
|
|
30
|
-
ctx.showSidebar()
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
syncByUser()
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
ctx.on('user-changed', (user) => {
|
|
37
|
-
syncByUser()
|
|
38
|
-
})
|
|
39
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { ArtalkPlugin } from '~/types'
|
|
2
|
-
import * as Utils from '@/lib/utils'
|
|
3
|
-
|
|
4
|
-
/** 评论时间自动更新 */
|
|
5
|
-
export const TimeTicking: ArtalkPlugin = (ctx) => {
|
|
6
|
-
let timer: number|null = null
|
|
7
|
-
|
|
8
|
-
ctx.on('inited', () => {
|
|
9
|
-
timer = window.setInterval(() => {
|
|
10
|
-
const list = ctx.get('list')
|
|
11
|
-
|
|
12
|
-
list.$el.querySelectorAll<HTMLElement>('[data-atk-comment-date]').forEach(el => {
|
|
13
|
-
const date = el.getAttribute('data-atk-comment-date')
|
|
14
|
-
el.innerText = Utils.timeAgo(new Date(Number(date)), ctx.$t)
|
|
15
|
-
})
|
|
16
|
-
}, 30 * 1000) // 30s 更新一次
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
ctx.on('destroy', () => {
|
|
20
|
-
timer && window.clearInterval(timer)
|
|
21
|
-
})
|
|
22
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { ArtalkPlugin } from '~/types'
|
|
2
|
-
|
|
3
|
-
export const UnreadBadge: ArtalkPlugin = (ctx) => {
|
|
4
|
-
let $unreadBadge: HTMLElement|null = null
|
|
5
|
-
|
|
6
|
-
const showUnreadBadge = (count: number) => {
|
|
7
|
-
if (!$unreadBadge) return
|
|
8
|
-
|
|
9
|
-
if (count > 0) {
|
|
10
|
-
$unreadBadge.innerText = `${Number(count || 0)}`
|
|
11
|
-
$unreadBadge.style.display = 'block'
|
|
12
|
-
} else {
|
|
13
|
-
$unreadBadge.style.display = 'none'
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
ctx.on('conf-loaded', () => {
|
|
18
|
-
const list = ctx.get('list')
|
|
19
|
-
|
|
20
|
-
$unreadBadge = list.$el.querySelector<HTMLElement>('.atk-unread-badge')
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
ctx.on('unreads-updated', (unreads) => {
|
|
24
|
-
showUnreadBadge(unreads.length || 0)
|
|
25
|
-
})
|
|
26
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import type { ArtalkPlugin } from '~/types'
|
|
2
|
-
import * as Utils from '@/lib/utils'
|
|
3
|
-
|
|
4
|
-
export const Unread: ArtalkPlugin = (ctx) => {
|
|
5
|
-
ctx.on('comment-rendered', (comment) => {
|
|
6
|
-
// comment unread highlight
|
|
7
|
-
if (ctx.conf.listUnreadHighlight === true) {
|
|
8
|
-
const unreads = ctx.getData().getUnreads()
|
|
9
|
-
const notify = unreads.find(o => o.comment_id === comment.getID())
|
|
10
|
-
|
|
11
|
-
if (notify) {
|
|
12
|
-
// if comment contains in unread list
|
|
13
|
-
comment.getRender().setUnread(true)
|
|
14
|
-
comment.getRender().setOpenAction(() => {
|
|
15
|
-
window.open(notify.read_link)
|
|
16
|
-
|
|
17
|
-
// remove notify which has been read
|
|
18
|
-
ctx.getData().updateUnreads(unreads.filter(o => o.comment_id !== comment.getID()))
|
|
19
|
-
})
|
|
20
|
-
} else {
|
|
21
|
-
// comment not in unread list
|
|
22
|
-
comment.getRender().setUnread(false)
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
ctx.on('list-goto', (commentID) => {
|
|
28
|
-
const notifyKey = Utils.getQueryParam('atk_notify_key')
|
|
29
|
-
if (notifyKey) {
|
|
30
|
-
// mark as read
|
|
31
|
-
ctx.getApi().user.markRead(commentID, notifyKey)
|
|
32
|
-
.then(() => {
|
|
33
|
-
// remove from unread list
|
|
34
|
-
ctx.getData().updateUnreads(ctx.getData().getUnreads().filter(o => o.comment_id !== commentID))
|
|
35
|
-
})
|
|
36
|
-
}
|
|
37
|
-
})
|
|
38
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import type { ContextApi, ArtalkPlugin, PageData } from '~/types'
|
|
2
|
-
import $t from '@/i18n'
|
|
3
|
-
|
|
4
|
-
export const WithEditor: ArtalkPlugin = (ctx) => {
|
|
5
|
-
let $closeCommentBtn: HTMLElement|undefined
|
|
6
|
-
|
|
7
|
-
// on Artalk inited
|
|
8
|
-
// (after all components had mounted)
|
|
9
|
-
ctx.on('inited', () => {
|
|
10
|
-
const list = ctx.get('list')
|
|
11
|
-
|
|
12
|
-
$closeCommentBtn = list.$el.querySelector<HTMLElement>('[data-action="admin-close-comment"]')!
|
|
13
|
-
|
|
14
|
-
// bind editor close button click event
|
|
15
|
-
$closeCommentBtn.addEventListener('click', () => {
|
|
16
|
-
const page = ctx.getData().getPage()
|
|
17
|
-
if (!page) throw new Error('Page data not found')
|
|
18
|
-
|
|
19
|
-
page.admin_only = !page.admin_only
|
|
20
|
-
adminPageEditSave(ctx, page)
|
|
21
|
-
})
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
// on comment list loaded (it will include page data update)
|
|
25
|
-
ctx.on('page-loaded', (page) => {
|
|
26
|
-
const editor = ctx.get('editor')
|
|
27
|
-
|
|
28
|
-
// if page comment is closed
|
|
29
|
-
if (page?.admin_only === true) {
|
|
30
|
-
// then close editor
|
|
31
|
-
editor.getPlugs()?.getEvents().trigger('editor-close')
|
|
32
|
-
$closeCommentBtn && ($closeCommentBtn.innerText = $t('openComment'))
|
|
33
|
-
} else {
|
|
34
|
-
// the open editor
|
|
35
|
-
editor.getPlugs()?.getEvents().trigger('editor-open')
|
|
36
|
-
$closeCommentBtn && ($closeCommentBtn.innerText = $t('closeComment'))
|
|
37
|
-
}
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
ctx.on('list-loaded', (comments) => {
|
|
41
|
-
// 防止评论框被吞
|
|
42
|
-
ctx.editorResetState()
|
|
43
|
-
})
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** 管理员设置页面信息 */
|
|
47
|
-
function adminPageEditSave(ctx: ContextApi, page: PageData) {
|
|
48
|
-
ctx.editorShowLoading()
|
|
49
|
-
ctx.getApi().page.pageEdit(page)
|
|
50
|
-
.then((respPage) => {
|
|
51
|
-
ctx.getData().updatePage(respPage)
|
|
52
|
-
})
|
|
53
|
-
.catch(err => {
|
|
54
|
-
ctx.editorShowNotify(`${$t('editFail')}: ${err.msg || String(err)}`, 'e')
|
|
55
|
-
})
|
|
56
|
-
.finally(() => {
|
|
57
|
-
ctx.editorHideLoading()
|
|
58
|
-
})
|
|
59
|
-
}
|
package/src/plugins/markdown.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { ArtalkPlugin } from '~/types'
|
|
2
|
-
import * as marked from '@/lib/marked'
|
|
3
|
-
|
|
4
|
-
export const Markdown: ArtalkPlugin = (ctx) => {
|
|
5
|
-
marked.initMarked()
|
|
6
|
-
|
|
7
|
-
ctx.on('conf-loaded', (conf) => {
|
|
8
|
-
if (conf.markedReplacers) marked.setReplacers(conf.markedReplacers)
|
|
9
|
-
})
|
|
10
|
-
}
|