artalk 2.7.1 → 2.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/Artalk.css +1 -1
- package/dist/Artalk.es.js +155 -147
- package/dist/Artalk.es.js.map +1 -1
- package/dist/Artalk.iife.js +1 -1
- package/dist/Artalk.iife.js.map +1 -1
- package/dist/Artalk.js +1 -1
- package/dist/Artalk.js.map +1 -1
- package/dist/ArtalkLite.css +1 -1
- package/dist/ArtalkLite.es.js +155 -147
- package/dist/ArtalkLite.es.js.map +1 -1
- package/dist/ArtalkLite.iife.js +1 -1
- package/dist/ArtalkLite.iife.js.map +1 -1
- package/dist/ArtalkLite.js +1 -1
- package/dist/ArtalkLite.js.map +1 -1
- package/dist/i18n/jp.js +1 -1
- package/dist/i18n/zh-TW.js +1 -1
- package/dist/src/api/_base.d.ts +7 -0
- package/dist/src/api/_options.d.ts +5 -0
- package/dist/src/components/checker/index.d.ts +2 -0
- package/dist/src/i18n/en.d.ts +1 -0
- package/dist/src/i18n/external.d.ts +1 -0
- package/dist/src/i18n/index.d.ts +3 -0
- package/dist/src/i18n/jp.d.ts +1 -0
- package/dist/src/i18n/zh-TW.d.ts +1 -0
- package/dist/src/lib/user.d.ts +8 -6
- package/dist/src/plugins/editor/_kit.d.ts +7 -1
- package/dist/src/service.d.ts +2 -8
- package/dist/types/event.d.ts +1 -1
- package/package.json +43 -17
- package/dist/src/components/dark-mode.d.ts +0 -2
- package/src/api/_base.ts +0 -18
- package/src/api/_options.ts +0 -25
- package/src/api/_request.ts +0 -104
- package/src/api/admin.ts +0 -18
- package/src/api/captcha.ts +0 -24
- package/src/api/comment.ts +0 -84
- package/src/api/index.ts +0 -29
- package/src/api/page.ts +0 -83
- package/src/api/site.ts +0 -51
- package/src/api/system.ts +0 -49
- package/src/api/upload.ts +0 -28
- package/src/api/user.ts +0 -132
- package/src/artalk.ts +0 -115
- package/src/comment/actions.ts +0 -83
- package/src/comment/comment.html +0 -18
- package/src/comment/comment.ts +0 -192
- package/src/comment/height-limit.ts +0 -100
- package/src/comment/index.ts +0 -3
- package/src/comment/render.ts +0 -150
- package/src/comment/renders/actions.ts +0 -114
- package/src/comment/renders/avatar.ts +0 -22
- package/src/comment/renders/content.ts +0 -39
- package/src/comment/renders/header.ts +0 -84
- package/src/comment/renders/index.ts +0 -19
- package/src/comment/renders/pending.ts +0 -12
- package/src/comment/renders/reply-at.ts +0 -16
- package/src/comment/renders/reply-to.ts +0 -24
- package/src/components/action-btn.ts +0 -170
- package/src/components/checker/admin.ts +0 -37
- package/src/components/checker/captcha-renders.ts +0 -69
- package/src/components/checker/captcha.ts +0 -26
- package/src/components/checker/index.ts +0 -192
- package/src/components/dark-mode.ts +0 -30
- package/src/components/dialog.ts +0 -62
- package/src/components/error-dialog.ts +0 -35
- package/src/components/pagination.ts +0 -153
- package/src/components/read-more-btn.ts +0 -92
- package/src/config.ts +0 -119
- package/src/context.ts +0 -146
- package/src/data.ts +0 -110
- package/src/defaults.ts +0 -61
- package/src/editor/editor.html +0 -18
- package/src/editor/editor.ts +0 -114
- package/src/editor/state.ts +0 -52
- package/src/editor/ui.ts +0 -36
- package/src/i18n/en.ts +0 -84
- package/src/i18n/external.ts +0 -10
- package/src/i18n/index.ts +0 -67
- package/src/i18n/jp.ts +0 -86
- package/src/i18n/zh-CN.ts +0 -88
- package/src/i18n/zh-TW.ts +0 -86
- package/src/layer/index.ts +0 -4
- package/src/layer/layer-manager.ts +0 -32
- package/src/layer/layer.ts +0 -39
- package/src/layer/scrollbar-helper.ts +0 -24
- package/src/layer/sidebar-layer.html +0 -8
- package/src/layer/sidebar-layer.ts +0 -155
- package/src/layer/wrap.ts +0 -77
- package/src/lib/component.ts +0 -12
- package/src/lib/detect.ts +0 -222
- package/src/lib/event-manager.ts +0 -47
- package/src/lib/highlight.ts +0 -5
- package/src/lib/marked-renderer.ts +0 -32
- package/src/lib/marked.ts +0 -72
- package/src/lib/sanitizer.ts +0 -57
- package/src/lib/ui.ts +0 -193
- package/src/lib/user.ts +0 -55
- package/src/lib/utils.ts +0 -236
- package/src/list/comment.ts +0 -20
- package/src/list/layout.ts +0 -120
- package/src/list/list.html +0 -20
- package/src/list/list.ts +0 -79
- package/src/list/nest.ts +0 -66
- package/src/list/page.ts +0 -97
- package/src/list/paginator/index.ts +0 -25
- package/src/list/paginator/read-more.ts +0 -67
- package/src/list/paginator/up-down.ts +0 -51
- package/src/main.ts +0 -14
- package/src/plugins/admin-only-elem.ts +0 -40
- package/src/plugins/conf-remoter.ts +0 -54
- package/src/plugins/dark-mode.ts +0 -12
- package/src/plugins/editor/_kit.ts +0 -57
- package/src/plugins/editor/_plug.ts +0 -60
- package/src/plugins/editor/_sample.ts +0 -8
- package/src/plugins/editor/closable.ts +0 -44
- package/src/plugins/editor/emoticons.scss +0 -72
- package/src/plugins/editor/emoticons.ts +0 -293
- package/src/plugins/editor/header-event.ts +0 -35
- package/src/plugins/editor/header-link.ts +0 -31
- package/src/plugins/editor/header-user.ts +0 -98
- package/src/plugins/editor/index.ts +0 -40
- package/src/plugins/editor/local-storage.ts +0 -36
- package/src/plugins/editor/mover.ts +0 -29
- package/src/plugins/editor/preview.scss +0 -7
- package/src/plugins/editor/preview.ts +0 -41
- package/src/plugins/editor/state-edit.ts +0 -116
- package/src/plugins/editor/state-reply.ts +0 -91
- package/src/plugins/editor/submit-add.ts +0 -33
- package/src/plugins/editor/submit-btn.ts +0 -25
- package/src/plugins/editor/submit.ts +0 -73
- package/src/plugins/editor/textarea.ts +0 -55
- package/src/plugins/editor/upload.ts +0 -145
- package/src/plugins/editor-kit.ts +0 -161
- package/src/plugins/index.ts +0 -16
- package/src/plugins/list/copyright.ts +0 -16
- package/src/plugins/list/count.ts +0 -31
- package/src/plugins/list/dropdown.ts +0 -95
- package/src/plugins/list/error-dialog.ts +0 -22
- package/src/plugins/list/fetch.ts +0 -64
- package/src/plugins/list/goto-first.ts +0 -23
- package/src/plugins/list/goto.ts +0 -72
- package/src/plugins/list/index.ts +0 -25
- package/src/plugins/list/loading.ts +0 -17
- package/src/plugins/list/no-comment.ts +0 -25
- package/src/plugins/list/reach-bottom.ts +0 -46
- package/src/plugins/list/sidebar-btn.ts +0 -39
- package/src/plugins/list/time-ticking.ts +0 -22
- package/src/plugins/list/unread-badge.ts +0 -26
- package/src/plugins/list/unread.ts +0 -38
- package/src/plugins/list/with-editor.ts +0 -59
- package/src/plugins/markdown.ts +0 -10
- package/src/plugins/stat.ts +0 -82
- package/src/plugins/version-check.ts +0 -36
- package/src/service.ts +0 -91
- package/src/style/_color.scss +0 -41
- package/src/style/_extend.scss +0 -20
- package/src/style/_variables.scss +0 -3
- package/src/style/comment.scss +0 -360
- package/src/style/editor.scss +0 -194
- package/src/style/list.scss +0 -212
- package/src/style/main.scss +0 -486
- package/src/style/pagination.scss +0 -58
- package/src/style/sidebar-layer.scss +0 -92
- package/types/config.ts +0 -153
- package/types/context.ts +0 -84
- package/types/data.ts +0 -271
- package/types/editor.ts +0 -95
- package/types/event.ts +0 -40
- package/types/global.d.ts +0 -1
- package/types/index.ts +0 -7
- package/types/plugin.ts +0 -3
- package/types/sidebar.ts +0 -3
package/src/lib/event-manager.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
export type EventHandler<T> = (payload: T) => void
|
|
2
|
-
export interface Event<PayloadMap, K extends keyof PayloadMap = keyof PayloadMap> extends EventOptions {
|
|
3
|
-
name: K
|
|
4
|
-
handler: EventHandler<PayloadMap[K]>
|
|
5
|
-
}
|
|
6
|
-
export interface EventOptions {
|
|
7
|
-
once?: boolean
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface EventManagerFuncs<PayloadMap> {
|
|
11
|
-
on<K extends keyof PayloadMap>(name: K, handler: EventHandler<PayloadMap[K]>, opts?: EventOptions): void
|
|
12
|
-
off<K extends keyof PayloadMap>(name: K, handler: EventHandler<PayloadMap[K]>): void
|
|
13
|
-
trigger<K extends keyof PayloadMap>(name: K, payload?: PayloadMap[K]): void
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export default class EventManager<PayloadMap> implements EventManagerFuncs<PayloadMap> {
|
|
17
|
-
private events: Event<PayloadMap>[] = []
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Add an event listener for a specific event name
|
|
21
|
-
*/
|
|
22
|
-
public on<K extends keyof PayloadMap>(name: K, handler: EventHandler<PayloadMap[K]>, opts: EventOptions = {}) {
|
|
23
|
-
this.events.push({ name, handler: handler as EventHandler<PayloadMap[keyof PayloadMap]>, ...opts })
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Remove an event listener for a specific event name and handler
|
|
28
|
-
*/
|
|
29
|
-
public off<K extends keyof PayloadMap>(name: K, handler: EventHandler<PayloadMap[K]>) {
|
|
30
|
-
if (!handler) return // not allow remove all events with same name
|
|
31
|
-
this.events = this.events.filter((evt) =>
|
|
32
|
-
!(evt.name === name && evt.handler === handler))
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Trigger an event with an optional payload
|
|
37
|
-
*/
|
|
38
|
-
public trigger<K extends keyof PayloadMap>(name: K, payload?: PayloadMap[K]) {
|
|
39
|
-
this.events
|
|
40
|
-
.slice(0) // make a copy, in case listeners are removed while iterating
|
|
41
|
-
.filter((evt) => evt.name === name && typeof evt.handler === 'function')
|
|
42
|
-
.forEach((evt) => {
|
|
43
|
-
if (evt.once) this.off(name, evt.handler)
|
|
44
|
-
evt.handler(payload!)
|
|
45
|
-
})
|
|
46
|
-
}
|
|
47
|
-
}
|
package/src/lib/highlight.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { marked as libMarked } from 'marked'
|
|
2
|
-
import { renderCode } from './highlight'
|
|
3
|
-
|
|
4
|
-
export function getRenderer() {
|
|
5
|
-
const renderer = new libMarked.Renderer()
|
|
6
|
-
renderer.link = markedLinkRenderer(renderer, renderer.link)
|
|
7
|
-
renderer.code = markedCodeRenderer()
|
|
8
|
-
return renderer
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const markedLinkRenderer = (renderer: any, orgLinkRenderer: Function) => (href: string, title: string, text: string): string => {
|
|
12
|
-
const localLink = href?.startsWith(`${window.location.protocol}//${window.location.hostname}`);
|
|
13
|
-
const html = orgLinkRenderer.call(renderer, href, title, text);
|
|
14
|
-
return html.replace(/^<a /, `<a target="_blank" ${!localLink ? `rel="noreferrer noopener nofollow"` : ''} `);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const markedCodeRenderer = () => (block: string, lang: string | undefined): string => {
|
|
18
|
-
// Colorize the block only if the language is known to highlight.js
|
|
19
|
-
const realLang = (!lang ? 'plaintext' : lang)
|
|
20
|
-
let colorized = block
|
|
21
|
-
if ((window as any).hljs) {
|
|
22
|
-
if (realLang && (window as any).hljs.getLanguage(realLang)) {
|
|
23
|
-
colorized = (window as any).hljs.highlight(realLang, block).value
|
|
24
|
-
}
|
|
25
|
-
} else {
|
|
26
|
-
colorized = renderCode(block)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return `<pre rel="${realLang}">\n`
|
|
30
|
-
+ `<code class="hljs language-${realLang}">${colorized.replace(/&/g, '&')}</code>\n`
|
|
31
|
-
+ `</pre>`
|
|
32
|
-
}
|
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
|
-
}
|
package/src/lib/sanitizer.ts
DELETED
|
@@ -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
|