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.
Files changed (175) hide show
  1. package/README.md +1 -1
  2. package/dist/Artalk.css +1 -1
  3. package/dist/Artalk.es.js +209 -188
  4. package/dist/Artalk.es.js.map +1 -0
  5. package/dist/Artalk.iife.js +2 -1
  6. package/dist/Artalk.iife.js.map +1 -0
  7. package/dist/Artalk.js +2 -1
  8. package/dist/Artalk.js.map +1 -0
  9. package/dist/ArtalkLite.css +1 -1
  10. package/dist/ArtalkLite.es.js +209 -188
  11. package/dist/ArtalkLite.es.js.map +1 -0
  12. package/dist/ArtalkLite.iife.js +2 -1
  13. package/dist/ArtalkLite.iife.js.map +1 -0
  14. package/dist/ArtalkLite.js +2 -1
  15. package/dist/ArtalkLite.js.map +1 -0
  16. package/dist/i18n/jp.js +1 -1
  17. package/dist/i18n/zh-TW.js +1 -1
  18. package/dist/src/api/_base.d.ts +7 -0
  19. package/dist/src/api/_options.d.ts +5 -0
  20. package/dist/src/artalk.d.ts +0 -2
  21. package/dist/src/components/checker/index.d.ts +2 -0
  22. package/dist/src/i18n/en.d.ts +4 -0
  23. package/dist/src/i18n/external.d.ts +4 -0
  24. package/dist/src/i18n/index.d.ts +12 -0
  25. package/dist/src/i18n/jp.d.ts +4 -0
  26. package/dist/src/i18n/zh-TW.d.ts +4 -0
  27. package/dist/src/lib/user.d.ts +8 -6
  28. package/dist/src/main.d.ts +3 -0
  29. package/dist/src/plugins/editor/_kit.d.ts +7 -1
  30. package/dist/src/service.d.ts +2 -8
  31. package/dist/types/event.d.ts +1 -1
  32. package/package.json +43 -17
  33. package/dist/src/components/dark-mode.d.ts +0 -2
  34. package/src/api/_base.ts +0 -18
  35. package/src/api/_options.ts +0 -25
  36. package/src/api/_request.ts +0 -104
  37. package/src/api/admin.ts +0 -18
  38. package/src/api/captcha.ts +0 -24
  39. package/src/api/comment.ts +0 -84
  40. package/src/api/index.ts +0 -29
  41. package/src/api/page.ts +0 -83
  42. package/src/api/site.ts +0 -51
  43. package/src/api/system.ts +0 -49
  44. package/src/api/upload.ts +0 -28
  45. package/src/api/user.ts +0 -132
  46. package/src/artalk.ts +0 -119
  47. package/src/comment/actions.ts +0 -83
  48. package/src/comment/comment.html +0 -18
  49. package/src/comment/comment.ts +0 -192
  50. package/src/comment/height-limit.ts +0 -100
  51. package/src/comment/index.ts +0 -3
  52. package/src/comment/render.ts +0 -150
  53. package/src/comment/renders/actions.ts +0 -114
  54. package/src/comment/renders/avatar.ts +0 -22
  55. package/src/comment/renders/content.ts +0 -39
  56. package/src/comment/renders/header.ts +0 -84
  57. package/src/comment/renders/index.ts +0 -19
  58. package/src/comment/renders/pending.ts +0 -12
  59. package/src/comment/renders/reply-at.ts +0 -16
  60. package/src/comment/renders/reply-to.ts +0 -24
  61. package/src/components/action-btn.ts +0 -170
  62. package/src/components/checker/admin.ts +0 -37
  63. package/src/components/checker/captcha-renders.ts +0 -69
  64. package/src/components/checker/captcha.ts +0 -26
  65. package/src/components/checker/index.ts +0 -192
  66. package/src/components/dark-mode.ts +0 -30
  67. package/src/components/dialog.ts +0 -62
  68. package/src/components/error-dialog.ts +0 -35
  69. package/src/components/pagination.ts +0 -153
  70. package/src/components/read-more-btn.ts +0 -92
  71. package/src/config.ts +0 -119
  72. package/src/context.ts +0 -146
  73. package/src/data.ts +0 -110
  74. package/src/defaults.ts +0 -61
  75. package/src/editor/editor.html +0 -18
  76. package/src/editor/editor.ts +0 -114
  77. package/src/editor/state.ts +0 -52
  78. package/src/editor/ui.ts +0 -36
  79. package/src/i18n/en.ts +0 -81
  80. package/src/i18n/external.ts +0 -10
  81. package/src/i18n/index.ts +0 -67
  82. package/src/i18n/jp.ts +0 -83
  83. package/src/i18n/zh-CN.ts +0 -85
  84. package/src/i18n/zh-TW.ts +0 -83
  85. package/src/layer/index.ts +0 -4
  86. package/src/layer/layer-manager.ts +0 -32
  87. package/src/layer/layer.ts +0 -39
  88. package/src/layer/scrollbar-helper.ts +0 -24
  89. package/src/layer/sidebar-layer.html +0 -8
  90. package/src/layer/sidebar-layer.ts +0 -155
  91. package/src/layer/wrap.ts +0 -77
  92. package/src/lib/component.ts +0 -12
  93. package/src/lib/detect.ts +0 -222
  94. package/src/lib/event-manager.ts +0 -47
  95. package/src/lib/highlight.ts +0 -5
  96. package/src/lib/marked-renderer.ts +0 -32
  97. package/src/lib/marked.ts +0 -72
  98. package/src/lib/sanitizer.ts +0 -57
  99. package/src/lib/ui.ts +0 -193
  100. package/src/lib/user.ts +0 -55
  101. package/src/lib/utils.ts +0 -236
  102. package/src/list/comment.ts +0 -20
  103. package/src/list/layout.ts +0 -120
  104. package/src/list/list.html +0 -20
  105. package/src/list/list.ts +0 -79
  106. package/src/list/nest.ts +0 -66
  107. package/src/list/page.ts +0 -97
  108. package/src/list/paginator/index.ts +0 -25
  109. package/src/list/paginator/read-more.ts +0 -67
  110. package/src/list/paginator/up-down.ts +0 -51
  111. package/src/main.ts +0 -5
  112. package/src/plugins/admin-only-elem.ts +0 -40
  113. package/src/plugins/conf-remoter.ts +0 -54
  114. package/src/plugins/dark-mode.ts +0 -12
  115. package/src/plugins/editor/_kit.ts +0 -57
  116. package/src/plugins/editor/_plug.ts +0 -60
  117. package/src/plugins/editor/_sample.ts +0 -8
  118. package/src/plugins/editor/closable.ts +0 -44
  119. package/src/plugins/editor/emoticons.scss +0 -72
  120. package/src/plugins/editor/emoticons.ts +0 -293
  121. package/src/plugins/editor/header-event.ts +0 -35
  122. package/src/plugins/editor/header-link.ts +0 -31
  123. package/src/plugins/editor/header-user.ts +0 -98
  124. package/src/plugins/editor/index.ts +0 -40
  125. package/src/plugins/editor/local-storage.ts +0 -36
  126. package/src/plugins/editor/mover.ts +0 -29
  127. package/src/plugins/editor/preview.scss +0 -7
  128. package/src/plugins/editor/preview.ts +0 -41
  129. package/src/plugins/editor/state-edit.ts +0 -116
  130. package/src/plugins/editor/state-reply.ts +0 -91
  131. package/src/plugins/editor/submit-add.ts +0 -33
  132. package/src/plugins/editor/submit-btn.ts +0 -25
  133. package/src/plugins/editor/submit.ts +0 -73
  134. package/src/plugins/editor/textarea.ts +0 -55
  135. package/src/plugins/editor/upload.ts +0 -145
  136. package/src/plugins/editor-kit.ts +0 -161
  137. package/src/plugins/index.ts +0 -16
  138. package/src/plugins/list/copyright.ts +0 -16
  139. package/src/plugins/list/count.ts +0 -31
  140. package/src/plugins/list/dropdown.ts +0 -95
  141. package/src/plugins/list/error-dialog.ts +0 -22
  142. package/src/plugins/list/fetch.ts +0 -64
  143. package/src/plugins/list/goto-first.ts +0 -23
  144. package/src/plugins/list/goto.ts +0 -72
  145. package/src/plugins/list/index.ts +0 -25
  146. package/src/plugins/list/loading.ts +0 -17
  147. package/src/plugins/list/no-comment.ts +0 -25
  148. package/src/plugins/list/reach-bottom.ts +0 -46
  149. package/src/plugins/list/sidebar-btn.ts +0 -39
  150. package/src/plugins/list/time-ticking.ts +0 -22
  151. package/src/plugins/list/unread-badge.ts +0 -26
  152. package/src/plugins/list/unread.ts +0 -38
  153. package/src/plugins/list/with-editor.ts +0 -59
  154. package/src/plugins/markdown.ts +0 -10
  155. package/src/plugins/stat.ts +0 -79
  156. package/src/plugins/version-check.ts +0 -37
  157. package/src/service.ts +0 -91
  158. package/src/style/_color.scss +0 -41
  159. package/src/style/_extend.scss +0 -20
  160. package/src/style/_variables.scss +0 -3
  161. package/src/style/comment.scss +0 -360
  162. package/src/style/editor.scss +0 -194
  163. package/src/style/list.scss +0 -212
  164. package/src/style/main.scss +0 -486
  165. package/src/style/pagination.scss +0 -58
  166. package/src/style/sidebar-layer.scss +0 -92
  167. package/types/config.ts +0 -153
  168. package/types/context.ts +0 -84
  169. package/types/data.ts +0 -271
  170. package/types/editor.ts +0 -95
  171. package/types/event.ts +0 -40
  172. package/types/global.d.ts +0 -1
  173. package/types/index.ts +0 -7
  174. package/types/plugin.ts +0 -3
  175. package/types/sidebar.ts +0 -3
package/src/lib/marked.ts DELETED
@@ -1,72 +0,0 @@
1
- import { marked as libMarked, MarkedOptions } from 'marked'
2
-
3
- import { sanitize } from './sanitizer'
4
- import { renderCode } from './highlight'
5
- import { getRenderer } from './marked-renderer'
6
-
7
- type Replacer = (raw: string) => string
8
- export type TMarked = typeof libMarked
9
-
10
- let instance: (typeof libMarked)|undefined
11
- let replacers: Replacer[] = []
12
-
13
- const markedOptions: MarkedOptions = {
14
- pedantic: false,
15
- gfm: true,
16
- breaks: true,
17
- smartLists: true,
18
- smartypants: true,
19
- xhtml: false,
20
- sanitize: false,
21
- silent: true,
22
- }
23
-
24
- /** Get Marked instance */
25
- export function getInstance() {
26
- return instance
27
- }
28
-
29
- export function setReplacers(arr: Replacer[]) {
30
- replacers = arr
31
- }
32
-
33
- /** 初始化 marked */
34
- export function initMarked() {
35
- try { if (!libMarked.name) return } catch { return }
36
-
37
- // @see https://github.com/markedjs/marked/blob/4afb228d956a415624c4e5554bb8f25d047676fe/src/Tokenizer.js#L329
38
- libMarked.setOptions({
39
- renderer: getRenderer(),
40
- ...markedOptions
41
- })
42
-
43
- instance = libMarked
44
- }
45
-
46
- /** 解析 markdown */
47
- export default function marked(src: string): string {
48
- let markedContent = getInstance()?.parse(src)
49
- if (!markedContent) { // 无 Markdown 模式简单处理
50
- markedContent = simpleMarked(src)
51
- }
52
-
53
- let dest = sanitize(markedContent)
54
-
55
- // 内容替换器
56
- replacers.forEach((replacer) => {
57
- if (typeof replacer === 'function')
58
- dest = replacer(dest)
59
- })
60
-
61
- return dest
62
- }
63
-
64
- function simpleMarked(src: string) {
65
- return src
66
- // .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
67
- .replace(/```\s*([^]+?.*?[^]+?[^]+?)```/g, (_, code) => `<pre><code>${renderCode(code)}</code></pre>`)
68
- // .replace(/`([^`]+?)`/g, '<code>$1</code>')
69
- .replace(/!\[(.*?)\]\((.*?)\)/g, (_, alt, imgSrc) => `<img src="${imgSrc}" alt="${alt}" />`)
70
- .replace(/\[(.*?)\]\((.*?)\)/g, (_, text, link) => `<a href="${link}" target="_blank">${text}</a>`)
71
- .replace(/\n/g, '<br>')
72
- }
@@ -1,57 +0,0 @@
1
- import insane from 'insane'
2
-
3
- const insaneOptions = {
4
- allowedClasses: {},
5
- // @refer CVE-2018-8495
6
- // @link https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-8495
7
- // @link https://leucosite.com/Microsoft-Edge-RCE/
8
- // @link https://medium.com/@knownsec404team/analysis-of-the-security-issues-of-url-scheme-in-pc-from-cve-2018-8495-934478a36756
9
- allowedSchemes: [
10
- 'http', 'https', 'mailto',
11
- 'data' // for support base64 encoded image (安全性有待考虑)
12
- ],
13
- allowedTags: [
14
- 'a', 'abbr', 'article', 'b', 'blockquote', 'br', 'caption', 'code', 'del', 'details', 'div', 'em',
15
- 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'ins', 'kbd', 'li', 'main', 'mark',
16
- 'ol', 'p', 'pre', 'section', 'span', 'strike', 'strong', 'sub', 'summary', 'sup', 'table',
17
- 'tbody', 'td', 'th', 'thead', 'tr', 'u', 'ul'
18
- ],
19
- allowedAttributes: {
20
- '*': ['title', 'accesskey'],
21
- a: ['href', 'name', 'target', 'aria-label', 'rel'],
22
- img: ['src', 'alt', 'title', 'atk-emoticon', 'aria-label'],
23
- // for code highlight
24
- code: ['class'],
25
- span: ['class', 'style'],
26
- },
27
- filter: node => {
28
- // allow hljs style
29
- const allowed = [
30
- [ 'code', /^hljs\W+language-(.*)$/ ],
31
- [ 'span', /^(hljs-.*)$/ ]
32
- ]
33
- allowed.forEach(([ tag, reg ]) => {
34
- if (
35
- node.tag === tag
36
- && !!node.attrs.class
37
- && !(reg as RegExp).test(node.attrs.class)
38
- ) {
39
- delete node.attrs.class
40
- }
41
- })
42
-
43
- // allow <span> set color sty
44
- if (node.tag === 'span' && !!node.attrs.style
45
- && !/^color:(\W+)?#[0-9a-f]{3,6};?$/i.test(node.attrs.style)) {
46
- delete node.attrs.style
47
- }
48
-
49
- return true
50
- }
51
- }
52
-
53
- export function sanitize(content: string): string {
54
- // @link https://github.com/markedjs/marked/discussions/1232
55
- // @link https://gist.github.com/lionel-rowe/bb384465ba4e4c81a9c8dada84167225
56
- return insane(content, insaneOptions)
57
- }
package/src/lib/ui.ts DELETED
@@ -1,193 +0,0 @@
1
- import type { NotifyLevel } from '~/types'
2
- import * as Utils from './utils'
3
-
4
- /** 显示加载 */
5
- export function showLoading(parentElem: HTMLElement, conf?: { transparentBg?: boolean }) {
6
- // Use :scope https://developer.mozilla.org/en-US/docs/Web/CSS/:scope
7
- let $loading = parentElem.querySelector<HTMLElement>(':scope > .atk-loading')
8
- if (!$loading) {
9
- $loading = Utils.createElement(
10
- `<div class="atk-loading atk-fade-in" style="display: none;">
11
- <div class="atk-loading-spinner">
12
- <svg viewBox="25 25 50 50"><circle cx="50" cy="50" r="20" fill="none" stroke-width="2" stroke-miterlimit="10"></circle></svg>
13
- </div>
14
- </div>`)
15
- if (conf?.transparentBg) $loading.style.background = 'transparent'
16
- parentElem.appendChild($loading)
17
- }
18
- $loading.style.display = ''
19
-
20
- // spinner 延迟显示,若加载等待时间太短,没必要显示(闪一下即可)
21
- const $spinner = $loading.querySelector<HTMLElement>('.atk-loading-spinner')
22
- if ($spinner) {
23
- $spinner.style.display = 'none'
24
- window.setTimeout(() => {
25
- $spinner.style.display = ''
26
- }, 500)
27
- }
28
- }
29
-
30
- /** 隐藏加载 */
31
- export function hideLoading(parentElem: HTMLElement) {
32
- const $loading = parentElem.querySelector<HTMLElement>(':scope > .atk-loading')
33
- if ($loading) $loading.style.display = 'none'
34
- }
35
-
36
- /** 元素加载动画 */
37
- export function setLoading(val: boolean, parentElem: HTMLElement) {
38
- if (val) showLoading(parentElem)
39
- else hideLoading(parentElem)
40
- }
41
-
42
- /** 元素是否用户可见 */
43
- export function isVisible(el: HTMLElement, viewport: HTMLElement = document.documentElement) {
44
- const viewportHeight = viewport.clientHeight
45
-
46
- const docViewTop = viewport.scrollTop
47
- const docViewBottom = docViewTop + viewportHeight
48
-
49
- const rect = el.getBoundingClientRect()
50
- const elemTop = rect.top + docViewTop
51
- const elemBottom = elemTop + rect.height
52
-
53
- return (elemBottom <= docViewBottom) //&& (elemTop >= docViewTop) 注释因为假如 el 比 viewport 还高就会失效
54
- }
55
-
56
- /** 滚动到元素中心 */
57
- export function scrollIntoView(elem: HTMLElement, enableAnim: boolean = true, relativeTo?: HTMLElement) {
58
- let top: number
59
-
60
- if (relativeTo) {
61
- const containerRect = relativeTo.getBoundingClientRect()
62
- const elementRect = elem.getBoundingClientRect()
63
- top = elementRect.top - containerRect.top + relativeTo.scrollTop
64
- top += Utils.getHeight(relativeTo) / 2 - Utils.getHeight(elem) / 2
65
- } else {
66
- const rect = elem.getBoundingClientRect()
67
- const elemTop = rect.top + window.scrollY
68
- top = elemTop - (window.innerHeight / 2 - rect.height / 2)
69
- }
70
-
71
- const scrollOptions: ScrollToOptions = {
72
- top, left: 0,
73
- // behavior: enableAnim ? 'smooth' : 'instant',
74
- behavior: 'instant'
75
- }
76
-
77
- if (relativeTo) relativeTo.scroll(scrollOptions)
78
- else window.scroll(scrollOptions)
79
- }
80
-
81
- /** 显示消息 */
82
- export function showNotify(
83
- wrapElem: HTMLElement,
84
- msg: string,
85
- type: NotifyLevel,
86
- ) {
87
- const colors = { s: '#57d59f', e: '#ff6f6c', w: '#ffc721', i: '#2ebcfc' }
88
- const timeout = 3000 // 持续显示时间 ms
89
-
90
- const notifyElem = Utils.createElement(
91
- `<div class="atk-notify atk-fade-in" style="background-color: ${colors[type]}"><span class="atk-notify-content"></span></div>`
92
- )
93
- const notifyContentEl = notifyElem.querySelector<HTMLElement>('.atk-notify-content')!
94
- notifyContentEl.innerHTML = Utils.htmlEncode(msg).replace('\n', '<br/>')
95
-
96
- wrapElem.appendChild(notifyElem)
97
-
98
- const notifyRemove = () => {
99
- notifyElem.classList.add('atk-fade-out')
100
- setTimeout(() => {
101
- notifyElem.remove()
102
- }, 200)
103
- }
104
-
105
- let timeoutFn: number
106
- if (timeout > 0) {
107
- timeoutFn = window.setTimeout(() => {
108
- notifyRemove()
109
- }, timeout)
110
- }
111
-
112
- notifyElem.addEventListener('click', () => {
113
- notifyRemove()
114
- window.clearTimeout(timeoutFn)
115
- })
116
- }
117
-
118
- /** fade 动画 */
119
- export function playFadeAnim(
120
- elem: HTMLElement,
121
- after?: () => void,
122
- type: 'in' | 'out' = 'in'
123
- ) {
124
- elem.classList.add(`atk-fade-${type}`)
125
- // 动画结束清除 class
126
- const onAnimEnded = () => {
127
- elem.classList.remove(`atk-fade-${type}`)
128
- elem.removeEventListener('animationend', onAnimEnded)
129
- if (after) after()
130
- }
131
- elem.addEventListener('animationend', onAnimEnded)
132
- }
133
-
134
- /** 渐入动画 */
135
- export function playFadeInAnim(elem: HTMLElement, after?: () => void) {
136
- playFadeAnim(elem, after, 'in')
137
- }
138
-
139
- /** 渐出动画 */
140
- export function playFadeOutAnim(elem: HTMLElement, after?: () => void) {
141
- playFadeAnim(elem, after, 'out')
142
- }
143
-
144
- /** 设置全局错误 */
145
- export function setError(parentElem: HTMLElement, html: string | HTMLElement | null, title: string = '<span class="atk-error-title">Artalk Error</span>') {
146
- let elem = parentElem.querySelector<HTMLElement>('.atk-error-layer')
147
- if (html === null) {
148
- if (elem !== null) elem.remove()
149
- return
150
- }
151
- if (!elem) {
152
- elem = Utils.createElement(
153
- `<div class="atk-error-layer">${title}<span class="atk-error-text"></span></div>`
154
- )
155
- parentElem.appendChild(elem)
156
- }
157
-
158
- const errorTextEl = elem.querySelector<HTMLElement>('.atk-error-text')!
159
- errorTextEl.innerHTML = ''
160
- if (html === null) return
161
-
162
- if (html instanceof HTMLElement) {
163
- errorTextEl.appendChild(html)
164
- } else {
165
- errorTextEl.innerText = html
166
- }
167
- }
168
-
169
- export function getScrollBarWidth() {
170
- const inner = document.createElement('p')
171
- inner.style.width = '100%'
172
- inner.style.height = '200px'
173
-
174
- const outer = document.createElement('div')
175
- outer.style.position = 'absolute'
176
- outer.style.top = '0px'
177
- outer.style.left = '0px'
178
- outer.style.visibility = 'hidden'
179
- outer.style.width = '200px'
180
- outer.style.height = '150px'
181
- outer.style.overflow = 'hidden'
182
- outer.appendChild(inner)
183
-
184
- document.body.appendChild(outer)
185
- const w1 = inner.offsetWidth
186
- outer.style.overflow = 'scroll'
187
- let w2 = inner.offsetWidth
188
- if (w1 === w2) w2 = outer.clientWidth
189
-
190
- document.body.removeChild(outer)
191
-
192
- return (w1 - w2)
193
- }
package/src/lib/user.ts DELETED
@@ -1,55 +0,0 @@
1
- import type { LocalUser } from '~/types'
2
-
3
- const LOCAL_USER_KEY = 'ArtalkUser'
4
-
5
- class User {
6
- data: LocalUser
7
- onUserChanged: ((user: LocalUser) => void) | null = null
8
-
9
- constructor() {
10
- // 从 localStorage 导入
11
- const localUser = JSON.parse(window.localStorage.getItem(LOCAL_USER_KEY) || '{}')
12
- this.data = {
13
- nick: localUser.nick || '',
14
- email: localUser.email || '',
15
- link: localUser.link || '',
16
- token: localUser.token || '',
17
- isAdmin: localUser.isAdmin || false
18
- }
19
- }
20
-
21
- /** 保存用户到 localStorage 中 */
22
- update(obj: Partial<LocalUser> = {}) {
23
- Object.entries(obj).forEach(([key, value]) => {
24
- this.data[key] = value
25
- })
26
-
27
- window.localStorage.setItem(LOCAL_USER_KEY, JSON.stringify(this.data))
28
- this.onUserChanged && this.onUserChanged(this.data)
29
- }
30
-
31
- public setOnUserChanged(fn: ((user: LocalUser) => void) | null) {
32
- this.onUserChanged = fn
33
- }
34
-
35
- /**
36
- * Logout
37
- *
38
- * @description Logout will clear login status, but not clear user data (nick, email, link)
39
- */
40
- logout() {
41
- this.update({
42
- token: '',
43
- isAdmin: false
44
- })
45
- }
46
-
47
- /** 是否已填写基本用户信息 */
48
- checkHasBasicUserInfo() {
49
- return !!this.data.nick && !!this.data.email
50
- }
51
- }
52
-
53
- const UserInstance = new User()
54
-
55
- export default UserInstance
package/src/lib/utils.ts DELETED
@@ -1,236 +0,0 @@
1
- export function createElement<E extends HTMLElement = HTMLElement>(htmlStr: string = ''): E {
2
- const div = document.createElement('div')
3
- div.innerHTML = htmlStr.trim()
4
- return (div.firstElementChild || div) as E
5
- }
6
-
7
- export function getHeight(el: HTMLElement) {
8
- return parseFloat(getComputedStyle(el, null).height.replace('px', ''))
9
- }
10
-
11
- export function htmlEncode(str: string) {
12
- const temp = document.createElement('div')
13
- temp.innerText = str
14
- const output = temp.innerHTML
15
- return output
16
- }
17
-
18
- export function htmlDecode(str: string) {
19
- const temp = document.createElement('div')
20
- temp.innerHTML = str
21
- const output = temp.innerText
22
- return output
23
- }
24
-
25
- export function getQueryParam(name: string) {
26
- const match = RegExp(`[?&]${name}=([^&]*)`).exec(window.location.search);
27
- return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
28
- }
29
-
30
- export function getOffset(el: HTMLElement, relativeTo?: HTMLElement) {
31
- const getOffsetRecursive = (element: HTMLElement): { top: number, left: number } => {
32
- const rect = element.getBoundingClientRect()
33
- const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft
34
- const scrollTop = window.pageYOffset || document.documentElement.scrollTop
35
- return {
36
- top: rect.top + scrollTop,
37
- left: rect.left + scrollLeft
38
- }
39
- }
40
-
41
- const elOffset = getOffsetRecursive(el)
42
- if (!relativeTo) return elOffset
43
-
44
- const relativeToOffset = getOffsetRecursive(relativeTo)
45
-
46
- return {
47
- top: elOffset.top - relativeToOffset.top,
48
- left: elOffset.left - relativeToOffset.left
49
- }
50
- }
51
-
52
- export function padWithZeros(vNumber: number, width: number) {
53
- let numAsString = vNumber.toString()
54
- while (numAsString.length < width) {
55
- numAsString = `0${numAsString}`
56
- }
57
- return numAsString
58
- }
59
-
60
- export function dateFormat(date: Date) {
61
- const vDay = padWithZeros(date.getDate(), 2)
62
- const vMonth = padWithZeros(date.getMonth() + 1, 2)
63
- const vYear = padWithZeros(date.getFullYear(), 2)
64
- // var vHour = padWithZeros(date.getHours(), 2);
65
- // var vMinute = padWithZeros(date.getMinutes(), 2);
66
- // var vSecond = padWithZeros(date.getSeconds(), 2);
67
- return `${vYear}-${vMonth}-${vDay}`
68
- }
69
-
70
- export function timeAgo(date: Date, $t: Function = (n: string) => n) {
71
- try {
72
- const oldTime = date.getTime()
73
- const currTime = new Date().getTime()
74
- const diffValue = currTime - oldTime
75
-
76
- const days = Math.floor(diffValue / (24 * 3600 * 1000))
77
- if (days === 0) {
78
- // 计算相差小时数
79
- const leave1 = diffValue % (24 * 3600 * 1000) // 计算天数后剩余的毫秒数
80
- const hours = Math.floor(leave1 / (3600 * 1000))
81
- if (hours === 0) {
82
- // 计算相差分钟数
83
- const leave2 = leave1 % (3600 * 1000) // 计算小时数后剩余的毫秒数
84
- const minutes = Math.floor(leave2 / (60 * 1000))
85
- if (minutes === 0) {
86
- // 计算相差秒数
87
- const leave3 = leave2 % (60 * 1000) // 计算分钟数后剩余的毫秒数
88
- const seconds = Math.round(leave3 / 1000)
89
- if (seconds < 10) return $t('now')
90
- return `${seconds} ${$t('seconds')}`
91
- }
92
- return `${minutes} ${$t('minutes')}`
93
- }
94
- return `${hours} ${$t('hours')}`
95
- }
96
- if (days < 0) return $t('now')
97
-
98
- if (days < 8) {
99
- return `${days} ${$t('days')}`
100
- }
101
-
102
- return dateFormat(date)
103
- } catch (error) {
104
- console.error(error)
105
- return ' - '
106
- }
107
- }
108
-
109
- /** 所有图片加载完毕后执行 */
110
- export function onImagesLoaded($container: HTMLElement, event: Function) {
111
- if (!$container) return
112
- const images = $container.getElementsByTagName('img')
113
- if (!images.length) return
114
- let loaded = images.length
115
- for (let i = 0; i < images.length; i++) {
116
- if (images[i].complete) {
117
- loaded--
118
- } else {
119
- // eslint-disable-next-line @typescript-eslint/no-loop-func
120
- images[i].addEventListener('load', () => {
121
- loaded--
122
- if (loaded === 0) event()
123
- })
124
- }
125
-
126
- if (loaded === 0) event()
127
- }
128
- }
129
-
130
- export function getGravatarURL(opts: { params: string, mirror: string, emailMD5: string }) {
131
- return `${opts.mirror.replace(/\/$/, '')}/${opts.emailMD5}?${opts.params.replace(/^\?/, '')}`
132
- }
133
-
134
- export function sleep(ms: number) {
135
- return new Promise(resolve => { setTimeout(resolve, ms) });
136
- }
137
-
138
- /** 版本号比较(a < b :-1 | 0 | b < a :1) */
139
- export function versionCompare(a: string, b: string) {
140
- const pa = a.split('.')
141
- const pb = b.split('.')
142
- for (let i = 0; i < 3; i++) {
143
- const na = Number(pa[i])
144
- const nb = Number(pb[i])
145
- if (na > nb) return 1
146
- if (nb > na) return -1
147
- if (!Number.isNaN(na) && Number.isNaN(nb)) return 1
148
- if (Number.isNaN(na) && !Number.isNaN(nb)) return -1
149
- }
150
- return 0
151
- }
152
-
153
- /** 获取修正后的 UserAgent */
154
- export async function getCorrectUserAgent() {
155
- const uaRaw = navigator.userAgent
156
- if (!(navigator as any).userAgentData || !(navigator as any).userAgentData.getHighEntropyValues) {
157
- return uaRaw
158
- }
159
-
160
- // @link https://web.dev/migrate-to-ua-ch/
161
- // @link https://web.dev/user-agent-client-hints/
162
- const uaData = (navigator as any).userAgentData
163
- let uaGot: any = null
164
- try {
165
- uaGot = await uaData.getHighEntropyValues(["platformVersion"])
166
- } catch (err) { console.error(err); return uaRaw }
167
- const majorPlatformVersion = Number(uaGot.platformVersion.split('.')[0])
168
-
169
- if (uaData.platform === "Windows") {
170
- if (majorPlatformVersion >= 13) { // @link https://docs.microsoft.com/en-us/microsoft-edge/web-platform/how-to-detect-win11
171
- // @date 2022-4-29
172
- // Win 11 样本:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36"
173
- return uaRaw.replace(/Windows NT 10.0/, 'Windows NT 11.0')
174
- }
175
- }
176
- if (uaData.platform === "macOS") {
177
- if (majorPlatformVersion >= 11) { // 11 => BigSur
178
- // @date 2022-4-29
179
- // (Intel Chip) macOS 12.3 样本:"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36"
180
- // (Arm Chip) macOS 样本:"Mozilla/5.0 (Macintosh; ARM Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Safari/605.1.15"
181
- return uaRaw.replace(/(Mac OS X \d+_\d+_\d+|Mac OS X)/, `Mac OS X ${uaGot.platformVersion.replace(/\./g, '_')}`)
182
- }
183
- }
184
-
185
- return uaRaw
186
- }
187
-
188
- /** 是否为完整的 URL */
189
- export function isValidURL(urlRaw: string) {
190
- // @link https://www.rfc-editor.org/rfc/rfc3986
191
- let url: URL
192
- try {
193
- url = new URL(urlRaw)
194
- } catch (_) { return false }
195
- return url.protocol === "http:" || url.protocol === "https:"
196
- }
197
-
198
- /** 获取基于 conf.server 的 URL */
199
- export function getURLBasedOnApi(opts: { base: string, path: string }) {
200
- return getURLBasedOn(opts.base, opts.path)
201
- }
202
-
203
- /** 获取基于某个 baseURL 的 URL */
204
- export function getURLBasedOn(baseURL: string, path: string) {
205
- return `${baseURL.replace(/\/$/, '')}/${path.replace(/^\//, '')}`
206
- }
207
-
208
- /**
209
- * Performs a deep merge of `source` into `target`.
210
- * Mutates `target` only but not its objects and arrays.
211
- *
212
- * @author inspired by [jhildenbiddle](https://stackoverflow.com/a/48218209).
213
- * @link https://gist.github.com/ahtcx/0cd94e62691f539160b32ecda18af3d6
214
- */
215
- export function mergeDeep(target: any, source: any) {
216
- const isObject = (obj: any) => obj && typeof obj === 'object'
217
-
218
- if (!isObject(target) || !isObject(source)) {
219
- return source
220
- }
221
-
222
- Object.keys(source).forEach(key => {
223
- const targetValue = target[key]
224
- const sourceValue = source[key]
225
-
226
- if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
227
- target[key] = targetValue.concat(sourceValue)
228
- } else if (isObject(targetValue) && isObject(sourceValue)) {
229
- target[key] = mergeDeep({ ...targetValue}, sourceValue)
230
- } else {
231
- target[key] = sourceValue
232
- }
233
- })
234
-
235
- return target
236
- }
@@ -1,20 +0,0 @@
1
- import type { ContextApi, CommentData } from '~/types'
2
- import Comment from '../comment/comment'
3
-
4
- export function createComment(ctx: ContextApi, comment: CommentData, ctxComments: CommentData[]): Comment {
5
- const instance = new Comment(ctx, comment, {
6
- isFlatMode: ctx.getData().getListLastFetch()?.params.flatMode!,
7
- afterRender: () => {
8
- ctx.trigger('comment-rendered', instance)
9
- },
10
- onDelete: (c: Comment) => {
11
- ctx.getData().deleteComment(c.getID())
12
- },
13
- replyTo: (comment.rid ? ctxComments.find(c => c.id === comment.rid) : undefined)
14
- })
15
-
16
- // 渲染元素
17
- instance.render()
18
-
19
- return instance
20
- }