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.
Files changed (145) hide show
  1. package/README.md +27 -0
  2. package/dist/Artalk.css +1 -1
  3. package/dist/Artalk.es.js +4895 -4067
  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 +2496 -1779
  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 +15 -29
  30. package/dist/src/editor/edit-mode.d.ts +9 -0
  31. package/dist/src/editor/editor-ui.d.ts +25 -0
  32. package/dist/src/editor/editor.d.ts +37 -45
  33. package/dist/src/editor/funcs.d.ts +2 -0
  34. package/dist/src/editor/mover.d.ts +8 -0
  35. package/dist/src/editor/plug-manager.d.ts +17 -0
  36. package/dist/src/editor/plugs/editor-plug.d.ts +13 -1
  37. package/dist/src/editor/reply.d.ts +9 -0
  38. package/dist/src/editor/submit.d.ts +16 -0
  39. package/dist/src/editor/ui.d.ts +25 -0
  40. package/dist/src/i18n/index.d.ts +11 -7
  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 +18 -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/utils.d.ts +1 -5
  48. package/dist/src/list/list-lite.d.ts +15 -11
  49. package/dist/src/list/list-nest.d.ts +2 -2
  50. package/dist/src/list/list-ui.d.ts +7 -0
  51. package/dist/src/list/paginator/adaptors/index.d.ts +12 -0
  52. package/dist/src/list/paginator/adaptors/pagination.d.ts +9 -0
  53. package/dist/src/list/paginator/adaptors/read-more.d.ts +10 -0
  54. package/dist/src/list/paginator/holder.d.ts +16 -0
  55. package/dist/src/list/paginator/index.d.ts +10 -0
  56. package/dist/src/service.d.ts +40 -0
  57. package/dist/types/artalk-config.d.ts +5 -2
  58. package/dist/types/artalk-data.d.ts +3 -3
  59. package/dist/types/context.d.ts +11 -17
  60. package/dist/types/global.d.ts +1 -0
  61. package/package.json +6 -7
  62. package/src/api/system.ts +3 -12
  63. package/src/artalk.ts +101 -158
  64. package/src/comment/comment.ts +7 -6
  65. package/src/comment/height-limit.ts +100 -0
  66. package/src/comment/render-ctx.ts +30 -0
  67. package/src/comment/render.ts +31 -375
  68. package/src/comment/renders/actions.ts +114 -0
  69. package/src/comment/renders/avatar.ts +22 -0
  70. package/src/comment/renders/content.ts +39 -0
  71. package/src/comment/renders/header.ts +84 -0
  72. package/src/comment/renders/index.ts +19 -0
  73. package/src/comment/renders/pending.ts +12 -0
  74. package/src/comment/renders/reply-at.ts +16 -0
  75. package/src/comment/renders/reply-to.ts +24 -0
  76. package/src/config.ts +65 -0
  77. package/src/context.ts +31 -88
  78. package/src/defaults.ts +1 -0
  79. package/src/editor/edit-mode.ts +102 -0
  80. package/src/editor/editor.ts +100 -394
  81. package/src/editor/funcs.ts +96 -0
  82. package/src/editor/mover.ts +43 -0
  83. package/src/editor/plug-manager.ts +172 -0
  84. package/src/editor/plugs/editor-plug.ts +20 -2
  85. package/src/editor/plugs/{emoticons-plug.less → emoticons-plug.scss} +5 -4
  86. package/src/editor/plugs/emoticons-plug.ts +2 -1
  87. package/src/editor/plugs/header-input-plug.ts +5 -5
  88. package/src/editor/plugs/{preview-plug.less → preview-plug.scss} +1 -1
  89. package/src/editor/plugs/preview-plug.ts +8 -4
  90. package/src/editor/plugs/upload-plug.ts +10 -13
  91. package/src/editor/reply.ts +62 -0
  92. package/src/editor/submit.ts +98 -0
  93. package/src/editor/ui.ts +36 -0
  94. package/src/i18n/index.ts +21 -10
  95. package/src/layer/index.ts +2 -2
  96. package/src/layer/layer.ts +4 -4
  97. package/src/layer/sidebar-layer.ts +2 -1
  98. package/src/lib/checker/admin.ts +36 -0
  99. package/src/lib/checker/captcha-renders.ts +68 -0
  100. package/src/lib/checker/captcha.ts +26 -0
  101. package/src/lib/checker/index.ts +38 -20
  102. package/src/lib/dark-mode.ts +48 -0
  103. package/src/lib/marked.ts +133 -0
  104. package/src/lib/utils.ts +1 -123
  105. package/src/list/list-lite.ts +59 -162
  106. package/src/list/list-ui.ts +80 -0
  107. package/src/list/list.ts +16 -8
  108. package/src/list/paginator/adaptors/index.ts +21 -0
  109. package/src/list/paginator/adaptors/pagination.ts +42 -0
  110. package/src/list/paginator/adaptors/read-more.ts +56 -0
  111. package/src/list/paginator/holder.ts +55 -0
  112. package/src/list/paginator/index.ts +13 -0
  113. package/src/service.ts +118 -0
  114. package/src/style/{_color.less → _color.scss} +3 -3
  115. package/src/style/{_extend.less → _extend.scss} +1 -1
  116. package/src/style/_variables.scss +3 -0
  117. package/src/style/{comment.less → comment.scss} +36 -12
  118. package/src/style/{editor.less → editor.scss} +11 -11
  119. package/src/style/{list.less → list.scss} +8 -6
  120. package/src/style/{main.less → main.scss} +24 -23
  121. package/src/style/{sidebar-layer.less → sidebar-layer.scss} +8 -8
  122. package/types/artalk-config.d.ts +5 -2
  123. package/types/artalk-data.d.ts +3 -3
  124. package/types/context.d.ts +11 -17
  125. package/types/global.d.ts +1 -0
  126. package/CNAME +0 -1
  127. package/LICENSE +0 -165
  128. package/index.html +0 -157
  129. package/postcss.config.js +0 -5
  130. package/scripts/build-i18n.mjs +0 -46
  131. package/src/lib/checker/admin-checker.ts +0 -36
  132. package/src/lib/checker/captcha-checker.ts +0 -84
  133. package/src/style/_variables.less +0 -3
  134. package/test/ArtalkCount.html +0 -16
  135. package/test/ArtalkI18n.html +0 -16
  136. package/test/ArtalkLite.html +0 -15
  137. package/test/CSRF.html +0 -26
  138. package/tsconfig.json +0 -12
  139. package/vite-base.config.ts +0 -21
  140. package/vite-demo.config.ts +0 -12
  141. package/vite-lite.config.ts +0 -22
  142. package/vite.config.ts +0 -27
  143. /package/dist/src/lib/checker/{admin-checker.d.ts → admin.d.ts} +0 -0
  144. /package/dist/src/lib/checker/{captcha-checker.d.ts → captcha.d.ts} +0 -0
  145. /package/src/style/{pagination.less → pagination.scss} +0 -0
@@ -1,177 +1,97 @@
1
- import { CommentData } from '~/types/artalk-data'
2
- import Context from '~/types/context'
1
+ import type { CommentData } from '~/types/artalk-data'
2
+ import type Context from '~/types/context'
3
3
  import Component from '../lib/component'
4
4
  import * as Utils from '../lib/utils'
5
5
  import * as Ui from '../lib/ui'
6
-
7
- import EditorHTML from './editor.html?raw'
8
-
9
- import EmoticonsPlug from './plugs/emoticons-plug'
10
- import UploadPlug from './plugs/upload-plug'
11
- import PreviewPlug from './plugs/preview-plug'
12
- import EditorPlug from './plugs/editor-plug'
13
- import HeaderInputPlug from './plugs/header-input-plug'
6
+ import marked from '../lib/marked'
7
+ import initEditorFuncs from './funcs'
8
+ import { render, EditorUI } from './ui'
9
+ import { PlugManager } from './plug-manager'
10
+ import { Mover } from './mover'
11
+ import { ReplyManager } from './reply'
12
+ import { EditModeManager } from './edit-mode'
13
+ import { SubmitManager } from './submit'
14
14
 
15
15
  export default class Editor extends Component {
16
- private get user() { return this.ctx.user }
17
-
18
- public $header: HTMLElement
19
- public $nick: HTMLInputElement
20
- public $email: HTMLInputElement
21
- public $link: HTMLInputElement
22
- public get $inputs() {
23
- return { nick: this.$nick, email: this.$email, link: this.$link }
24
- }
16
+ /** 界面 */
17
+ private ui: EditorUI
18
+ public getUI() { return this.ui }
25
19
 
26
- public $textareaWrap: HTMLElement
27
- public $textarea: HTMLTextAreaElement
28
- public $bottom: HTMLElement
29
- public $submitBtn: HTMLButtonElement
30
- public $notifyWrap: HTMLElement
20
+ /** 插件管理器 */
21
+ private plugs?: PlugManager
22
+ public getPlugs() { return this.plugs }
23
+ public setPlugs(p: PlugManager) { this.plugs = p }
31
24
 
32
- // Editor 的不同状态
33
- private isTraveling = false
25
+ /** 评论框移动 */
26
+ private mover?: Mover
27
+ public setMover(m: Mover) { this.mover = m }
34
28
 
35
29
  /** 回复评论 */
36
- private replyComment: CommentData|null = null
37
- private $sendReply: HTMLElement|null = null
38
- public get isReplyMode() { return this.replyComment !== null }
30
+ private reply?: ReplyManager
31
+ public setReplyManager(m: ReplyManager) { this.reply = m }
32
+ public getReplyManager() { return this.reply }
33
+ public get isReplyMode() { return !!this.reply?.comment }
39
34
 
40
35
  /** 编辑评论 */
41
- private editComment: CommentData|null = null
42
- private $editCancelBtn: HTMLElement|null = null
43
- public get isEditMode() { return this.editComment !== null }
36
+ private editMode?: EditModeManager
37
+ public setEditModeManager(m: EditModeManager) { this.editMode = m }
38
+ public get isEditMode() { return !!this.editMode?.comment }
44
39
 
45
- /** 启用的插件 */
46
- private readonly ENABLED_PLUGS = [ EmoticonsPlug, UploadPlug, PreviewPlug, HeaderInputPlug ]
47
- public plugList: { [name: string]: EditorPlug } = {}
48
- private openedPlugName: string|null = null
49
- public $plugPanelWrap: HTMLElement
50
- public $plugBtnWrap: HTMLElement
40
+ /** 提交评论 */
41
+ private submitManager?: SubmitManager
42
+ public setSubmitManager(m: SubmitManager) { this.submitManager = m }
43
+ public getSubmitManager() { return this.submitManager }
51
44
 
52
45
  public constructor(ctx: Context) {
53
46
  super(ctx)
54
47
 
55
- this.$el = Utils.createElement(EditorHTML)
56
-
57
- this.$header = this.$el.querySelector('.atk-header')!
58
- this.$nick = this.$header.querySelector('[name="nick"]')!
59
- this.$email = this.$header.querySelector('[name="email"]')!
60
- this.$link = this.$header.querySelector('[name="link"]')!
61
-
62
- this.$textareaWrap = this.$el.querySelector('.atk-textarea-wrap')!
63
- this.$textarea = this.$el.querySelector('.atk-textarea')!
64
- this.$bottom = this.$el.querySelector('.atk-bottom')!
65
- this.$submitBtn = this.$el.querySelector('.atk-send-btn')!
66
- this.$notifyWrap = this.$el.querySelector('.atk-notify-wrap')!
48
+ // init editor ui
49
+ this.ui = render()
50
+ this.$el = this.ui.$el
67
51
 
68
- this.$plugBtnWrap = this.$el.querySelector('.atk-plug-btn-wrap')!
69
- this.$plugPanelWrap = this.$el.querySelector('.atk-plug-panel-wrap')!
70
-
71
- // 监听事件
52
+ // event listen
72
53
  this.ctx.on('conf-loaded', () => {
73
- // 执行初始化
74
- this.initLocalStorage()
75
- this.initHeader()
76
- this.initTextarea()
77
- this.initSubmitBtn()
78
- this.initPlugs()
54
+ initEditorFuncs(this) // init editor funcs
79
55
  })
80
56
  }
81
57
 
82
- private initLocalStorage() {
83
- const localContent = window.localStorage.getItem('ArtalkContent') || ''
84
- if (localContent.trim() !== '') {
85
- this.showNotify(this.$t('restoredMsg'), 'i')
86
- this.setContent(localContent)
87
- }
88
-
89
- this.$textarea.addEventListener('input', () => (this.saveToLocalStorage()))
58
+ public getHeaderInputEls() {
59
+ return { nick: this.ui.$nick, email: this.ui.$email, link: this.ui.$link }
90
60
  }
91
61
 
92
- private saveToLocalStorage() {
62
+ public saveToLocalStorage() {
93
63
  window.localStorage.setItem('ArtalkContent', this.getContentOriginal().trim())
94
64
  }
95
65
 
96
- private initHeader() {
97
- Object.entries(this.$inputs).forEach(([key, $input]) => {
98
- $input.value = this.user.data[key] || ''
99
- $input.addEventListener('input', () => this.onHeaderInput(key, $input))
100
-
101
- // 设置 Placeholder
102
- $input.placeholder = `${this.$t(key as any)}`
103
- })
104
- }
105
-
106
- private onHeaderInput(key: string, $input: HTMLInputElement) {
107
- if (this.isEditMode) return // 评论编辑模式,不修改个人信息
108
-
109
- this.user.update({
110
- [key]: $input.value.trim()
111
- })
112
-
113
- // 插件监听事件响应
114
- Object.entries(this.plugList).forEach(([plugName, plug]) => {
115
- if (plug.onHeaderInput) plug.onHeaderInput(key, $input)
116
- })
117
- }
118
-
119
- private initTextarea() {
120
- // 占位符
121
- this.$textarea.placeholder = this.ctx.conf.placeholder || this.$t('placeholder')
122
-
123
- // 修复按下 Tab 输入的内容
124
- this.$textarea.addEventListener('keydown', (e) => {
125
- const keyCode = e.keyCode || e.which
126
-
127
- if (keyCode === 9) {
128
- e.preventDefault()
129
- this.insertContent('\t')
130
- }
131
- })
132
-
133
- // 输入框高度随内容而变化
134
- this.$textarea.addEventListener('input', () => {
135
- this.adjustTextareaHeight()
136
- })
137
- }
138
-
139
- private refreshSendBtnText() {
140
- if (this.isEditMode) this.$submitBtn.innerText = this.$t('save')
141
- else this.$submitBtn.innerText = this.ctx.conf.sendBtn || this.$t('send')
142
- }
143
-
144
- private initSubmitBtn() {
145
- this.refreshSendBtnText()
146
- this.$submitBtn.addEventListener('click', () => (this.submit()))
66
+ public refreshSendBtnText() {
67
+ if (this.isEditMode) this.ui.$submitBtn.innerText = this.$t('save')
68
+ else this.ui.$submitBtn.innerText = this.ctx.conf.sendBtn || this.$t('send')
147
69
  }
148
70
 
149
71
  /** 最终用于 submit 的数据 */
150
72
  public getFinalContent() {
151
73
  let content = this.getContentOriginal()
152
74
 
153
- // 表情包处理
154
- if (this.plugList.emoticons) {
155
- content = (this.plugList.emoticons as EmoticonsPlug).transEmoticonImageText(content)
156
- }
75
+ // plug hook: final content transformer
76
+ if (this.plugs) content = this.plugs.getTransformedContent(content)
157
77
 
158
78
  return content
159
79
  }
160
80
 
161
81
  public getContentOriginal() {
162
- return this.$textarea.value || '' // Tip: !!"0" === true
82
+ return this.ui.$textarea.value || '' // Tip: !!"0" === true
163
83
  }
164
84
 
165
85
  public getContentMarked() {
166
- return Utils.marked(this.ctx, this.getFinalContent())
86
+ return marked(this.ctx, this.getFinalContent())
167
87
  }
168
88
 
169
89
  public setContent(val: string) {
170
- this.$textarea.value = val
90
+ this.ui.$textarea.value = val
171
91
  this.saveToLocalStorage()
172
- if (this.plugList.preview) {
173
- ;(this.plugList.preview as PreviewPlug).updateContent()
174
- }
92
+
93
+ // plug hook: content updated
94
+ if (this.plugs) this.plugs.triggerContentUpdatedEvt(val)
175
95
 
176
96
  // 延迟执行防止无效
177
97
  window.setTimeout(() => {
@@ -181,332 +101,118 @@ export default class Editor extends Component {
181
101
 
182
102
  public insertContent(val: string) {
183
103
  if ((document as any).selection) {
184
- this.$textarea.focus();
104
+ this.ui.$textarea.focus();
185
105
  (document as any).selection.createRange().text = val
186
- this.$textarea.focus()
187
- } else if (this.$textarea.selectionStart || this.$textarea.selectionStart === 0) {
188
- const sStart = this.$textarea.selectionStart
189
- const sEnd = this.$textarea.selectionEnd
190
- const sT = this.$textarea.scrollTop
191
- this.setContent(this.$textarea.value.substring(0, sStart) + val + this.$textarea.value.substring(sEnd, this.$textarea.value.length))
192
- this.$textarea.focus()
193
- this.$textarea.selectionStart = sStart + val.length
194
- this.$textarea.selectionEnd = sStart + val.length
195
- this.$textarea.scrollTop = sT
106
+ this.ui.$textarea.focus()
107
+ } else if (this.ui.$textarea.selectionStart || this.ui.$textarea.selectionStart === 0) {
108
+ const sStart = this.ui.$textarea.selectionStart
109
+ const sEnd = this.ui.$textarea.selectionEnd
110
+ const sT = this.ui.$textarea.scrollTop
111
+ this.setContent(this.ui.$textarea.value.substring(0, sStart) + val + this.ui.$textarea.value.substring(sEnd, this.ui.$textarea.value.length))
112
+ this.ui.$textarea.focus()
113
+ this.ui.$textarea.selectionStart = sStart + val.length
114
+ this.ui.$textarea.selectionEnd = sStart + val.length
115
+ this.ui.$textarea.scrollTop = sT
196
116
  } else {
197
- this.$textarea.focus()
198
- this.$textarea.value += val
117
+ this.ui.$textarea.focus()
118
+ this.ui.$textarea.value += val
199
119
  }
200
120
  }
201
121
 
202
- private adjustTextareaHeight() {
203
- const diff = this.$textarea.offsetHeight - this.$textarea.clientHeight
204
- this.$textarea.style.height = '0px' // it's a magic. 若不加此行,内容减少,高度回不去
205
- this.$textarea.style.height = `${this.$textarea.scrollHeight + diff}px`
122
+ public adjustTextareaHeight() {
123
+ const diff = this.ui.$textarea.offsetHeight - this.ui.$textarea.clientHeight
124
+ this.ui.$textarea.style.height = '0px' // it's a magic. 若不加此行,内容减少,高度回不去
125
+ this.ui.$textarea.style.height = `${this.ui.$textarea.scrollHeight + diff}px`
206
126
  }
207
127
 
208
- /** 设置回复评论 */
209
- public setReply(commentData: CommentData, $comment: HTMLElement, scroll = true) {
210
- if (this.isEditMode) this.cancelEditComment()
211
- if (this.isReplyMode) this.cancelReply()
212
-
213
- if (this.$sendReply === null) {
214
- this.$sendReply = Utils.createElement(`<div class="atk-send-reply">${this.$t('reply')} <span class="atk-text"></span><span class="atk-cancel">×</span></div>`);
215
- this.$sendReply.querySelector<HTMLElement>('.atk-text')!.innerText = `@${commentData.nick}`
216
- this.$sendReply.addEventListener('click', () => {
217
- this.cancelReply()
218
- })
219
- this.$textareaWrap.append(this.$sendReply)
220
- }
221
- this.replyComment = commentData
222
-
223
- if (this.ctx.conf.editorTravel === true) {
224
- this.travel($comment)
225
- }
128
+ public focus() {
129
+ this.ui.$textarea.focus()
130
+ }
226
131
 
227
- if (scroll) Ui.scrollIntoView(this.$el)
132
+ public reset() {
133
+ this.setContent('')
134
+ this.cancelReply()
135
+ this.cancelEditComment()
136
+ }
228
137
 
229
- this.$textarea.focus()
138
+ /** 设置回复评论 */
139
+ public setReply(commentData: CommentData, $comment: HTMLElement, scroll = true) {
140
+ this.reply?.setReply(commentData, $comment, scroll)
230
141
  }
231
142
 
232
143
  /** 取消回复评论 */
233
144
  public cancelReply() {
234
- if (this.$sendReply !== null) {
235
- this.$sendReply.remove()
236
- this.$sendReply = null
237
- }
238
- this.replyComment = null
239
-
240
- if (this.ctx.conf.editorTravel === true) {
241
- this.travelBack()
242
- }
145
+ this.reply?.cancelReply()
243
146
  }
244
147
 
245
148
  /** 设置编辑评论 */
246
149
  public setEditComment(commentData: CommentData, $comment: HTMLElement) {
247
- if (this.isEditMode) this.cancelEditComment()
248
- if (this.isReplyMode) this.cancelReply()
249
-
250
- if (this.$editCancelBtn === null) {
251
- this.$editCancelBtn = Utils.createElement(`<div class="atk-send-reply">${this.$t('editCancel')}<span class="atk-cancel">×</span></div>`);
252
- this.$editCancelBtn.onclick = () => {
253
- this.cancelEditComment()
254
- }
255
- this.$textareaWrap.append(this.$editCancelBtn)
256
- }
257
- this.editComment = commentData
258
-
259
- this.$header.style.display = 'none' // TODO 暂时隐藏
260
-
261
- this.travel($comment)
262
-
263
- this.$nick.value = commentData.nick
264
- this.$email.value = commentData.email || ''
265
- this.$link.value = commentData.link
266
-
267
- this.setContent(commentData.content)
268
- this.$textarea.focus()
269
-
270
- this.refreshSendBtnText()
150
+ this.editMode?.setEdit(commentData, $comment)
271
151
  }
272
152
 
273
153
  /** 取消编辑评论 */
274
154
  public cancelEditComment() {
275
- if (this.$editCancelBtn !== null) {
276
- this.$editCancelBtn.remove()
277
- this.$editCancelBtn = null
278
- }
279
-
280
- this.editComment = null
281
- this.travelBack()
282
-
283
- this.$nick.value = this.user.data.nick
284
- this.$email.value = this.user.data.email
285
- this.$link.value = this.user.data.link
286
-
287
- this.setContent('')
288
- this.refreshSendBtnText()
289
- this.$header.style.display = '' // TODO
155
+ this.editMode?.cancelEdit()
290
156
  }
291
157
 
292
158
  public showNotify(msg: string, type: "i"|"s"|"w"|"e") {
293
- Ui.showNotify(this.$notifyWrap, msg, type)
159
+ Ui.showNotify(this.ui.$notifyWrap, msg, type)
294
160
  }
295
161
 
296
162
  public showLoading() {
297
- Ui.showLoading(this.$el)
163
+ Ui.showLoading(this.ui.$el)
298
164
  }
299
165
 
300
166
  public hideLoading() {
301
- Ui.hideLoading(this.$el)
167
+ Ui.hideLoading(this.ui.$el)
302
168
  }
303
169
 
304
170
  /** 点击评论提交按钮事件 */
305
171
  public async submit() {
306
- if (this.getFinalContent().trim() === '') {
307
- this.$textarea.focus()
308
- return
309
- }
310
-
311
- this.ctx.trigger('editor-submit')
312
-
313
- this.showLoading()
314
-
315
- if (!this.isEditMode) {
316
- await this.submitAdd()
317
- } else {
318
- await this.submitEdit()
319
- }
320
-
321
- this.hideLoading()
322
- }
323
-
324
- /** 创建评论 */
325
- public async submitAdd() {
326
- try {
327
- const nComment = await this.ctx.getApi().comment.add({
328
- content: this.getFinalContent(),
329
- nick: this.user.data.nick,
330
- email: this.user.data.email,
331
- link: this.user.data.link,
332
- rid: (this.replyComment === null) ? 0 : this.replyComment.id,
333
- page_key: (this.replyComment === null) ? this.ctx.conf.pageKey : this.replyComment.page_key,
334
- page_title: (this.replyComment === null) ? this.ctx.conf.pageTitle : undefined,
335
- site_name: (this.replyComment === null) ? this.ctx.conf.site : this.replyComment.site_name
336
- })
337
-
338
- // 回复不同页面的评论
339
- if (this.replyComment !== null && this.replyComment.page_key !== this.ctx.conf.pageKey) {
340
- window.open(`${this.replyComment.page_url}#atk-comment-${nComment.id}`)
341
- }
342
-
343
- this.ctx.insertComment(nComment)
344
-
345
- // 复原编辑器
346
- this.setContent('')
347
- this.cancelReply()
348
-
349
- this.ctx.trigger('editor-submitted')
350
- } catch (err: any) {
351
- console.error(err)
352
- this.showNotify(`${this.$t('commentFail')},${err.msg || String(err)}`, 'e')
353
- }
354
- }
355
-
356
- /** 修改评论 */
357
- public async submitEdit() {
358
- try {
359
- const saveData = {
360
- content: this.getFinalContent(),
361
- nick: this.$nick.value,
362
- email: this.$email.value,
363
- link: this.$link.value,
364
- }
365
- const nComment = await this.ctx.getApi().comment.commentEdit({
366
- ...this.editComment, ...saveData
367
- })
368
- this.ctx.updateComment(nComment)
369
-
370
- // 复原编辑器
371
- this.setContent('')
372
- this.cancelEditComment()
373
-
374
- this.ctx.trigger('editor-submitted')
375
- } catch (err: any) {
376
- console.error(err)
377
- this.showNotify(`${this.$t('commentFail')},${err.msg || String(err)}`, 'e')
378
- }
172
+ if (!this.submitManager) return
173
+ await this.submitManager.do()
379
174
  }
380
175
 
381
176
  /** 关闭评论 */
382
177
  public close() {
383
- if (!this.$textareaWrap.querySelector('.atk-comment-closed'))
384
- this.$textareaWrap.prepend(Utils.createElement(`<div class="atk-comment-closed">${this.$t('onlyAdminCanReply')}</div>`))
178
+ if (!this.ui.$textareaWrap.querySelector('.atk-comment-closed'))
179
+ this.ui.$textareaWrap.prepend(Utils.createElement(`<div class="atk-comment-closed">${this.$t('onlyAdminCanReply')}</div>`))
385
180
 
386
- if (!this.user.data.isAdmin) {
387
- this.$textarea.style.display = 'none'
181
+ if (!this.ctx.user.data.isAdmin) {
182
+ this.ui.$textarea.style.display = 'none'
388
183
  this.closePlugPanel()
389
- this.$bottom.style.display = 'none'
184
+ this.ui.$bottom.style.display = 'none'
390
185
  } else {
391
186
  // 管理员一直打开评论
392
- this.$textarea.style.display = ''
393
- this.$bottom.style.display = ''
187
+ this.ui.$textarea.style.display = ''
188
+ this.ui.$bottom.style.display = ''
394
189
  }
395
190
  }
396
191
 
397
192
  /** 打开评论 */
398
193
  public open() {
399
- this.$textareaWrap.querySelector('.atk-comment-closed')?.remove()
400
- this.$textarea.style.display = ''
401
- this.$bottom.style.display = ''
194
+ this.ui.$textareaWrap.querySelector('.atk-comment-closed')?.remove()
195
+ this.ui.$textarea.style.display = ''
196
+ this.ui.$bottom.style.display = ''
402
197
  }
403
198
 
404
199
  /** 移动评论框到置顶元素之后 */
405
200
  public travel($afterEl: HTMLElement) {
406
- if (this.isTraveling) return
407
- this.isTraveling = true
408
- this.$el.after(Utils.createElement('<div class="atk-editor-travel-placeholder"></div>'))
409
-
410
- const $travelPlace = Utils.createElement('<div></div>')
411
- $afterEl.after($travelPlace)
412
- $travelPlace.replaceWith(this.$el)
413
-
414
- this.$el.classList.add('atk-fade-in') // 添加渐入动画
201
+ this.mover?.move($afterEl)
415
202
  }
416
203
 
417
204
  /** 评论框归位 */
418
205
  public travelBack() {
419
- if (!this.isTraveling) return
420
- this.isTraveling = false
421
- this.ctx.$root.querySelector('.atk-editor-travel-placeholder')?.replaceWith(this.$el)
422
-
423
- // 取消回复
424
- if (this.replyComment !== null) this.cancelReply()
425
- }
426
-
427
- /** 插件初始化 */
428
- private initPlugs() {
429
- this.plugList = {}
430
- this.$plugPanelWrap.innerHTML = ''
431
- this.$plugPanelWrap.style.display = 'none'
432
- this.openedPlugName = null
433
- this.$plugBtnWrap.innerHTML = ''
434
-
435
- const disabledPlugs: string[] = []
436
- if (!this.conf.emoticons) disabledPlugs.push('emoticons')
437
- if (!this.conf.preview) disabledPlugs.push('preview')
438
-
439
- // 初始化 Editor 插件
440
- this.ENABLED_PLUGS.forEach((Plug) => {
441
- const plugName = Plug.Name
442
-
443
- // 禁用的插件
444
- if (disabledPlugs.includes(plugName)) return
445
-
446
- // 插件对象实例化
447
- const plug = new Plug(this)
448
- this.plugList[plugName] = plug
449
-
450
- // 插件按钮
451
- const $btn = plug.getBtn()
452
- if ($btn) {
453
- this.$plugBtnWrap.appendChild($btn)
454
- $btn.onclick = $btn.onclick || (() => {
455
- // 其他按钮去除 Active
456
- this.$plugBtnWrap.querySelectorAll('.active').forEach(item => item.classList.remove('active'))
457
-
458
- // 若点击已打开的,则关闭打开的面板
459
- if (plugName === this.openedPlugName) {
460
- this.closePlugPanel()
461
- return
462
- }
463
-
464
- this.openPlugPanel(plugName)
465
-
466
- // 当前按钮添加 Active
467
- $btn.classList.add('active')
468
- })
469
-
470
- // 插件面板初始化
471
- const $panel = plug.getPanel()
472
- if ($panel) {
473
- $panel.setAttribute('data-plug-name', plugName)
474
- $panel.style.display = 'none'
475
- this.$plugPanelWrap.appendChild($panel)
476
- }
477
- }
478
- })
206
+ this.mover?.back()
479
207
  }
480
208
 
481
209
  /** 展开插件面板 */
482
210
  public openPlugPanel(plugName: string) {
483
- Object.entries(this.plugList).forEach(([aPlugName, plug]) => {
484
- const plugPanel = plug.getPanel()
485
- if (!plugPanel) return
486
-
487
- if (aPlugName === plugName) {
488
- plugPanel.style.display = ''
489
- if (plug.onPanelShow) plug.onPanelShow()
490
- } else {
491
- plugPanel.style.display = 'none'
492
- if (plug.onPanelHide) plug.onPanelHide()
493
- }
494
- })
495
-
496
- this.$plugPanelWrap.style.display = ''
497
- this.openedPlugName = plugName
211
+ this.plugs?.openPlugPanel(plugName)
498
212
  }
499
213
 
500
214
  /** 收起插件面板 */
501
215
  public closePlugPanel() {
502
- if (!this.openedPlugName) return
503
-
504
- const plug = this.plugList[this.openedPlugName]
505
- if (!plug) return
506
-
507
- if (plug.onPanelHide) plug.onPanelHide()
508
-
509
- this.$plugPanelWrap.style.display = 'none'
510
- this.openedPlugName = null
216
+ this.plugs?.closePlugPanel()
511
217
  }
512
218
  }