artalk 2.4.3 → 2.5.0
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 +27 -0
- package/dist/Artalk.css +1 -1
- package/dist/Artalk.es.js +4895 -4067
- package/dist/Artalk.iife.js +1 -1
- package/dist/Artalk.js +1 -1
- package/dist/ArtalkLite.css +1 -1
- package/dist/ArtalkLite.es.js +2496 -1779
- package/dist/ArtalkLite.iife.js +1 -1
- package/dist/ArtalkLite.js +1 -1
- package/dist/i18n/jp.js +1 -1
- package/dist/i18n/zh-TW.js +1 -1
- package/dist/src/api/index.d.ts +2 -2
- package/dist/src/api/system.d.ts +5 -2
- package/dist/src/artalk.d.ts +42 -34
- package/dist/src/comment/comment.d.ts +2 -2
- package/dist/src/comment/height-limit.d.ts +30 -0
- package/dist/src/comment/render-ctx.d.ts +23 -0
- package/dist/src/comment/render.d.ts +2 -50
- package/dist/src/comment/renders/actions.d.ts +5 -0
- package/dist/src/comment/renders/avatar.d.ts +5 -0
- package/dist/src/comment/renders/content.d.ts +5 -0
- package/dist/src/comment/renders/header.d.ts +5 -0
- package/dist/src/comment/renders/index.d.ts +2 -0
- package/dist/src/comment/renders/pending.d.ts +5 -0
- package/dist/src/comment/renders/reply-at.d.ts +5 -0
- package/dist/src/comment/renders/reply-to.d.ts +5 -0
- package/dist/src/components/dialog.d.ts +1 -1
- package/dist/src/config.d.ts +8 -0
- package/dist/src/context.d.ts +15 -29
- package/dist/src/editor/edit-mode.d.ts +9 -0
- package/dist/src/editor/editor-ui.d.ts +25 -0
- package/dist/src/editor/editor.d.ts +37 -45
- package/dist/src/editor/funcs.d.ts +2 -0
- package/dist/src/editor/mover.d.ts +8 -0
- package/dist/src/editor/plug-manager.d.ts +17 -0
- package/dist/src/editor/plugs/editor-plug.d.ts +13 -1
- package/dist/src/editor/reply.d.ts +9 -0
- package/dist/src/editor/submit.d.ts +16 -0
- package/dist/src/editor/ui.d.ts +25 -0
- package/dist/src/i18n/index.d.ts +11 -7
- package/dist/src/layer/index.d.ts +2 -2
- package/dist/src/layer/layer.d.ts +1 -1
- package/dist/src/lib/checker/captcha-renders.d.ts +5 -0
- package/dist/src/lib/checker/index.d.ts +18 -10
- package/dist/src/lib/dark-mode.d.ts +4 -0
- package/dist/src/lib/marked.d.ts +9 -0
- package/dist/src/lib/utils.d.ts +1 -5
- package/dist/src/list/list-lite.d.ts +15 -11
- package/dist/src/list/list-nest.d.ts +2 -2
- package/dist/src/list/list-ui.d.ts +7 -0
- package/dist/src/list/paginator/adaptors/index.d.ts +12 -0
- package/dist/src/list/paginator/adaptors/pagination.d.ts +9 -0
- package/dist/src/list/paginator/adaptors/read-more.d.ts +10 -0
- package/dist/src/list/paginator/holder.d.ts +16 -0
- package/dist/src/list/paginator/index.d.ts +10 -0
- package/dist/src/service.d.ts +40 -0
- package/dist/types/artalk-config.d.ts +5 -2
- package/dist/types/artalk-data.d.ts +3 -3
- package/dist/types/context.d.ts +11 -17
- package/dist/types/global.d.ts +1 -0
- package/package.json +6 -7
- package/src/api/system.ts +3 -12
- package/src/artalk.ts +101 -158
- package/src/comment/comment.ts +7 -6
- package/src/comment/height-limit.ts +100 -0
- package/src/comment/render-ctx.ts +30 -0
- package/src/comment/render.ts +31 -375
- package/src/comment/renders/actions.ts +114 -0
- package/src/comment/renders/avatar.ts +22 -0
- package/src/comment/renders/content.ts +39 -0
- package/src/comment/renders/header.ts +84 -0
- package/src/comment/renders/index.ts +19 -0
- package/src/comment/renders/pending.ts +12 -0
- package/src/comment/renders/reply-at.ts +16 -0
- package/src/comment/renders/reply-to.ts +24 -0
- package/src/config.ts +65 -0
- package/src/context.ts +31 -88
- package/src/defaults.ts +1 -0
- package/src/editor/edit-mode.ts +102 -0
- package/src/editor/editor.ts +100 -394
- package/src/editor/funcs.ts +96 -0
- package/src/editor/mover.ts +43 -0
- package/src/editor/plug-manager.ts +172 -0
- package/src/editor/plugs/editor-plug.ts +20 -2
- package/src/editor/plugs/{emoticons-plug.less → emoticons-plug.scss} +5 -4
- package/src/editor/plugs/emoticons-plug.ts +2 -1
- package/src/editor/plugs/header-input-plug.ts +5 -5
- package/src/editor/plugs/{preview-plug.less → preview-plug.scss} +1 -1
- package/src/editor/plugs/preview-plug.ts +8 -4
- package/src/editor/plugs/upload-plug.ts +10 -13
- package/src/editor/reply.ts +62 -0
- package/src/editor/submit.ts +98 -0
- package/src/editor/ui.ts +36 -0
- package/src/i18n/index.ts +21 -10
- package/src/layer/index.ts +2 -2
- package/src/layer/layer.ts +4 -4
- package/src/layer/sidebar-layer.ts +2 -1
- package/src/lib/checker/admin.ts +36 -0
- package/src/lib/checker/captcha-renders.ts +68 -0
- package/src/lib/checker/captcha.ts +26 -0
- package/src/lib/checker/index.ts +38 -20
- package/src/lib/dark-mode.ts +48 -0
- package/src/lib/marked.ts +133 -0
- package/src/lib/utils.ts +1 -123
- package/src/list/list-lite.ts +59 -162
- package/src/list/list-ui.ts +80 -0
- package/src/list/list.ts +16 -8
- package/src/list/paginator/adaptors/index.ts +21 -0
- package/src/list/paginator/adaptors/pagination.ts +42 -0
- package/src/list/paginator/adaptors/read-more.ts +56 -0
- package/src/list/paginator/holder.ts +55 -0
- package/src/list/paginator/index.ts +13 -0
- package/src/service.ts +118 -0
- package/src/style/{_color.less → _color.scss} +3 -3
- package/src/style/{_extend.less → _extend.scss} +1 -1
- package/src/style/_variables.scss +3 -0
- package/src/style/{comment.less → comment.scss} +36 -12
- package/src/style/{editor.less → editor.scss} +11 -11
- package/src/style/{list.less → list.scss} +8 -6
- package/src/style/{main.less → main.scss} +24 -23
- package/src/style/{sidebar-layer.less → sidebar-layer.scss} +8 -8
- package/types/artalk-config.d.ts +5 -2
- package/types/artalk-data.d.ts +3 -3
- package/types/context.d.ts +11 -17
- package/types/global.d.ts +1 -0
- package/CNAME +0 -1
- package/LICENSE +0 -165
- package/index.html +0 -157
- package/postcss.config.js +0 -5
- package/scripts/build-i18n.mjs +0 -46
- package/src/lib/checker/admin-checker.ts +0 -36
- package/src/lib/checker/captcha-checker.ts +0 -84
- package/src/style/_variables.less +0 -3
- package/test/ArtalkCount.html +0 -16
- package/test/ArtalkI18n.html +0 -16
- package/test/ArtalkLite.html +0 -15
- package/test/CSRF.html +0 -26
- package/tsconfig.json +0 -12
- package/vite-base.config.ts +0 -21
- package/vite-demo.config.ts +0 -12
- package/vite-lite.config.ts +0 -22
- package/vite.config.ts +0 -27
- /package/dist/src/lib/checker/{admin-checker.d.ts → admin.d.ts} +0 -0
- /package/dist/src/lib/checker/{captcha-checker.d.ts → captcha.d.ts} +0 -0
- /package/src/style/{pagination.less → pagination.scss} +0 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as Utils from '../../lib/utils'
|
|
2
|
+
import RenderCtx from '../render-ctx'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 评论头部界面
|
|
6
|
+
*/
|
|
7
|
+
export default function renderHeader(ctx: RenderCtx) {
|
|
8
|
+
Object.entries({
|
|
9
|
+
renderNick, renderVerifyBadge, renderDate, renderUABadge
|
|
10
|
+
}).forEach(([name, render]) => {
|
|
11
|
+
render(ctx)
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function renderNick(ctx: RenderCtx) {
|
|
16
|
+
ctx.$headerNick = ctx.$el.querySelector<HTMLElement>('.atk-nick')!
|
|
17
|
+
|
|
18
|
+
if (ctx.data.link) {
|
|
19
|
+
const $nickA = Utils.createElement<HTMLLinkElement>('<a target="_blank" rel="noreferrer noopener nofollow"></a>')
|
|
20
|
+
$nickA.innerText = ctx.data.nick
|
|
21
|
+
$nickA.href = Utils.isValidURL(ctx.data.link) ? ctx.data.link : `https://${ctx.data.link}`
|
|
22
|
+
ctx.$headerNick.append($nickA)
|
|
23
|
+
} else {
|
|
24
|
+
ctx.$headerNick.innerText = ctx.data.nick
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function renderVerifyBadge(ctx: RenderCtx) {
|
|
29
|
+
ctx.$headerBadgeWrap = ctx.$el.querySelector<HTMLElement>('.atk-badge-wrap')!
|
|
30
|
+
ctx.$headerBadgeWrap.innerHTML = ''
|
|
31
|
+
|
|
32
|
+
const badgeText = ctx.data.badge_name
|
|
33
|
+
const badgeColor = ctx.data.badge_color
|
|
34
|
+
if (badgeText) {
|
|
35
|
+
const $badge = Utils.createElement(`<span class="atk-badge"></span>`)
|
|
36
|
+
$badge.innerText = badgeText.replace('管理员', ctx.ctx.$t('admin')) // i18n patch
|
|
37
|
+
$badge.style.backgroundColor = badgeColor || ''
|
|
38
|
+
ctx.$headerBadgeWrap.append($badge)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (ctx.data.is_pinned) {
|
|
42
|
+
const $pinnedBadge = Utils.createElement(`<span class="atk-pinned-badge">${ctx.ctx.$t('pin')}</span>`) // 置顶徽章
|
|
43
|
+
ctx.$headerBadgeWrap.append($pinnedBadge)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function renderDate(ctx: RenderCtx) {
|
|
48
|
+
const $date = ctx.$el.querySelector<HTMLElement>('.atk-date')!
|
|
49
|
+
$date.innerText = ctx.comment.getDateFormatted()
|
|
50
|
+
$date.setAttribute('data-atk-comment-date', String(+new Date(ctx.data.date)))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function renderUABadge(ctx: RenderCtx) {
|
|
54
|
+
if (!ctx.ctx.conf.uaBadge && !ctx.data.ip_region) return
|
|
55
|
+
|
|
56
|
+
let $uaWrap = ctx.$header.querySelector('atk-ua-wrap')
|
|
57
|
+
if (!$uaWrap) {
|
|
58
|
+
$uaWrap = Utils.createElement(`<span class="atk-ua-wrap"></span>`)
|
|
59
|
+
ctx.$header.append($uaWrap)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
$uaWrap.innerHTML = ''
|
|
63
|
+
|
|
64
|
+
if (ctx.data.ip_region) {
|
|
65
|
+
const $regionBadge = Utils.createElement(`<span class="atk-region-badge"></span>`)
|
|
66
|
+
$regionBadge.innerText = ctx.data.ip_region
|
|
67
|
+
$uaWrap.append($regionBadge)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (ctx.ctx.conf.uaBadge) {
|
|
71
|
+
const { browser, os } = ctx.comment.getUserUA()
|
|
72
|
+
if (String(browser).trim()) {
|
|
73
|
+
const $uaBrowser = Utils.createElement(`<span class="atk-ua ua-browser"></span>`)
|
|
74
|
+
$uaBrowser.innerText = browser
|
|
75
|
+
$uaWrap.append($uaBrowser)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (String(os).trim()) {
|
|
79
|
+
const $usOS = Utils.createElement(`<span class="atk-ua ua-os"></span>`)
|
|
80
|
+
$usOS.innerText = os
|
|
81
|
+
$uaWrap.append($usOS)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import RenderCtx from '../render-ctx'
|
|
2
|
+
import Avatar from './avatar'
|
|
3
|
+
import Header from './header'
|
|
4
|
+
import Content from './content'
|
|
5
|
+
import ReplyAt from './reply-at'
|
|
6
|
+
import ReplyTo from './reply-to'
|
|
7
|
+
import Pending from './pending'
|
|
8
|
+
import Actions from './actions'
|
|
9
|
+
|
|
10
|
+
const Renders = {
|
|
11
|
+
Avatar, Header, Content, ReplyAt,
|
|
12
|
+
ReplyTo, Pending, Actions
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default function loadRenders(ctx: RenderCtx) {
|
|
16
|
+
Object.entries(Renders).forEach(([ name, render ]) => {
|
|
17
|
+
render(ctx)
|
|
18
|
+
})
|
|
19
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as Utils from '../../lib/utils'
|
|
2
|
+
import RenderCtx from '../render-ctx'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 待审核状态界面
|
|
6
|
+
*/
|
|
7
|
+
export default function renderPending(ctx: RenderCtx) {
|
|
8
|
+
if (!ctx.data.is_pending) return
|
|
9
|
+
|
|
10
|
+
const pendingEl = Utils.createElement(`<div class="atk-pending">${ctx.ctx.$t('pendingMsg')}</div>`)
|
|
11
|
+
ctx.$body.prepend(pendingEl)
|
|
12
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as Utils from '../../lib/utils'
|
|
2
|
+
import RenderCtx from '../render-ctx'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 层级嵌套模式显示 AT 界面
|
|
6
|
+
*/
|
|
7
|
+
export default function renderReplyAt(ctx: RenderCtx) {
|
|
8
|
+
if (ctx.cConf.isFlatMode || ctx.data.rid === 0) return // not 平铺模式 或 根评论
|
|
9
|
+
if (!ctx.cConf.replyTo) return
|
|
10
|
+
|
|
11
|
+
ctx.$replyAt = Utils.createElement(`<span class="atk-item atk-reply-at"><span class="atk-arrow"></span><span class="atk-nick"></span></span>`)
|
|
12
|
+
ctx.$replyAt.querySelector<HTMLElement>('.atk-nick')!.innerText = `${ctx.cConf.replyTo.nick}`
|
|
13
|
+
ctx.$replyAt.onclick = () => { ctx.comment.getActions().goToReplyComment() }
|
|
14
|
+
|
|
15
|
+
ctx.$headerBadgeWrap.insertAdjacentElement('afterend', ctx.$replyAt)
|
|
16
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as Utils from '../../lib/utils'
|
|
2
|
+
import marked from '../../lib/marked'
|
|
3
|
+
import RenderCtx from '../render-ctx'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 回复的对象界面
|
|
7
|
+
*/
|
|
8
|
+
export default function renderReplyTo(ctx: RenderCtx) {
|
|
9
|
+
if (!ctx.cConf.isFlatMode) return // 仅平铺模式显示
|
|
10
|
+
if (!ctx.cConf.replyTo) return
|
|
11
|
+
|
|
12
|
+
ctx.$replyTo = Utils.createElement(`
|
|
13
|
+
<div class="atk-reply-to">
|
|
14
|
+
<div class="atk-meta">${ctx.ctx.$t('reply')} <span class="atk-nick"></span>:</div>
|
|
15
|
+
<div class="atk-content"></div>
|
|
16
|
+
</div>`)
|
|
17
|
+
const $nick = ctx.$replyTo.querySelector<HTMLElement>('.atk-nick')!
|
|
18
|
+
$nick.innerText = `@${ctx.cConf.replyTo.nick}`
|
|
19
|
+
$nick.onclick = () => { ctx.comment.getActions().goToReplyComment() }
|
|
20
|
+
let replyContent = marked(ctx.ctx, ctx.cConf.replyTo.content)
|
|
21
|
+
if (ctx.cConf.replyTo.is_collapsed) replyContent = `[${ctx.ctx.$t('collapsed')}]`
|
|
22
|
+
ctx.$replyTo.querySelector<HTMLElement>('.atk-content')!.innerHTML = replyContent
|
|
23
|
+
ctx.$body.prepend(ctx.$replyTo)
|
|
24
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type ArtalkConfig from '~/types/artalk-config'
|
|
2
|
+
import * as Utils from './lib/utils'
|
|
3
|
+
import Defaults from './defaults'
|
|
4
|
+
|
|
5
|
+
export default { handelBaseConf }
|
|
6
|
+
|
|
7
|
+
/** 基本配置初始化 */
|
|
8
|
+
export function handelBaseConf(customConf: Partial<ArtalkConfig>): ArtalkConfig {
|
|
9
|
+
// 合并默认配置
|
|
10
|
+
const conf: ArtalkConfig = Utils.mergeDeep(Defaults, customConf)
|
|
11
|
+
|
|
12
|
+
// 绑定元素
|
|
13
|
+
if (typeof conf.el === 'string' && !!conf.el) {
|
|
14
|
+
try {
|
|
15
|
+
const findEl = document.querySelector<HTMLElement>(conf.el)
|
|
16
|
+
if (!findEl) throw Error(`Target element "${conf.el}" was not found.`)
|
|
17
|
+
conf.el = findEl
|
|
18
|
+
} catch (e) {
|
|
19
|
+
console.error(e)
|
|
20
|
+
throw new Error('Please check your Artalk `el` config.')
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// 服务器配置
|
|
25
|
+
conf.server = conf.server.replace(/\/$/, '').replace(/\/api\/?$/, '')
|
|
26
|
+
|
|
27
|
+
// 默认 pageKey
|
|
28
|
+
if (!conf.pageKey) {
|
|
29
|
+
// @link http://bl.ocks.org/abernier/3070589
|
|
30
|
+
conf.pageKey = `${window.location.pathname}`
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 默认 pageTitle
|
|
34
|
+
if (!conf.pageTitle) {
|
|
35
|
+
conf.pageTitle = `${document.title}`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 自适应语言
|
|
39
|
+
if (conf.locale === 'auto') {
|
|
40
|
+
conf.locale = navigator.language
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return conf
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function handleBackendRefConf(conf: Partial<ArtalkConfig>) {
|
|
47
|
+
const DisabledKeys: (keyof ArtalkConfig)[] = [
|
|
48
|
+
'el', 'pageKey', 'pageTitle', 'server', 'site', 'darkMode'
|
|
49
|
+
]
|
|
50
|
+
Object.keys(conf).forEach(k => {
|
|
51
|
+
if (DisabledKeys.includes(k as any)) delete conf[k]
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
// Patch: `emoticons` config string to json
|
|
55
|
+
if (conf.emoticons && typeof conf.emoticons === "string") {
|
|
56
|
+
conf.emoticons = conf.emoticons.trim()
|
|
57
|
+
if (conf.emoticons.startsWith("[") || conf.emoticons.startsWith("{")) {
|
|
58
|
+
conf.emoticons = JSON.parse(conf.emoticons) // pase json
|
|
59
|
+
} else if (conf.emoticons === "false") {
|
|
60
|
+
conf.emoticons = false
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return conf
|
|
65
|
+
}
|
package/src/context.ts
CHANGED
|
@@ -1,37 +1,31 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
|
|
1
|
+
import type ArtalkConfig from '~/types/artalk-config'
|
|
2
|
+
import type { CommentData, NotifyData } from '~/types/artalk-data'
|
|
3
|
+
import type { Event } from '~/types/event'
|
|
4
|
+
import type ContextApi from '~/types/context'
|
|
5
|
+
import type { TInjectedServices } from './service'
|
|
6
|
+
|
|
7
7
|
import * as Utils from './lib/utils'
|
|
8
|
-
import
|
|
9
|
-
import
|
|
8
|
+
import * as DarkMode from './lib/dark-mode'
|
|
9
|
+
import * as marked from './lib/marked'
|
|
10
|
+
import { CheckerCaptchaPayload, CheckerPayload } from './lib/checker'
|
|
11
|
+
|
|
12
|
+
import * as I18n from './i18n'
|
|
13
|
+
import { getLayerWrap } from './layer'
|
|
14
|
+
import { SidebarShowPayload } from './layer/sidebar-layer'
|
|
10
15
|
import Comment from './comment'
|
|
11
|
-
import ListLite from './list/list-lite'
|
|
12
|
-
import SidebarLayer, { SidebarShowPayload } from './layer/sidebar-layer'
|
|
13
|
-
import CheckerLauncher, { CheckerCaptchaPayload, CheckerPayload } from './lib/checker'
|
|
14
|
-
import Layer, { GetLayerWrap } from './layer'
|
|
15
16
|
import Api from './api'
|
|
16
17
|
import List from './list'
|
|
17
18
|
|
|
19
|
+
// Auto dependency injection
|
|
20
|
+
interface Context extends TInjectedServices { }
|
|
21
|
+
|
|
18
22
|
/**
|
|
19
23
|
* Artalk Context
|
|
20
24
|
*/
|
|
21
|
-
|
|
22
|
-
/* 持有同事类 */
|
|
23
|
-
private api!: Api
|
|
24
|
-
private editor!: Editor
|
|
25
|
-
private list?: ListLite
|
|
26
|
-
private sidebarLayer!: SidebarLayer
|
|
27
|
-
private checkerLauncher!: CheckerLauncher
|
|
28
|
-
|
|
25
|
+
class Context implements ContextApi {
|
|
29
26
|
/* 运行参数 */
|
|
30
|
-
public cid: number // Context 唯一标识
|
|
31
27
|
public conf: ArtalkConfig
|
|
32
|
-
public user: User
|
|
33
28
|
public $root: HTMLElement
|
|
34
|
-
public markedInstance?: typeof libMarked
|
|
35
29
|
public markedReplacers: ((raw: string) => string)[] = []
|
|
36
30
|
|
|
37
31
|
private commentList: Comment[] = [] // Note: 无层级结构 + 无须排列
|
|
@@ -40,41 +34,17 @@ export default class Context implements ContextApi {
|
|
|
40
34
|
private eventList: Event[] = []
|
|
41
35
|
|
|
42
36
|
public constructor(conf: ArtalkConfig, $root?: HTMLElement) {
|
|
43
|
-
this.cid = +new Date()
|
|
44
37
|
this.conf = conf
|
|
45
|
-
this.user = new User(this)
|
|
46
38
|
|
|
47
39
|
this.$root = $root || document.createElement('div')
|
|
48
|
-
this.$root.setAttribute('atk-run-id', this.cid.toString())
|
|
49
40
|
this.$root.classList.add('artalk')
|
|
50
41
|
this.$root.innerHTML = ''
|
|
51
42
|
|
|
52
43
|
this.api = new Api(this)
|
|
53
|
-
|
|
54
|
-
this.on('conf-loaded', () => {
|
|
55
|
-
this.refreshDarkModeConf()
|
|
56
|
-
})
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/* 设置持有的同事类 */
|
|
60
|
-
public setApi(api: Api): void {
|
|
61
|
-
this.api = api
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
public setEditor(editor: Editor): void {
|
|
65
|
-
this.editor = editor
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
public setList(list: ListLite): void {
|
|
69
|
-
this.list = list
|
|
70
44
|
}
|
|
71
45
|
|
|
72
|
-
public
|
|
73
|
-
this
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
public setCheckerLauncher(checkerLauncher: CheckerLauncher): void {
|
|
77
|
-
this.checkerLauncher = checkerLauncher
|
|
46
|
+
public inject(depName: string, obj: any) {
|
|
47
|
+
this[depName] = obj
|
|
78
48
|
}
|
|
79
49
|
|
|
80
50
|
public getApi() {
|
|
@@ -164,7 +134,7 @@ export default class Context implements ContextApi {
|
|
|
164
134
|
}
|
|
165
135
|
|
|
166
136
|
public listHashGotoCheck(): void {
|
|
167
|
-
if (!this.list || this.list
|
|
137
|
+
if (!this.list || !(this.list instanceof List)) return
|
|
168
138
|
const list = this.list as List
|
|
169
139
|
|
|
170
140
|
list.goToCommentDelay = false
|
|
@@ -224,7 +194,7 @@ export default class Context implements ContextApi {
|
|
|
224
194
|
this.$root.querySelectorAll<HTMLElement>(`[atk-only-admin-show]`).forEach(item => items.push(item))
|
|
225
195
|
|
|
226
196
|
// for layer
|
|
227
|
-
const { $wrap: $layerWrap } =
|
|
197
|
+
const { $wrap: $layerWrap } = getLayerWrap()
|
|
228
198
|
if ($layerWrap) $layerWrap.querySelectorAll<HTMLElement>(`[atk-only-admin-show]`).forEach(item => items.push(item))
|
|
229
199
|
|
|
230
200
|
// for sidebar
|
|
@@ -258,49 +228,22 @@ export default class Context implements ContextApi {
|
|
|
258
228
|
}
|
|
259
229
|
|
|
260
230
|
/* i18n */
|
|
261
|
-
public $t(key:
|
|
262
|
-
return
|
|
231
|
+
public $t(key: I18n.I18nKeys, args: {[key: string]: string} = {}): string {
|
|
232
|
+
return I18n.t(key, args)
|
|
263
233
|
}
|
|
264
234
|
|
|
265
235
|
public setDarkMode(darkMode: boolean): void {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
const darkModeClassName = 'atk-dark-mode'
|
|
269
|
-
|
|
270
|
-
this.conf.darkMode = darkMode
|
|
271
|
-
|
|
272
|
-
if (darkMode) this.$root.classList.add(darkModeClassName)
|
|
273
|
-
else this.$root.classList.remove(darkModeClassName)
|
|
274
|
-
|
|
275
|
-
// for Layer
|
|
276
|
-
const { $wrap: $layerWrap } = GetLayerWrap(this)
|
|
277
|
-
if ($layerWrap) {
|
|
278
|
-
if (darkMode) $layerWrap.classList.add(darkModeClassName)
|
|
279
|
-
else $layerWrap.classList.remove(darkModeClassName)
|
|
280
|
-
}
|
|
236
|
+
DarkMode.setDarkMode(this, darkMode)
|
|
281
237
|
}
|
|
282
238
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
if (this.conf.darkMode === 'auto') {
|
|
287
|
-
// 自动切换暗黑模式,事件监听
|
|
288
|
-
this.setDarkMode(this.darkModeMedia.matches)
|
|
289
|
-
if (!this.darkModeAutoFunc) {
|
|
290
|
-
this.darkModeAutoFunc = (evt) => { this.setDarkMode(evt.matches) }
|
|
291
|
-
this.darkModeMedia.addEventListener('change', this.darkModeAutoFunc)
|
|
292
|
-
}
|
|
293
|
-
} else {
|
|
294
|
-
if (this.darkModeAutoFunc) {
|
|
295
|
-
// 解除事件监听绑定
|
|
296
|
-
this.darkModeMedia.removeEventListener('change', this.darkModeAutoFunc)
|
|
297
|
-
}
|
|
298
|
-
this.setDarkMode(this.conf.darkMode || false)
|
|
299
|
-
}
|
|
239
|
+
public updateConf(nConf: Partial<ArtalkConfig>): void {
|
|
240
|
+
this.conf = Utils.mergeDeep(this.conf, nConf)
|
|
241
|
+
this.trigger('conf-loaded')
|
|
300
242
|
}
|
|
301
243
|
|
|
302
|
-
public
|
|
303
|
-
|
|
304
|
-
this.trigger('conf-loaded')
|
|
244
|
+
public getMarkedInstance() {
|
|
245
|
+
return marked.getInstance()
|
|
305
246
|
}
|
|
306
247
|
}
|
|
248
|
+
|
|
249
|
+
export default Context
|
package/src/defaults.ts
CHANGED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { CommentData } from '~/types/artalk-data'
|
|
2
|
+
import * as Utils from '../lib/utils'
|
|
3
|
+
import Editor from './editor'
|
|
4
|
+
|
|
5
|
+
export interface EditModeManager {
|
|
6
|
+
editor: Editor
|
|
7
|
+
comment?: CommentData
|
|
8
|
+
setEdit: (comment: CommentData, $comment: HTMLElement) => void
|
|
9
|
+
cancelEdit: () => void
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function createEditModeManager(editor: Editor) {
|
|
13
|
+
const m: EditModeManager = {
|
|
14
|
+
editor,
|
|
15
|
+
comment: undefined,
|
|
16
|
+
setEdit: (c, e) => edit(m, c, e),
|
|
17
|
+
cancelEdit: () => cancelEdit(m)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
initEditModeSubmit(m)
|
|
21
|
+
|
|
22
|
+
return m
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function edit(m: EditModeManager, comment: CommentData, $comment: HTMLElement) {
|
|
26
|
+
cancelEdit(m)
|
|
27
|
+
m.editor.cancelReply()
|
|
28
|
+
|
|
29
|
+
const ui = m.editor.getUI()
|
|
30
|
+
if (!ui.$editCancelBtn) {
|
|
31
|
+
const $btn = Utils.createElement(
|
|
32
|
+
`<div class="atk-send-reply">` +
|
|
33
|
+
`${m.editor.$t('editCancel')} ` +
|
|
34
|
+
`<span class="atk-cancel">×</span>` +
|
|
35
|
+
`</div>`
|
|
36
|
+
)
|
|
37
|
+
$btn.onclick = () => {
|
|
38
|
+
cancelEdit(m)
|
|
39
|
+
}
|
|
40
|
+
ui.$textareaWrap.append($btn)
|
|
41
|
+
ui.$editCancelBtn = $btn
|
|
42
|
+
}
|
|
43
|
+
m.comment = comment
|
|
44
|
+
|
|
45
|
+
ui.$header.style.display = 'none' // TODO support modify header information
|
|
46
|
+
|
|
47
|
+
m.editor.travel($comment)
|
|
48
|
+
|
|
49
|
+
ui.$nick.value = comment.nick || ''
|
|
50
|
+
ui.$email.value = comment.email || ''
|
|
51
|
+
ui.$link.value = comment.link || ''
|
|
52
|
+
|
|
53
|
+
m.editor.setContent(comment.content)
|
|
54
|
+
ui.$textarea.focus()
|
|
55
|
+
|
|
56
|
+
m.editor.refreshSendBtnText()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function cancelEdit(m: EditModeManager) {
|
|
60
|
+
if (!m.comment) return
|
|
61
|
+
|
|
62
|
+
const ui = m.editor.getUI()
|
|
63
|
+
|
|
64
|
+
if (ui.$editCancelBtn) {
|
|
65
|
+
ui.$editCancelBtn.remove()
|
|
66
|
+
ui.$editCancelBtn = undefined
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
m.comment = undefined
|
|
70
|
+
m.editor.travelBack()
|
|
71
|
+
|
|
72
|
+
const { nick, email, link } = m.editor.ctx.user.data
|
|
73
|
+
ui.$nick.value = nick
|
|
74
|
+
ui.$email.value = email
|
|
75
|
+
ui.$link.value = link
|
|
76
|
+
|
|
77
|
+
m.editor.setContent('')
|
|
78
|
+
m.editor.refreshSendBtnText()
|
|
79
|
+
|
|
80
|
+
ui.$header.style.display = '' // TODO support modify header information
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function initEditModeSubmit(m: EditModeManager) {
|
|
84
|
+
m.editor.getSubmitManager()!.registerCustom({
|
|
85
|
+
activeCond: () => !!m.comment, // active this custom submit when edit mode
|
|
86
|
+
req: async () => {
|
|
87
|
+
const saveData = {
|
|
88
|
+
content: m.editor.getFinalContent(),
|
|
89
|
+
nick: m.editor.getUI().$nick.value,
|
|
90
|
+
email: m.editor.getUI().$email.value,
|
|
91
|
+
link: m.editor.getUI().$link.value,
|
|
92
|
+
}
|
|
93
|
+
const nComment = await m.editor.ctx.getApi().comment.commentEdit({
|
|
94
|
+
...m.comment, ...saveData
|
|
95
|
+
})
|
|
96
|
+
return nComment
|
|
97
|
+
},
|
|
98
|
+
post: (nComment: CommentData) => {
|
|
99
|
+
m.editor.ctx.updateComment(nComment)
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
}
|