artalk 2.8.5 → 2.8.7

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.
@@ -142,11 +142,14 @@ class HttpClient {
142
142
  /* Json */
143
143
  ];
144
144
  const responseFormat = format || requestParams.format;
145
- return this.customFetch(`${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`, __spreadProps(__spreadValues({}, requestParams), {
146
- headers: __spreadValues(__spreadValues({}, requestParams.headers || {}), type && type !== "multipart/form-data" ? { "Content-Type": type } : {}),
147
- signal: (cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal) || null,
148
- body: typeof body === "undefined" || body === null ? null : payloadFormatter(body)
149
- })).then((response) => __async(this, null, function* () {
145
+ return this.customFetch(
146
+ `${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`,
147
+ __spreadProps(__spreadValues({}, requestParams), {
148
+ headers: __spreadValues(__spreadValues({}, requestParams.headers || {}), type && type !== "multipart/form-data" ? { "Content-Type": type } : {}),
149
+ signal: (cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal) || null,
150
+ body: typeof body === "undefined" || body === null ? null : payloadFormatter(body)
151
+ })
152
+ ).then((response) => __async(this, null, function* () {
150
153
  const r = response;
151
154
  r.data = null;
152
155
  r.error = null;
@@ -185,7 +188,9 @@ class HttpClient {
185
188
  toQueryString(rawQuery) {
186
189
  const query = rawQuery || {};
187
190
  const keys = Object.keys(query).filter((key) => "undefined" !== typeof query[key]);
188
- return keys.map((key) => Array.isArray(query[key]) ? this.addArrayQueryParam(query, key) : this.addQueryParam(query, key)).join("&");
191
+ return keys.map(
192
+ (key) => Array.isArray(query[key]) ? this.addArrayQueryParam(query, key) : this.addQueryParam(query, key)
193
+ ).join("&");
189
194
  }
190
195
  addQueryParams(rawQuery) {
191
196
  const queryString = this.toQueryString(rawQuery);
@@ -209,6 +214,133 @@ class HttpClient {
209
214
  let Api$1 = class Api extends HttpClient {
210
215
  constructor() {
211
216
  super(...arguments);
217
+ __publicField(this, "auth", {
218
+ /**
219
+ * @description Login by email with verify code (Need send email verify code first) or password
220
+ *
221
+ * @tags Auth
222
+ * @name LoginByEmail
223
+ * @summary Login by email
224
+ * @request POST:/auth/email/login
225
+ * @response `200` `HandlerResponseUserLogin` OK
226
+ * @response `400` `(HandlerMap & {
227
+ msg?: string,
228
+
229
+ })` Bad Request
230
+ * @response `500` `(HandlerMap & {
231
+ msg?: string,
232
+
233
+ })` Internal Server Error
234
+ */
235
+ loginByEmail: (data, params = {}) => this.request(__spreadValues({
236
+ path: `/auth/email/login`,
237
+ method: "POST",
238
+ body: data,
239
+ type: "application/json",
240
+ format: "json"
241
+ }, params)),
242
+ /**
243
+ * @description Register by email and verify code (if user exists, will update user, like forget password. Need send email verify code first)
244
+ *
245
+ * @tags Auth
246
+ * @name RegisterByEmail
247
+ * @summary Register by email
248
+ * @request POST:/auth/email/register
249
+ * @response `200` `HandlerResponseUserLogin` OK
250
+ * @response `400` `(HandlerMap & {
251
+ msg?: string,
252
+
253
+ })` Bad Request
254
+ * @response `500` `(HandlerMap & {
255
+ msg?: string,
256
+
257
+ })` Internal Server Error
258
+ */
259
+ registerByEmail: (data, params = {}) => this.request(__spreadValues({
260
+ path: `/auth/email/register`,
261
+ method: "POST",
262
+ body: data,
263
+ type: "application/json",
264
+ format: "json"
265
+ }, params)),
266
+ /**
267
+ * @description Send email including verify code to user
268
+ *
269
+ * @tags Auth
270
+ * @name SendVerifyEmail
271
+ * @summary Send verify email
272
+ * @request POST:/auth/email/send
273
+ * @response `200` `(HandlerMap & {
274
+ msg?: string,
275
+
276
+ })` OK
277
+ * @response `400` `(HandlerMap & {
278
+ msg?: string,
279
+
280
+ })` Bad Request
281
+ * @response `500` `(HandlerMap & {
282
+ msg?: string,
283
+
284
+ })` Internal Server Error
285
+ */
286
+ sendVerifyEmail: (data, params = {}) => this.request(__spreadValues({
287
+ path: `/auth/email/send`,
288
+ method: "POST",
289
+ body: data,
290
+ type: "application/json",
291
+ format: "json"
292
+ }, params)),
293
+ /**
294
+ * @description Get all users with same email, if there are more than one user with same email, need merge
295
+ *
296
+ * @tags Auth
297
+ * @name CheckDataMerge
298
+ * @summary Check data merge
299
+ * @request GET:/auth/merge
300
+ * @secure
301
+ * @response `200` `HandlerResponseAuthDataMergeCheck` OK
302
+ * @response `400` `(HandlerMap & {
303
+ msg?: string,
304
+
305
+ })` Bad Request
306
+ * @response `500` `(HandlerMap & {
307
+ msg?: string,
308
+
309
+ })` Internal Server Error
310
+ */
311
+ checkDataMerge: (params = {}) => this.request(__spreadValues({
312
+ path: `/auth/merge`,
313
+ method: "GET",
314
+ secure: true,
315
+ format: "json"
316
+ }, params)),
317
+ /**
318
+ * @description This function is to solve the problem of multiple users with the same email address, should be called after user login and then check, and perform data merge.
319
+ *
320
+ * @tags Auth
321
+ * @name ApplyDataMerge
322
+ * @summary Apply data merge
323
+ * @request POST:/auth/merge
324
+ * @secure
325
+ * @response `200` `HandlerResponseAuthDataMergeApply` OK
326
+ * @response `400` `(HandlerMap & {
327
+ msg?: string,
328
+
329
+ })` Bad Request
330
+ * @response `500` `(HandlerMap & {
331
+ msg?: string,
332
+
333
+ })` Internal Server Error
334
+ */
335
+ applyDataMerge: (data, params = {}) => this.request(__spreadValues({
336
+ path: `/auth/merge`,
337
+ method: "POST",
338
+ body: data,
339
+ secure: true,
340
+ type: "application/json",
341
+ format: "json"
342
+ }, params))
343
+ });
212
344
  __publicField(this, "cache", {
213
345
  /**
214
346
  * @description Flush all cache on the server
@@ -469,6 +601,24 @@ let Api$1 = class Api extends HttpClient {
469
601
  method: "GET",
470
602
  format: "json"
471
603
  }, params)),
604
+ /**
605
+ * @description Get social login providers
606
+ *
607
+ * @tags System
608
+ * @name GetSocialLoginProviders
609
+ * @summary Get Social Login Providers
610
+ * @request GET:/conf/auth/providers
611
+ * @response `200` `HandlerResponseConfAuthProviders` OK
612
+ * @response `404` `(HandlerMap & {
613
+ msg?: string,
614
+
615
+ })` Not Found
616
+ */
617
+ getSocialLoginProviders: (params = {}) => this.request(__spreadValues({
618
+ path: `/conf/auth/providers`,
619
+ method: "GET",
620
+ format: "json"
621
+ }, params)),
472
622
  /**
473
623
  * @description Get Domain Info
474
624
  *
@@ -1328,8 +1478,6 @@ function getDefaultExportFromCjs(x) {
1328
1478
  function _createClass(Constructor, protoProps, staticProps) {
1329
1479
  if (protoProps)
1330
1480
  _defineProperties(Constructor.prototype, protoProps);
1331
- if (staticProps)
1332
- _defineProperties(Constructor, staticProps);
1333
1481
  Object.defineProperty(Constructor, "prototype", {
1334
1482
  writable: false
1335
1483
  });
@@ -2228,7 +2376,7 @@ const insaneOptions = {
2228
2376
  allowedAttributes: {
2229
2377
  "*": ["title", "accesskey"],
2230
2378
  a: ["href", "name", "target", "aria-label", "rel"],
2231
- img: ["src", "alt", "title", "atk-emoticon", "aria-label"],
2379
+ img: ["src", "alt", "title", "atk-emoticon", "aria-label", "data-src", "class", "loading"],
2232
2380
  // for code highlight
2233
2381
  code: ["class"],
2234
2382
  span: ["class", "style"]
@@ -2236,7 +2384,8 @@ const insaneOptions = {
2236
2384
  filter: (node) => {
2237
2385
  const allowed = [
2238
2386
  ["code", /^hljs\W+language-(.*)$/],
2239
- ["span", /^(hljs-.*)$/]
2387
+ ["span", /^(hljs-.*)$/],
2388
+ ["img", /^lazyload$/]
2240
2389
  ];
2241
2390
  allowed.forEach(([tag, reg]) => {
2242
2391
  if (node.tag === tag && !!node.attrs.class && !reg.test(node.attrs.class)) {
@@ -2313,18 +2462,26 @@ const hanabi = /* @__PURE__ */ getDefaultExportFromCjs(hanabiExports);
2313
2462
  function renderCode(code) {
2314
2463
  return hanabi(code);
2315
2464
  }
2316
- function getRenderer() {
2465
+ function getRenderer(options) {
2317
2466
  const renderer = new marked$1.Renderer();
2318
2467
  renderer.link = markedLinkRenderer(renderer, renderer.link);
2319
2468
  renderer.code = markedCodeRenderer();
2469
+ renderer.image = markedImageRenderer(renderer, renderer.image, options);
2320
2470
  return renderer;
2321
2471
  }
2322
2472
  const markedLinkRenderer = (renderer, orgLinkRenderer) => (href, title, text) => {
2323
- const localLink = href == null ? void 0 : href.startsWith(`${window.location.protocol}//${window.location.hostname}`);
2473
+ const getLinkOrigin = (link) => {
2474
+ try {
2475
+ return new URL(link).origin;
2476
+ } catch (e) {
2477
+ return "";
2478
+ }
2479
+ };
2480
+ const isSameOriginLink = getLinkOrigin(href) === window.location.origin;
2324
2481
  const html = orgLinkRenderer.call(renderer, href, title, text);
2325
2482
  return html.replace(
2326
2483
  /^<a /,
2327
- `<a target="_blank" ${!localLink ? `rel="noreferrer noopener nofollow"` : ""} `
2484
+ `<a target="_blank" ${!isSameOriginLink ? `rel="noreferrer noopener nofollow"` : ""} `
2328
2485
  );
2329
2486
  };
2330
2487
  const markedCodeRenderer = () => (block, lang) => {
@@ -2341,6 +2498,16 @@ const markedCodeRenderer = () => (block, lang) => {
2341
2498
  <code class="hljs language-${realLang}">${colorized.replace(/&amp;/g, "&")}</code>
2342
2499
  </pre>`;
2343
2500
  };
2501
+ const markedImageRenderer = (renderer, orgImageRenderer, { imgLazyLoad }) => (href, title, text) => {
2502
+ const html = orgImageRenderer.call(renderer, href, title, text);
2503
+ if (!imgLazyLoad)
2504
+ return html;
2505
+ if (imgLazyLoad === "native" || imgLazyLoad === true)
2506
+ return html.replace(/^<img /, '<img class="lazyload" loading="lazy" ');
2507
+ if (imgLazyLoad === "data-src")
2508
+ return html.replace(/^<img /, '<img class="lazyload" ').replace("src=", "data-src=");
2509
+ return html;
2510
+ };
2344
2511
  let instance;
2345
2512
  let replacers = [];
2346
2513
  const markedOptions = {
@@ -2354,16 +2521,18 @@ function getInstance() {
2354
2521
  function setReplacers(arr) {
2355
2522
  replacers = arr;
2356
2523
  }
2357
- function initMarked() {
2524
+ function initMarked(options) {
2358
2525
  try {
2359
2526
  if (!marked$1.name)
2360
2527
  return;
2361
2528
  } catch (e) {
2362
2529
  return;
2363
2530
  }
2364
- marked$1.setOptions(__spreadValues({
2365
- renderer: getRenderer()
2366
- }, markedOptions));
2531
+ marked$1.setOptions(__spreadValues(__spreadValues({
2532
+ renderer: getRenderer({
2533
+ imgLazyLoad: options.imgLazyLoad
2534
+ })
2535
+ }, markedOptions), options.markedOptions));
2367
2536
  instance = marked$1;
2368
2537
  }
2369
2538
  function marked(src) {
@@ -2494,176 +2663,14 @@ class DataManager {
2494
2663
  this.events.trigger("page-loaded", pageData);
2495
2664
  }
2496
2665
  }
2497
- const en = {
2498
- /* Editor */
2499
- placeholder: "Leave a comment",
2500
- noComment: "No Comment",
2501
- send: "Send",
2502
- save: "Save",
2503
- nick: "Nickname",
2504
- email: "Email",
2505
- link: "Website",
2506
- emoticon: "Emoji",
2507
- preview: "Preview",
2508
- uploadImage: "Upload Image",
2509
- uploadFail: "Upload Failed",
2510
- commentFail: "Failed to comment",
2511
- restoredMsg: "Content has been restored",
2512
- onlyAdminCanReply: "Only admin can reply",
2513
- uploadLoginMsg: "Please fill in your name and email to upload",
2514
- /* List */
2515
- counter: "{count} Comments",
2516
- sortLatest: "Latest",
2517
- sortOldest: "Oldest",
2518
- sortBest: "Best",
2519
- sortAuthor: "Author",
2520
- openComment: "Open Comment",
2521
- closeComment: "Close Comment",
2522
- listLoadFailMsg: "Failed to load comments",
2523
- listRetry: "Retry",
2524
- loadMore: "Load More",
2525
- /* Comment */
2526
- admin: "Admin",
2527
- reply: "Reply",
2528
- voteUp: "Up",
2529
- voteDown: "Down",
2530
- voteFail: "Vote Failed",
2531
- readMore: "Read More",
2532
- actionConfirm: "Confirm",
2533
- collapse: "Collapse",
2534
- collapsed: "Collapsed",
2535
- collapsedMsg: "This comment has been collapsed",
2536
- expand: "Expand",
2537
- approved: "Approved",
2538
- pending: "Pending",
2539
- pendingMsg: "Pending, visible only to commenter.",
2540
- edit: "Edit",
2541
- editCancel: "Cancel Edit",
2542
- delete: "Delete",
2543
- deleteConfirm: "Confirm",
2544
- pin: "Pin",
2545
- unpin: "Unpin",
2546
- /* Time */
2547
- seconds: "seconds ago",
2548
- minutes: "minutes ago",
2549
- hours: "hours ago",
2550
- days: "days ago",
2551
- now: "just now",
2552
- /* Checker */
2553
- adminCheck: "Enter admin password:",
2554
- captchaCheck: "Enter the CAPTCHA to continue:",
2555
- confirm: "Confirm",
2556
- cancel: "Cancel",
2557
- /* Sidebar */
2558
- msgCenter: "Messages",
2559
- ctrlCenter: "Admin",
2560
- emailVerified: "Email has been verified",
2561
- /* General */
2562
- frontend: "Frontend",
2563
- backend: "Backend",
2564
- loading: "Loading",
2565
- loadFail: "Load Failed",
2566
- editing: "Editing",
2567
- editFail: "Edit Failed",
2568
- deleting: "Deleting",
2569
- deleteFail: "Delete Failed",
2570
- reqGot: "Request got",
2571
- reqAborted: "Request timed out or terminated unexpectedly",
2572
- updateMsg: "Please update Artalk {name} to get the best experience!",
2573
- currentVersion: "Current Version",
2574
- ignore: "Ignore",
2575
- open: "Open",
2576
- openName: "Open {name}"
2577
- };
2578
- const zhCN = {
2579
- /* Editor */
2580
- placeholder: "键入内容...",
2581
- noComment: "「此时无声胜有声」",
2582
- send: "发送",
2583
- save: "保存",
2584
- nick: "昵称",
2585
- email: "邮箱",
2586
- link: "网址",
2587
- emoticon: "表情",
2588
- preview: "预览",
2589
- uploadImage: "上传图片",
2590
- uploadFail: "上传失败",
2591
- commentFail: "评论失败",
2592
- restoredMsg: "内容已自动恢复",
2593
- onlyAdminCanReply: "仅管理员可评论",
2594
- uploadLoginMsg: "填入你的名字邮箱才能上传哦",
2595
- /* List */
2596
- counter: "{count} 条评论",
2597
- sortLatest: "最新",
2598
- sortOldest: "最早",
2599
- sortBest: "最热",
2600
- sortAuthor: "作者",
2601
- openComment: "打开评论",
2602
- closeComment: "关闭评论",
2603
- listLoadFailMsg: "无法获取评论列表数据",
2604
- listRetry: "点击重新获取",
2605
- loadMore: "加载更多",
2606
- /* Comment */
2607
- admin: "管理员",
2608
- reply: "回复",
2609
- voteUp: "赞同",
2610
- voteDown: "反对",
2611
- voteFail: "投票失败",
2612
- readMore: "阅读更多",
2613
- actionConfirm: "确认操作",
2614
- collapse: "折叠",
2615
- collapsed: "已折叠",
2616
- collapsedMsg: "该评论已被系统或管理员折叠",
2617
- expand: "展开",
2618
- approved: "已审",
2619
- pending: "待审",
2620
- pendingMsg: "审核中,仅本人可见。",
2621
- edit: "编辑",
2622
- editCancel: "取消编辑",
2623
- delete: "删除",
2624
- deleteConfirm: "确认删除",
2625
- pin: "置顶",
2626
- unpin: "取消置顶",
2627
- /* Time */
2628
- seconds: "秒前",
2629
- minutes: "分钟前",
2630
- hours: "小时前",
2631
- days: "天前",
2632
- now: "刚刚",
2633
- /* Checker */
2634
- adminCheck: "键入密码来验证管理员身份:",
2635
- captchaCheck: "键入验证码继续:",
2636
- confirm: "确认",
2637
- cancel: "取消",
2638
- /* Sidebar */
2639
- msgCenter: "通知中心",
2640
- ctrlCenter: "控制中心",
2641
- emailVerified: "邮箱已验证",
2642
- /* General */
2643
- frontend: "前端",
2644
- backend: "后端",
2645
- loading: "加载中",
2646
- loadFail: "加载失败",
2647
- editing: "修改中",
2648
- editFail: "修改失败",
2649
- deleting: "删除中",
2650
- deleteFail: "删除失败",
2651
- reqGot: "请求响应",
2652
- reqAborted: "请求超时或意外终止",
2653
- updateMsg: "请更新 Artalk {name} 以获得更好的体验!",
2654
- currentVersion: "当前版本",
2655
- ignore: "忽略",
2656
- open: "打开",
2657
- openName: "打开{name}"
2658
- };
2659
- const GLOBAL_LOCALES_KEY = "ArtalkI18n";
2660
2666
  function createElement(htmlStr = "") {
2661
2667
  const div = document.createElement("div");
2662
2668
  div.innerHTML = htmlStr.trim();
2663
2669
  return div.firstElementChild || div;
2664
2670
  }
2665
2671
  function getHeight(el) {
2666
- return parseFloat(getComputedStyle(el, null).height.replace("px", ""));
2672
+ const num = parseFloat(getComputedStyle(el, null).height.replace("px", ""));
2673
+ return num || 0;
2667
2674
  }
2668
2675
  function htmlEncode(str) {
2669
2676
  const temp = document.createElement("div");
@@ -2741,29 +2748,8 @@ function timeAgo(date, $t = (n) => n) {
2741
2748
  return " - ";
2742
2749
  }
2743
2750
  }
2744
- function onImagesLoaded($container, event) {
2745
- if (!$container)
2746
- return;
2747
- const images = $container.getElementsByTagName("img");
2748
- if (!images.length)
2749
- return;
2750
- let loaded = images.length;
2751
- for (let i = 0; i < images.length; i++) {
2752
- if (images[i].complete) {
2753
- loaded--;
2754
- } else {
2755
- images[i].addEventListener("load", () => {
2756
- loaded--;
2757
- if (loaded === 0)
2758
- event();
2759
- });
2760
- }
2761
- if (loaded === 0)
2762
- event();
2763
- }
2764
- }
2765
2751
  function getGravatarURL(opts) {
2766
- return `${opts.mirror.replace(/\/$/, "")}/${opts.emailMD5}?${opts.params.replace(/^\?/, "")}`;
2752
+ return `${opts.mirror.replace(/\/$/, "")}/${opts.emailHash}?${opts.params.replace(/^\?/, "")}`;
2767
2753
  }
2768
2754
  function versionCompare(a, b) {
2769
2755
  const pa = a.split(".");
@@ -2828,6 +2814,211 @@ function getURLBasedOnApi(opts) {
2828
2814
  function getURLBasedOn(baseURL, path) {
2829
2815
  return `${baseURL.replace(/\/$/, "")}/${path.replace(/^\//, "")}`;
2830
2816
  }
2817
+ const en = {
2818
+ /* Editor */
2819
+ placeholder: "Leave a comment",
2820
+ noComment: "No Comment",
2821
+ send: "Send",
2822
+ signIn: "Sign in",
2823
+ signUp: "Sign up",
2824
+ save: "Save",
2825
+ nick: "Nickname",
2826
+ email: "Email",
2827
+ link: "Website",
2828
+ emoticon: "Emoji",
2829
+ preview: "Preview",
2830
+ uploadImage: "Upload Image",
2831
+ uploadFail: "Upload Failed",
2832
+ commentFail: "Failed to comment",
2833
+ restoredMsg: "Content has been restored",
2834
+ onlyAdminCanReply: "Only admin can reply",
2835
+ uploadLoginMsg: "Please fill in your name and email to upload",
2836
+ /* List */
2837
+ counter: "{count} Comments",
2838
+ sortLatest: "Latest",
2839
+ sortOldest: "Oldest",
2840
+ sortBest: "Best",
2841
+ sortAuthor: "Author",
2842
+ openComment: "Open Comment",
2843
+ closeComment: "Close Comment",
2844
+ listLoadFailMsg: "Failed to load comments",
2845
+ listRetry: "Retry",
2846
+ loadMore: "Load More",
2847
+ /* Comment */
2848
+ admin: "Admin",
2849
+ reply: "Reply",
2850
+ voteUp: "Up",
2851
+ voteDown: "Down",
2852
+ voteFail: "Vote Failed",
2853
+ readMore: "Read More",
2854
+ actionConfirm: "Confirm",
2855
+ collapse: "Collapse",
2856
+ collapsed: "Collapsed",
2857
+ collapsedMsg: "This comment has been collapsed",
2858
+ expand: "Expand",
2859
+ approved: "Approved",
2860
+ pending: "Pending",
2861
+ pendingMsg: "Pending, visible only to commenter.",
2862
+ edit: "Edit",
2863
+ editCancel: "Cancel Edit",
2864
+ delete: "Delete",
2865
+ deleteConfirm: "Confirm",
2866
+ pin: "Pin",
2867
+ unpin: "Unpin",
2868
+ /* Time */
2869
+ seconds: "seconds ago",
2870
+ minutes: "minutes ago",
2871
+ hours: "hours ago",
2872
+ days: "days ago",
2873
+ now: "just now",
2874
+ /* Checker */
2875
+ adminCheck: "Enter admin password:",
2876
+ captchaCheck: "Enter the CAPTCHA to continue:",
2877
+ confirm: "Confirm",
2878
+ cancel: "Cancel",
2879
+ /* Sidebar */
2880
+ msgCenter: "Messages",
2881
+ ctrlCenter: "Admin",
2882
+ /* Auth */
2883
+ noAccountPrompt: "Don't have an account?",
2884
+ haveAccountPrompt: "Already have an account?",
2885
+ forgetPassword: "Forget Password",
2886
+ resetPassword: "Reset Password",
2887
+ verificationCode: "Verification Code",
2888
+ verifySend: "Verify",
2889
+ verifyResend: "Resend",
2890
+ waitSeconds: "Wait {seconds}s",
2891
+ emailVerified: "Email has been verified",
2892
+ password: "Password",
2893
+ username: "Username",
2894
+ nextStep: "Next Step",
2895
+ skipNotVerify: "Skip, do not verify",
2896
+ logoutConfirm: "Are you sure to logout?",
2897
+ accountMergeNotice: "Your email has multiple accounts with different id.",
2898
+ accountMergeSelectOne: "Please select one you want to merge all the data into it.",
2899
+ accountMergeConfirm: "All data will be merged into one account, the id is {id}.",
2900
+ dismiss: "Dismiss",
2901
+ merge: "Merge",
2902
+ /* General */
2903
+ frontend: "Frontend",
2904
+ backend: "Backend",
2905
+ loading: "Loading",
2906
+ loadFail: "Load Failed",
2907
+ editing: "Editing",
2908
+ editFail: "Edit Failed",
2909
+ deleting: "Deleting",
2910
+ deleteFail: "Delete Failed",
2911
+ reqGot: "Request got",
2912
+ reqAborted: "Request timed out or terminated unexpectedly",
2913
+ updateMsg: "Please update Artalk {name} to get the best experience!",
2914
+ currentVersion: "Current Version",
2915
+ ignore: "Ignore",
2916
+ open: "Open",
2917
+ openName: "Open {name}"
2918
+ };
2919
+ const zhCN = {
2920
+ /* Editor */
2921
+ placeholder: "键入内容...",
2922
+ noComment: "「此时无声胜有声」",
2923
+ send: "发送",
2924
+ signIn: "登录",
2925
+ signUp: "注册",
2926
+ save: "保存",
2927
+ nick: "昵称",
2928
+ email: "邮箱",
2929
+ link: "网址",
2930
+ emoticon: "表情",
2931
+ preview: "预览",
2932
+ uploadImage: "上传图片",
2933
+ uploadFail: "上传失败",
2934
+ commentFail: "评论失败",
2935
+ restoredMsg: "内容已自动恢复",
2936
+ onlyAdminCanReply: "仅管理员可评论",
2937
+ uploadLoginMsg: "填入你的名字邮箱才能上传哦",
2938
+ /* List */
2939
+ counter: "{count} 条评论",
2940
+ sortLatest: "最新",
2941
+ sortOldest: "最早",
2942
+ sortBest: "最热",
2943
+ sortAuthor: "作者",
2944
+ openComment: "打开评论",
2945
+ closeComment: "关闭评论",
2946
+ listLoadFailMsg: "无法获取评论列表数据",
2947
+ listRetry: "点击重新获取",
2948
+ loadMore: "加载更多",
2949
+ /* Comment */
2950
+ admin: "管理员",
2951
+ reply: "回复",
2952
+ voteUp: "赞同",
2953
+ voteDown: "反对",
2954
+ voteFail: "投票失败",
2955
+ readMore: "阅读更多",
2956
+ actionConfirm: "确认操作",
2957
+ collapse: "折叠",
2958
+ collapsed: "已折叠",
2959
+ collapsedMsg: "该评论已被系统或管理员折叠",
2960
+ expand: "展开",
2961
+ approved: "已审",
2962
+ pending: "待审",
2963
+ pendingMsg: "审核中,仅本人可见。",
2964
+ edit: "编辑",
2965
+ editCancel: "取消编辑",
2966
+ delete: "删除",
2967
+ deleteConfirm: "确认删除",
2968
+ pin: "置顶",
2969
+ unpin: "取消置顶",
2970
+ /* Time */
2971
+ seconds: "秒前",
2972
+ minutes: "分钟前",
2973
+ hours: "小时前",
2974
+ days: "天前",
2975
+ now: "刚刚",
2976
+ /* Checker */
2977
+ adminCheck: "键入密码来验证管理员身份:",
2978
+ captchaCheck: "键入验证码继续:",
2979
+ confirm: "确认",
2980
+ cancel: "取消",
2981
+ /* Sidebar */
2982
+ msgCenter: "通知中心",
2983
+ ctrlCenter: "控制中心",
2984
+ /* Auth */
2985
+ noAccountPrompt: "没有账号?",
2986
+ haveAccountPrompt: "已有账号?",
2987
+ forgetPassword: "忘记密码",
2988
+ resetPassword: "重置密码",
2989
+ verificationCode: "验证码",
2990
+ verifySend: "发送验证码",
2991
+ verifyResend: "重新发送",
2992
+ waitSeconds: "等待 {seconds}秒",
2993
+ emailVerified: "邮箱已验证",
2994
+ password: "密码",
2995
+ username: "用户名",
2996
+ nextStep: "下一步",
2997
+ skipNotVerify: "跳过,不验证",
2998
+ logoutConfirm: "确定要退出登录吗?",
2999
+ accountMergeNotice: "您的电子邮件下有多个不同 ID 的账户。",
3000
+ accountMergeSelectOne: "请选择将所有数据合并到其中的一个。",
3001
+ accountMergeConfirm: "所有数据将合并到 ID 为 {id} 的账户中。",
3002
+ dismiss: "忽略",
3003
+ merge: "合并",
3004
+ /* General */
3005
+ frontend: "前端",
3006
+ backend: "后端",
3007
+ loading: "加载中",
3008
+ loadFail: "加载失败",
3009
+ editing: "修改中",
3010
+ editFail: "修改失败",
3011
+ deleting: "删除中",
3012
+ deleteFail: "删除失败",
3013
+ reqGot: "请求响应",
3014
+ reqAborted: "请求超时或意外终止",
3015
+ updateMsg: "请更新 Artalk {name} 以获得更好的体验!",
3016
+ currentVersion: "当前版本",
3017
+ ignore: "忽略",
3018
+ open: "打开",
3019
+ openName: "打开{name}"
3020
+ };
3021
+ const GLOBAL_LOCALES_KEY = "ArtalkI18n";
2831
3022
  const internal = {
2832
3023
  en,
2833
3024
  "en-US": en,
@@ -2911,11 +3102,12 @@ const defaults = {
2911
3102
  uaBadge: true,
2912
3103
  listSort: true,
2913
3104
  preview: true,
2914
- countEl: "#ArtalkCount",
2915
- pvEl: "#ArtalkPV",
3105
+ countEl: ".artalk-comment-count",
3106
+ pvEl: ".artalk-pv-count",
3107
+ statPageKeyAttr: "data-page-key",
2916
3108
  gravatar: {
2917
- mirror: "https://cravatar.cn/avatar/",
2918
- params: "d=mp&s=240"
3109
+ mirror: "https://www.gravatar.com/avatar/",
3110
+ params: "sha256=1&d=mp&s=240"
2919
3111
  },
2920
3112
  pagination: {
2921
3113
  pageSize: 20,
@@ -2961,16 +3153,20 @@ function handelCustomConf(customConf, full = false) {
2961
3153
  return conf;
2962
3154
  }
2963
3155
  function handleConfFormServer(conf) {
2964
- const DisabledKeys = [
3156
+ const ExcludedKeys = [
2965
3157
  "el",
2966
3158
  "pageKey",
2967
3159
  "pageTitle",
2968
3160
  "server",
2969
3161
  "site",
2970
- "darkMode"
3162
+ "pvEl",
3163
+ "countEl",
3164
+ "statPageKeyAttr"
2971
3165
  ];
2972
3166
  Object.keys(conf).forEach((k) => {
2973
- if (DisabledKeys.includes(k))
3167
+ if (ExcludedKeys.includes(k))
3168
+ delete conf[k];
3169
+ if (k === "darkMode" && conf[k] !== "auto")
2974
3170
  delete conf[k];
2975
3171
  });
2976
3172
  if (conf.emoticons && typeof conf.emoticons === "string") {
@@ -3147,50 +3343,6 @@ class Context {
3147
3343
  watchConf(this, keys, effect);
3148
3344
  }
3149
3345
  }
3150
- class Dialog {
3151
- constructor(contentEl) {
3152
- __publicField(this, "$el");
3153
- __publicField(this, "$content");
3154
- __publicField(this, "$actions");
3155
- this.$el = createElement(
3156
- `<div class="atk-layer-dialog-wrap">
3157
- <div class="atk-layer-dialog">
3158
- <div class="atk-layer-dialog-content"></div>
3159
- <div class="atk-layer-dialog-actions"></div>
3160
- </div>
3161
- </div>`
3162
- );
3163
- this.$actions = this.$el.querySelector(".atk-layer-dialog-actions");
3164
- this.$content = this.$el.querySelector(".atk-layer-dialog-content");
3165
- this.$content.appendChild(contentEl);
3166
- }
3167
- /** 按钮 · 确定 */
3168
- setYes(handler) {
3169
- const btn = createElement(
3170
- `<button data-action="confirm">${t("confirm")}</button>`
3171
- );
3172
- btn.onclick = this.onBtnClick(handler);
3173
- this.$actions.appendChild(btn);
3174
- return this;
3175
- }
3176
- /** 按钮 · 取消 */
3177
- setNo(handler) {
3178
- const btn = createElement(
3179
- `<button data-action="cancel">${t("cancel")}</button>`
3180
- );
3181
- btn.onclick = this.onBtnClick(handler);
3182
- this.$actions.appendChild(btn);
3183
- return this;
3184
- }
3185
- onBtnClick(handler) {
3186
- return (evt) => {
3187
- const re = handler(evt.currentTarget, this);
3188
- if (re === void 0 || re === true) {
3189
- this.$el.remove();
3190
- }
3191
- };
3192
- }
3193
- }
3194
3346
  function showLoading(parentElem, conf) {
3195
3347
  let $loading = parentElem.querySelector(":scope > .atk-loading");
3196
3348
  if (!$loading) {
@@ -3279,8 +3431,6 @@ function playFadeAnim(elem, after, type = "in") {
3279
3431
  const onAnimEnded = () => {
3280
3432
  elem.classList.remove(`atk-fade-${type}`);
3281
3433
  elem.removeEventListener("animationend", onAnimEnded);
3282
- if (after)
3283
- after();
3284
3434
  };
3285
3435
  elem.addEventListener("animationend", onAnimEnded);
3286
3436
  }
@@ -3438,6 +3588,50 @@ const AdminChecker = {
3438
3588
  onError(checker, err, inputVal, formEl) {
3439
3589
  }
3440
3590
  };
3591
+ class Dialog {
3592
+ constructor(contentEl) {
3593
+ __publicField(this, "$el");
3594
+ __publicField(this, "$content");
3595
+ __publicField(this, "$actions");
3596
+ this.$el = createElement(
3597
+ `<div class="atk-layer-dialog-wrap">
3598
+ <div class="atk-layer-dialog">
3599
+ <div class="atk-layer-dialog-content"></div>
3600
+ <div class="atk-layer-dialog-actions"></div>
3601
+ </div>
3602
+ </div>`
3603
+ );
3604
+ this.$actions = this.$el.querySelector(".atk-layer-dialog-actions");
3605
+ this.$content = this.$el.querySelector(".atk-layer-dialog-content");
3606
+ this.$content.appendChild(contentEl);
3607
+ }
3608
+ /** 按钮 · 确定 */
3609
+ setYes(handler) {
3610
+ const btn = createElement(
3611
+ `<button data-action="confirm">${t("confirm")}</button>`
3612
+ );
3613
+ btn.onclick = this.onBtnClick(handler);
3614
+ this.$actions.appendChild(btn);
3615
+ return this;
3616
+ }
3617
+ /** 按钮 · 取消 */
3618
+ setNo(handler) {
3619
+ const btn = createElement(
3620
+ `<button data-action="cancel">${t("cancel")}</button>`
3621
+ );
3622
+ btn.onclick = this.onBtnClick(handler);
3623
+ this.$actions.appendChild(btn);
3624
+ return this;
3625
+ }
3626
+ onBtnClick(handler) {
3627
+ return (evt) => {
3628
+ const re = handler(evt.currentTarget, this);
3629
+ if (re === void 0 || re === true) {
3630
+ this.$el.remove();
3631
+ }
3632
+ };
3633
+ }
3634
+ }
3441
3635
  function wrapPromise(fn) {
3442
3636
  return (payload) => new Promise((resolve, reject) => {
3443
3637
  const cancelFn = payload.onCancel;
@@ -3831,10 +4025,10 @@ class SidebarLayer extends Component {
3831
4025
  } else {
3832
4026
  const $iframe = this.$iframe;
3833
4027
  const iFrameSrc = $iframe.src;
3834
- if (this.conf.darkMode !== iFrameSrc.includes("darkMode=1")) {
4028
+ if (this.getDarkMode() !== iFrameSrc.includes("&darkMode=1")) {
3835
4029
  this.iframeLoad(
3836
4030
  $iframe,
3837
- this.conf.darkMode ? iFrameSrc.concat("&darkMode=1") : iFrameSrc.replace("&darkMode=1", "")
4031
+ this.getDarkMode() ? iFrameSrc.concat("&darkMode=1") : iFrameSrc.replace("&darkMode=1", "")
3838
4032
  );
3839
4033
  }
3840
4034
  }
@@ -3904,12 +4098,15 @@ class SidebarLayer extends Component {
3904
4098
  };
3905
4099
  if (view)
3906
4100
  query.view = view;
3907
- if (this.conf.darkMode)
4101
+ if (this.getDarkMode())
3908
4102
  query.darkMode = "1";
3909
4103
  const urlParams = new URLSearchParams(query);
3910
4104
  this.iframeLoad($iframe, `${baseURL}?${urlParams.toString()}`);
3911
4105
  return $iframe;
3912
4106
  }
4107
+ getDarkMode() {
4108
+ return this.conf.darkMode === "auto" ? window.matchMedia("(prefers-color-scheme: dark)").matches : this.conf.darkMode;
4109
+ }
3913
4110
  iframeLoad($iframe, src) {
3914
4111
  $iframe.src = src;
3915
4112
  showLoading(this.$iframeWrap);
@@ -4244,38 +4441,45 @@ function Detect(userAgent) {
4244
4441
  return dest;
4245
4442
  }
4246
4443
  function check(conf, rules) {
4247
- rules.forEach(({ el, max: maxHeight, imgContains }) => {
4248
- const _apply = () => {
4249
- if (!el)
4250
- return;
4251
- if (!conf.scrollable)
4252
- applyHeightLimit({
4253
- el,
4254
- maxHeight,
4255
- postBtnClick: conf.postExpandBtnClick
4256
- });
4257
- else
4258
- applyScrollableHeightLimit({ el, maxHeight });
4259
- };
4444
+ rules.forEach(({ el, max, imgCheck }) => {
4445
+ if (!el)
4446
+ return;
4447
+ if (imgCheck)
4448
+ el.style.maxHeight = `${max + 1}px`;
4449
+ let lock = false;
4260
4450
  const _check = () => {
4261
- if (el && getHeight(el) > maxHeight)
4262
- _apply();
4451
+ if (lock)
4452
+ return;
4453
+ if (getHeight(el) <= max)
4454
+ return;
4455
+ const afterExpandBtnClick = () => {
4456
+ var _a;
4457
+ lock = true;
4458
+ (_a = conf.afterExpandBtnClick) == null ? void 0 : _a.call(conf);
4459
+ };
4460
+ !conf.scrollable ? applyHeightLimit({ el, max, afterExpandBtnClick }) : applyScrollableHeightLimit({ el, max });
4263
4461
  };
4264
4462
  _check();
4265
- if (imgContains && el)
4266
- onImagesLoaded(el, () => _check());
4463
+ if (imgCheck) {
4464
+ const imgs = el.querySelectorAll(".atk-content img");
4465
+ if (imgs.length === 0)
4466
+ el.style.maxHeight = "";
4467
+ imgs.forEach((img) => {
4468
+ img.onload = () => _check();
4469
+ });
4470
+ }
4267
4471
  });
4268
4472
  }
4269
4473
  const HEIGHT_LIMIT_CSS = "atk-height-limit";
4270
4474
  function applyHeightLimit(obj) {
4271
4475
  if (!obj.el)
4272
4476
  return;
4273
- if (!obj.maxHeight)
4477
+ if (!obj.max)
4274
4478
  return;
4275
4479
  if (obj.el.classList.contains(HEIGHT_LIMIT_CSS))
4276
4480
  return;
4277
4481
  obj.el.classList.add(HEIGHT_LIMIT_CSS);
4278
- obj.el.style.height = `${obj.maxHeight}px`;
4482
+ obj.el.style.height = `${obj.max}px`;
4279
4483
  obj.el.style.overflow = "hidden";
4280
4484
  const $expandBtn = createElement(
4281
4485
  `<div class="atk-height-limit-btn">${t("readMore")}</span>`
@@ -4283,8 +4487,8 @@ function applyHeightLimit(obj) {
4283
4487
  $expandBtn.onclick = (e) => {
4284
4488
  e.stopPropagation();
4285
4489
  disposeHeightLimit(obj.el);
4286
- if (obj.postBtnClick)
4287
- obj.postBtnClick(e);
4490
+ if (obj.afterExpandBtnClick)
4491
+ obj.afterExpandBtnClick(e);
4288
4492
  };
4289
4493
  obj.el.append($expandBtn);
4290
4494
  }
@@ -4299,16 +4503,17 @@ function disposeHeightLimit($el) {
4299
4503
  e.remove();
4300
4504
  });
4301
4505
  $el.style.height = "";
4506
+ $el.style.maxHeight = "";
4302
4507
  $el.style.overflow = "";
4303
4508
  }
4304
4509
  const HEIGHT_LIMIT_SCROLL_CSS = "atk-height-limit-scroll";
4305
- function applyScrollableHeightLimit(obj) {
4306
- if (!obj.el)
4510
+ function applyScrollableHeightLimit(opt) {
4511
+ if (!opt.el)
4307
4512
  return;
4308
- if (obj.el.classList.contains(HEIGHT_LIMIT_SCROLL_CSS))
4513
+ if (opt.el.classList.contains(HEIGHT_LIMIT_SCROLL_CSS))
4309
4514
  return;
4310
- obj.el.classList.add(HEIGHT_LIMIT_SCROLL_CSS);
4311
- obj.el.style.height = `${obj.maxHeight}px`;
4515
+ opt.el.classList.add(HEIGHT_LIMIT_SCROLL_CSS);
4516
+ opt.el.style.height = `${opt.max}px`;
4312
4517
  }
4313
4518
  const CommentHTML = '<div class="atk-comment-wrap">\n <div class="atk-comment">\n <div class="atk-avatar"></div>\n <div class="atk-main">\n <div class="atk-header">\n <span class="atk-item atk-nick"></span>\n <span class="atk-badge-wrap"></span>\n <span class="atk-item atk-date"></span>\n </div>\n <div class="atk-body">\n <div class="atk-content"></div>\n </div>\n <div class="atk-footer">\n <div class="atk-actions"></div>\n </div>\n </div>\n </div>\n</div>\n';
4314
4519
  function renderAvatar(r) {
@@ -4772,7 +4977,7 @@ class Render {
4772
4977
  const childrenMaxH = conf.children;
4773
4978
  check(
4774
4979
  {
4775
- postExpandBtnClick: () => {
4980
+ afterExpandBtnClick: () => {
4776
4981
  const children = this.comment.getChildren();
4777
4982
  if (children.length === 1)
4778
4983
  disposeHeightLimit(children[0].getRender().$content);
@@ -4781,10 +4986,10 @@ class Render {
4781
4986
  },
4782
4987
  [
4783
4988
  // 评论内容限高
4784
- { el: this.$content, max: contentMaxH, imgContains: true },
4785
- { el: this.$replyTo, max: contentMaxH, imgContains: true },
4989
+ { el: this.$content, max: contentMaxH, imgCheck: true },
4990
+ { el: this.$replyTo, max: contentMaxH, imgCheck: true },
4786
4991
  // 子评论区域限高(仅嵌套模式)
4787
- { el: this.$childrenWrap, max: childrenMaxH, imgContains: false }
4992
+ { el: this.$childrenWrap, max: childrenMaxH, imgCheck: false }
4788
4993
  ]
4789
4994
  );
4790
4995
  }
@@ -4873,7 +5078,7 @@ class CommentActions {
4873
5078
  (_b = this.comment.getRender().voteBtnDown) == null ? void 0 : _b.updateText();
4874
5079
  }).catch((err) => {
4875
5080
  actionBtn == null ? void 0 : actionBtn.setError(t("voteFail"));
4876
- console.log(err);
5081
+ console.error(err);
4877
5082
  });
4878
5083
  }
4879
5084
  /** 管理员 - 评论状态修改 */
@@ -5057,7 +5262,7 @@ class CommentNode {
5057
5262
  return getGravatarURL({
5058
5263
  mirror: this.opts.gravatar.mirror,
5059
5264
  params: this.opts.gravatar.params,
5060
- emailMD5: this.data.email_encrypted
5265
+ emailHash: this.data.email_encrypted
5061
5266
  });
5062
5267
  }
5063
5268
  /** 获取评论 markdown 解析后的内容 */
@@ -5752,7 +5957,7 @@ const services = {
5752
5957
  }
5753
5958
  };
5754
5959
  const PvCountWidget = (ctx) => {
5755
- ctx.watchConf(["site", "pageKey", "pageTitle", "countEl", "pvEl"], (conf) => {
5960
+ ctx.watchConf(["site", "pageKey", "pageTitle", "countEl", "pvEl", "statPageKeyAttr"], (conf) => {
5756
5961
  initCountWidget({
5757
5962
  getApi: () => ctx.getApi(),
5758
5963
  siteName: conf.site,
@@ -5760,78 +5965,85 @@ const PvCountWidget = (ctx) => {
5760
5965
  pageTitle: conf.pageTitle,
5761
5966
  countEl: conf.countEl,
5762
5967
  pvEl: conf.pvEl,
5968
+ pageKeyAttr: conf.statPageKeyAttr,
5763
5969
  pvAdd: typeof ctx.conf.pvAdd === "boolean" ? ctx.conf.pvAdd : true
5764
5970
  });
5765
5971
  });
5766
5972
  };
5767
5973
  function initCountWidget(opt) {
5768
5974
  return __async(this, null, function* () {
5769
- if (opt.countEl && document.querySelector(opt.countEl)) {
5770
- refreshStatCount(opt, { query: "page_comment", numEl: opt.countEl });
5771
- }
5772
- const initialData = opt.pvAdd && opt.pageKey ? {
5773
- [opt.pageKey]: (yield opt.getApi().pages.logPv({
5774
- page_key: opt.pageKey,
5775
- page_title: opt.pageTitle,
5776
- site_name: opt.siteName
5777
- })).data.pv
5778
- // pv+1 and get pv count
5779
- } : void 0;
5780
- if (opt.pvEl && document.querySelector(opt.pvEl)) {
5781
- refreshStatCount(opt, {
5782
- query: "page_pv",
5783
- numEl: opt.pvEl,
5784
- data: initialData
5785
- });
5786
- }
5975
+ yield loadCommentCount(opt);
5976
+ const cacheData = yield incrementPvCount(opt);
5977
+ yield loadPvCount(opt, cacheData);
5978
+ });
5979
+ }
5980
+ function incrementPvCount(opt) {
5981
+ return __async(this, null, function* () {
5982
+ if (!opt.pvAdd || !opt.pageKey)
5983
+ return void 0;
5984
+ const pvCount = (yield opt.getApi().pages.logPv({
5985
+ page_key: opt.pageKey,
5986
+ page_title: opt.pageTitle,
5987
+ site_name: opt.siteName
5988
+ })).data.pv;
5989
+ return {
5990
+ [opt.pageKey]: pvCount
5991
+ };
5992
+ });
5993
+ }
5994
+ function loadCommentCount(opt) {
5995
+ return __async(this, null, function* () {
5996
+ yield loadStatCount({ opt, query: "page_comment", containers: [opt.countEl, "#ArtalkCount"] });
5997
+ });
5998
+ }
5999
+ function loadPvCount(opt, cache) {
6000
+ return __async(this, null, function* () {
6001
+ yield loadStatCount({ opt, query: "page_pv", containers: [opt.pvEl, "#ArtalkPv"], cache });
5787
6002
  });
5788
6003
  }
5789
- function refreshStatCount(opt, args) {
6004
+ function loadStatCount(args) {
5790
6005
  return __async(this, null, function* () {
5791
- let data = args.data || {};
5792
- let queryPageKeys = Array.from(document.querySelectorAll(args.numEl)).map((e) => e.getAttribute("data-page-key") || opt.pageKey).filter((k) => k && typeof data[k] !== "number");
5793
- queryPageKeys = [...new Set(queryPageKeys)];
5794
- if (queryPageKeys.length > 0) {
6006
+ const { opt } = args;
6007
+ let cache = args.cache || {};
6008
+ const els = retrieveElements(args.containers);
6009
+ const pageKeys = getPageKeys(els, opt.pageKeyAttr, opt.pageKey, cache);
6010
+ if (pageKeys.length > 0) {
5795
6011
  const res = (yield opt.getApi().stats.getStats(args.query, {
5796
- page_keys: queryPageKeys.join(","),
6012
+ page_keys: pageKeys.join(","),
5797
6013
  site_name: opt.siteName
5798
6014
  })).data.data;
5799
- data = __spreadValues(__spreadValues({}, data), res);
6015
+ cache = __spreadValues(__spreadValues({}, cache), res);
5800
6016
  }
5801
- const defaultCount = opt.pageKey ? data[opt.pageKey] : 0;
5802
- applyCountData(args.numEl, data, defaultCount);
6017
+ updateElementsText(els, cache, opt.pageKey);
5803
6018
  });
5804
6019
  }
5805
- function applyCountData(selector, data, defaultCount) {
5806
- document.querySelectorAll(selector).forEach((el) => {
5807
- const pageKey = el.getAttribute("data-page-key");
5808
- const count = Number(pageKey ? data[pageKey] : defaultCount);
5809
- el.innerHTML = `${count}`;
6020
+ function retrieveElements(containers) {
6021
+ const els = /* @__PURE__ */ new Set();
6022
+ new Set(containers).forEach((selector) => {
6023
+ document.querySelectorAll(selector).forEach((el) => els.add(el));
5810
6024
  });
6025
+ return els;
5811
6026
  }
5812
- function showErrorDialog(opts) {
5813
- const errEl = createElement(`<span><span class="error-message"></span><br/><br/></span>`);
5814
- errEl.querySelector(".error-message").innerText = `${t("listLoadFailMsg")}
5815
- ${opts.errMsg}`;
5816
- if (opts.retryFn) {
5817
- const $retryBtn = createElement(`<span style="cursor:pointer;">${t("listRetry")}</span>`);
5818
- $retryBtn.onclick = () => opts.retryFn && opts.retryFn();
5819
- errEl.appendChild($retryBtn);
5820
- }
5821
- if (opts.onOpenSidebar) {
5822
- const $openSidebar = createElement(
5823
- `<span atk-only-admin-show> | <span style="cursor:pointer;">${t("openName", { name: t("ctrlCenter") })}</span></span>`
5824
- );
5825
- errEl.appendChild($openSidebar);
5826
- $openSidebar.onclick = () => opts.onOpenSidebar && opts.onOpenSidebar();
5827
- }
5828
- setError(opts.$err, errEl);
6027
+ function getPageKeys(els, pageKeyAttr, pageKey, cache) {
6028
+ const pageKeys = Array.from(els).map((el) => el.getAttribute(pageKeyAttr) || pageKey).filter((key) => key && typeof cache[key] !== "number");
6029
+ return [...new Set(pageKeys)];
6030
+ }
6031
+ function updateElementsText(els, data, defaultPageKey) {
6032
+ els.forEach((el) => {
6033
+ const pageKey = el.getAttribute("data-page-key");
6034
+ const count = pageKey && data[pageKey] || defaultPageKey && data[defaultPageKey] || 0;
6035
+ el.innerText = `${Number(count)}`;
6036
+ });
5829
6037
  }
5830
6038
  const Markdown = (ctx) => {
5831
- initMarked();
5832
- ctx.on("updated", (conf) => {
5833
- if (conf.markedReplacers)
5834
- setReplacers(conf.markedReplacers);
6039
+ ctx.watchConf(["imgLazyLoad", "markedOptions"], (conf) => {
6040
+ initMarked({
6041
+ markedOptions: ctx.getConf().markedOptions,
6042
+ imgLazyLoad: ctx.getConf().imgLazyLoad
6043
+ });
6044
+ });
6045
+ ctx.watchConf(["markedReplacers"], (conf) => {
6046
+ conf.markedReplacers && setReplacers(conf.markedReplacers);
5835
6047
  });
5836
6048
  };
5837
6049
  const LocalStorageKey = "ArtalkContent";
@@ -7068,7 +7280,7 @@ const GotoFocus = (ctx) => {
7068
7280
  comment.focus();
7069
7281
  }));
7070
7282
  };
7071
- const version = "2.8.5";
7283
+ const version = "2.8.7";
7072
7284
  const Copyright = (ctx) => {
7073
7285
  ctx.on("mounted", () => {
7074
7286
  const list = ctx.get("list");
@@ -7173,8 +7385,7 @@ function renderDropdown(conf) {
7173
7385
  };
7174
7386
  const $dropdown = createElement(`<ul class="atk-dropdown atk-fade-in"></ul>`);
7175
7387
  dropdownList.forEach((item, i) => {
7176
- const name = item[0];
7177
- const action = item[1];
7388
+ const [name, action] = item;
7178
7389
  const $item = createElement(`<li class="atk-dropdown-item"><span></span></li>`);
7179
7390
  const $link = $item.querySelector("span");
7180
7391
  $link.innerText = name;
@@ -7209,6 +7420,24 @@ const TimeTicking = (ctx) => {
7209
7420
  timer && window.clearInterval(timer);
7210
7421
  });
7211
7422
  };
7423
+ function showErrorDialog(opts) {
7424
+ const errEl = createElement(`<span><span class="error-message"></span><br/><br/></span>`);
7425
+ errEl.querySelector(".error-message").innerText = `${t("listLoadFailMsg")}
7426
+ ${opts.errMsg}`;
7427
+ if (opts.retryFn) {
7428
+ const $retryBtn = createElement(`<span style="cursor:pointer;">${t("listRetry")}</span>`);
7429
+ $retryBtn.onclick = () => opts.retryFn && opts.retryFn();
7430
+ errEl.appendChild($retryBtn);
7431
+ }
7432
+ if (opts.onOpenSidebar) {
7433
+ const $openSidebar = createElement(
7434
+ `<span atk-only-admin-show> | <span style="cursor:pointer;">${t("openName", { name: t("ctrlCenter") })}</span></span>`
7435
+ );
7436
+ errEl.appendChild($openSidebar);
7437
+ $openSidebar.onclick = () => opts.onOpenSidebar && opts.onOpenSidebar();
7438
+ }
7439
+ setError(opts.$err, errEl);
7440
+ }
7212
7441
  const ErrorDialog = (ctx) => {
7213
7442
  ctx.on("list-fetch", () => {
7214
7443
  const list = ctx.get("list");
@@ -7473,16 +7702,16 @@ const DefaultPlugins = [
7473
7702
  VersionCheck,
7474
7703
  DarkMode
7475
7704
  ];
7476
- const GlobalPlugins = [...DefaultPlugins];
7705
+ const GlobalPlugins = /* @__PURE__ */ new Set([...DefaultPlugins]);
7477
7706
  function load(ctx) {
7478
7707
  return __async(this, null, function* () {
7479
7708
  var _a;
7480
- const loadedPlugins = [];
7709
+ const loadedPlugins = /* @__PURE__ */ new Set();
7481
7710
  const loadPlugins = (plugins) => {
7482
7711
  plugins.forEach((plugin) => {
7483
- if (typeof plugin === "function" && !loadedPlugins.includes(plugin)) {
7712
+ if (typeof plugin === "function" && !loadedPlugins.has(plugin)) {
7484
7713
  plugin(ctx);
7485
- loadedPlugins.push(plugin);
7714
+ loadedPlugins.add(plugin);
7486
7715
  }
7487
7716
  });
7488
7717
  };
@@ -7518,8 +7747,9 @@ function load(ctx) {
7518
7747
  }
7519
7748
  function loadNetworkPlugins(scripts, apiBase) {
7520
7749
  return __async(this, null, function* () {
7750
+ const networkPlugins = /* @__PURE__ */ new Set();
7521
7751
  if (!scripts || !Array.isArray(scripts))
7522
- return [];
7752
+ return networkPlugins;
7523
7753
  const tasks = [];
7524
7754
  scripts.forEach((url) => {
7525
7755
  if (!/^(http|https):\/\//.test(url))
@@ -7539,7 +7769,11 @@ function loadNetworkPlugins(scripts, apiBase) {
7539
7769
  );
7540
7770
  });
7541
7771
  yield Promise.all(tasks);
7542
- return Object.values(window.ArtalkPlugins || {});
7772
+ Object.values(window.ArtalkPlugins || {}).forEach((plugin) => {
7773
+ if (typeof plugin === "function")
7774
+ networkPlugins.add(plugin);
7775
+ });
7776
+ return networkPlugins;
7543
7777
  });
7544
7778
  }
7545
7779
  function onLoadErr(ctx, err) {
@@ -7625,9 +7859,7 @@ class Artalk {
7625
7859
  }
7626
7860
  /** Use plugin, the plugin will be used when Artalk.init */
7627
7861
  static use(plugin) {
7628
- if (GlobalPlugins.includes(plugin))
7629
- return;
7630
- GlobalPlugins.push(plugin);
7862
+ GlobalPlugins.add(plugin);
7631
7863
  }
7632
7864
  /** Load count widget */
7633
7865
  static loadCountWidget(c) {
@@ -7637,6 +7869,7 @@ class Artalk {
7637
7869
  siteName: conf.site,
7638
7870
  countEl: conf.countEl,
7639
7871
  pvEl: conf.pvEl,
7872
+ pageKeyAttr: conf.statPageKeyAttr,
7640
7873
  pvAdd: false
7641
7874
  });
7642
7875
  }