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.
package/dist/Artalk.mjs CHANGED
@@ -155,11 +155,14 @@ class HttpClient {
155
155
  /* Json */
156
156
  ];
157
157
  const responseFormat = format || requestParams.format;
158
- return this.customFetch(`${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`, __spreadProps(__spreadValues({}, requestParams), {
159
- headers: __spreadValues(__spreadValues({}, requestParams.headers || {}), type && type !== "multipart/form-data" ? { "Content-Type": type } : {}),
160
- signal: (cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal) || null,
161
- body: typeof body === "undefined" || body === null ? null : payloadFormatter(body)
162
- })).then((response) => __async(this, null, function* () {
158
+ return this.customFetch(
159
+ `${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`,
160
+ __spreadProps(__spreadValues({}, requestParams), {
161
+ headers: __spreadValues(__spreadValues({}, requestParams.headers || {}), type && type !== "multipart/form-data" ? { "Content-Type": type } : {}),
162
+ signal: (cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal) || null,
163
+ body: typeof body === "undefined" || body === null ? null : payloadFormatter(body)
164
+ })
165
+ ).then((response) => __async(this, null, function* () {
163
166
  const r = response;
164
167
  r.data = null;
165
168
  r.error = null;
@@ -198,7 +201,9 @@ class HttpClient {
198
201
  toQueryString(rawQuery) {
199
202
  const query = rawQuery || {};
200
203
  const keys = Object.keys(query).filter((key) => "undefined" !== typeof query[key]);
201
- return keys.map((key) => Array.isArray(query[key]) ? this.addArrayQueryParam(query, key) : this.addQueryParam(query, key)).join("&");
204
+ return keys.map(
205
+ (key) => Array.isArray(query[key]) ? this.addArrayQueryParam(query, key) : this.addQueryParam(query, key)
206
+ ).join("&");
202
207
  }
203
208
  addQueryParams(rawQuery) {
204
209
  const queryString = this.toQueryString(rawQuery);
@@ -222,6 +227,133 @@ class HttpClient {
222
227
  let Api$1 = class Api extends HttpClient {
223
228
  constructor() {
224
229
  super(...arguments);
230
+ __publicField(this, "auth", {
231
+ /**
232
+ * @description Login by email with verify code (Need send email verify code first) or password
233
+ *
234
+ * @tags Auth
235
+ * @name LoginByEmail
236
+ * @summary Login by email
237
+ * @request POST:/auth/email/login
238
+ * @response `200` `HandlerResponseUserLogin` OK
239
+ * @response `400` `(HandlerMap & {
240
+ msg?: string,
241
+
242
+ })` Bad Request
243
+ * @response `500` `(HandlerMap & {
244
+ msg?: string,
245
+
246
+ })` Internal Server Error
247
+ */
248
+ loginByEmail: (data, params = {}) => this.request(__spreadValues({
249
+ path: `/auth/email/login`,
250
+ method: "POST",
251
+ body: data,
252
+ type: "application/json",
253
+ format: "json"
254
+ }, params)),
255
+ /**
256
+ * @description Register by email and verify code (if user exists, will update user, like forget password. Need send email verify code first)
257
+ *
258
+ * @tags Auth
259
+ * @name RegisterByEmail
260
+ * @summary Register by email
261
+ * @request POST:/auth/email/register
262
+ * @response `200` `HandlerResponseUserLogin` OK
263
+ * @response `400` `(HandlerMap & {
264
+ msg?: string,
265
+
266
+ })` Bad Request
267
+ * @response `500` `(HandlerMap & {
268
+ msg?: string,
269
+
270
+ })` Internal Server Error
271
+ */
272
+ registerByEmail: (data, params = {}) => this.request(__spreadValues({
273
+ path: `/auth/email/register`,
274
+ method: "POST",
275
+ body: data,
276
+ type: "application/json",
277
+ format: "json"
278
+ }, params)),
279
+ /**
280
+ * @description Send email including verify code to user
281
+ *
282
+ * @tags Auth
283
+ * @name SendVerifyEmail
284
+ * @summary Send verify email
285
+ * @request POST:/auth/email/send
286
+ * @response `200` `(HandlerMap & {
287
+ msg?: string,
288
+
289
+ })` OK
290
+ * @response `400` `(HandlerMap & {
291
+ msg?: string,
292
+
293
+ })` Bad Request
294
+ * @response `500` `(HandlerMap & {
295
+ msg?: string,
296
+
297
+ })` Internal Server Error
298
+ */
299
+ sendVerifyEmail: (data, params = {}) => this.request(__spreadValues({
300
+ path: `/auth/email/send`,
301
+ method: "POST",
302
+ body: data,
303
+ type: "application/json",
304
+ format: "json"
305
+ }, params)),
306
+ /**
307
+ * @description Get all users with same email, if there are more than one user with same email, need merge
308
+ *
309
+ * @tags Auth
310
+ * @name CheckDataMerge
311
+ * @summary Check data merge
312
+ * @request GET:/auth/merge
313
+ * @secure
314
+ * @response `200` `HandlerResponseAuthDataMergeCheck` OK
315
+ * @response `400` `(HandlerMap & {
316
+ msg?: string,
317
+
318
+ })` Bad Request
319
+ * @response `500` `(HandlerMap & {
320
+ msg?: string,
321
+
322
+ })` Internal Server Error
323
+ */
324
+ checkDataMerge: (params = {}) => this.request(__spreadValues({
325
+ path: `/auth/merge`,
326
+ method: "GET",
327
+ secure: true,
328
+ format: "json"
329
+ }, params)),
330
+ /**
331
+ * @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.
332
+ *
333
+ * @tags Auth
334
+ * @name ApplyDataMerge
335
+ * @summary Apply data merge
336
+ * @request POST:/auth/merge
337
+ * @secure
338
+ * @response `200` `HandlerResponseAuthDataMergeApply` OK
339
+ * @response `400` `(HandlerMap & {
340
+ msg?: string,
341
+
342
+ })` Bad Request
343
+ * @response `500` `(HandlerMap & {
344
+ msg?: string,
345
+
346
+ })` Internal Server Error
347
+ */
348
+ applyDataMerge: (data, params = {}) => this.request(__spreadValues({
349
+ path: `/auth/merge`,
350
+ method: "POST",
351
+ body: data,
352
+ secure: true,
353
+ type: "application/json",
354
+ format: "json"
355
+ }, params))
356
+ });
225
357
  __publicField(this, "cache", {
226
358
  /**
227
359
  * @description Flush all cache on the server
@@ -482,6 +614,24 @@ let Api$1 = class Api extends HttpClient {
482
614
  method: "GET",
483
615
  format: "json"
484
616
  }, params)),
617
+ /**
618
+ * @description Get social login providers
619
+ *
620
+ * @tags System
621
+ * @name GetSocialLoginProviders
622
+ * @summary Get Social Login Providers
623
+ * @request GET:/conf/auth/providers
624
+ * @response `200` `HandlerResponseConfAuthProviders` OK
625
+ * @response `404` `(HandlerMap & {
626
+ msg?: string,
627
+
628
+ })` Not Found
629
+ */
630
+ getSocialLoginProviders: (params = {}) => this.request(__spreadValues({
631
+ path: `/conf/auth/providers`,
632
+ method: "GET",
633
+ format: "json"
634
+ }, params)),
485
635
  /**
486
636
  * @description Get Domain Info
487
637
  *
@@ -1341,8 +1491,6 @@ function getDefaultExportFromCjs(x) {
1341
1491
  function _createClass(Constructor, protoProps, staticProps) {
1342
1492
  if (protoProps)
1343
1493
  _defineProperties(Constructor.prototype, protoProps);
1344
- if (staticProps)
1345
- _defineProperties(Constructor, staticProps);
1346
1494
  Object.defineProperty(Constructor, "prototype", {
1347
1495
  writable: false
1348
1496
  });
@@ -2057,7 +2205,8 @@ class _Tokenizer {
2057
2205
  blockquote(src) {
2058
2206
  const cap = this.rules.block.blockquote.exec(src);
2059
2207
  if (cap) {
2060
- const text = rtrim(cap[0].replace(/^ *>[ \t]?/gm, ""), "\n");
2208
+ let text = cap[0].replace(/\n {0,3}((?:=+|-+) *)(?=\n|$)/g, "\n $1");
2209
+ text = rtrim(text.replace(/^ *>[ \t]?/gm, ""), "\n");
2061
2210
  const top = this.lexer.state.top;
2062
2211
  this.lexer.state.top = true;
2063
2212
  const tokens = this.lexer.blockTokens(text);
@@ -4249,7 +4398,7 @@ const insaneOptions = {
4249
4398
  allowedAttributes: {
4250
4399
  "*": ["title", "accesskey"],
4251
4400
  a: ["href", "name", "target", "aria-label", "rel"],
4252
- img: ["src", "alt", "title", "atk-emoticon", "aria-label"],
4401
+ img: ["src", "alt", "title", "atk-emoticon", "aria-label", "data-src", "class", "loading"],
4253
4402
  // for code highlight
4254
4403
  code: ["class"],
4255
4404
  span: ["class", "style"]
@@ -4257,7 +4406,8 @@ const insaneOptions = {
4257
4406
  filter: (node) => {
4258
4407
  const allowed = [
4259
4408
  ["code", /^hljs\W+language-(.*)$/],
4260
- ["span", /^(hljs-.*)$/]
4409
+ ["span", /^(hljs-.*)$/],
4410
+ ["img", /^lazyload$/]
4261
4411
  ];
4262
4412
  allowed.forEach(([tag2, reg]) => {
4263
4413
  if (node.tag === tag2 && !!node.attrs.class && !reg.test(node.attrs.class)) {
@@ -4334,18 +4484,26 @@ const hanabi = /* @__PURE__ */ getDefaultExportFromCjs(hanabiExports);
4334
4484
  function renderCode(code) {
4335
4485
  return hanabi(code);
4336
4486
  }
4337
- function getRenderer() {
4487
+ function getRenderer(options) {
4338
4488
  const renderer = new marked$1.Renderer();
4339
4489
  renderer.link = markedLinkRenderer(renderer, renderer.link);
4340
4490
  renderer.code = markedCodeRenderer();
4491
+ renderer.image = markedImageRenderer(renderer, renderer.image, options);
4341
4492
  return renderer;
4342
4493
  }
4343
4494
  const markedLinkRenderer = (renderer, orgLinkRenderer) => (href, title, text) => {
4344
- const localLink = href == null ? void 0 : href.startsWith(`${window.location.protocol}//${window.location.hostname}`);
4495
+ const getLinkOrigin = (link2) => {
4496
+ try {
4497
+ return new URL(link2).origin;
4498
+ } catch (e) {
4499
+ return "";
4500
+ }
4501
+ };
4502
+ const isSameOriginLink = getLinkOrigin(href) === window.location.origin;
4345
4503
  const html2 = orgLinkRenderer.call(renderer, href, title, text);
4346
4504
  return html2.replace(
4347
4505
  /^<a /,
4348
- `<a target="_blank" ${!localLink ? `rel="noreferrer noopener nofollow"` : ""} `
4506
+ `<a target="_blank" ${!isSameOriginLink ? `rel="noreferrer noopener nofollow"` : ""} `
4349
4507
  );
4350
4508
  };
4351
4509
  const markedCodeRenderer = () => (block2, lang) => {
@@ -4362,6 +4520,16 @@ const markedCodeRenderer = () => (block2, lang) => {
4362
4520
  <code class="hljs language-${realLang}">${colorized.replace(/&amp;/g, "&")}</code>
4363
4521
  </pre>`;
4364
4522
  };
4523
+ const markedImageRenderer = (renderer, orgImageRenderer, { imgLazyLoad }) => (href, title, text) => {
4524
+ const html2 = orgImageRenderer.call(renderer, href, title, text);
4525
+ if (!imgLazyLoad)
4526
+ return html2;
4527
+ if (imgLazyLoad === "native" || imgLazyLoad === true)
4528
+ return html2.replace(/^<img /, '<img class="lazyload" loading="lazy" ');
4529
+ if (imgLazyLoad === "data-src")
4530
+ return html2.replace(/^<img /, '<img class="lazyload" ').replace("src=", "data-src=");
4531
+ return html2;
4532
+ };
4365
4533
  let instance;
4366
4534
  let replacers = [];
4367
4535
  const markedOptions = {
@@ -4375,16 +4543,18 @@ function getInstance() {
4375
4543
  function setReplacers(arr) {
4376
4544
  replacers = arr;
4377
4545
  }
4378
- function initMarked() {
4546
+ function initMarked(options) {
4379
4547
  try {
4380
4548
  if (!marked$1.name)
4381
4549
  return;
4382
4550
  } catch (e) {
4383
4551
  return;
4384
4552
  }
4385
- marked$1.setOptions(__spreadValues({
4386
- renderer: getRenderer()
4387
- }, markedOptions));
4553
+ marked$1.setOptions(__spreadValues(__spreadValues({
4554
+ renderer: getRenderer({
4555
+ imgLazyLoad: options.imgLazyLoad
4556
+ })
4557
+ }, markedOptions), options.markedOptions));
4388
4558
  instance = marked$1;
4389
4559
  }
4390
4560
  function marked(src) {
@@ -4515,176 +4685,14 @@ class DataManager {
4515
4685
  this.events.trigger("page-loaded", pageData);
4516
4686
  }
4517
4687
  }
4518
- const en = {
4519
- /* Editor */
4520
- placeholder: "Leave a comment",
4521
- noComment: "No Comment",
4522
- send: "Send",
4523
- save: "Save",
4524
- nick: "Nickname",
4525
- email: "Email",
4526
- link: "Website",
4527
- emoticon: "Emoji",
4528
- preview: "Preview",
4529
- uploadImage: "Upload Image",
4530
- uploadFail: "Upload Failed",
4531
- commentFail: "Failed to comment",
4532
- restoredMsg: "Content has been restored",
4533
- onlyAdminCanReply: "Only admin can reply",
4534
- uploadLoginMsg: "Please fill in your name and email to upload",
4535
- /* List */
4536
- counter: "{count} Comments",
4537
- sortLatest: "Latest",
4538
- sortOldest: "Oldest",
4539
- sortBest: "Best",
4540
- sortAuthor: "Author",
4541
- openComment: "Open Comment",
4542
- closeComment: "Close Comment",
4543
- listLoadFailMsg: "Failed to load comments",
4544
- listRetry: "Retry",
4545
- loadMore: "Load More",
4546
- /* Comment */
4547
- admin: "Admin",
4548
- reply: "Reply",
4549
- voteUp: "Up",
4550
- voteDown: "Down",
4551
- voteFail: "Vote Failed",
4552
- readMore: "Read More",
4553
- actionConfirm: "Confirm",
4554
- collapse: "Collapse",
4555
- collapsed: "Collapsed",
4556
- collapsedMsg: "This comment has been collapsed",
4557
- expand: "Expand",
4558
- approved: "Approved",
4559
- pending: "Pending",
4560
- pendingMsg: "Pending, visible only to commenter.",
4561
- edit: "Edit",
4562
- editCancel: "Cancel Edit",
4563
- delete: "Delete",
4564
- deleteConfirm: "Confirm",
4565
- pin: "Pin",
4566
- unpin: "Unpin",
4567
- /* Time */
4568
- seconds: "seconds ago",
4569
- minutes: "minutes ago",
4570
- hours: "hours ago",
4571
- days: "days ago",
4572
- now: "just now",
4573
- /* Checker */
4574
- adminCheck: "Enter admin password:",
4575
- captchaCheck: "Enter the CAPTCHA to continue:",
4576
- confirm: "Confirm",
4577
- cancel: "Cancel",
4578
- /* Sidebar */
4579
- msgCenter: "Messages",
4580
- ctrlCenter: "Admin",
4581
- emailVerified: "Email has been verified",
4582
- /* General */
4583
- frontend: "Frontend",
4584
- backend: "Backend",
4585
- loading: "Loading",
4586
- loadFail: "Load Failed",
4587
- editing: "Editing",
4588
- editFail: "Edit Failed",
4589
- deleting: "Deleting",
4590
- deleteFail: "Delete Failed",
4591
- reqGot: "Request got",
4592
- reqAborted: "Request timed out or terminated unexpectedly",
4593
- updateMsg: "Please update Artalk {name} to get the best experience!",
4594
- currentVersion: "Current Version",
4595
- ignore: "Ignore",
4596
- open: "Open",
4597
- openName: "Open {name}"
4598
- };
4599
- const zhCN = {
4600
- /* Editor */
4601
- placeholder: "键入内容...",
4602
- noComment: "「此时无声胜有声」",
4603
- send: "发送",
4604
- save: "保存",
4605
- nick: "昵称",
4606
- email: "邮箱",
4607
- link: "网址",
4608
- emoticon: "表情",
4609
- preview: "预览",
4610
- uploadImage: "上传图片",
4611
- uploadFail: "上传失败",
4612
- commentFail: "评论失败",
4613
- restoredMsg: "内容已自动恢复",
4614
- onlyAdminCanReply: "仅管理员可评论",
4615
- uploadLoginMsg: "填入你的名字邮箱才能上传哦",
4616
- /* List */
4617
- counter: "{count} 条评论",
4618
- sortLatest: "最新",
4619
- sortOldest: "最早",
4620
- sortBest: "最热",
4621
- sortAuthor: "作者",
4622
- openComment: "打开评论",
4623
- closeComment: "关闭评论",
4624
- listLoadFailMsg: "无法获取评论列表数据",
4625
- listRetry: "点击重新获取",
4626
- loadMore: "加载更多",
4627
- /* Comment */
4628
- admin: "管理员",
4629
- reply: "回复",
4630
- voteUp: "赞同",
4631
- voteDown: "反对",
4632
- voteFail: "投票失败",
4633
- readMore: "阅读更多",
4634
- actionConfirm: "确认操作",
4635
- collapse: "折叠",
4636
- collapsed: "已折叠",
4637
- collapsedMsg: "该评论已被系统或管理员折叠",
4638
- expand: "展开",
4639
- approved: "已审",
4640
- pending: "待审",
4641
- pendingMsg: "审核中,仅本人可见。",
4642
- edit: "编辑",
4643
- editCancel: "取消编辑",
4644
- delete: "删除",
4645
- deleteConfirm: "确认删除",
4646
- pin: "置顶",
4647
- unpin: "取消置顶",
4648
- /* Time */
4649
- seconds: "秒前",
4650
- minutes: "分钟前",
4651
- hours: "小时前",
4652
- days: "天前",
4653
- now: "刚刚",
4654
- /* Checker */
4655
- adminCheck: "键入密码来验证管理员身份:",
4656
- captchaCheck: "键入验证码继续:",
4657
- confirm: "确认",
4658
- cancel: "取消",
4659
- /* Sidebar */
4660
- msgCenter: "通知中心",
4661
- ctrlCenter: "控制中心",
4662
- emailVerified: "邮箱已验证",
4663
- /* General */
4664
- frontend: "前端",
4665
- backend: "后端",
4666
- loading: "加载中",
4667
- loadFail: "加载失败",
4668
- editing: "修改中",
4669
- editFail: "修改失败",
4670
- deleting: "删除中",
4671
- deleteFail: "删除失败",
4672
- reqGot: "请求响应",
4673
- reqAborted: "请求超时或意外终止",
4674
- updateMsg: "请更新 Artalk {name} 以获得更好的体验!",
4675
- currentVersion: "当前版本",
4676
- ignore: "忽略",
4677
- open: "打开",
4678
- openName: "打开{name}"
4679
- };
4680
- const GLOBAL_LOCALES_KEY = "ArtalkI18n";
4681
4688
  function createElement(htmlStr = "") {
4682
4689
  const div = document.createElement("div");
4683
4690
  div.innerHTML = htmlStr.trim();
4684
4691
  return div.firstElementChild || div;
4685
4692
  }
4686
4693
  function getHeight(el) {
4687
- return parseFloat(getComputedStyle(el, null).height.replace("px", ""));
4694
+ const num = parseFloat(getComputedStyle(el, null).height.replace("px", ""));
4695
+ return num || 0;
4688
4696
  }
4689
4697
  function htmlEncode(str) {
4690
4698
  const temp = document.createElement("div");
@@ -4762,29 +4770,8 @@ function timeAgo(date, $t = (n) => n) {
4762
4770
  return " - ";
4763
4771
  }
4764
4772
  }
4765
- function onImagesLoaded($container, event) {
4766
- if (!$container)
4767
- return;
4768
- const images = $container.getElementsByTagName("img");
4769
- if (!images.length)
4770
- return;
4771
- let loaded = images.length;
4772
- for (let i = 0; i < images.length; i++) {
4773
- if (images[i].complete) {
4774
- loaded--;
4775
- } else {
4776
- images[i].addEventListener("load", () => {
4777
- loaded--;
4778
- if (loaded === 0)
4779
- event();
4780
- });
4781
- }
4782
- if (loaded === 0)
4783
- event();
4784
- }
4785
- }
4786
4773
  function getGravatarURL(opts) {
4787
- return `${opts.mirror.replace(/\/$/, "")}/${opts.emailMD5}?${opts.params.replace(/^\?/, "")}`;
4774
+ return `${opts.mirror.replace(/\/$/, "")}/${opts.emailHash}?${opts.params.replace(/^\?/, "")}`;
4788
4775
  }
4789
4776
  function versionCompare(a, b) {
4790
4777
  const pa = a.split(".");
@@ -4849,6 +4836,211 @@ function getURLBasedOnApi(opts) {
4849
4836
  function getURLBasedOn(baseURL, path) {
4850
4837
  return `${baseURL.replace(/\/$/, "")}/${path.replace(/^\//, "")}`;
4851
4838
  }
4839
+ const en = {
4840
+ /* Editor */
4841
+ placeholder: "Leave a comment",
4842
+ noComment: "No Comment",
4843
+ send: "Send",
4844
+ signIn: "Sign in",
4845
+ signUp: "Sign up",
4846
+ save: "Save",
4847
+ nick: "Nickname",
4848
+ email: "Email",
4849
+ link: "Website",
4850
+ emoticon: "Emoji",
4851
+ preview: "Preview",
4852
+ uploadImage: "Upload Image",
4853
+ uploadFail: "Upload Failed",
4854
+ commentFail: "Failed to comment",
4855
+ restoredMsg: "Content has been restored",
4856
+ onlyAdminCanReply: "Only admin can reply",
4857
+ uploadLoginMsg: "Please fill in your name and email to upload",
4858
+ /* List */
4859
+ counter: "{count} Comments",
4860
+ sortLatest: "Latest",
4861
+ sortOldest: "Oldest",
4862
+ sortBest: "Best",
4863
+ sortAuthor: "Author",
4864
+ openComment: "Open Comment",
4865
+ closeComment: "Close Comment",
4866
+ listLoadFailMsg: "Failed to load comments",
4867
+ listRetry: "Retry",
4868
+ loadMore: "Load More",
4869
+ /* Comment */
4870
+ admin: "Admin",
4871
+ reply: "Reply",
4872
+ voteUp: "Up",
4873
+ voteDown: "Down",
4874
+ voteFail: "Vote Failed",
4875
+ readMore: "Read More",
4876
+ actionConfirm: "Confirm",
4877
+ collapse: "Collapse",
4878
+ collapsed: "Collapsed",
4879
+ collapsedMsg: "This comment has been collapsed",
4880
+ expand: "Expand",
4881
+ approved: "Approved",
4882
+ pending: "Pending",
4883
+ pendingMsg: "Pending, visible only to commenter.",
4884
+ edit: "Edit",
4885
+ editCancel: "Cancel Edit",
4886
+ delete: "Delete",
4887
+ deleteConfirm: "Confirm",
4888
+ pin: "Pin",
4889
+ unpin: "Unpin",
4890
+ /* Time */
4891
+ seconds: "seconds ago",
4892
+ minutes: "minutes ago",
4893
+ hours: "hours ago",
4894
+ days: "days ago",
4895
+ now: "just now",
4896
+ /* Checker */
4897
+ adminCheck: "Enter admin password:",
4898
+ captchaCheck: "Enter the CAPTCHA to continue:",
4899
+ confirm: "Confirm",
4900
+ cancel: "Cancel",
4901
+ /* Sidebar */
4902
+ msgCenter: "Messages",
4903
+ ctrlCenter: "Admin",
4904
+ /* Auth */
4905
+ noAccountPrompt: "Don't have an account?",
4906
+ haveAccountPrompt: "Already have an account?",
4907
+ forgetPassword: "Forget Password",
4908
+ resetPassword: "Reset Password",
4909
+ verificationCode: "Verification Code",
4910
+ verifySend: "Verify",
4911
+ verifyResend: "Resend",
4912
+ waitSeconds: "Wait {seconds}s",
4913
+ emailVerified: "Email has been verified",
4914
+ password: "Password",
4915
+ username: "Username",
4916
+ nextStep: "Next Step",
4917
+ skipNotVerify: "Skip, do not verify",
4918
+ logoutConfirm: "Are you sure to logout?",
4919
+ accountMergeNotice: "Your email has multiple accounts with different id.",
4920
+ accountMergeSelectOne: "Please select one you want to merge all the data into it.",
4921
+ accountMergeConfirm: "All data will be merged into one account, the id is {id}.",
4922
+ dismiss: "Dismiss",
4923
+ merge: "Merge",
4924
+ /* General */
4925
+ frontend: "Frontend",
4926
+ backend: "Backend",
4927
+ loading: "Loading",
4928
+ loadFail: "Load Failed",
4929
+ editing: "Editing",
4930
+ editFail: "Edit Failed",
4931
+ deleting: "Deleting",
4932
+ deleteFail: "Delete Failed",
4933
+ reqGot: "Request got",
4934
+ reqAborted: "Request timed out or terminated unexpectedly",
4935
+ updateMsg: "Please update Artalk {name} to get the best experience!",
4936
+ currentVersion: "Current Version",
4937
+ ignore: "Ignore",
4938
+ open: "Open",
4939
+ openName: "Open {name}"
4940
+ };
4941
+ const zhCN = {
4942
+ /* Editor */
4943
+ placeholder: "键入内容...",
4944
+ noComment: "「此时无声胜有声」",
4945
+ send: "发送",
4946
+ signIn: "登录",
4947
+ signUp: "注册",
4948
+ save: "保存",
4949
+ nick: "昵称",
4950
+ email: "邮箱",
4951
+ link: "网址",
4952
+ emoticon: "表情",
4953
+ preview: "预览",
4954
+ uploadImage: "上传图片",
4955
+ uploadFail: "上传失败",
4956
+ commentFail: "评论失败",
4957
+ restoredMsg: "内容已自动恢复",
4958
+ onlyAdminCanReply: "仅管理员可评论",
4959
+ uploadLoginMsg: "填入你的名字邮箱才能上传哦",
4960
+ /* List */
4961
+ counter: "{count} 条评论",
4962
+ sortLatest: "最新",
4963
+ sortOldest: "最早",
4964
+ sortBest: "最热",
4965
+ sortAuthor: "作者",
4966
+ openComment: "打开评论",
4967
+ closeComment: "关闭评论",
4968
+ listLoadFailMsg: "无法获取评论列表数据",
4969
+ listRetry: "点击重新获取",
4970
+ loadMore: "加载更多",
4971
+ /* Comment */
4972
+ admin: "管理员",
4973
+ reply: "回复",
4974
+ voteUp: "赞同",
4975
+ voteDown: "反对",
4976
+ voteFail: "投票失败",
4977
+ readMore: "阅读更多",
4978
+ actionConfirm: "确认操作",
4979
+ collapse: "折叠",
4980
+ collapsed: "已折叠",
4981
+ collapsedMsg: "该评论已被系统或管理员折叠",
4982
+ expand: "展开",
4983
+ approved: "已审",
4984
+ pending: "待审",
4985
+ pendingMsg: "审核中,仅本人可见。",
4986
+ edit: "编辑",
4987
+ editCancel: "取消编辑",
4988
+ delete: "删除",
4989
+ deleteConfirm: "确认删除",
4990
+ pin: "置顶",
4991
+ unpin: "取消置顶",
4992
+ /* Time */
4993
+ seconds: "秒前",
4994
+ minutes: "分钟前",
4995
+ hours: "小时前",
4996
+ days: "天前",
4997
+ now: "刚刚",
4998
+ /* Checker */
4999
+ adminCheck: "键入密码来验证管理员身份:",
5000
+ captchaCheck: "键入验证码继续:",
5001
+ confirm: "确认",
5002
+ cancel: "取消",
5003
+ /* Sidebar */
5004
+ msgCenter: "通知中心",
5005
+ ctrlCenter: "控制中心",
5006
+ /* Auth */
5007
+ noAccountPrompt: "没有账号?",
5008
+ haveAccountPrompt: "已有账号?",
5009
+ forgetPassword: "忘记密码",
5010
+ resetPassword: "重置密码",
5011
+ verificationCode: "验证码",
5012
+ verifySend: "发送验证码",
5013
+ verifyResend: "重新发送",
5014
+ waitSeconds: "等待 {seconds}秒",
5015
+ emailVerified: "邮箱已验证",
5016
+ password: "密码",
5017
+ username: "用户名",
5018
+ nextStep: "下一步",
5019
+ skipNotVerify: "跳过,不验证",
5020
+ logoutConfirm: "确定要退出登录吗?",
5021
+ accountMergeNotice: "您的电子邮件下有多个不同 ID 的账户。",
5022
+ accountMergeSelectOne: "请选择将所有数据合并到其中的一个。",
5023
+ accountMergeConfirm: "所有数据将合并到 ID 为 {id} 的账户中。",
5024
+ dismiss: "忽略",
5025
+ merge: "合并",
5026
+ /* General */
5027
+ frontend: "前端",
5028
+ backend: "后端",
5029
+ loading: "加载中",
5030
+ loadFail: "加载失败",
5031
+ editing: "修改中",
5032
+ editFail: "修改失败",
5033
+ deleting: "删除中",
5034
+ deleteFail: "删除失败",
5035
+ reqGot: "请求响应",
5036
+ reqAborted: "请求超时或意外终止",
5037
+ updateMsg: "请更新 Artalk {name} 以获得更好的体验!",
5038
+ currentVersion: "当前版本",
5039
+ ignore: "忽略",
5040
+ open: "打开",
5041
+ openName: "打开{name}"
5042
+ };
5043
+ const GLOBAL_LOCALES_KEY = "ArtalkI18n";
4852
5044
  const internal = {
4853
5045
  en,
4854
5046
  "en-US": en,
@@ -4932,11 +5124,12 @@ const defaults = {
4932
5124
  uaBadge: true,
4933
5125
  listSort: true,
4934
5126
  preview: true,
4935
- countEl: "#ArtalkCount",
4936
- pvEl: "#ArtalkPV",
5127
+ countEl: ".artalk-comment-count",
5128
+ pvEl: ".artalk-pv-count",
5129
+ statPageKeyAttr: "data-page-key",
4937
5130
  gravatar: {
4938
- mirror: "https://cravatar.cn/avatar/",
4939
- params: "d=mp&s=240"
5131
+ mirror: "https://www.gravatar.com/avatar/",
5132
+ params: "sha256=1&d=mp&s=240"
4940
5133
  },
4941
5134
  pagination: {
4942
5135
  pageSize: 20,
@@ -4982,16 +5175,20 @@ function handelCustomConf(customConf, full = false) {
4982
5175
  return conf;
4983
5176
  }
4984
5177
  function handleConfFormServer(conf) {
4985
- const DisabledKeys = [
5178
+ const ExcludedKeys = [
4986
5179
  "el",
4987
5180
  "pageKey",
4988
5181
  "pageTitle",
4989
5182
  "server",
4990
5183
  "site",
4991
- "darkMode"
5184
+ "pvEl",
5185
+ "countEl",
5186
+ "statPageKeyAttr"
4992
5187
  ];
4993
5188
  Object.keys(conf).forEach((k) => {
4994
- if (DisabledKeys.includes(k))
5189
+ if (ExcludedKeys.includes(k))
5190
+ delete conf[k];
5191
+ if (k === "darkMode" && conf[k] !== "auto")
4995
5192
  delete conf[k];
4996
5193
  });
4997
5194
  if (conf.emoticons && typeof conf.emoticons === "string") {
@@ -5168,50 +5365,6 @@ class Context {
5168
5365
  watchConf(this, keys, effect);
5169
5366
  }
5170
5367
  }
5171
- class Dialog {
5172
- constructor(contentEl) {
5173
- __publicField(this, "$el");
5174
- __publicField(this, "$content");
5175
- __publicField(this, "$actions");
5176
- this.$el = createElement(
5177
- `<div class="atk-layer-dialog-wrap">
5178
- <div class="atk-layer-dialog">
5179
- <div class="atk-layer-dialog-content"></div>
5180
- <div class="atk-layer-dialog-actions"></div>
5181
- </div>
5182
- </div>`
5183
- );
5184
- this.$actions = this.$el.querySelector(".atk-layer-dialog-actions");
5185
- this.$content = this.$el.querySelector(".atk-layer-dialog-content");
5186
- this.$content.appendChild(contentEl);
5187
- }
5188
- /** 按钮 · 确定 */
5189
- setYes(handler) {
5190
- const btn = createElement(
5191
- `<button data-action="confirm">${t("confirm")}</button>`
5192
- );
5193
- btn.onclick = this.onBtnClick(handler);
5194
- this.$actions.appendChild(btn);
5195
- return this;
5196
- }
5197
- /** 按钮 · 取消 */
5198
- setNo(handler) {
5199
- const btn = createElement(
5200
- `<button data-action="cancel">${t("cancel")}</button>`
5201
- );
5202
- btn.onclick = this.onBtnClick(handler);
5203
- this.$actions.appendChild(btn);
5204
- return this;
5205
- }
5206
- onBtnClick(handler) {
5207
- return (evt) => {
5208
- const re = handler(evt.currentTarget, this);
5209
- if (re === void 0 || re === true) {
5210
- this.$el.remove();
5211
- }
5212
- };
5213
- }
5214
- }
5215
5368
  function showLoading(parentElem, conf) {
5216
5369
  let $loading = parentElem.querySelector(":scope > .atk-loading");
5217
5370
  if (!$loading) {
@@ -5300,8 +5453,6 @@ function playFadeAnim(elem, after, type = "in") {
5300
5453
  const onAnimEnded = () => {
5301
5454
  elem.classList.remove(`atk-fade-${type}`);
5302
5455
  elem.removeEventListener("animationend", onAnimEnded);
5303
- if (after)
5304
- after();
5305
5456
  };
5306
5457
  elem.addEventListener("animationend", onAnimEnded);
5307
5458
  }
@@ -5459,6 +5610,50 @@ const AdminChecker = {
5459
5610
  onError(checker, err, inputVal, formEl) {
5460
5611
  }
5461
5612
  };
5613
+ class Dialog {
5614
+ constructor(contentEl) {
5615
+ __publicField(this, "$el");
5616
+ __publicField(this, "$content");
5617
+ __publicField(this, "$actions");
5618
+ this.$el = createElement(
5619
+ `<div class="atk-layer-dialog-wrap">
5620
+ <div class="atk-layer-dialog">
5621
+ <div class="atk-layer-dialog-content"></div>
5622
+ <div class="atk-layer-dialog-actions"></div>
5623
+ </div>
5624
+ </div>`
5625
+ );
5626
+ this.$actions = this.$el.querySelector(".atk-layer-dialog-actions");
5627
+ this.$content = this.$el.querySelector(".atk-layer-dialog-content");
5628
+ this.$content.appendChild(contentEl);
5629
+ }
5630
+ /** 按钮 · 确定 */
5631
+ setYes(handler) {
5632
+ const btn = createElement(
5633
+ `<button data-action="confirm">${t("confirm")}</button>`
5634
+ );
5635
+ btn.onclick = this.onBtnClick(handler);
5636
+ this.$actions.appendChild(btn);
5637
+ return this;
5638
+ }
5639
+ /** 按钮 · 取消 */
5640
+ setNo(handler) {
5641
+ const btn = createElement(
5642
+ `<button data-action="cancel">${t("cancel")}</button>`
5643
+ );
5644
+ btn.onclick = this.onBtnClick(handler);
5645
+ this.$actions.appendChild(btn);
5646
+ return this;
5647
+ }
5648
+ onBtnClick(handler) {
5649
+ return (evt) => {
5650
+ const re = handler(evt.currentTarget, this);
5651
+ if (re === void 0 || re === true) {
5652
+ this.$el.remove();
5653
+ }
5654
+ };
5655
+ }
5656
+ }
5462
5657
  function wrapPromise(fn) {
5463
5658
  return (payload) => new Promise((resolve, reject) => {
5464
5659
  const cancelFn = payload.onCancel;
@@ -5852,10 +6047,10 @@ class SidebarLayer extends Component {
5852
6047
  } else {
5853
6048
  const $iframe = this.$iframe;
5854
6049
  const iFrameSrc = $iframe.src;
5855
- if (this.conf.darkMode !== iFrameSrc.includes("darkMode=1")) {
6050
+ if (this.getDarkMode() !== iFrameSrc.includes("&darkMode=1")) {
5856
6051
  this.iframeLoad(
5857
6052
  $iframe,
5858
- this.conf.darkMode ? iFrameSrc.concat("&darkMode=1") : iFrameSrc.replace("&darkMode=1", "")
6053
+ this.getDarkMode() ? iFrameSrc.concat("&darkMode=1") : iFrameSrc.replace("&darkMode=1", "")
5859
6054
  );
5860
6055
  }
5861
6056
  }
@@ -5925,12 +6120,15 @@ class SidebarLayer extends Component {
5925
6120
  };
5926
6121
  if (view)
5927
6122
  query.view = view;
5928
- if (this.conf.darkMode)
6123
+ if (this.getDarkMode())
5929
6124
  query.darkMode = "1";
5930
6125
  const urlParams = new URLSearchParams(query);
5931
6126
  this.iframeLoad($iframe, `${baseURL}?${urlParams.toString()}`);
5932
6127
  return $iframe;
5933
6128
  }
6129
+ getDarkMode() {
6130
+ return this.conf.darkMode === "auto" ? window.matchMedia("(prefers-color-scheme: dark)").matches : this.conf.darkMode;
6131
+ }
5934
6132
  iframeLoad($iframe, src) {
5935
6133
  $iframe.src = src;
5936
6134
  showLoading(this.$iframeWrap);
@@ -6265,38 +6463,45 @@ function Detect(userAgent) {
6265
6463
  return dest;
6266
6464
  }
6267
6465
  function check(conf, rules) {
6268
- rules.forEach(({ el, max: maxHeight, imgContains }) => {
6269
- const _apply = () => {
6270
- if (!el)
6271
- return;
6272
- if (!conf.scrollable)
6273
- applyHeightLimit({
6274
- el,
6275
- maxHeight,
6276
- postBtnClick: conf.postExpandBtnClick
6277
- });
6278
- else
6279
- applyScrollableHeightLimit({ el, maxHeight });
6280
- };
6466
+ rules.forEach(({ el, max, imgCheck }) => {
6467
+ if (!el)
6468
+ return;
6469
+ if (imgCheck)
6470
+ el.style.maxHeight = `${max + 1}px`;
6471
+ let lock = false;
6281
6472
  const _check = () => {
6282
- if (el && getHeight(el) > maxHeight)
6283
- _apply();
6473
+ if (lock)
6474
+ return;
6475
+ if (getHeight(el) <= max)
6476
+ return;
6477
+ const afterExpandBtnClick = () => {
6478
+ var _a;
6479
+ lock = true;
6480
+ (_a = conf.afterExpandBtnClick) == null ? void 0 : _a.call(conf);
6481
+ };
6482
+ !conf.scrollable ? applyHeightLimit({ el, max, afterExpandBtnClick }) : applyScrollableHeightLimit({ el, max });
6284
6483
  };
6285
6484
  _check();
6286
- if (imgContains && el)
6287
- onImagesLoaded(el, () => _check());
6485
+ if (imgCheck) {
6486
+ const imgs = el.querySelectorAll(".atk-content img");
6487
+ if (imgs.length === 0)
6488
+ el.style.maxHeight = "";
6489
+ imgs.forEach((img) => {
6490
+ img.onload = () => _check();
6491
+ });
6492
+ }
6288
6493
  });
6289
6494
  }
6290
6495
  const HEIGHT_LIMIT_CSS = "atk-height-limit";
6291
6496
  function applyHeightLimit(obj) {
6292
6497
  if (!obj.el)
6293
6498
  return;
6294
- if (!obj.maxHeight)
6499
+ if (!obj.max)
6295
6500
  return;
6296
6501
  if (obj.el.classList.contains(HEIGHT_LIMIT_CSS))
6297
6502
  return;
6298
6503
  obj.el.classList.add(HEIGHT_LIMIT_CSS);
6299
- obj.el.style.height = `${obj.maxHeight}px`;
6504
+ obj.el.style.height = `${obj.max}px`;
6300
6505
  obj.el.style.overflow = "hidden";
6301
6506
  const $expandBtn = createElement(
6302
6507
  `<div class="atk-height-limit-btn">${t("readMore")}</span>`
@@ -6304,8 +6509,8 @@ function applyHeightLimit(obj) {
6304
6509
  $expandBtn.onclick = (e) => {
6305
6510
  e.stopPropagation();
6306
6511
  disposeHeightLimit(obj.el);
6307
- if (obj.postBtnClick)
6308
- obj.postBtnClick(e);
6512
+ if (obj.afterExpandBtnClick)
6513
+ obj.afterExpandBtnClick(e);
6309
6514
  };
6310
6515
  obj.el.append($expandBtn);
6311
6516
  }
@@ -6320,16 +6525,17 @@ function disposeHeightLimit($el) {
6320
6525
  e.remove();
6321
6526
  });
6322
6527
  $el.style.height = "";
6528
+ $el.style.maxHeight = "";
6323
6529
  $el.style.overflow = "";
6324
6530
  }
6325
6531
  const HEIGHT_LIMIT_SCROLL_CSS = "atk-height-limit-scroll";
6326
- function applyScrollableHeightLimit(obj) {
6327
- if (!obj.el)
6532
+ function applyScrollableHeightLimit(opt) {
6533
+ if (!opt.el)
6328
6534
  return;
6329
- if (obj.el.classList.contains(HEIGHT_LIMIT_SCROLL_CSS))
6535
+ if (opt.el.classList.contains(HEIGHT_LIMIT_SCROLL_CSS))
6330
6536
  return;
6331
- obj.el.classList.add(HEIGHT_LIMIT_SCROLL_CSS);
6332
- obj.el.style.height = `${obj.maxHeight}px`;
6537
+ opt.el.classList.add(HEIGHT_LIMIT_SCROLL_CSS);
6538
+ opt.el.style.height = `${opt.max}px`;
6333
6539
  }
6334
6540
  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';
6335
6541
  function renderAvatar(r) {
@@ -6793,7 +6999,7 @@ class Render {
6793
6999
  const childrenMaxH = conf.children;
6794
7000
  check(
6795
7001
  {
6796
- postExpandBtnClick: () => {
7002
+ afterExpandBtnClick: () => {
6797
7003
  const children = this.comment.getChildren();
6798
7004
  if (children.length === 1)
6799
7005
  disposeHeightLimit(children[0].getRender().$content);
@@ -6802,10 +7008,10 @@ class Render {
6802
7008
  },
6803
7009
  [
6804
7010
  // 评论内容限高
6805
- { el: this.$content, max: contentMaxH, imgContains: true },
6806
- { el: this.$replyTo, max: contentMaxH, imgContains: true },
7011
+ { el: this.$content, max: contentMaxH, imgCheck: true },
7012
+ { el: this.$replyTo, max: contentMaxH, imgCheck: true },
6807
7013
  // 子评论区域限高(仅嵌套模式)
6808
- { el: this.$childrenWrap, max: childrenMaxH, imgContains: false }
7014
+ { el: this.$childrenWrap, max: childrenMaxH, imgCheck: false }
6809
7015
  ]
6810
7016
  );
6811
7017
  }
@@ -6894,7 +7100,7 @@ class CommentActions {
6894
7100
  (_b = this.comment.getRender().voteBtnDown) == null ? void 0 : _b.updateText();
6895
7101
  }).catch((err) => {
6896
7102
  actionBtn == null ? void 0 : actionBtn.setError(t("voteFail"));
6897
- console.log(err);
7103
+ console.error(err);
6898
7104
  });
6899
7105
  }
6900
7106
  /** 管理员 - 评论状态修改 */
@@ -7078,7 +7284,7 @@ class CommentNode {
7078
7284
  return getGravatarURL({
7079
7285
  mirror: this.opts.gravatar.mirror,
7080
7286
  params: this.opts.gravatar.params,
7081
- emailMD5: this.data.email_encrypted
7287
+ emailHash: this.data.email_encrypted
7082
7288
  });
7083
7289
  }
7084
7290
  /** 获取评论 markdown 解析后的内容 */
@@ -7773,7 +7979,7 @@ const services = {
7773
7979
  }
7774
7980
  };
7775
7981
  const PvCountWidget = (ctx) => {
7776
- ctx.watchConf(["site", "pageKey", "pageTitle", "countEl", "pvEl"], (conf) => {
7982
+ ctx.watchConf(["site", "pageKey", "pageTitle", "countEl", "pvEl", "statPageKeyAttr"], (conf) => {
7777
7983
  initCountWidget({
7778
7984
  getApi: () => ctx.getApi(),
7779
7985
  siteName: conf.site,
@@ -7781,78 +7987,85 @@ const PvCountWidget = (ctx) => {
7781
7987
  pageTitle: conf.pageTitle,
7782
7988
  countEl: conf.countEl,
7783
7989
  pvEl: conf.pvEl,
7990
+ pageKeyAttr: conf.statPageKeyAttr,
7784
7991
  pvAdd: typeof ctx.conf.pvAdd === "boolean" ? ctx.conf.pvAdd : true
7785
7992
  });
7786
7993
  });
7787
7994
  };
7788
7995
  function initCountWidget(opt) {
7789
7996
  return __async(this, null, function* () {
7790
- if (opt.countEl && document.querySelector(opt.countEl)) {
7791
- refreshStatCount(opt, { query: "page_comment", numEl: opt.countEl });
7792
- }
7793
- const initialData = opt.pvAdd && opt.pageKey ? {
7794
- [opt.pageKey]: (yield opt.getApi().pages.logPv({
7795
- page_key: opt.pageKey,
7796
- page_title: opt.pageTitle,
7797
- site_name: opt.siteName
7798
- })).data.pv
7799
- // pv+1 and get pv count
7800
- } : void 0;
7801
- if (opt.pvEl && document.querySelector(opt.pvEl)) {
7802
- refreshStatCount(opt, {
7803
- query: "page_pv",
7804
- numEl: opt.pvEl,
7805
- data: initialData
7806
- });
7807
- }
7997
+ yield loadCommentCount(opt);
7998
+ const cacheData = yield incrementPvCount(opt);
7999
+ yield loadPvCount(opt, cacheData);
8000
+ });
8001
+ }
8002
+ function incrementPvCount(opt) {
8003
+ return __async(this, null, function* () {
8004
+ if (!opt.pvAdd || !opt.pageKey)
8005
+ return void 0;
8006
+ const pvCount = (yield opt.getApi().pages.logPv({
8007
+ page_key: opt.pageKey,
8008
+ page_title: opt.pageTitle,
8009
+ site_name: opt.siteName
8010
+ })).data.pv;
8011
+ return {
8012
+ [opt.pageKey]: pvCount
8013
+ };
8014
+ });
8015
+ }
8016
+ function loadCommentCount(opt) {
8017
+ return __async(this, null, function* () {
8018
+ yield loadStatCount({ opt, query: "page_comment", containers: [opt.countEl, "#ArtalkCount"] });
8019
+ });
8020
+ }
8021
+ function loadPvCount(opt, cache) {
8022
+ return __async(this, null, function* () {
8023
+ yield loadStatCount({ opt, query: "page_pv", containers: [opt.pvEl, "#ArtalkPv"], cache });
7808
8024
  });
7809
8025
  }
7810
- function refreshStatCount(opt, args) {
8026
+ function loadStatCount(args) {
7811
8027
  return __async(this, null, function* () {
7812
- let data = args.data || {};
7813
- let queryPageKeys = Array.from(document.querySelectorAll(args.numEl)).map((e) => e.getAttribute("data-page-key") || opt.pageKey).filter((k) => k && typeof data[k] !== "number");
7814
- queryPageKeys = [...new Set(queryPageKeys)];
7815
- if (queryPageKeys.length > 0) {
8028
+ const { opt } = args;
8029
+ let cache = args.cache || {};
8030
+ const els = retrieveElements(args.containers);
8031
+ const pageKeys = getPageKeys(els, opt.pageKeyAttr, opt.pageKey, cache);
8032
+ if (pageKeys.length > 0) {
7816
8033
  const res = (yield opt.getApi().stats.getStats(args.query, {
7817
- page_keys: queryPageKeys.join(","),
8034
+ page_keys: pageKeys.join(","),
7818
8035
  site_name: opt.siteName
7819
8036
  })).data.data;
7820
- data = __spreadValues(__spreadValues({}, data), res);
8037
+ cache = __spreadValues(__spreadValues({}, cache), res);
7821
8038
  }
7822
- const defaultCount = opt.pageKey ? data[opt.pageKey] : 0;
7823
- applyCountData(args.numEl, data, defaultCount);
8039
+ updateElementsText(els, cache, opt.pageKey);
7824
8040
  });
7825
8041
  }
7826
- function applyCountData(selector, data, defaultCount) {
7827
- document.querySelectorAll(selector).forEach((el) => {
7828
- const pageKey = el.getAttribute("data-page-key");
7829
- const count = Number(pageKey ? data[pageKey] : defaultCount);
7830
- el.innerHTML = `${count}`;
8042
+ function retrieveElements(containers) {
8043
+ const els = /* @__PURE__ */ new Set();
8044
+ new Set(containers).forEach((selector) => {
8045
+ document.querySelectorAll(selector).forEach((el) => els.add(el));
7831
8046
  });
8047
+ return els;
7832
8048
  }
7833
- function showErrorDialog(opts) {
7834
- const errEl = createElement(`<span><span class="error-message"></span><br/><br/></span>`);
7835
- errEl.querySelector(".error-message").innerText = `${t("listLoadFailMsg")}
7836
- ${opts.errMsg}`;
7837
- if (opts.retryFn) {
7838
- const $retryBtn = createElement(`<span style="cursor:pointer;">${t("listRetry")}</span>`);
7839
- $retryBtn.onclick = () => opts.retryFn && opts.retryFn();
7840
- errEl.appendChild($retryBtn);
7841
- }
7842
- if (opts.onOpenSidebar) {
7843
- const $openSidebar = createElement(
7844
- `<span atk-only-admin-show> | <span style="cursor:pointer;">${t("openName", { name: t("ctrlCenter") })}</span></span>`
7845
- );
7846
- errEl.appendChild($openSidebar);
7847
- $openSidebar.onclick = () => opts.onOpenSidebar && opts.onOpenSidebar();
7848
- }
7849
- setError(opts.$err, errEl);
8049
+ function getPageKeys(els, pageKeyAttr, pageKey, cache) {
8050
+ const pageKeys = Array.from(els).map((el) => el.getAttribute(pageKeyAttr) || pageKey).filter((key) => key && typeof cache[key] !== "number");
8051
+ return [...new Set(pageKeys)];
8052
+ }
8053
+ function updateElementsText(els, data, defaultPageKey) {
8054
+ els.forEach((el) => {
8055
+ const pageKey = el.getAttribute("data-page-key");
8056
+ const count = pageKey && data[pageKey] || defaultPageKey && data[defaultPageKey] || 0;
8057
+ el.innerText = `${Number(count)}`;
8058
+ });
7850
8059
  }
7851
8060
  const Markdown = (ctx) => {
7852
- initMarked();
7853
- ctx.on("updated", (conf) => {
7854
- if (conf.markedReplacers)
7855
- setReplacers(conf.markedReplacers);
8061
+ ctx.watchConf(["imgLazyLoad", "markedOptions"], (conf) => {
8062
+ initMarked({
8063
+ markedOptions: ctx.getConf().markedOptions,
8064
+ imgLazyLoad: ctx.getConf().imgLazyLoad
8065
+ });
8066
+ });
8067
+ ctx.watchConf(["markedReplacers"], (conf) => {
8068
+ conf.markedReplacers && setReplacers(conf.markedReplacers);
7856
8069
  });
7857
8070
  };
7858
8071
  const LocalStorageKey = "ArtalkContent";
@@ -9089,7 +9302,7 @@ const GotoFocus = (ctx) => {
9089
9302
  comment.focus();
9090
9303
  }));
9091
9304
  };
9092
- const version = "2.8.5";
9305
+ const version = "2.8.7";
9093
9306
  const Copyright = (ctx) => {
9094
9307
  ctx.on("mounted", () => {
9095
9308
  const list2 = ctx.get("list");
@@ -9194,8 +9407,7 @@ function renderDropdown(conf) {
9194
9407
  };
9195
9408
  const $dropdown = createElement(`<ul class="atk-dropdown atk-fade-in"></ul>`);
9196
9409
  dropdownList.forEach((item, i) => {
9197
- const name = item[0];
9198
- const action = item[1];
9410
+ const [name, action] = item;
9199
9411
  const $item = createElement(`<li class="atk-dropdown-item"><span></span></li>`);
9200
9412
  const $link = $item.querySelector("span");
9201
9413
  $link.innerText = name;
@@ -9230,6 +9442,24 @@ const TimeTicking = (ctx) => {
9230
9442
  timer && window.clearInterval(timer);
9231
9443
  });
9232
9444
  };
9445
+ function showErrorDialog(opts) {
9446
+ const errEl = createElement(`<span><span class="error-message"></span><br/><br/></span>`);
9447
+ errEl.querySelector(".error-message").innerText = `${t("listLoadFailMsg")}
9448
+ ${opts.errMsg}`;
9449
+ if (opts.retryFn) {
9450
+ const $retryBtn = createElement(`<span style="cursor:pointer;">${t("listRetry")}</span>`);
9451
+ $retryBtn.onclick = () => opts.retryFn && opts.retryFn();
9452
+ errEl.appendChild($retryBtn);
9453
+ }
9454
+ if (opts.onOpenSidebar) {
9455
+ const $openSidebar = createElement(
9456
+ `<span atk-only-admin-show> | <span style="cursor:pointer;">${t("openName", { name: t("ctrlCenter") })}</span></span>`
9457
+ );
9458
+ errEl.appendChild($openSidebar);
9459
+ $openSidebar.onclick = () => opts.onOpenSidebar && opts.onOpenSidebar();
9460
+ }
9461
+ setError(opts.$err, errEl);
9462
+ }
9233
9463
  const ErrorDialog = (ctx) => {
9234
9464
  ctx.on("list-fetch", () => {
9235
9465
  const list2 = ctx.get("list");
@@ -9494,16 +9724,16 @@ const DefaultPlugins = [
9494
9724
  VersionCheck,
9495
9725
  DarkMode
9496
9726
  ];
9497
- const GlobalPlugins = [...DefaultPlugins];
9727
+ const GlobalPlugins = /* @__PURE__ */ new Set([...DefaultPlugins]);
9498
9728
  function load(ctx) {
9499
9729
  return __async(this, null, function* () {
9500
9730
  var _a;
9501
- const loadedPlugins = [];
9731
+ const loadedPlugins = /* @__PURE__ */ new Set();
9502
9732
  const loadPlugins = (plugins) => {
9503
9733
  plugins.forEach((plugin) => {
9504
- if (typeof plugin === "function" && !loadedPlugins.includes(plugin)) {
9734
+ if (typeof plugin === "function" && !loadedPlugins.has(plugin)) {
9505
9735
  plugin(ctx);
9506
- loadedPlugins.push(plugin);
9736
+ loadedPlugins.add(plugin);
9507
9737
  }
9508
9738
  });
9509
9739
  };
@@ -9539,8 +9769,9 @@ function load(ctx) {
9539
9769
  }
9540
9770
  function loadNetworkPlugins(scripts, apiBase) {
9541
9771
  return __async(this, null, function* () {
9772
+ const networkPlugins = /* @__PURE__ */ new Set();
9542
9773
  if (!scripts || !Array.isArray(scripts))
9543
- return [];
9774
+ return networkPlugins;
9544
9775
  const tasks = [];
9545
9776
  scripts.forEach((url) => {
9546
9777
  if (!/^(http|https):\/\//.test(url))
@@ -9560,7 +9791,11 @@ function loadNetworkPlugins(scripts, apiBase) {
9560
9791
  );
9561
9792
  });
9562
9793
  yield Promise.all(tasks);
9563
- return Object.values(window.ArtalkPlugins || {});
9794
+ Object.values(window.ArtalkPlugins || {}).forEach((plugin) => {
9795
+ if (typeof plugin === "function")
9796
+ networkPlugins.add(plugin);
9797
+ });
9798
+ return networkPlugins;
9564
9799
  });
9565
9800
  }
9566
9801
  function onLoadErr(ctx, err) {
@@ -9646,9 +9881,7 @@ class Artalk {
9646
9881
  }
9647
9882
  /** Use plugin, the plugin will be used when Artalk.init */
9648
9883
  static use(plugin) {
9649
- if (GlobalPlugins.includes(plugin))
9650
- return;
9651
- GlobalPlugins.push(plugin);
9884
+ GlobalPlugins.add(plugin);
9652
9885
  }
9653
9886
  /** Load count widget */
9654
9887
  static loadCountWidget(c) {
@@ -9658,6 +9891,7 @@ class Artalk {
9658
9891
  siteName: conf.site,
9659
9892
  countEl: conf.countEl,
9660
9893
  pvEl: conf.pvEl,
9894
+ pageKeyAttr: conf.statPageKeyAttr,
9661
9895
  pvAdd: false
9662
9896
  });
9663
9897
  }