artalk 2.7.0 → 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 +209 -188
- package/dist/Artalk.es.js.map +1 -0
- package/dist/Artalk.iife.js +2 -1
- package/dist/Artalk.iife.js.map +1 -0
- package/dist/Artalk.js +2 -1
- package/dist/Artalk.js.map +1 -0
- package/dist/ArtalkLite.css +1 -1
- package/dist/ArtalkLite.es.js +209 -188
- package/dist/ArtalkLite.es.js.map +1 -0
- package/dist/ArtalkLite.iife.js +2 -1
- package/dist/ArtalkLite.iife.js.map +1 -0
- package/dist/ArtalkLite.js +2 -1
- package/dist/ArtalkLite.js.map +1 -0
- 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/artalk.d.ts +0 -2
- package/dist/src/components/checker/index.d.ts +2 -0
- package/dist/src/i18n/en.d.ts +4 -0
- package/dist/src/i18n/external.d.ts +4 -0
- package/dist/src/i18n/index.d.ts +12 -0
- package/dist/src/i18n/jp.d.ts +4 -0
- package/dist/src/i18n/zh-TW.d.ts +4 -0
- package/dist/src/lib/user.d.ts +8 -6
- package/dist/src/main.d.ts +3 -0
- 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 -119
- 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 -81
- package/src/i18n/external.ts +0 -10
- package/src/i18n/index.ts +0 -67
- package/src/i18n/jp.ts +0 -83
- package/src/i18n/zh-CN.ts +0 -85
- package/src/i18n/zh-TW.ts +0 -83
- 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 -5
- 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 -79
- package/src/plugins/version-check.ts +0 -37
- 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
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import type { PlugManager } from '../editor-kit'
|
|
2
|
-
import type EditorPlug from "./_plug"
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* PlugKit provides a set of methods to help you develop editor plug
|
|
6
|
-
*/
|
|
7
|
-
export default class PlugKit {
|
|
8
|
-
constructor(
|
|
9
|
-
private plugs: PlugManager
|
|
10
|
-
) {
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/** Use the editor */
|
|
14
|
-
useEditor() {
|
|
15
|
-
return this.plugs.editor
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/** Use the context of global */
|
|
19
|
-
useGlobalCtx() {
|
|
20
|
-
return this.plugs.editor.ctx
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** Use the config of Artalk */
|
|
24
|
-
useConf() {
|
|
25
|
-
return this.plugs.editor.ctx.conf
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Use the http api client */
|
|
29
|
-
useApi() {
|
|
30
|
-
return this.plugs.editor.ctx.getApi()
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Use the ui of editor */
|
|
34
|
-
useUI() {
|
|
35
|
-
return this.plugs.editor.getUI()
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** Use the events in editor scope */
|
|
39
|
-
useEvents() {
|
|
40
|
-
return this.plugs.getEvents()
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/** Listen the event when plug is mounted */
|
|
44
|
-
useMounted(func: () => void) {
|
|
45
|
-
this.useEvents().on('mounted', func)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Listen the event when plug is unmounted */
|
|
49
|
-
useUnmounted(func: () => void) {
|
|
50
|
-
this.useEvents().on('unmounted', func)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** Use the deps of other plug */
|
|
54
|
-
useDeps<T extends typeof EditorPlug>(plug: T) {
|
|
55
|
-
return this.plugs.get(plug)
|
|
56
|
-
}
|
|
57
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import type { CommentData, EditorState } from '~/types'
|
|
2
|
-
import * as Utils from '@/lib/utils'
|
|
3
|
-
import type PlugKit from './_kit'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Editor 插件
|
|
7
|
-
*/
|
|
8
|
-
interface EditorPlug {
|
|
9
|
-
$btn?: HTMLElement
|
|
10
|
-
$panel?: HTMLElement
|
|
11
|
-
contentTransformer?(rawContent: string): string
|
|
12
|
-
editorStateEffectWhen?: EditorState
|
|
13
|
-
editorStateEffect?(comment: CommentData): () => void
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
class EditorPlug {
|
|
17
|
-
constructor(
|
|
18
|
-
protected kit: PlugKit
|
|
19
|
-
) {
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** Use plug btn will add a btn on the bottom of editor */
|
|
23
|
-
useBtn(html: string = '<div></div>') {
|
|
24
|
-
this.$btn = Utils.createElement(`<span class="atk-plug-btn">${html}</span>`)
|
|
25
|
-
return this.$btn
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Use plug panel will show the panel when btn is clicked */
|
|
29
|
-
usePanel(html: string = '<div></div>') {
|
|
30
|
-
this.$panel = Utils.createElement(html)
|
|
31
|
-
return this.$panel
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** Use the content transformer to handle the content of the last submit by the editor */
|
|
35
|
-
useContentTransformer(func: (raw: string) => string) {
|
|
36
|
-
this.contentTransformer = func
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** Listen the event of panel show */
|
|
40
|
-
usePanelShow(func: () => void) {
|
|
41
|
-
this.kit.useEvents().on('panel-show', (aPlug) => {
|
|
42
|
-
if (aPlug === this) func()
|
|
43
|
-
})
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** Listen the event of panel hide */
|
|
47
|
-
usePanelHide(func: () => void) {
|
|
48
|
-
this.kit.useEvents().on('panel-hide', (aPlug) => {
|
|
49
|
-
if (aPlug === this) func()
|
|
50
|
-
})
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** Use editor state modifier */
|
|
54
|
-
useEditorStateEffect(stateName: EditorState, effectFn: (comment: CommentData) => () => void) {
|
|
55
|
-
this.editorStateEffectWhen = stateName
|
|
56
|
-
this.editorStateEffect = effectFn
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export default EditorPlug
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import User from '@/lib/user'
|
|
2
|
-
import * as Utils from '@/lib/utils'
|
|
3
|
-
import $t from '@/i18n'
|
|
4
|
-
import EditorPlug from './_plug'
|
|
5
|
-
import type PlugKit from './_kit'
|
|
6
|
-
|
|
7
|
-
export default class Closable extends EditorPlug {
|
|
8
|
-
constructor(kit: PlugKit) {
|
|
9
|
-
super(kit)
|
|
10
|
-
|
|
11
|
-
const onOpen = () => this.open()
|
|
12
|
-
const onClose = () => this.close()
|
|
13
|
-
|
|
14
|
-
this.kit.useMounted(() => {
|
|
15
|
-
this.kit.useEvents().on('editor-open', onOpen)
|
|
16
|
-
this.kit.useEvents().on('editor-close', onClose)
|
|
17
|
-
})
|
|
18
|
-
this.kit.useUnmounted(() => {
|
|
19
|
-
this.kit.useEvents().off('editor-open', onOpen)
|
|
20
|
-
this.kit.useEvents().off('editor-close', onClose)
|
|
21
|
-
})
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
private open() {
|
|
25
|
-
this.kit.useUI().$textareaWrap.querySelector('.atk-comment-closed')?.remove()
|
|
26
|
-
this.kit.useUI().$textarea.style.display = ''
|
|
27
|
-
this.kit.useUI().$bottom.style.display = ''
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
private close() {
|
|
31
|
-
if (!this.kit.useUI().$textareaWrap.querySelector('.atk-comment-closed'))
|
|
32
|
-
this.kit.useUI().$textareaWrap.prepend(Utils.createElement(`<div class="atk-comment-closed">${$t('onlyAdminCanReply')}</div>`))
|
|
33
|
-
|
|
34
|
-
if (!User.data.isAdmin) {
|
|
35
|
-
this.kit.useUI().$textarea.style.display = 'none'
|
|
36
|
-
this.kit.useEvents().trigger('panel-close')
|
|
37
|
-
this.kit.useUI().$bottom.style.display = 'none'
|
|
38
|
-
} else {
|
|
39
|
-
// 管理员一直打开评论
|
|
40
|
-
this.kit.useUI().$textarea.style.display = ''
|
|
41
|
-
this.kit.useUI().$bottom.style.display = ''
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
.atk-editor-plug-emoticons {
|
|
2
|
-
height: 100%;
|
|
3
|
-
width: 100%;
|
|
4
|
-
|
|
5
|
-
& > .atk-grp-wrap {
|
|
6
|
-
@extend .atk-slim-scrollbar;
|
|
7
|
-
overflow-y: scroll;
|
|
8
|
-
overflow-x: hidden;
|
|
9
|
-
height: 100%; // calc(100% - 30px);
|
|
10
|
-
width: 100%;
|
|
11
|
-
|
|
12
|
-
& > .atk-grp {
|
|
13
|
-
display: flex;
|
|
14
|
-
flex-wrap: wrap;
|
|
15
|
-
flex-direction: row;
|
|
16
|
-
padding: 5px 10px 35px 10px;
|
|
17
|
-
|
|
18
|
-
&[data-type="image"] > .atk-item {
|
|
19
|
-
height: 63px;
|
|
20
|
-
width: 63px;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
& > .atk-item {
|
|
24
|
-
display: flex;
|
|
25
|
-
align-items: center;
|
|
26
|
-
justify-content: center;
|
|
27
|
-
padding: 5px;
|
|
28
|
-
cursor: pointer;
|
|
29
|
-
user-select: none;
|
|
30
|
-
border-radius: calc($block-radius / 2);
|
|
31
|
-
font-size: 15px;
|
|
32
|
-
min-width: 35px;
|
|
33
|
-
margin: 2px;
|
|
34
|
-
|
|
35
|
-
& > img {
|
|
36
|
-
max-height: 100%;
|
|
37
|
-
width: auto;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
&:hover {
|
|
41
|
-
background: var(--at-color-bg-grey);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
& > .atk-grp-switcher {
|
|
48
|
-
position: absolute;
|
|
49
|
-
bottom: 0;
|
|
50
|
-
left: 0;
|
|
51
|
-
width: 100%;
|
|
52
|
-
background: var(--at-color-bg-transl);
|
|
53
|
-
height: 30px;
|
|
54
|
-
border-top: 1px solid var(--at-color-border);
|
|
55
|
-
border-bottom: 1px solid var(--at-color-border);
|
|
56
|
-
|
|
57
|
-
& > span {
|
|
58
|
-
user-select: none;
|
|
59
|
-
padding: 0 10px;
|
|
60
|
-
line-height: 30px;
|
|
61
|
-
float: left;
|
|
62
|
-
display: block;
|
|
63
|
-
cursor: pointer;
|
|
64
|
-
font-size: $font-size;
|
|
65
|
-
|
|
66
|
-
&:hover,
|
|
67
|
-
&.active {
|
|
68
|
-
background: var(--at-color-bg-grey);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
import './emoticons.scss'
|
|
2
|
-
|
|
3
|
-
import type { EmoticonListData, EmoticonGrpData } from '~/types'
|
|
4
|
-
import * as Utils from '@/lib/utils'
|
|
5
|
-
import * as Ui from '@/lib/ui'
|
|
6
|
-
import $t from '@/i18n'
|
|
7
|
-
import EditorPlug from './_plug'
|
|
8
|
-
import type PlugKit from './_kit'
|
|
9
|
-
|
|
10
|
-
type OwOFormatType = {
|
|
11
|
-
[key: string] : {
|
|
12
|
-
type: 'emoticon'|'emoji'|'image',
|
|
13
|
-
container: {icon: string, text: string}[]
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export default class Emoticons extends EditorPlug {
|
|
18
|
-
private emoticons: EmoticonListData = []
|
|
19
|
-
private loadingTask: Promise<void>|null = null
|
|
20
|
-
|
|
21
|
-
private $grpWrap!: HTMLElement
|
|
22
|
-
private $grpSwitcher!: HTMLElement
|
|
23
|
-
|
|
24
|
-
constructor(kit: PlugKit) {
|
|
25
|
-
super(kit)
|
|
26
|
-
|
|
27
|
-
this.kit.useMounted(() => {
|
|
28
|
-
this.usePanel(`<div class="atk-editor-plug-emoticons"></div>`)
|
|
29
|
-
this.useBtn($t('emoticon'))
|
|
30
|
-
})
|
|
31
|
-
this.kit.useUnmounted(() => {})
|
|
32
|
-
|
|
33
|
-
this.useContentTransformer((raw) => this.transEmoticonImageText(raw))
|
|
34
|
-
this.usePanelShow(() => {
|
|
35
|
-
;(async () => {
|
|
36
|
-
await this.loadEmoticonsData()
|
|
37
|
-
|
|
38
|
-
// 初始化元素
|
|
39
|
-
if (!this.isImgLoaded) {
|
|
40
|
-
this.initEmoticonsList()
|
|
41
|
-
this.isImgLoaded = true
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// 延迟执行,防止无法读取高度
|
|
45
|
-
setTimeout(() => {
|
|
46
|
-
this.changeListHeight()
|
|
47
|
-
}, 30)
|
|
48
|
-
})()
|
|
49
|
-
})
|
|
50
|
-
this.usePanelHide(() => {
|
|
51
|
-
this.$panel!.parentElement!.style.height = ''
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
// 表情包预加载
|
|
55
|
-
window.setTimeout(() => {
|
|
56
|
-
this.loadEmoticonsData()
|
|
57
|
-
}, 1000) // 延迟 1s 加载
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
private isListLoaded = false
|
|
61
|
-
private isImgLoaded = false
|
|
62
|
-
|
|
63
|
-
public async loadEmoticonsData() {
|
|
64
|
-
if (this.isListLoaded) return
|
|
65
|
-
if (this.loadingTask !== null) {
|
|
66
|
-
await this.loadingTask
|
|
67
|
-
return
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// 数据处理
|
|
71
|
-
this.loadingTask = (async () => {
|
|
72
|
-
Ui.showLoading(this.$panel!)
|
|
73
|
-
this.emoticons = await this.handleData(this.kit.useConf().emoticons)
|
|
74
|
-
Ui.hideLoading(this.$panel!)
|
|
75
|
-
this.loadingTask = null
|
|
76
|
-
this.isListLoaded = true
|
|
77
|
-
})()
|
|
78
|
-
await this.loadingTask
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
private async handleData(data: any): Promise<any[]> {
|
|
82
|
-
if (!Array.isArray(data) && ['object', 'string'].includes(typeof data)) {
|
|
83
|
-
data = [data]
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (!Array.isArray(data)) {
|
|
87
|
-
Ui.setError(this.$panel!, "表情包数据必须为 Array/Object/String 类型")
|
|
88
|
-
Ui.hideLoading(this.$panel!)
|
|
89
|
-
return []
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const pushGrp = (grp: EmoticonGrpData) => {
|
|
93
|
-
if (typeof grp !== 'object') return
|
|
94
|
-
if (grp.name && data.find(o => o.name === grp.name)) return
|
|
95
|
-
data.push(grp)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// 加载子内容
|
|
99
|
-
const remoteLoad = async (d: any[]) => {
|
|
100
|
-
await Promise.all(d.map(async (grp, index) => {
|
|
101
|
-
if (typeof grp === 'object' && !Array.isArray(grp)) {
|
|
102
|
-
pushGrp(grp)
|
|
103
|
-
} else if (Array.isArray(grp)) {
|
|
104
|
-
await remoteLoad(grp)
|
|
105
|
-
} else if (typeof grp === "string") {
|
|
106
|
-
const grpData = await this.remoteLoad(grp)
|
|
107
|
-
|
|
108
|
-
if (Array.isArray(grpData)) await remoteLoad(grpData)
|
|
109
|
-
else if (typeof grpData === 'object') pushGrp(grpData)
|
|
110
|
-
}
|
|
111
|
-
}))
|
|
112
|
-
}
|
|
113
|
-
await remoteLoad(data)
|
|
114
|
-
|
|
115
|
-
// 处理数组数据
|
|
116
|
-
data.forEach((item: any) => {
|
|
117
|
-
if (this.isOwOFormat(item)) {
|
|
118
|
-
const c = this.convertOwO(item)
|
|
119
|
-
c.forEach((grp) => { pushGrp(grp) })
|
|
120
|
-
} else if (Array.isArray(item)) {
|
|
121
|
-
item.forEach((grp) => { pushGrp(grp) })
|
|
122
|
-
}
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
// 剔除非法数据
|
|
126
|
-
data = data.filter((item: any) => (typeof item === 'object' && !Array.isArray(item) && !!item && !!item.name))
|
|
127
|
-
|
|
128
|
-
// console.log(data)
|
|
129
|
-
|
|
130
|
-
this.solveNullKey(data)
|
|
131
|
-
this.solveSameKey(data)
|
|
132
|
-
|
|
133
|
-
return data
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/** 远程加载 */
|
|
137
|
-
private async remoteLoad(url: string): Promise<any> {
|
|
138
|
-
if (!url) return []
|
|
139
|
-
|
|
140
|
-
try {
|
|
141
|
-
const resp = await fetch(url)
|
|
142
|
-
const json = await resp.json()
|
|
143
|
-
return json
|
|
144
|
-
} catch (err) {
|
|
145
|
-
Ui.hideLoading(this.$panel!)
|
|
146
|
-
Ui.setError(this.$panel!, `表情加载失败 ${String(err)}`)
|
|
147
|
-
return []
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/** 避免表情 item.key 为 null 的情况 */
|
|
152
|
-
private solveNullKey(data: EmoticonGrpData[]) {
|
|
153
|
-
data.forEach((grp) => {
|
|
154
|
-
grp.items.forEach((item, index) => {
|
|
155
|
-
if (!item.key) item.key = `${grp.name} ${index+1}`
|
|
156
|
-
})
|
|
157
|
-
})
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/** 避免相同 item.key */
|
|
161
|
-
private solveSameKey(data: EmoticonGrpData[]) {
|
|
162
|
-
const tmp: {[key: string]: number} = {}
|
|
163
|
-
data.forEach((grp) => {
|
|
164
|
-
grp.items.forEach(item => {
|
|
165
|
-
if (!item.key || String(item.key).trim() === "") return
|
|
166
|
-
if (!tmp[item.key]) tmp[item.key] = 1
|
|
167
|
-
else tmp[item.key]++
|
|
168
|
-
|
|
169
|
-
if (tmp[item.key] > 1) item.key = `${item.key} ${tmp[item.key]}`
|
|
170
|
-
})
|
|
171
|
-
})
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/** 判断是否为 OwO 格式 */
|
|
175
|
-
private isOwOFormat(data: any) {
|
|
176
|
-
try {
|
|
177
|
-
return (typeof data === 'object') && !!Object.values(data).length
|
|
178
|
-
&& Array.isArray(Object.keys(Object.values<any>(data)[0].container))
|
|
179
|
-
&& Object.keys(Object.values<any>(data)[0].container[0]).includes('icon')
|
|
180
|
-
} catch { return false }
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/** 转换 OwO 格式数据 */
|
|
184
|
-
private convertOwO(owoData: OwOFormatType): EmoticonListData {
|
|
185
|
-
const dest: EmoticonListData = []
|
|
186
|
-
Object.entries(owoData).forEach(([grpName, grp]) => {
|
|
187
|
-
const nGrp: EmoticonGrpData = { name: grpName, type: grp.type, items: [] }
|
|
188
|
-
grp.container.forEach((item, index) => {
|
|
189
|
-
// 图片标签提取 src 属性值
|
|
190
|
-
const iconStr = item.icon
|
|
191
|
-
if (/<(img|IMG)/.test(iconStr)) {
|
|
192
|
-
const find = /src=["'](.*?)["']/.exec(iconStr)
|
|
193
|
-
if (find && find.length > 1) item.icon = find[1]
|
|
194
|
-
}
|
|
195
|
-
nGrp.items.push({ key: item.text || `${grpName} ${index+1}`, val: item.icon })
|
|
196
|
-
})
|
|
197
|
-
dest.push(nGrp)
|
|
198
|
-
})
|
|
199
|
-
return dest
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/** 初始化表情列表界面 */
|
|
203
|
-
private initEmoticonsList() {
|
|
204
|
-
// 表情列表
|
|
205
|
-
this.$grpWrap = Utils.createElement(`<div class="atk-grp-wrap"></div>`)
|
|
206
|
-
this.$panel!.append(this.$grpWrap)
|
|
207
|
-
|
|
208
|
-
this.emoticons.forEach((grp, index) => {
|
|
209
|
-
const $grp = Utils.createElement(`<div class="atk-grp" style="display: none;"></div>`)
|
|
210
|
-
this.$grpWrap.append($grp)
|
|
211
|
-
$grp.setAttribute('data-index', String(index))
|
|
212
|
-
$grp.setAttribute('data-grp-name', grp.name)
|
|
213
|
-
$grp.setAttribute('data-type', grp.type)
|
|
214
|
-
grp.items.forEach((item) => {
|
|
215
|
-
const $item = Utils.createElement(`<span class="atk-item"></span>`)
|
|
216
|
-
$grp.append($item)
|
|
217
|
-
|
|
218
|
-
if (!!item.key && !(new RegExp(`^(${grp.name})?\\s?[0-9]+$`).test(item.key)))
|
|
219
|
-
$item.setAttribute('title', item.key)
|
|
220
|
-
|
|
221
|
-
if (grp.type === 'image') {
|
|
222
|
-
const imgEl = document.createElement('img')
|
|
223
|
-
imgEl.src = item.val
|
|
224
|
-
imgEl.alt = item.key
|
|
225
|
-
$item.append(imgEl)
|
|
226
|
-
} else {
|
|
227
|
-
$item.innerText = item.val
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
$item.onclick = () => {
|
|
231
|
-
if (grp.type === 'image') {
|
|
232
|
-
this.kit.useEditor().insertContent(`:[${item.key}]`)
|
|
233
|
-
} else {
|
|
234
|
-
this.kit.useEditor().insertContent(item.val || '')
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
})
|
|
238
|
-
})
|
|
239
|
-
|
|
240
|
-
// 表情分类切换 bar
|
|
241
|
-
if (this.emoticons.length > 1) {
|
|
242
|
-
this.$grpSwitcher = Utils.createElement(`<div class="atk-grp-switcher"></div>`)
|
|
243
|
-
this.$panel!.append(this.$grpSwitcher)
|
|
244
|
-
this.emoticons.forEach((grp, index) => {
|
|
245
|
-
const $item = Utils.createElement('<span />')
|
|
246
|
-
$item.innerText = grp.name
|
|
247
|
-
$item.setAttribute('data-index', String(index))
|
|
248
|
-
$item.onclick = () => (this.openGrp(index))
|
|
249
|
-
this.$grpSwitcher.append($item)
|
|
250
|
-
})
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// 默认打开第一个分类
|
|
254
|
-
if (this.emoticons.length > 0) this.openGrp(0)
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/** 打开一个表情组 */
|
|
258
|
-
public openGrp(index: number) {
|
|
259
|
-
Array.from(this.$grpWrap.children).forEach((item) => {
|
|
260
|
-
const el = item as HTMLElement
|
|
261
|
-
if (el.getAttribute('data-index') !== String(index)) {
|
|
262
|
-
el.style.display = 'none'
|
|
263
|
-
} else {
|
|
264
|
-
el.style.display = ''
|
|
265
|
-
}
|
|
266
|
-
})
|
|
267
|
-
|
|
268
|
-
this.$grpSwitcher?.querySelectorAll('span.active').forEach(item => item.classList.remove('active'))
|
|
269
|
-
this.$grpSwitcher?.querySelector(`span[data-index="${index}"]`)?.classList.add('active')
|
|
270
|
-
|
|
271
|
-
this.changeListHeight()
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
private changeListHeight() {
|
|
275
|
-
/* const listWrapHeight = Utils.getHeight(this.$grpWrapem)
|
|
276
|
-
this.editor.plugWrapEl.style.height = `${listWrapHeight > 150 ? listWrapHeight : 150}px` */
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/** 处理评论 content 中的表情内容 */
|
|
280
|
-
public transEmoticonImageText(text: string) {
|
|
281
|
-
if (!this.emoticons || !Array.isArray(this.emoticons))
|
|
282
|
-
return text
|
|
283
|
-
|
|
284
|
-
this.emoticons.forEach((grp) => {
|
|
285
|
-
if (grp.type !== 'image') return
|
|
286
|
-
Object.entries(grp.items).forEach(([index, item]) => {
|
|
287
|
-
text = text.split(`:[${item.key}]`).join(`<img src="${item.val}" atk-emoticon="${item.key}">`) // replaceAll(...)
|
|
288
|
-
})
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
return text
|
|
292
|
-
}
|
|
293
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import EditorPlug from './_plug'
|
|
2
|
-
import type PlugKit from './_kit'
|
|
3
|
-
|
|
4
|
-
export default class HeaderEvent extends EditorPlug {
|
|
5
|
-
private get $inputs() {
|
|
6
|
-
return this.kit.useEditor().getHeaderInputEls()
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
constructor(kit: PlugKit) {
|
|
10
|
-
super(kit)
|
|
11
|
-
|
|
12
|
-
const inputEventFns: {[name: string]: () => void} = {}
|
|
13
|
-
const changeEventFns: {[name: string]: () => void} = {}
|
|
14
|
-
|
|
15
|
-
const trigger = (evt: 'header-input'|'header-change', $input: HTMLInputElement, field: string) => () => {
|
|
16
|
-
this.kit.useEvents().trigger(evt, { field, $input })
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
this.kit.useMounted(() => {
|
|
20
|
-
// batch bind the events
|
|
21
|
-
Object.entries(this.$inputs).forEach(([key, $input]) => {
|
|
22
|
-
$input.addEventListener('input', inputEventFns[key] = trigger('header-input', $input, key))
|
|
23
|
-
$input.addEventListener('change', changeEventFns[key] = trigger('header-change', $input, key))
|
|
24
|
-
})
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
this.kit.useUnmounted(() => {
|
|
28
|
-
// unmount the event
|
|
29
|
-
Object.entries(this.$inputs).forEach(([key, $input]) => {
|
|
30
|
-
$input.removeEventListener('input', inputEventFns[key])
|
|
31
|
-
$input.removeEventListener('change', changeEventFns[key])
|
|
32
|
-
})
|
|
33
|
-
})
|
|
34
|
-
}
|
|
35
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import User from '@/lib/user'
|
|
2
|
-
import EditorPlug from './_plug'
|
|
3
|
-
import type PlugKit from './_kit'
|
|
4
|
-
|
|
5
|
-
export default class HeaderLink extends EditorPlug {
|
|
6
|
-
constructor(kit: PlugKit) {
|
|
7
|
-
super(kit)
|
|
8
|
-
|
|
9
|
-
const onLinkChange = ({ field }: { field: string }) => {
|
|
10
|
-
if (field === 'link') this.onLinkInputChange()
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// bind events
|
|
14
|
-
this.kit.useMounted(() => {
|
|
15
|
-
this.kit.useEvents().on('header-change', onLinkChange)
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
this.kit.useUnmounted(() => {
|
|
19
|
-
this.kit.useEvents().off('header-change', onLinkChange)
|
|
20
|
-
})
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
private onLinkInputChange() {
|
|
24
|
-
// auto and force add protocol prefix for user input link
|
|
25
|
-
const link = this.kit.useUI().$link.value.trim()
|
|
26
|
-
if (!!link && !/^(http|https):\/\//.test(link)) {
|
|
27
|
-
this.kit.useUI().$link.value = `https://${link}`
|
|
28
|
-
User.update({ link: this.kit.useUI().$link.value })
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import User from '@/lib/user'
|
|
2
|
-
import $t from '@/i18n'
|
|
3
|
-
import EditorPlug from './_plug'
|
|
4
|
-
import type PlugKit from './_kit'
|
|
5
|
-
|
|
6
|
-
export default class HeaderUser extends EditorPlug {
|
|
7
|
-
constructor(kit: PlugKit) {
|
|
8
|
-
super(kit)
|
|
9
|
-
|
|
10
|
-
const onInput = ({ $input, field }: { $input: HTMLInputElement, field: string }) => {
|
|
11
|
-
if (this.kit.useEditor().getState() === 'edit')
|
|
12
|
-
return // TODO prevent execute when editing, since update comment.user not support
|
|
13
|
-
|
|
14
|
-
// update user data
|
|
15
|
-
User.update({ [field]: $input.value.trim() })
|
|
16
|
-
|
|
17
|
-
// remote fetch user info
|
|
18
|
-
if (field === 'nick' || field === 'email')
|
|
19
|
-
this.fetchUserInfo() // must after update user data, since fetchUserInfo() will use User.data
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
this.kit.useMounted(() => {
|
|
23
|
-
Object.entries(this.kit.useEditor().getHeaderInputEls())
|
|
24
|
-
.forEach(([key, $input]) => {
|
|
25
|
-
// set placeholder
|
|
26
|
-
$input.placeholder = `${$t(key as any)}`
|
|
27
|
-
|
|
28
|
-
// sync header values from User.data
|
|
29
|
-
$input.value = User.data[key] || ''
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
// bind events
|
|
33
|
-
this.kit.useEvents().on('header-input', onInput)
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
this.kit.useUnmounted(() => {
|
|
37
|
-
this.kit.useEvents().off('header-input', onInput)
|
|
38
|
-
})
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
private query = {
|
|
42
|
-
timer: <number|null>null,
|
|
43
|
-
abortFn: <(() => void)|null>null
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Fetch user info from server
|
|
48
|
-
*/
|
|
49
|
-
private fetchUserInfo() {
|
|
50
|
-
User.logout() // clear login status
|
|
51
|
-
|
|
52
|
-
if (this.query.timer) window.clearTimeout(this.query.timer) // clear the not executed timeout task
|
|
53
|
-
if (this.query.abortFn) this.query.abortFn() // abort the last request (if request is pending not finished)
|
|
54
|
-
|
|
55
|
-
this.query.timer = window.setTimeout(() => {
|
|
56
|
-
this.query.timer = null // clear the timer (clarify the timer is executing)
|
|
57
|
-
|
|
58
|
-
const {req, abort} = this.kit.useApi().user.userGet(
|
|
59
|
-
User.data.nick, User.data.email
|
|
60
|
-
)
|
|
61
|
-
this.query.abortFn = abort
|
|
62
|
-
req.then(data => this.onUserInfoFetched(data))
|
|
63
|
-
.catch((err) => {})
|
|
64
|
-
.finally(() => {
|
|
65
|
-
this.query.abortFn = null // clear the abort function (clarify the request is finished)
|
|
66
|
-
})
|
|
67
|
-
}, 400) // delay to reduce request
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Function called when user info fetched
|
|
72
|
-
*
|
|
73
|
-
* @param data The response data from server
|
|
74
|
-
*/
|
|
75
|
-
private onUserInfoFetched(
|
|
76
|
-
data: any // TODO fix type
|
|
77
|
-
) {
|
|
78
|
-
// If api response is not login, logout
|
|
79
|
-
if (!data.is_login) User.logout()
|
|
80
|
-
|
|
81
|
-
// Update unread notifies
|
|
82
|
-
this.kit.useGlobalCtx().getData().updateUnreads(data.unread)
|
|
83
|
-
|
|
84
|
-
// If user is admin and not login,
|
|
85
|
-
if (User.checkHasBasicUserInfo() && !data.is_login && data.user?.is_admin) {
|
|
86
|
-
// then show login window
|
|
87
|
-
this.kit.useGlobalCtx().checkAdmin({
|
|
88
|
-
onSuccess: () => {}
|
|
89
|
-
})
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Auto fill user link from server
|
|
93
|
-
if (data.user && data.user.link) {
|
|
94
|
-
this.kit.useUI().$link.value = data.user.link
|
|
95
|
-
User.update({ link: data.user.link })
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|