artalk 2.4.4 → 2.5.1

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.
Files changed (156) hide show
  1. package/README.md +27 -0
  2. package/dist/Artalk.css +1 -1
  3. package/dist/Artalk.es.js +7066 -6256
  4. package/dist/Artalk.iife.js +1 -1
  5. package/dist/Artalk.js +1 -1
  6. package/dist/ArtalkLite.css +1 -1
  7. package/dist/ArtalkLite.es.js +5095 -4396
  8. package/dist/ArtalkLite.iife.js +1 -1
  9. package/dist/ArtalkLite.js +1 -1
  10. package/dist/i18n/jp.js +1 -1
  11. package/dist/i18n/zh-TW.js +1 -1
  12. package/dist/src/api/index.d.ts +2 -2
  13. package/dist/src/api/system.d.ts +5 -2
  14. package/dist/src/artalk.d.ts +42 -34
  15. package/dist/src/comment/comment.d.ts +2 -2
  16. package/dist/src/comment/height-limit.d.ts +30 -0
  17. package/dist/src/comment/render-ctx.d.ts +23 -0
  18. package/dist/src/comment/render.d.ts +2 -50
  19. package/dist/src/comment/renders/actions.d.ts +5 -0
  20. package/dist/src/comment/renders/avatar.d.ts +5 -0
  21. package/dist/src/comment/renders/content.d.ts +5 -0
  22. package/dist/src/comment/renders/header.d.ts +5 -0
  23. package/dist/src/comment/renders/index.d.ts +2 -0
  24. package/dist/src/comment/renders/pending.d.ts +5 -0
  25. package/dist/src/comment/renders/reply-at.d.ts +5 -0
  26. package/dist/src/comment/renders/reply-to.d.ts +5 -0
  27. package/dist/src/components/dialog.d.ts +1 -1
  28. package/dist/src/config.d.ts +8 -0
  29. package/dist/src/context.d.ts +16 -29
  30. package/dist/src/editor/edit-mode.d.ts +9 -0
  31. package/dist/src/editor/editor.d.ts +37 -45
  32. package/dist/src/editor/funcs.d.ts +2 -0
  33. package/dist/src/editor/mover.d.ts +8 -0
  34. package/dist/src/editor/plug-manager.d.ts +17 -0
  35. package/dist/src/editor/plugs/editor-plug.d.ts +13 -1
  36. package/dist/src/editor/reply.d.ts +9 -0
  37. package/dist/src/editor/submit.d.ts +16 -0
  38. package/dist/src/editor/ui.d.ts +25 -0
  39. package/dist/src/i18n/external.d.ts +70 -0
  40. package/dist/src/i18n/index.d.ts +11 -75
  41. package/dist/src/layer/index.d.ts +2 -2
  42. package/dist/src/layer/layer.d.ts +1 -1
  43. package/dist/src/lib/checker/captcha-renders.d.ts +5 -0
  44. package/dist/src/lib/checker/index.d.ts +16 -10
  45. package/dist/src/lib/dark-mode.d.ts +4 -0
  46. package/dist/src/lib/marked.d.ts +9 -0
  47. package/dist/src/lib/user.d.ts +6 -3
  48. package/dist/src/lib/utils.d.ts +1 -5
  49. package/dist/src/list/list-lite.d.ts +15 -11
  50. package/dist/src/list/list-nest.d.ts +2 -2
  51. package/dist/src/list/list-ui.d.ts +16 -0
  52. package/dist/src/list/list.d.ts +0 -1
  53. package/dist/src/list/paginator/adaptors/index.d.ts +12 -0
  54. package/dist/src/list/paginator/adaptors/pagination.d.ts +9 -0
  55. package/dist/src/list/paginator/adaptors/read-more.d.ts +10 -0
  56. package/dist/src/list/paginator/holder.d.ts +16 -0
  57. package/dist/src/list/paginator/index.d.ts +10 -0
  58. package/dist/src/service.d.ts +46 -0
  59. package/dist/types/artalk-config.d.ts +5 -2
  60. package/dist/types/artalk-data.d.ts +3 -3
  61. package/dist/types/context.d.ts +13 -20
  62. package/dist/types/global.d.ts +1 -0
  63. package/package.json +6 -7
  64. package/src/api/comment.ts +7 -7
  65. package/src/api/page.ts +0 -2
  66. package/src/api/request.ts +6 -4
  67. package/src/api/system.ts +3 -12
  68. package/src/api/upload.ts +3 -4
  69. package/src/api/user.ts +6 -8
  70. package/src/artalk.ts +103 -158
  71. package/src/comment/comment.ts +7 -6
  72. package/src/comment/height-limit.ts +100 -0
  73. package/src/comment/render-ctx.ts +30 -0
  74. package/src/comment/render.ts +31 -375
  75. package/src/comment/renders/actions.ts +114 -0
  76. package/src/comment/renders/avatar.ts +22 -0
  77. package/src/comment/renders/content.ts +39 -0
  78. package/src/comment/renders/header.ts +84 -0
  79. package/src/comment/renders/index.ts +19 -0
  80. package/src/comment/renders/pending.ts +12 -0
  81. package/src/comment/renders/reply-at.ts +16 -0
  82. package/src/comment/renders/reply-to.ts +24 -0
  83. package/src/config.ts +65 -0
  84. package/src/context.ts +33 -86
  85. package/src/defaults.ts +1 -0
  86. package/src/editor/edit-mode.ts +103 -0
  87. package/src/editor/editor.ts +101 -395
  88. package/src/editor/funcs.ts +97 -0
  89. package/src/editor/mover.ts +43 -0
  90. package/src/editor/plug-manager.ts +172 -0
  91. package/src/editor/plugs/editor-plug.ts +20 -2
  92. package/src/editor/plugs/{emoticons-plug.less → emoticons-plug.scss} +5 -4
  93. package/src/editor/plugs/emoticons-plug.ts +2 -1
  94. package/src/editor/plugs/header-input-plug.ts +11 -10
  95. package/src/editor/plugs/{preview-plug.less → preview-plug.scss} +1 -1
  96. package/src/editor/plugs/preview-plug.ts +8 -4
  97. package/src/editor/plugs/upload-plug.ts +12 -14
  98. package/src/editor/reply.ts +62 -0
  99. package/src/editor/submit.ts +99 -0
  100. package/src/editor/ui.ts +36 -0
  101. package/src/i18n/external.ts +10 -0
  102. package/src/i18n/index.ts +22 -19
  103. package/src/i18n/jp.ts +1 -1
  104. package/src/i18n/zh-TW.ts +1 -1
  105. package/src/layer/index.ts +2 -2
  106. package/src/layer/layer.ts +4 -4
  107. package/src/layer/sidebar-layer.ts +4 -2
  108. package/src/lib/checker/admin.ts +37 -0
  109. package/src/lib/checker/captcha-renders.ts +68 -0
  110. package/src/lib/checker/captcha.ts +26 -0
  111. package/src/lib/checker/index.ts +36 -20
  112. package/src/lib/dark-mode.ts +48 -0
  113. package/src/lib/marked.ts +133 -0
  114. package/src/lib/user.ts +13 -7
  115. package/src/lib/utils.ts +1 -123
  116. package/src/list/list-lite.ts +59 -162
  117. package/src/list/list-ui.ts +137 -0
  118. package/src/list/list.ts +33 -60
  119. package/src/list/paginator/adaptors/index.ts +21 -0
  120. package/src/list/paginator/adaptors/pagination.ts +42 -0
  121. package/src/list/paginator/adaptors/read-more.ts +56 -0
  122. package/src/list/paginator/holder.ts +56 -0
  123. package/src/list/paginator/index.ts +13 -0
  124. package/src/service.ts +118 -0
  125. package/src/style/{_color.less → _color.scss} +3 -3
  126. package/src/style/{_extend.less → _extend.scss} +1 -1
  127. package/src/style/_variables.scss +3 -0
  128. package/src/style/{comment.less → comment.scss} +36 -12
  129. package/src/style/{editor.less → editor.scss} +11 -11
  130. package/src/style/{list.less → list.scss} +12 -10
  131. package/src/style/{main.less → main.scss} +24 -23
  132. package/src/style/{sidebar-layer.less → sidebar-layer.scss} +8 -8
  133. package/types/artalk-config.d.ts +5 -2
  134. package/types/artalk-data.d.ts +3 -3
  135. package/types/context.d.ts +13 -20
  136. package/types/global.d.ts +1 -0
  137. package/CNAME +0 -1
  138. package/LICENSE +0 -165
  139. package/index.html +0 -157
  140. package/postcss.config.js +0 -5
  141. package/scripts/build-i18n.mjs +0 -46
  142. package/src/lib/checker/admin-checker.ts +0 -36
  143. package/src/lib/checker/captcha-checker.ts +0 -84
  144. package/src/style/_variables.less +0 -3
  145. package/test/ArtalkCount.html +0 -16
  146. package/test/ArtalkI18n.html +0 -16
  147. package/test/ArtalkLite.html +0 -15
  148. package/test/CSRF.html +0 -26
  149. package/tsconfig.json +0 -12
  150. package/vite-base.config.ts +0 -21
  151. package/vite-demo.config.ts +0 -12
  152. package/vite-lite.config.ts +0 -22
  153. package/vite.config.ts +0 -27
  154. /package/dist/src/lib/checker/{admin-checker.d.ts → admin.d.ts} +0 -0
  155. /package/dist/src/lib/checker/{captcha-checker.d.ts → captcha.d.ts} +0 -0
  156. /package/src/style/{pagination.less → pagination.scss} +0 -0
package/package.json CHANGED
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "name": "artalk",
3
- "version": "2.4.4",
4
- "backendMinVersion": "2.3.0",
3
+ "version": "2.5.1",
5
4
  "license": "LGPL-3.0",
6
5
  "description": "A Selfhosted Comment System",
7
6
  "keywords": [
@@ -14,7 +13,7 @@
14
13
  "repository": {
15
14
  "type": "git",
16
15
  "url": "https://github.com/ArtalkJS/Artalk.git",
17
- "directory": "packages/artalk"
16
+ "directory": "ui/packages/artalk"
18
17
  },
19
18
  "homepage": "https://artalk.js.org",
20
19
  "bugs": "https://github.com/ArtalkJS/Artalk/issue",
@@ -24,17 +23,17 @@
24
23
  "module": "./dist/Artalk.es.js",
25
24
  "types": "./dist/src/main.d.ts",
26
25
  "devDependencies": {
27
- "@types/marked": "4.0.6"
26
+ "@types/marked": "4.0.8"
28
27
  },
29
28
  "dependencies": {
29
+ "abortcontroller-polyfill": "^1.7.5",
30
30
  "hanabi": "^0.4.0",
31
31
  "insane": "^2.6.2",
32
- "marked": "^4.0.19",
33
- "abortcontroller-polyfill": "^1.7.5"
32
+ "marked": "^4.2.12"
34
33
  },
35
34
  "scripts": {
36
35
  "dev": "vite",
37
- "build": "pnpm lint && vite build && pnpm build:lite && pnpm build:i18n",
36
+ "build": "vite build && pnpm build:lite && pnpm build:i18n",
38
37
  "build:lite": "vite build --config vite-lite.config.ts",
39
38
  "build:i18n": "node scripts/build-i18n.mjs",
40
39
  "build:demo": "vite build --config vite-demo.config.ts",
@@ -1,6 +1,7 @@
1
1
  import { CommentData, ListData } from '~/types/artalk-data'
2
2
  import * as Utils from '../lib/utils'
3
3
  import ApiBase from './api-base'
4
+ import User from '../lib/user'
4
5
 
5
6
  /**
6
7
  * 评论 API
@@ -16,9 +17,9 @@ export default class CommentApi extends ApiBase {
16
17
  }
17
18
 
18
19
  if (flatMode) params.flat_mode = flatMode // 平铺模式
19
- if (this.ctx.user.checkHasBasicUserInfo()) {
20
- params.name = this.ctx.user.data.nick
21
- params.email = this.ctx.user.data.email
20
+ if (User.checkHasBasicUserInfo()) {
21
+ params.name = User.data.nick
22
+ params.email = User.data.email
22
23
  }
23
24
 
24
25
  if (paramsEditor) paramsEditor(params)
@@ -68,14 +69,13 @@ export default class CommentApi extends ApiBase {
68
69
  /** 投票 */
69
70
  public async vote(targetID: number, type: string) {
70
71
  const params: any = {
71
- site_name: this.ctx.conf.site || '',
72
72
  target_id: targetID,
73
73
  type,
74
74
  }
75
75
 
76
- if (this.ctx.user.checkHasBasicUserInfo()) {
77
- params.name = this.ctx.user.data.nick
78
- params.email = this.ctx.user.data.email
76
+ if (User.checkHasBasicUserInfo()) {
77
+ params.name = User.data.nick
78
+ params.email = User.data.email
79
79
  }
80
80
 
81
81
  const data = await this.POST<any>('/vote', params)
package/src/api/page.ts CHANGED
@@ -55,7 +55,6 @@ export default class PageApi extends ApiBase {
55
55
  /** PV */
56
56
  public async pv() {
57
57
  const params: any = {
58
- site_name: this.ctx.conf.site || '',
59
58
  page_key: this.ctx.conf.pageKey || '',
60
59
  page_title: this.ctx.conf.pageTitle || ''
61
60
  }
@@ -73,7 +72,6 @@ export default class PageApi extends ApiBase {
73
72
  ) {
74
73
  const params: any = {
75
74
  type,
76
- site_name: this.ctx.conf.site || '',
77
75
  }
78
76
 
79
77
  if (pageKeys) params.page_keys = Array.isArray(pageKeys) ? pageKeys.join(',') : pageKeys
@@ -1,11 +1,13 @@
1
1
  import Context from '~/types/context'
2
+ import User from '../lib/user'
3
+ import $t from '../i18n'
2
4
 
3
5
  /** 公共请求函数 */
4
6
  export async function Fetch(ctx: Context, input: RequestInfo, init: RequestInit, timeout?: number): Promise<any> {
5
7
  // JWT
6
- if (ctx.user.data.token) {
8
+ if (User.data.token) {
7
9
  const headers = new Headers(init.headers) // 保留原有 headers
8
- headers.set('Authorization', `Bearer ${ctx.user.data.token}`)
10
+ headers.set('Authorization', `Bearer ${User.data.token}`)
9
11
  init.headers = headers
10
12
  }
11
13
 
@@ -20,7 +22,7 @@ export async function Fetch(ctx: Context, input: RequestInfo, init: RequestInit,
20
22
  const isNoAccess = noAccessCodes.includes(respHttpCode)
21
23
 
22
24
  if (!resp.ok && !isNoAccess)
23
- throw new Error(`${ctx.$t('reqGot')} ${respHttpCode}`)
25
+ throw new Error(`${$t('reqGot')} ${respHttpCode}`)
24
26
 
25
27
  // 解析获取响应的 json
26
28
  let json: any = await resp.json()
@@ -95,7 +97,7 @@ function timeoutFetch(ctx: Context, url: RequestInfo, ms: number, opts: RequestI
95
97
  promise.finally(() => { clearTimeout(timer) })
96
98
  }
97
99
  promise = promise.catch((err) => {
98
- throw ((err || {}).name === 'AbortError') ? new Error(ctx.$t('reqAborted')) : err
100
+ throw ((err || {}).name === 'AbortError') ? new Error($t('reqAborted')) : err
99
101
  })
100
102
  return promise
101
103
  }
package/src/api/system.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import ArtalkConfig from '~/types/artalk-config'
2
2
  import ApiBase from './api-base'
3
+ import { handleBackendRefConf } from '../config'
3
4
 
4
5
  /**
5
6
  * 系统 API
@@ -10,22 +11,12 @@ export default class SystemApi extends ApiBase {
10
11
  const data = await this.POST<any>(`/conf`)
11
12
  const conf = (data.frontend_conf || {}) as ArtalkConfig
12
13
 
13
- // Patch: `emoticons` config string to json
14
- if (conf.emoticons && typeof conf.emoticons === "string") {
15
- conf.emoticons = conf.emoticons.trim()
16
- if (conf.emoticons.startsWith("[") || conf.emoticons.startsWith("{")) {
17
- conf.emoticons = JSON.parse(conf.emoticons) // pase json
18
- } else if (conf.emoticons === "false") {
19
- conf.emoticons = false
20
- }
21
- }
22
-
23
- return conf
14
+ return handleBackendRefConf(conf)
24
15
  }
25
16
 
26
17
  /** 获取配置数据 */
27
18
  public async getSettings() {
28
- const data = await this.POST<string>('/admin/setting-get')
19
+ const data = await this.POST<{custom: string, template: string}>('/admin/setting-get')
29
20
  return data
30
21
  }
31
22
 
package/src/api/upload.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { ToFormData } from './request'
2
2
  import ApiBase from './api-base'
3
+ import User from '../lib/user'
3
4
 
4
5
  /**
5
6
  * 上传 API
@@ -8,13 +9,11 @@ export default class UploadApi extends ApiBase {
8
9
  /** 图片上传 */
9
10
  public async imgUpload(file: File) {
10
11
  const params: any = {
11
- name: this.ctx.user.data.nick,
12
- email: this.ctx.user.data.email,
12
+ name: User.data.nick,
13
+ email: User.data.email,
13
14
  page_key: this.ctx.conf.pageKey,
14
15
  }
15
16
 
16
- if (this.ctx.conf.site) params.site_name = this.ctx.conf.site
17
-
18
17
  const form = ToFormData(params)
19
18
  form.set('file', file)
20
19
 
package/src/api/user.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { UserData, NotifyData, UserDataForAdmin } from '~/types/artalk-data'
2
2
  import ApiBase from './api-base'
3
3
  import { ToFormData } from './request'
4
+ import User from '../lib/user'
4
5
 
5
6
  /**
6
7
  * 用户 API
@@ -12,8 +13,6 @@ export default class UserApi extends ApiBase {
12
13
  name, email, password
13
14
  }
14
15
 
15
- if (this.ctx.conf.site) params.site_name = this.ctx.conf.site
16
-
17
16
  const data = await this.POST<any>('/login', params)
18
17
  return data as { token: string, user: UserData }
19
18
  }
@@ -22,7 +21,7 @@ export default class UserApi extends ApiBase {
22
21
  public userGet(name: string, email: string) {
23
22
  const ctrl = new AbortController()
24
23
  const params: any = {
25
- name, email, site_name: (this.ctx.conf.site || '')
24
+ name, email
26
25
  }
27
26
 
28
27
  const req = this.Fetch(`/user-get`, {
@@ -45,8 +44,8 @@ export default class UserApi extends ApiBase {
45
44
  /** 用户 · 登录状态 */
46
45
  public async loginStatus() {
47
46
  const data = await this.POST<any>('/login-status', {
48
- name: this.ctx.user.data.nick,
49
- email: this.ctx.user.data.email
47
+ name: User.data.nick,
48
+ email: User.data.email
50
49
  })
51
50
  return (data || { is_login: false, is_admin: false }) as { is_login: boolean, is_admin: boolean }
52
51
  }
@@ -59,15 +58,14 @@ export default class UserApi extends ApiBase {
59
58
  /** 已读标记 */
60
59
  public markRead(notifyKey: string, readAll = false) {
61
60
  const params: any = {
62
- site_name: this.ctx.conf.site || '',
63
61
  notify_key: notifyKey,
64
62
  }
65
63
 
66
64
  if (readAll) {
67
65
  delete params.notify_key
68
66
  params.read_all = true
69
- params.name = this.ctx.user.data.nick
70
- params.email = this.ctx.user.data.email
67
+ params.name = User.data.nick
68
+ params.email = User.data.email
71
69
  }
72
70
 
73
71
  return this.POST(`/mark-read`, params)
package/src/artalk.ts CHANGED
@@ -1,29 +1,25 @@
1
- import './style/main.less'
1
+ import './style/main.scss'
2
2
 
3
- import ArtalkConfig from '~/types/artalk-config'
4
- import { EventPayloadMap, Handler } from '~/types/event'
5
- import ArtalkPlug from '~/types/plug'
6
- import Context from '~/types/context'
3
+ import type ArtalkConfig from '~/types/artalk-config'
4
+ import type { EventPayloadMap, Handler } from '~/types/event'
5
+ import type ArtalkPlug from '~/types/plug'
6
+ import type Context from '~/types/context'
7
7
  import ConcreteContext from './context'
8
8
  import defaults from './defaults'
9
-
10
- import ListLite from './list/list-lite'
11
- import CheckerLauncher from './lib/checker'
12
- import Editor from './editor'
13
- import List from './list'
14
- import SidebarLayer from './layer/sidebar-layer'
15
-
16
- import Layer from './layer'
17
- import * as Utils from './lib/utils'
18
- import * as Ui from './lib/ui'
9
+ import { handelBaseConf } from './config'
10
+ import Services from './service'
19
11
  import * as Stat from './lib/stat'
12
+ import ListLite from './list/list-lite'
13
+ import Api from './api'
20
14
 
21
15
  /**
22
16
  * Artalk
23
17
  *
24
- * @website https://artalk.js.org
18
+ * @see https://artalk.js.org
25
19
  */
26
20
  export default class Artalk {
21
+ private static instance?: Artalk
22
+
27
23
  public static ListLite = ListLite
28
24
  public static readonly defaults: ArtalkConfig = defaults
29
25
 
@@ -31,192 +27,141 @@ export default class Artalk {
31
27
  public ctx!: Context
32
28
  public $root!: HTMLElement
33
29
 
34
- /** Plugins (in global scope) */
35
- protected static Plugins: ArtalkPlug[] = [ Stat.PvCountWidget ]
36
-
37
- /** Plugins (in a instance scope) */
38
- protected instancePlugins: ArtalkPlug[] = []
39
-
40
- /** 禁用的组件 */
30
+ /** Plugins */
31
+ protected static plugins: ArtalkPlug[] = [ Stat.PvCountWidget ]
41
32
  public static DisabledComponents: string[] = []
42
33
 
43
- constructor(customConf: Partial<ArtalkConfig>) {
44
- /* 初始化基本配置 */
45
- this.conf = Artalk.HandelBaseConf(customConf)
34
+ constructor(conf: Partial<ArtalkConfig>) {
35
+ if (Artalk.instance) return Artalk.instance.update(conf)
36
+
37
+ // 初始化基本配置
38
+ this.conf = handelBaseConf(conf)
46
39
  if (this.conf.el instanceof HTMLElement) this.$root = this.conf.el
47
40
 
48
- /* 初始化 Context */
41
+ // 初始化 Context
49
42
  this.ctx = new ConcreteContext(this.conf, this.$root)
50
43
 
51
- /* 初始化组件 */
52
- this.initComponents()
53
- }
54
-
55
- /** 组件初始化 */
56
- private initComponents() {
57
- this.initLocale()
58
- this.initLayer()
59
- Utils.initMarked(this.ctx)
60
-
61
- const Components: { [name: string]: () => void } = {
62
- // CheckerLauncher
63
- checkerLauncher: () => {
64
- const checkerLauncher = new CheckerLauncher(this.ctx)
65
- this.ctx.setCheckerLauncher(checkerLauncher)
66
- },
67
-
68
- // 编辑器
69
- editor: () => {
70
- const editor = new Editor(this.ctx)
71
- this.ctx.setEditor(editor)
72
- this.$root.appendChild(editor.$el)
73
- },
74
-
75
- // 评论列表
76
- list: () => {
77
- // 评论列表
78
- const list = new List(this.ctx)
79
- this.ctx.setList(list)
80
- this.$root.appendChild(list.$el)
81
-
82
- // 评论获取
83
- list.fetchComments(0)
84
- },
85
-
86
- // 侧边栏 Layer
87
- sidebarLayer: () => {
88
- const sidebarLayer = new SidebarLayer(this.ctx)
89
- this.ctx.setSidebarLayer(sidebarLayer)
90
- },
91
-
92
- // 默认事件绑定
93
- eventsDefault: () => {
94
- this.initEventBind()
95
- }
96
- }
97
-
98
- // 组件初始化
99
- Object.entries(Components).forEach(([name, initComponent]) => {
44
+ // 内建服务初始化
45
+ Object.entries(Services).forEach(([name, initService]) => {
100
46
  if (Artalk.DisabledComponents.includes(name)) return
101
-
102
- initComponent()
47
+ const obj = initService(this.ctx)
48
+ if (obj) this.ctx.inject(name as any, obj) // auto inject deps to ctx
103
49
  })
104
50
 
105
51
  // 插件初始化 (global scope)
106
- Artalk.Plugins.forEach(plugin => {
52
+ Artalk.plugins.forEach(plugin => {
107
53
  if (typeof plugin === 'function')
108
54
  plugin(this.ctx)
109
55
  })
110
56
  }
111
57
 
112
- /** 基本配置初始化 */
113
- public static HandelBaseConf(customConf: Partial<ArtalkConfig>): ArtalkConfig {
114
- // 合并默认配置
115
- const conf: ArtalkConfig = Utils.mergeDeep(Artalk.defaults, customConf)
116
-
117
- // 绑定元素
118
- if (typeof conf.el === 'string' && !!conf.el) {
119
- try {
120
- const findEl = document.querySelector<HTMLElement>(conf.el)
121
- if (!findEl) throw Error(`Target element "${conf.el}" was not found.`)
122
- conf.el = findEl
123
- } catch (e) {
124
- console.error(e)
125
- throw new Error('Please check your Artalk `el` config.')
126
- }
127
- }
128
-
129
- // 服务器配置
130
- conf.server = conf.server.replace(/\/$/, '').replace(/\/api\/?$/, '')
131
-
132
- // 默认 pageKey
133
- if (!conf.pageKey) {
134
- // @link http://bl.ocks.org/abernier/3070589
135
- conf.pageKey = `${window.location.pathname}`
136
- }
137
-
138
- // 默认 pageTitle
139
- if (!conf.pageTitle) {
140
- conf.pageTitle = `${document.title}`
141
- }
142
-
143
- return conf
144
- }
145
-
146
- /** 事件绑定初始化 */
147
- private initEventBind() {
148
- // 锚点快速跳转评论
149
- window.addEventListener('hashchange', () => {
150
- this.ctx.listHashGotoCheck()
151
- })
58
+ /** Init Artalk */
59
+ public static init(conf: Partial<ArtalkConfig>): Artalk {
60
+ if (this.instance) return this.instance.update(conf) // 单例模式
61
+ this.instance = new Artalk(conf)
62
+ return this.instance
63
+ }
152
64
 
153
- // 本地用户数据变更
154
- this.ctx.on('user-changed', () => {
155
- this.ctx.checkAdminShowEl()
156
- this.ctx.listRefreshUI()
157
- })
65
+ /** Use Plugin (plugin will be called in instance `use` func) */
66
+ public use(plugin: ArtalkPlug) {
67
+ Artalk.plugins.push(plugin)
68
+ if (typeof plugin === 'function') plugin(this.ctx)
158
69
  }
159
70
 
160
- /** 语言初始化 */
161
- private initLocale() {
162
- if (this.conf.locale === 'auto') { // 自适应语言
163
- this.conf.locale = navigator.language
164
- }
71
+ /** Update config of Artalk */
72
+ public update(conf: Partial<ArtalkConfig>) {
73
+ if (!Artalk.instance) throw Error('cannot call `update` function before call `load`')
74
+ Artalk.instance.ctx.updateConf(conf)
75
+ return Artalk.instance
165
76
  }
166
77
 
167
- /** Layer 初始化 */
168
- private initLayer() {
169
- // 记录页面原始 Styles
170
- Layer.BodyOrgOverflow = document.body.style.overflow
171
- Layer.BodyOrgPaddingRight = document.body.style.paddingRight
78
+ /** Reload comment list of Artalk */
79
+ public reload() {
80
+ this.ctx.listReload()
172
81
  }
173
82
 
174
- /** 监听事件 */
83
+ /** Destroy instance of Artalk */
84
+ public destroy() {
85
+ if (!Artalk.instance) throw Error('cannot call `destroy` function before call `load`')
86
+ Artalk.instance.$root.remove()
87
+ delete Artalk.instance
88
+ }
89
+
90
+ /** Add an event listener */
175
91
  public on<K extends keyof EventPayloadMap>(name: K, handler: Handler<EventPayloadMap[K]>) {
176
92
  this.ctx.on(name, handler, 'external')
177
93
  }
178
94
 
179
- /** 解除监听事件 */
95
+ /** Remove an event listener */
180
96
  public off<K extends keyof EventPayloadMap>(name: K, handler: Handler<EventPayloadMap[K]>) {
181
97
  this.ctx.off(name, handler, 'external')
182
98
  }
183
99
 
184
- /** 触发事件 */
100
+ /** Trigger an event */
185
101
  public trigger<K extends keyof EventPayloadMap>(name: K, payload?: EventPayloadMap[K]) {
186
102
  this.ctx.trigger(name, payload, 'external')
187
103
  }
188
104
 
189
- /** 重新加载 */
190
- public reload() {
191
- this.ctx.listReload()
192
- }
193
-
194
- /** 设置暗黑模式 */
105
+ /** Set dark mode */
195
106
  public setDarkMode(darkMode: boolean) {
196
107
  this.ctx.setDarkMode(darkMode)
197
108
  }
198
109
 
199
- /** Use Plugin (specific instance) */
200
- public use(plugin: ArtalkPlug) {
201
- this.instancePlugins.push(plugin)
202
- if (typeof plugin === 'function') plugin(this.ctx)
203
- }
110
+ // ===========================
111
+ // Static methods
112
+ // ===========================
204
113
 
205
- /** Use Plugin (static method for global scope) */
114
+ /** Use Plugin (static method) */
206
115
  public static use(plugin: ArtalkPlug) {
207
- this.Plugins.push(plugin)
116
+ this.plugins.push(plugin)
117
+ if (this.instance && typeof plugin === 'function') plugin(this.instance.ctx)
208
118
  }
209
119
 
210
- /** @deprecated Please replace it with lowercase function name `use(...)`. */
211
- public static Use(plugin: ArtalkPlug) {
212
- this.use(plugin)
213
- console.warn('`Use(...)` is deprecated, replace it with lowercase `use(...)`.')
120
+ /** Update config of Artalk */
121
+ public static update(conf: Partial<ArtalkConfig>) {
122
+ return this.instance?.update(conf)
214
123
  }
215
124
 
216
- /** 装载数量统计元素 */
217
- public static LoadCountWidget(customConf: Partial<ArtalkConfig>) {
218
- const conf = this.HandelBaseConf(customConf)
219
- const ctx = new ConcreteContext(conf)
125
+ /** Reload comment list of Artalk */
126
+ public static reload() {
127
+ this.instance?.reload()
128
+ }
129
+
130
+ /** Destroy instance of Artalk */
131
+ public static destroy() {
132
+ this.instance?.destroy()
133
+ }
134
+
135
+ /** Add an event listener */
136
+ public static on<K extends keyof EventPayloadMap>(name: K, handler: Handler<EventPayloadMap[K]>) {
137
+ this.instance?.on(name, handler)
138
+ }
139
+
140
+ /** Remove an event listener */
141
+ public static off<K extends keyof EventPayloadMap>(name: K, handler: Handler<EventPayloadMap[K]>) {
142
+ this.instance?.off(name, handler)
143
+ }
144
+
145
+ /** Trigger an event */
146
+ public static trigger<K extends keyof EventPayloadMap>(name: K, payload?: EventPayloadMap[K]) {
147
+ this.instance?.trigger(name, payload)
148
+ }
149
+
150
+ /** Set dark mode */
151
+ public static setDarkMode(darkMode: boolean) {
152
+ this.instance?.setDarkMode(darkMode)
153
+ }
154
+
155
+ /** Load count widget */
156
+ public static loadCountWidget(conf: Partial<ArtalkConfig>) {
157
+ const ctx: Context = new ConcreteContext(handelBaseConf(conf))
158
+ ctx.inject('api', new Api(ctx))
220
159
  Stat.initCountWidget({ ctx, pvAdd: false })
221
160
  }
161
+
162
+ /** @deprecated Please use `loadCountWidget` instead */
163
+ public static LoadCountWidget(conf: Partial<ArtalkConfig>) {
164
+ console.warn('The method `LoadCountWidget` is deprecated, please use `loadCountWidget` instead.')
165
+ this.loadCountWidget(conf)
166
+ }
222
167
  }
@@ -2,8 +2,9 @@ import { CommentData } from '~/types/artalk-data'
2
2
  import Context from '~/types/context'
3
3
  import Component from '../lib/component'
4
4
  import * as Utils from '../lib/utils'
5
+ import marked from '../lib/marked'
5
6
  import UADetect from '../lib/detect'
6
- import CommentRender from './render'
7
+ import CommentUI from './render'
7
8
  import CommentActions from './actions'
8
9
 
9
10
  export interface CommentConf {
@@ -18,7 +19,7 @@ export interface CommentConf {
18
19
  }
19
20
 
20
21
  export default class Comment extends Component {
21
- private renderInstance: CommentRender
22
+ private renderInstance: CommentUI
22
23
  private actionInstance: CommentActions
23
24
 
24
25
  private data: CommentData
@@ -44,7 +45,7 @@ export default class Comment extends Component {
44
45
  this.nestCurt = 1 // 现在已嵌套 n 层
45
46
 
46
47
  this.actionInstance = new CommentActions(this)
47
- this.renderInstance = new CommentRender(this)
48
+ this.renderInstance = new CommentUI(this)
48
49
  }
49
50
 
50
51
  /** 渲染 UI */
@@ -119,7 +120,7 @@ export default class Comment extends Component {
119
120
  childC.getRender().playFadeAnim()
120
121
 
121
122
  // 内容限高
122
- childC.getRender().checkHeightLimitArea('content')
123
+ childC.getRender().checkHeightLimit()
123
124
  }
124
125
 
125
126
  /** 获取存放子评论的元素对象 */
@@ -131,7 +132,7 @@ export default class Comment extends Component {
131
132
  if (this.nestCurt < this.nestMax) {
132
133
  $children = this.getRender().renderChildrenWrap()
133
134
  } else {
134
- $children = this.parent!.getChildrenEl()
135
+ $children = this.parent!.getChildrenEl()
135
136
  }
136
137
  }
137
138
 
@@ -164,7 +165,7 @@ export default class Comment extends Component {
164
165
 
165
166
  /** 获取评论 markdown 解析后的内容 */
166
167
  public getContentMarked() {
167
- return Utils.marked(this.ctx, this.data.content)
168
+ return marked(this.ctx, this.data.content)
168
169
  }
169
170
 
170
171
  /** 获取格式化后的日期 */