artalk 2.2.12 → 2.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/Artalk.css +1 -1
  2. package/dist/Artalk.es.js +1535 -1175
  3. package/dist/Artalk.iife.js +1 -1
  4. package/dist/Artalk.js +1 -1
  5. package/dist/ArtalkLite.css +1 -0
  6. package/dist/ArtalkLite.es.js +5123 -0
  7. package/dist/ArtalkLite.iife.js +1 -0
  8. package/dist/ArtalkLite.js +1 -0
  9. package/index.html +2 -2
  10. package/package.json +6 -5
  11. package/src/api/index.ts +20 -1
  12. package/src/api/request.ts +7 -7
  13. package/src/artalk.ts +117 -121
  14. package/src/comment/actions.ts +86 -0
  15. package/src/{components/html → comment}/comment.html +1 -1
  16. package/src/comment/comment.ts +191 -0
  17. package/src/comment/index.ts +3 -0
  18. package/src/comment/render.ts +425 -0
  19. package/src/components/action-btn.ts +6 -3
  20. package/src/components/dialog.ts +7 -3
  21. package/src/components/read-more-btn.ts +6 -2
  22. package/src/context.ts +204 -13
  23. package/src/defaults.ts +16 -5
  24. package/src/editor/editor.html +18 -0
  25. package/src/editor/editor.ts +419 -0
  26. package/src/editor/index.ts +3 -0
  27. package/src/editor/plugs/editor-plug.ts +51 -0
  28. package/src/{components/editor-plugs → editor/plugs}/emoticons-plug.less +2 -1
  29. package/src/{components/editor-plugs → editor/plugs}/emoticons-plug.ts +65 -63
  30. package/src/editor/plugs/header-input-plug.ts +78 -0
  31. package/src/{components/editor-plugs → editor/plugs}/preview-plug.less +0 -0
  32. package/src/editor/plugs/preview-plug.ts +40 -0
  33. package/src/editor/plugs/upload-plug.ts +124 -0
  34. package/src/i18n/en.ts +78 -0
  35. package/src/i18n/index.ts +21 -0
  36. package/src/i18n/zh-CN.ts +82 -0
  37. package/src/layer/index.ts +4 -0
  38. package/src/{components → layer}/layer.ts +9 -9
  39. package/src/{components/html → layer}/sidebar-layer.html +0 -0
  40. package/src/{components → layer}/sidebar-layer.ts +17 -47
  41. package/src/lib/checker/admin-checker.ts +7 -7
  42. package/src/lib/checker/captcha-checker.ts +5 -6
  43. package/src/lib/checker/index.ts +22 -17
  44. package/src/lib/component.ts +7 -2
  45. package/src/lib/stat.ts +63 -0
  46. package/src/lib/ui.ts +6 -12
  47. package/src/lib/user.ts +20 -3
  48. package/src/lib/utils.ts +23 -9
  49. package/src/list/index.ts +3 -0
  50. package/src/{components → list}/list-lite.ts +80 -100
  51. package/src/{components → list}/list-nest.ts +0 -0
  52. package/src/{components/html → list}/list.html +3 -6
  53. package/src/{components → list}/list.ts +44 -42
  54. package/src/style/_variables.less +1 -2
  55. package/src/style/comment.less +26 -13
  56. package/src/style/editor.less +22 -14
  57. package/src/style/list.less +6 -9
  58. package/src/style/main.less +9 -8
  59. package/src/style/pagination.less +1 -1
  60. package/test/ArtalkCount.html +16 -0
  61. package/test/ArtalkI18n.html +15 -0
  62. package/test/ArtalkLite.html +15 -0
  63. package/types/artalk-config.d.ts +50 -32
  64. package/types/context.d.ts +128 -0
  65. package/types/event.d.ts +6 -66
  66. package/types/global.d.ts +1 -0
  67. package/types/plug.d.ts +4 -0
  68. package/{vite-common.config.ts → vite-base.config.ts} +6 -4
  69. package/vite-demo.config.ts +2 -4
  70. package/vite-lite.config.ts +22 -0
  71. package/vite.config.ts +9 -12
  72. package/src/components/_component.ts.txt +0 -10
  73. package/src/components/comment.ts +0 -605
  74. package/src/components/editor-plugs/_example-plug.ts.txt +0 -29
  75. package/src/components/editor-plugs/editor-plug.ts +0 -20
  76. package/src/components/editor-plugs/preview-plug.ts +0 -48
  77. package/src/components/editor.ts +0 -607
  78. package/src/components/html/editor.html +0 -18
package/dist/Artalk.es.js CHANGED
@@ -42,9 +42,146 @@ var __async = (__this, __arguments, generator) => {
42
42
  });
43
43
  };
44
44
  var main = "";
45
+ var en = {
46
+ placeholder: "Leave a comment",
47
+ noComment: "No Comment",
48
+ send: "Send",
49
+ nick: "Nickname",
50
+ email: "Email",
51
+ link: "Website",
52
+ emoticon: "Emoji",
53
+ preview: "Preview",
54
+ image: "Image",
55
+ uploadFail: "Upload Failed",
56
+ commentFail: "Failed to comment",
57
+ restoredMsg: "Content has been restored",
58
+ onlyAdminCanReply: "Only admin can reply",
59
+ uploadLoginMsg: "Please fill in your name and email to upload",
60
+ counter: "{count} Comments",
61
+ sortLatest: "Latest",
62
+ sortOldest: "Oldest",
63
+ sortBest: "Best",
64
+ sortAuthor: "Author",
65
+ openComment: "Open Comment",
66
+ closeComment: "Close Comment",
67
+ listLoadFailMsg: "Failed to load comments",
68
+ listRetry: "Click to retry",
69
+ loadMore: "Load More",
70
+ admin: "Admin",
71
+ reply: "Reply",
72
+ voteUp: "Up",
73
+ voteDown: "Down",
74
+ voteFail: "Vote Failed",
75
+ readMore: "Read More",
76
+ actionConfirm: "Confirm",
77
+ collapse: "Collapse",
78
+ collapsed: "Collapsed",
79
+ collapsedMsg: "This comment has been collapsed",
80
+ expand: "Expand",
81
+ approved: "Approved",
82
+ pending: "Pending",
83
+ pendingMsg: "Pending, visible only to commenter.",
84
+ delete: "Delete",
85
+ deleteConfirm: "Confirm",
86
+ pin: "Pin",
87
+ unpin: "Unpin",
88
+ seconds: "seconds ago",
89
+ minutes: "minutes ago",
90
+ hours: "hours ago",
91
+ days: "days ago",
92
+ now: "just now",
93
+ adminCheck: "Enter admin password:",
94
+ captchaCheck: "Enter the CAPTCHA to continue:",
95
+ confirm: "Confirm",
96
+ cancel: "Cancel",
97
+ msgCenter: "Messages",
98
+ ctrlCenter: "Admin",
99
+ frontend: "Frontend",
100
+ backend: "Backend",
101
+ loading: "Loading",
102
+ loadFail: "Load Failed",
103
+ editing: "Editing",
104
+ editFail: "Edit Failed",
105
+ deleting: "Deleting",
106
+ deleteFail: "Delete Failed",
107
+ reqGot: "Request got",
108
+ reqAborted: "Request timed out or terminated unexpectedly"
109
+ };
110
+ const zhCN = {
111
+ placeholder: "\u952E\u5165\u5185\u5BB9...",
112
+ noComment: "\u300C\u6B64\u65F6\u65E0\u58F0\u80DC\u6709\u58F0\u300D",
113
+ send: "\u53D1\u9001\u8BC4\u8BBA",
114
+ nick: "\u6635\u79F0",
115
+ email: "\u90AE\u4EF6",
116
+ link: "\u7F51\u5740",
117
+ emoticon: "\u8868\u60C5",
118
+ preview: "\u9884\u89C8",
119
+ image: "\u56FE\u7247",
120
+ uploadFail: "\u4E0A\u4F20\u5931\u8D25",
121
+ commentFail: "\u8BC4\u8BBA\u5931\u8D25",
122
+ restoredMsg: "\u5185\u5BB9\u5DF2\u81EA\u52A8\u6062\u590D",
123
+ onlyAdminCanReply: "\u4EC5\u7BA1\u7406\u5458\u53EF\u8BC4\u8BBA",
124
+ uploadLoginMsg: "\u586B\u5165\u4F60\u7684\u540D\u5B57\u90AE\u7BB1\u624D\u80FD\u4E0A\u4F20\u54E6",
125
+ counter: "{count} \u6761\u8BC4\u8BBA",
126
+ sortLatest: "\u6700\u65B0",
127
+ sortOldest: "\u6700\u65E9",
128
+ sortBest: "\u6700\u70ED",
129
+ sortAuthor: "\u4F5C\u8005",
130
+ openComment: "\u6253\u5F00\u8BC4\u8BBA",
131
+ closeComment: "\u5173\u95ED\u8BC4\u8BBA",
132
+ listLoadFailMsg: "\u65E0\u6CD5\u83B7\u53D6\u8BC4\u8BBA\u5217\u8868\u6570\u636E",
133
+ listRetry: "\u70B9\u51FB\u91CD\u65B0\u83B7\u53D6",
134
+ loadMore: "\u52A0\u8F7D\u66F4\u591A",
135
+ admin: "\u7BA1\u7406\u5458",
136
+ reply: "\u56DE\u590D",
137
+ voteUp: "\u8D5E\u540C",
138
+ voteDown: "\u53CD\u5BF9",
139
+ voteFail: "\u6295\u7968\u5931\u8D25",
140
+ readMore: "\u9605\u8BFB\u66F4\u591A",
141
+ actionConfirm: "\u786E\u8BA4\u64CD\u4F5C",
142
+ collapse: "\u6298\u53E0",
143
+ collapsed: "\u5DF2\u6298\u53E0",
144
+ collapsedMsg: "\u8BE5\u8BC4\u8BBA\u5DF2\u88AB\u7CFB\u7EDF\u6216\u7BA1\u7406\u5458\u6298\u53E0",
145
+ expand: "\u53D6\u6D88\u6298\u53E0",
146
+ approved: "\u5DF2\u5BA1",
147
+ pending: "\u5F85\u5BA1",
148
+ pendingMsg: "\u5BA1\u6838\u4E2D\uFF0C\u4EC5\u672C\u4EBA\u53EF\u89C1\u3002",
149
+ delete: "\u5220\u9664",
150
+ deleteConfirm: "\u786E\u8BA4\u5220\u9664",
151
+ pin: "\u7F6E\u9876",
152
+ unpin: "\u53D6\u6D88\u7F6E\u9876",
153
+ seconds: "\u79D2\u524D",
154
+ minutes: "\u5206\u949F\u524D",
155
+ hours: "\u5C0F\u65F6\u524D",
156
+ days: "\u5929\u524D",
157
+ now: "\u521A\u521A",
158
+ adminCheck: "\u952E\u5165\u5BC6\u7801\u6765\u9A8C\u8BC1\u7BA1\u7406\u5458\u8EAB\u4EFD\uFF1A",
159
+ captchaCheck: "\u952E\u5165\u9A8C\u8BC1\u7801\u7EE7\u7EED\uFF1A",
160
+ confirm: "\u786E\u8BA4",
161
+ cancel: "\u53D6\u6D88",
162
+ msgCenter: "\u901A\u77E5\u4E2D\u5FC3",
163
+ ctrlCenter: "\u63A7\u5236\u4E2D\u5FC3",
164
+ frontend: "\u524D\u7AEF",
165
+ backend: "\u540E\u7AEF",
166
+ loading: "\u52A0\u8F7D\u4E2D",
167
+ loadFail: "\u52A0\u8F7D\u5931\u8D25",
168
+ editing: "\u4FEE\u6539\u4E2D",
169
+ editFail: "\u4FEE\u6539\u5931\u8D25",
170
+ deleting: "\u5220\u9664\u4E2D",
171
+ deleteFail: "\u5220\u9664\u5931\u8D25",
172
+ reqGot: "\u8BF7\u6C42\u54CD\u5E94",
173
+ reqAborted: "\u8BF7\u6C42\u8D85\u65F6\u6216\u610F\u5916\u7EC8\u6B62"
174
+ };
175
+ const internal = {
176
+ "en": en,
177
+ "en-US": en,
178
+ "zh-CN": zhCN
179
+ };
45
180
  class User {
46
- constructor(conf) {
181
+ constructor(ctx) {
182
+ __publicField(this, "ctx");
47
183
  __publicField(this, "data");
184
+ this.ctx = ctx;
48
185
  const localUser = JSON.parse(window.localStorage.getItem("ArtalkUser") || "{}");
49
186
  this.data = {
50
187
  nick: localUser.nick || "",
@@ -54,79 +191,23 @@ class User {
54
191
  isAdmin: localUser.isAdmin || false
55
192
  };
56
193
  }
57
- save() {
194
+ update(obj = {}) {
195
+ Object.entries(obj).forEach(([key, value]) => {
196
+ this.data[key] = value;
197
+ });
58
198
  window.localStorage.setItem("ArtalkUser", JSON.stringify(this.data));
199
+ this.ctx.trigger("user-changed", this.ctx.user.data);
59
200
  }
60
- checkHasBasicUserInfo() {
61
- return !!this.data.nick && !!this.data.email;
62
- }
63
- }
64
- class Context {
65
- constructor(rootEl, conf) {
66
- __publicField(this, "cid");
67
- __publicField(this, "$root");
68
- __publicField(this, "conf");
69
- __publicField(this, "user");
70
- __publicField(this, "eventList", []);
71
- __publicField(this, "markedInstance");
72
- __publicField(this, "markedReplacers", []);
73
- this.cid = +new Date();
74
- this.$root = rootEl;
75
- this.conf = conf;
76
- this.user = new User(this.conf);
77
- this.$root.setAttribute("atk-run-id", this.cid.toString());
78
- }
79
- on(name, handler, scope = "internal") {
80
- this.eventList.push({ name, handler, scope });
81
- }
82
- off(name, handler, scope = "internal") {
83
- this.eventList = this.eventList.filter((evt) => {
84
- if (handler)
85
- return !(evt.name === name && evt.handler === handler && evt.scope === scope);
86
- return !(evt.name === name && evt.scope === scope);
201
+ logout() {
202
+ this.update({
203
+ token: "",
204
+ isAdmin: false
87
205
  });
88
206
  }
89
- trigger(name, payload, scope) {
90
- this.eventList.filter((evt) => evt.name === name && (scope ? evt.scope === scope : true)).map((evt) => evt.handler).forEach((handler) => handler(payload));
207
+ checkHasBasicUserInfo() {
208
+ return !!this.data.nick && !!this.data.email;
91
209
  }
92
210
  }
93
- const defaults$3 = {
94
- el: "",
95
- pageKey: "",
96
- server: "",
97
- site: "",
98
- placeholder: "\u952E\u5165\u5185\u5BB9...",
99
- noComment: "\u300C\u6B64\u65F6\u65E0\u58F0\u80DC\u6709\u58F0\u300D",
100
- sendBtn: "\u53D1\u9001\u8BC4\u8BBA",
101
- darkMode: "auto",
102
- editorTravel: true,
103
- flatMode: "auto",
104
- nestMax: 2,
105
- nestSort: "DATE_ASC",
106
- emoticons: "https://cdn.jsdelivr.net/gh/ArtalkJS/Emoticons/grps/default.json",
107
- vote: true,
108
- voteDown: false,
109
- uaBadge: true,
110
- listSort: true,
111
- pvEl: "#ArtalkPV",
112
- gravatar: {
113
- default: "mp",
114
- mirror: "https://sdn.geekzu.org/avatar/"
115
- },
116
- pagination: {
117
- pageSize: 20,
118
- readMore: true,
119
- autoLoad: true
120
- },
121
- heightLimit: {
122
- content: 300,
123
- children: 400
124
- },
125
- imgUpload: true,
126
- reqTimeout: 15e3,
127
- versionCheck: true,
128
- useBackendConf: false
129
- };
130
211
  class Component {
131
212
  constructor(ctx) {
132
213
  __publicField(this, "$el");
@@ -135,6 +216,9 @@ class Component {
135
216
  this.ctx = ctx;
136
217
  this.conf = ctx.conf;
137
218
  }
219
+ $t(key, args = {}) {
220
+ return this.ctx.$t(key, args);
221
+ }
138
222
  }
139
223
  function getDefaults() {
140
224
  return {
@@ -159,9 +243,9 @@ function getDefaults() {
159
243
  xhtml: false
160
244
  };
161
245
  }
162
- let defaults$2 = getDefaults();
246
+ let defaults$3 = getDefaults();
163
247
  function changeDefaults(newDefaults) {
164
- defaults$2 = newDefaults;
248
+ defaults$3 = newDefaults;
165
249
  }
166
250
  const escapeTest = /[&<>"']/;
167
251
  const escapeReplace = /[&<>"']/g;
@@ -384,15 +468,14 @@ function outputLink(cap, link, raw, lexer) {
384
468
  };
385
469
  lexer.state.inLink = false;
386
470
  return token;
387
- } else {
388
- return {
389
- type: "image",
390
- raw,
391
- href,
392
- title,
393
- text: escape(text)
394
- };
395
471
  }
472
+ return {
473
+ type: "image",
474
+ raw,
475
+ href,
476
+ title,
477
+ text: escape(text)
478
+ };
396
479
  }
397
480
  function indentCodeCompensation(raw, text) {
398
481
  const matchIndentToCode = raw.match(/^(\s+)(?:```)/);
@@ -414,7 +497,7 @@ function indentCodeCompensation(raw, text) {
414
497
  }
415
498
  class Tokenizer {
416
499
  constructor(options) {
417
- this.options = options || defaults$2;
500
+ this.options = options || defaults$3;
418
501
  }
419
502
  space(src) {
420
503
  const cap = this.rules.block.newline.exec(src);
@@ -541,7 +624,8 @@ class Tokenizer {
541
624
  endEarly = true;
542
625
  }
543
626
  if (!endEarly) {
544
- const nextBulletRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])`);
627
+ const nextBulletRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])((?: [^\\n]*)?(?:\\n|$))`);
628
+ const hrRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`);
545
629
  while (src) {
546
630
  rawLine = src.split("\n", 1)[0];
547
631
  line = rawLine;
@@ -551,6 +635,9 @@ class Tokenizer {
551
635
  if (nextBulletRegex.test(line)) {
552
636
  break;
553
637
  }
638
+ if (hrRegex.test(src)) {
639
+ break;
640
+ }
554
641
  if (line.search(/[^ ]/) >= indent || !line.trim()) {
555
642
  itemContents += "\n" + line.slice(indent);
556
643
  } else if (!blankLine) {
@@ -686,14 +773,14 @@ class Tokenizer {
686
773
  l = item.header.length;
687
774
  for (j = 0; j < l; j++) {
688
775
  item.header[j].tokens = [];
689
- this.lexer.inlineTokens(item.header[j].text, item.header[j].tokens);
776
+ this.lexer.inline(item.header[j].text, item.header[j].tokens);
690
777
  }
691
778
  l = item.rows.length;
692
779
  for (j = 0; j < l; j++) {
693
780
  row = item.rows[j];
694
781
  for (k = 0; k < row.length; k++) {
695
782
  row[k].tokens = [];
696
- this.lexer.inlineTokens(row[k].text, row[k].tokens);
783
+ this.lexer.inline(row[k].text, row[k].tokens);
697
784
  }
698
785
  }
699
786
  return item;
@@ -1129,7 +1216,7 @@ class Lexer {
1129
1216
  constructor(options) {
1130
1217
  this.tokens = [];
1131
1218
  this.tokens.links = /* @__PURE__ */ Object.create(null);
1132
- this.options = options || defaults$2;
1219
+ this.options = options || defaults$3;
1133
1220
  this.options.tokenizer = this.options.tokenizer || new Tokenizer();
1134
1221
  this.tokenizer = this.options.tokenizer;
1135
1222
  this.tokenizer.options = this.options;
@@ -1477,7 +1564,7 @@ class Lexer {
1477
1564
  }
1478
1565
  class Renderer {
1479
1566
  constructor(options) {
1480
- this.options = options || defaults$2;
1567
+ this.options = options || defaults$3;
1481
1568
  }
1482
1569
  code(code, infostring, escaped) {
1483
1570
  const lang = (infostring || "").match(/\S*/)[0];
@@ -1647,7 +1734,7 @@ class Slugger {
1647
1734
  }
1648
1735
  class Parser {
1649
1736
  constructor(options) {
1650
- this.options = options || defaults$2;
1737
+ this.options = options || defaults$3;
1651
1738
  this.options.renderer = this.options.renderer || new Renderer();
1652
1739
  this.renderer = this.options.renderer;
1653
1740
  this.renderer.options = this.options;
@@ -1938,7 +2025,7 @@ marked$1.options = marked$1.setOptions = function(opt) {
1938
2025
  return marked$1;
1939
2026
  };
1940
2027
  marked$1.getDefaults = getDefaults;
1941
- marked$1.defaults = defaults$2;
2028
+ marked$1.defaults = defaults$3;
1942
2029
  marked$1.use = function(...args) {
1943
2030
  const opts = merge({}, ...args);
1944
2031
  const extensions = marked$1.defaults.extensions || { renderers: {}, childTokens: {} };
@@ -2100,6 +2187,11 @@ marked$1.lexer = Lexer.lex;
2100
2187
  marked$1.Tokenizer = Tokenizer;
2101
2188
  marked$1.Slugger = Slugger;
2102
2189
  marked$1.parse = marked$1;
2190
+ marked$1.options;
2191
+ marked$1.setOptions;
2192
+ marked$1.use;
2193
+ marked$1.walkTokens;
2194
+ marked$1.parseInline;
2103
2195
  Parser.parse;
2104
2196
  Lexer.lex;
2105
2197
  var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
@@ -2421,7 +2513,7 @@ function sanitizer$1(buffer, options) {
2421
2513
  }
2422
2514
  }
2423
2515
  var sanitizer_1 = sanitizer$1;
2424
- var defaults$1 = {
2516
+ var defaults$2 = {
2425
2517
  allowedAttributes: {
2426
2518
  a: ["href", "name", "target", "title", "aria-label"],
2427
2519
  iframe: ["allowfullscreen", "frameborder", "src"],
@@ -2477,19 +2569,19 @@ var defaults$1 = {
2477
2569
  ],
2478
2570
  filter: null
2479
2571
  };
2480
- var defaults_1 = defaults$1;
2572
+ var defaults_1 = defaults$2;
2481
2573
  var assign = assignment_1;
2482
2574
  var parser = parser_1;
2483
2575
  var sanitizer = sanitizer_1;
2484
- var defaults = defaults_1;
2576
+ var defaults$1 = defaults_1;
2485
2577
  function insane(html, options, strict) {
2486
2578
  var buffer = [];
2487
- var configuration = strict === true ? options : assign({}, defaults, options);
2579
+ var configuration = strict === true ? options : assign({}, defaults$1, options);
2488
2580
  var handler = sanitizer(buffer, configuration);
2489
2581
  parser(html, handler);
2490
2582
  return buffer.join("");
2491
2583
  }
2492
- insane.defaults = defaults;
2584
+ insane.defaults = defaults$1;
2493
2585
  var insane_1 = insane;
2494
2586
  var hanabi$1 = { exports: {} };
2495
2587
  (function(module, exports) {
@@ -2586,7 +2678,7 @@ function dateFormat(date) {
2586
2678
  const vYear = padWithZeros(date.getFullYear(), 2);
2587
2679
  return `${vYear}-${vMonth}-${vDay}`;
2588
2680
  }
2589
- function timeAgo(date) {
2681
+ function timeAgo(date, ctx) {
2590
2682
  try {
2591
2683
  const oldTime = date.getTime();
2592
2684
  const currTime = new Date().getTime();
@@ -2601,16 +2693,16 @@ function timeAgo(date) {
2601
2693
  if (minutes === 0) {
2602
2694
  const leave3 = leave2 % (60 * 1e3);
2603
2695
  const seconds = Math.round(leave3 / 1e3);
2604
- return `${seconds} \u79D2\u524D`;
2696
+ return `${seconds} ${ctx.$t("seconds")}`;
2605
2697
  }
2606
- return `${minutes} \u5206\u949F\u524D`;
2698
+ return `${minutes} ${ctx.$t("minutes")}`;
2607
2699
  }
2608
- return `${hours} \u5C0F\u65F6\u524D`;
2700
+ return `${hours} ${ctx.$t("hours")}`;
2609
2701
  }
2610
2702
  if (days < 0)
2611
- return "\u521A\u521A";
2703
+ return ctx.$t("now");
2612
2704
  if (days < 8) {
2613
- return `${days} \u5929\u524D`;
2705
+ return `${days} ${ctx.$t("days")}`;
2614
2706
  }
2615
2707
  return dateFormat(date);
2616
2708
  } catch (error) {
@@ -2640,8 +2732,7 @@ function onImagesLoaded($container, event) {
2640
2732
  }
2641
2733
  }
2642
2734
  function getGravatarURL(ctx, emailMD5) {
2643
- var _a, _b;
2644
- return `${(((_a = ctx.conf.gravatar) == null ? void 0 : _a.mirror) || "").replace(/\/$/, "")}/${emailMD5}?d=${encodeURIComponent(((_b = ctx.conf.gravatar) == null ? void 0 : _b.default) || "")}&s=80`;
2735
+ return `${ctx.conf.gravatar.mirror.replace(/\/$/, "")}/${emailMD5}?d=${encodeURIComponent(ctx.conf.gravatar.default)}&s=80`;
2645
2736
  }
2646
2737
  function versionCompare(a, b) {
2647
2738
  const pa = a.split(".");
@@ -2661,6 +2752,12 @@ function versionCompare(a, b) {
2661
2752
  return 0;
2662
2753
  }
2663
2754
  function initMarked(ctx) {
2755
+ try {
2756
+ if (!marked$1.name)
2757
+ return;
2758
+ } catch (e) {
2759
+ return;
2760
+ }
2664
2761
  const renderer = new marked$1.Renderer();
2665
2762
  const orgLinkRenderer = renderer.link;
2666
2763
  renderer.link = (href, title, text) => {
@@ -2697,7 +2794,12 @@ function initMarked(ctx) {
2697
2794
  ctx.markedInstance = nMarked;
2698
2795
  }
2699
2796
  function marked(ctx, src) {
2700
- let dest = insane_1(ctx.markedInstance.parse(src), {
2797
+ var _a;
2798
+ let markedContent = (_a = ctx.markedInstance) == null ? void 0 : _a.parse(src);
2799
+ if (!markedContent) {
2800
+ markedContent = src.replace(/```\s*([^]+?.*?[^]+?[^]+?)```/g, (_, code) => `<pre><code>${hanabi(code)}</code></pre>`).replace(/!\[(.*?)\]\((.*?)\)/g, (_, alt, imgSrc) => `<img src="${imgSrc}" alt="${alt}" />`).replace(/\[(.*?)\]\((.*?)\)/g, (_, text, link) => `<a href="${link}" target="_blank">${text}</a>`).replace(/\n/g, "<br>");
2801
+ }
2802
+ let dest = insane_1(markedContent, {
2701
2803
  allowedClasses: {},
2702
2804
  allowedSchemes: ["http", "https", "mailto"],
2703
2805
  allowedTags: [
@@ -2837,9 +2939,7 @@ function mergeDeep(target, source) {
2837
2939
  return target;
2838
2940
  }
2839
2941
  function showLoading(parentElem, conf) {
2840
- if (parentElem instanceof Context)
2841
- parentElem = parentElem.$root;
2842
- let $loading = parentElem.querySelector(".atk-loading");
2942
+ let $loading = parentElem.querySelector(":scope > .atk-loading");
2843
2943
  if (!$loading) {
2844
2944
  $loading = createElement(`<div class="atk-loading atk-fade-in" style="display: none;">
2845
2945
  <div class="atk-loading-spinner">
@@ -2860,9 +2960,7 @@ function showLoading(parentElem, conf) {
2860
2960
  }
2861
2961
  }
2862
2962
  function hideLoading(parentElem) {
2863
- if (parentElem instanceof Context)
2864
- parentElem = parentElem.$root;
2865
- const $loading = parentElem.querySelector(".atk-loading");
2963
+ const $loading = parentElem.querySelector(":scope > .atk-loading");
2866
2964
  if ($loading)
2867
2965
  $loading.style.display = "none";
2868
2966
  }
@@ -2924,8 +3022,6 @@ function playFadeInAnim(elem, after) {
2924
3022
  playFadeAnim(elem, after, "in");
2925
3023
  }
2926
3024
  function setError(parentElem, html, title = '<span class="atk-error-title">Artalk Error</span>') {
2927
- if (parentElem instanceof Context)
2928
- parentElem = parentElem.$root;
2929
3025
  let elem = parentElem.querySelector(".atk-error-layer");
2930
3026
  if (html === null) {
2931
3027
  if (elem !== null)
@@ -3084,43 +3180,6 @@ function GetLayerWrap(ctx) {
3084
3180
  const $mask = $wrap.querySelector(".atk-layer-mask");
3085
3181
  return { $wrap, $mask };
3086
3182
  }
3087
- class Dialog {
3088
- constructor(contentEl) {
3089
- __publicField(this, "$el");
3090
- __publicField(this, "$content");
3091
- __publicField(this, "$actions");
3092
- this.$el = createElement(`<div class="atk-layer-dialog-wrap">
3093
- <div class="atk-layer-dialog">
3094
- <div class="atk-layer-dialog-content"></div>
3095
- <div class="atk-layer-dialog-actions"></div>
3096
- </div>
3097
- </div>`);
3098
- this.$actions = this.$el.querySelector(".atk-layer-dialog-actions");
3099
- this.$content = this.$el.querySelector(".atk-layer-dialog-content");
3100
- this.$content.appendChild(contentEl);
3101
- return this;
3102
- }
3103
- setYes(handler) {
3104
- const btn = createElement('<button data-action="confirm">\u786E\u5B9A</button>');
3105
- btn.onclick = this.onBtnClick(handler);
3106
- this.$actions.appendChild(btn);
3107
- return this;
3108
- }
3109
- setNo(handler) {
3110
- const btn = createElement('<button data-action="cancel">\u53D6\u6D88</button>');
3111
- btn.onclick = this.onBtnClick(handler);
3112
- this.$actions.appendChild(btn);
3113
- return this;
3114
- }
3115
- onBtnClick(handler) {
3116
- return (evt) => {
3117
- const re = handler(evt.currentTarget, this);
3118
- if (re === void 0 || re === true) {
3119
- this.$el.remove();
3120
- }
3121
- };
3122
- }
3123
- }
3124
3183
  function Fetch(ctx, input, init, timeout) {
3125
3184
  return __async(this, null, function* () {
3126
3185
  var _a, _b;
@@ -3129,12 +3188,12 @@ function Fetch(ctx, input, init, timeout) {
3129
3188
  headers.set("Authorization", `Bearer ${ctx.user.data.token}`);
3130
3189
  init.headers = headers;
3131
3190
  }
3132
- const resp = yield timeoutFetch(input, timeout || ctx.conf.reqTimeout || 15e3, init);
3191
+ const resp = yield timeoutFetch(ctx, input, timeout || ctx.conf.reqTimeout || 15e3, init);
3133
3192
  const respHttpCode = resp.status;
3134
3193
  const noAccessCodes = [401, 400];
3135
3194
  const isNoAccess = noAccessCodes.includes(respHttpCode);
3136
3195
  if (!resp.ok && !isNoAccess)
3137
- throw new Error(`\u8BF7\u6C42\u54CD\u5E94 ${respHttpCode}`);
3196
+ throw new Error(`${ctx.$t("reqGot")} ${respHttpCode}`);
3138
3197
  let json = yield resp.json();
3139
3198
  const recall = (resolve, reject) => {
3140
3199
  Fetch(ctx, input, init).then((d) => {
@@ -3145,7 +3204,7 @@ function Fetch(ctx, input, init, timeout) {
3145
3204
  };
3146
3205
  if ((_a = json.data) == null ? void 0 : _a.need_captcha) {
3147
3206
  json = yield new Promise((resolve, reject) => {
3148
- ctx.trigger("checker-captcha", {
3207
+ ctx.checkCaptcha({
3149
3208
  imgData: json.data.img_data,
3150
3209
  iframe: json.data.iframe,
3151
3210
  onSuccess: () => {
@@ -3158,7 +3217,7 @@ function Fetch(ctx, input, init, timeout) {
3158
3217
  });
3159
3218
  } else if (((_b = json.data) == null ? void 0 : _b.need_login) || isNoAccess) {
3160
3219
  json = yield new Promise((resolve, reject) => {
3161
- ctx.trigger("checker-admin", {
3220
+ ctx.checkAdmin({
3162
3221
  onSuccess: () => {
3163
3222
  recall(resolve, reject);
3164
3223
  },
@@ -3189,7 +3248,7 @@ function ToFormData(object) {
3189
3248
  Object.keys(object).forEach((key) => formData.append(key, String(object[key])));
3190
3249
  return formData;
3191
3250
  }
3192
- function timeoutFetch(url, ms, opts) {
3251
+ function timeoutFetch(ctx, url, ms, opts) {
3193
3252
  var _a;
3194
3253
  const controller = new AbortController();
3195
3254
  (_a = opts.signal) == null ? void 0 : _a.addEventListener("abort", () => controller.abort());
@@ -3201,7 +3260,7 @@ function timeoutFetch(url, ms, opts) {
3201
3260
  });
3202
3261
  }
3203
3262
  promise = promise.catch((err) => {
3204
- throw (err || {}).name === "AbortError" ? new Error("\u8BF7\u6C42\u8D85\u65F6\u6216\u610F\u5916\u7EC8\u6B62") : err;
3263
+ throw (err || {}).name === "AbortError" ? new Error(ctx.$t("reqAborted")) : err;
3205
3264
  });
3206
3265
  return promise;
3207
3266
  }
@@ -3798,6 +3857,20 @@ class Api {
3798
3857
  return p.pv;
3799
3858
  });
3800
3859
  }
3860
+ stat(type, pageKeys, limit) {
3861
+ return __async(this, null, function* () {
3862
+ const params = {
3863
+ type,
3864
+ site_name: this.ctx.conf.site || ""
3865
+ };
3866
+ if (pageKeys)
3867
+ params.page_keys = Array.isArray(pageKeys) ? pageKeys.join(",") : pageKeys;
3868
+ if (limit)
3869
+ params.limit = limit;
3870
+ const data = yield POST(this.ctx, `${this.baseURL}/stat`, params);
3871
+ return data;
3872
+ });
3873
+ }
3801
3874
  imgUpload(file) {
3802
3875
  return __async(this, null, function* () {
3803
3876
  const params = {
@@ -3859,136 +3932,374 @@ class Api {
3859
3932
  return POST(this.ctx, `${this.baseURL}/admin/cache-warm`, params);
3860
3933
  }
3861
3934
  }
3862
- const CaptchaChecker = {
3863
- request(that, ctx, inputVal) {
3864
- return new Api(that.ctx).captchaCheck(inputVal);
3865
- },
3866
- body(that, ctx) {
3867
- if (that.captchaConf.iframe) {
3868
- const $iframeWrap = createElement(`<div class="atk-checker-iframe-wrap"></div>`);
3869
- const $iframe = createElement(`<iframe class="atk-fade-in"></iframe>`);
3870
- $iframe.style.display = "none";
3871
- showLoading($iframeWrap, { transparentBg: true });
3872
- $iframe.src = `${that.ctx.conf.server}/api/captcha/get?t=${+new Date()}`;
3873
- $iframe.onload = () => {
3874
- $iframe.style.display = "";
3875
- hideLoading($iframeWrap);
3876
- };
3877
- $iframeWrap.append($iframe);
3878
- const $closeBtn = createElement(`<div class="atk-close-btn"><i class="atk-icon atk-icon-close"></i></div>`);
3879
- $iframeWrap.append($closeBtn);
3880
- ctx.hideInteractInput();
3881
- let stop = false;
3882
- const sleep = (ms) => new Promise((resolve) => {
3883
- window.setTimeout(() => {
3884
- resolve(null);
3885
- }, ms);
3886
- });
3887
- (function queryStatus() {
3888
- return __async(this, null, function* () {
3889
- yield sleep(1e3);
3890
- if (stop)
3891
- return;
3892
- let isPass = false;
3893
- try {
3894
- const resp = yield new Api(that.ctx).captchaStatus();
3895
- isPass = resp.is_pass;
3896
- } catch (e) {
3897
- isPass = false;
3898
- }
3899
- if (isPass) {
3900
- ctx.triggerSuccess();
3901
- } else {
3902
- queryStatus();
3903
- }
3904
- });
3905
- })();
3906
- $closeBtn.onclick = () => {
3907
- stop = true;
3908
- ctx.cancel();
3909
- };
3910
- return $iframeWrap;
3911
- }
3912
- const elem = createElement(`<span><img class="atk-captcha-img" src="${that.captchaConf.imgData || ""}" alt="\u9A8C\u8BC1\u7801">\u6572\u5165\u9A8C\u8BC1\u7801\u7EE7\u7EED\uFF1A</span>`);
3913
- elem.querySelector(".atk-captcha-img").onclick = () => {
3914
- const imgEl = elem.querySelector(".atk-captcha-img");
3915
- new Api(that.ctx).captchaGet().then((imgData) => {
3916
- imgEl.setAttribute("src", imgData);
3917
- }).catch((err) => {
3918
- console.error("\u9A8C\u8BC1\u7801\u83B7\u53D6\u5931\u8D25 ", err);
3919
- });
3920
- };
3921
- return elem;
3922
- },
3923
- onSuccess(that, ctx, data, inputVal, formEl) {
3924
- that.captchaConf.val = inputVal;
3925
- },
3926
- onError(that, ctx, err, inputVal, formEl) {
3927
- formEl.querySelector(".atk-captcha-img").click();
3935
+ class Context {
3936
+ constructor(conf, $root) {
3937
+ __publicField(this, "api");
3938
+ __publicField(this, "editor");
3939
+ __publicField(this, "list");
3940
+ __publicField(this, "sidebarLayer");
3941
+ __publicField(this, "checkerLauncher");
3942
+ __publicField(this, "cid");
3943
+ __publicField(this, "conf");
3944
+ __publicField(this, "user");
3945
+ __publicField(this, "$root");
3946
+ __publicField(this, "markedInstance");
3947
+ __publicField(this, "markedReplacers", []);
3948
+ __publicField(this, "commentList", []);
3949
+ __publicField(this, "eventList", []);
3950
+ this.cid = +new Date();
3951
+ this.conf = conf;
3952
+ this.user = new User(this);
3953
+ this.$root = $root || document.createElement("div");
3954
+ this.$root.setAttribute("atk-run-id", this.cid.toString());
3955
+ this.$root.classList.add("artalk");
3956
+ this.$root.innerHTML = "";
3957
+ this.api = new Api(this);
3928
3958
  }
3929
- };
3930
- const AdminChecker = {
3931
- inputType: "password",
3932
- request(that, ctx, inputVal) {
3933
- const data = {
3934
- name: that.ctx.user.data.nick,
3935
- email: that.ctx.user.data.email,
3936
- password: inputVal
3937
- };
3938
- return new Api(that.ctx).login(data.name, data.email, data.password);
3939
- },
3940
- body(that, ctx) {
3941
- return createElement("<span>\u6572\u5165\u5BC6\u7801\u6765\u9A8C\u8BC1\u7BA1\u7406\u5458\u8EAB\u4EFD\uFF1A</span>");
3942
- },
3943
- onSuccess(that, ctx, userToken, inputVal, formEl) {
3944
- that.ctx.user.data.isAdmin = true;
3945
- that.ctx.user.data.token = userToken;
3946
- that.ctx.user.save();
3947
- that.ctx.trigger("user-changed", that.ctx.user.data);
3948
- that.ctx.trigger("list-reload");
3949
- },
3950
- onError(that, ctx, err, inputVal, formEl) {
3959
+ setApi(api) {
3960
+ this.api = api;
3951
3961
  }
3952
- };
3953
- class CheckerLauncher {
3954
- constructor(ctx) {
3955
- __publicField(this, "ctx");
3956
- __publicField(this, "launched", []);
3957
- __publicField(this, "captchaConf", {});
3958
- this.ctx = ctx;
3959
- this.initEventBind();
3962
+ setEditor(editor2) {
3963
+ this.editor = editor2;
3960
3964
  }
3961
- initEventBind() {
3962
- this.ctx.on("checker-captcha", (conf) => {
3963
- if (conf.imgData)
3964
- this.captchaConf.imgData = conf.imgData;
3965
- if (conf.iframe)
3966
- this.captchaConf.iframe = conf.iframe;
3967
- this.fire(CaptchaChecker, conf);
3968
- });
3969
- this.ctx.on("checker-admin", (conf) => {
3970
- this.fire(AdminChecker, conf);
3971
- });
3965
+ setList(list2) {
3966
+ this.list = list2;
3972
3967
  }
3973
- fire(checker, payload) {
3974
- if (this.launched.includes(checker))
3975
- return;
3976
- this.launched.push(checker);
3977
- const layer = new Layer(this.ctx, `checker-${new Date().getTime()}`);
3978
- layer.setMaskClickHide(false);
3979
- layer.show();
3980
- let hideInteractInput = false;
3981
- const checkerCtx = {
3982
- getLayer: () => layer,
3983
- hideInteractInput: () => {
3984
- hideInteractInput = true;
3985
- },
3986
- triggerSuccess: () => {
3987
- this.close(checker, layer);
3988
- if (checker.onSuccess)
3989
- checker.onSuccess(this, checkerCtx, "", "", formEl);
3990
- if (payload.onSuccess)
3991
- payload.onSuccess("", dialog.$el);
3968
+ setSidebarLayer(sidebarLayer2) {
3969
+ this.sidebarLayer = sidebarLayer2;
3970
+ }
3971
+ setCheckerLauncher(checkerLauncher) {
3972
+ this.checkerLauncher = checkerLauncher;
3973
+ }
3974
+ getApi() {
3975
+ return this.api;
3976
+ }
3977
+ getCommentList() {
3978
+ return this.commentList;
3979
+ }
3980
+ getCommentDataList() {
3981
+ return this.commentList.map((c) => c.getData());
3982
+ }
3983
+ findComment(id) {
3984
+ return this.commentList.find((c) => c.getData().id === id);
3985
+ }
3986
+ deleteComment(_comment) {
3987
+ let comment2;
3988
+ if (typeof _comment === "number") {
3989
+ const findComment = this.findComment(_comment);
3990
+ if (!findComment)
3991
+ throw Error(`Comment ${_comment} cannot be found`);
3992
+ comment2 = findComment;
3993
+ } else
3994
+ comment2 = _comment;
3995
+ comment2.getEl().remove();
3996
+ this.commentList.splice(this.commentList.indexOf(comment2), 1);
3997
+ const listData = this.list.getData();
3998
+ if (listData)
3999
+ listData.total -= 1;
4000
+ this.list.refreshUI();
4001
+ }
4002
+ clearAllComments() {
4003
+ this.list.getCommentsWrapEl().innerHTML = "";
4004
+ this.list.clearData();
4005
+ this.commentList = [];
4006
+ }
4007
+ insertComment(commentData) {
4008
+ this.list.insertComment(commentData);
4009
+ }
4010
+ replyComment(commentData, $comment, scroll) {
4011
+ this.editor.setReply(commentData, $comment, scroll);
4012
+ }
4013
+ cancelReplyComment() {
4014
+ this.editor.cancelReply();
4015
+ }
4016
+ updateNotifies(notifies) {
4017
+ this.list.updateUnread(notifies);
4018
+ }
4019
+ listReload() {
4020
+ this.list.reload();
4021
+ }
4022
+ listRefreshUI() {
4023
+ this.list.refreshUI();
4024
+ }
4025
+ editorOpen() {
4026
+ this.editor.open();
4027
+ }
4028
+ editorClose() {
4029
+ this.editor.close();
4030
+ }
4031
+ editorShowLoading() {
4032
+ this.editor.showLoading();
4033
+ }
4034
+ editorHideLoading() {
4035
+ this.editor.hideLoading();
4036
+ }
4037
+ editorShowNotify(msg, type) {
4038
+ this.editor.showNotify(msg, type);
4039
+ }
4040
+ editorTravel($el) {
4041
+ this.editor.travel($el);
4042
+ }
4043
+ editorTravelBack() {
4044
+ this.editor.travelBack();
4045
+ }
4046
+ showSidebar(payload) {
4047
+ this.sidebarLayer.show(payload);
4048
+ }
4049
+ hideSidebar() {
4050
+ this.sidebarLayer.hide();
4051
+ }
4052
+ checkAdmin(payload) {
4053
+ this.checkerLauncher.checkAdmin(payload);
4054
+ }
4055
+ checkCaptcha(payload) {
4056
+ this.checkerLauncher.checkCaptcha(payload);
4057
+ }
4058
+ checkAdminShowEl() {
4059
+ const items = [];
4060
+ this.$root.querySelectorAll(`[atk-only-admin-show]`).forEach((item) => items.push(item));
4061
+ const { $wrap: $layerWrap } = GetLayerWrap(this);
4062
+ if ($layerWrap)
4063
+ $layerWrap.querySelectorAll(`[atk-only-admin-show]`).forEach((item) => items.push(item));
4064
+ const $sidebarEl = document.querySelector(".atk-sidebar");
4065
+ if ($sidebarEl)
4066
+ $sidebarEl.querySelectorAll(`[atk-only-admin-show]`).forEach((item) => items.push(item));
4067
+ items.forEach(($item) => {
4068
+ if (this.user.data.isAdmin)
4069
+ $item.classList.remove("atk-hide");
4070
+ else
4071
+ $item.classList.add("atk-hide");
4072
+ });
4073
+ }
4074
+ on(name, handler, scope = "internal") {
4075
+ this.eventList.push({ name, handler, scope });
4076
+ }
4077
+ off(name, handler, scope = "internal") {
4078
+ this.eventList = this.eventList.filter((evt) => {
4079
+ if (handler)
4080
+ return !(evt.name === name && evt.handler === handler && evt.scope === scope);
4081
+ return !(evt.name === name && evt.scope === scope);
4082
+ });
4083
+ }
4084
+ trigger(name, payload, scope) {
4085
+ this.eventList.filter((evt) => evt.name === name && (scope ? evt.scope === scope : true)).map((evt) => evt.handler).forEach((handler) => handler(payload));
4086
+ }
4087
+ $t(key, args = {}) {
4088
+ let locales = this.conf.locale;
4089
+ if (typeof locales === "string") {
4090
+ locales = internal[locales];
4091
+ }
4092
+ let str = (locales == null ? void 0 : locales[key]) || key;
4093
+ str = str.replace(/\{\s*(\w+?)\s*\}/g, (_, token) => args[token] || "");
4094
+ return str;
4095
+ }
4096
+ }
4097
+ const defaults = {
4098
+ el: "",
4099
+ pageKey: "",
4100
+ pageTitle: "",
4101
+ server: "",
4102
+ site: "",
4103
+ placeholder: "",
4104
+ noComment: "",
4105
+ sendBtn: "",
4106
+ darkMode: false,
4107
+ editorTravel: true,
4108
+ flatMode: "auto",
4109
+ nestMax: 2,
4110
+ nestSort: "DATE_ASC",
4111
+ emoticons: "https://cdn.jsdelivr.net/gh/ArtalkJS/Emoticons/grps/default.json",
4112
+ vote: true,
4113
+ voteDown: false,
4114
+ uaBadge: true,
4115
+ listSort: true,
4116
+ preview: true,
4117
+ countEl: "#ArtalkCount",
4118
+ pvEl: "#ArtalkPV",
4119
+ gravatar: {
4120
+ default: "mp",
4121
+ mirror: "https://sdn.geekzu.org/avatar/"
4122
+ },
4123
+ pagination: {
4124
+ pageSize: 20,
4125
+ readMore: true,
4126
+ autoLoad: true
4127
+ },
4128
+ heightLimit: {
4129
+ content: 300,
4130
+ children: 400
4131
+ },
4132
+ imgUpload: true,
4133
+ reqTimeout: 15e3,
4134
+ versionCheck: true,
4135
+ useBackendConf: false,
4136
+ locale: "zh-CN"
4137
+ };
4138
+ class Dialog {
4139
+ constructor(ctx, contentEl) {
4140
+ __publicField(this, "ctx");
4141
+ __publicField(this, "$el");
4142
+ __publicField(this, "$content");
4143
+ __publicField(this, "$actions");
4144
+ this.ctx = ctx;
4145
+ this.$el = createElement(`<div class="atk-layer-dialog-wrap">
4146
+ <div class="atk-layer-dialog">
4147
+ <div class="atk-layer-dialog-content"></div>
4148
+ <div class="atk-layer-dialog-actions"></div>
4149
+ </div>
4150
+ </div>`);
4151
+ this.$actions = this.$el.querySelector(".atk-layer-dialog-actions");
4152
+ this.$content = this.$el.querySelector(".atk-layer-dialog-content");
4153
+ this.$content.appendChild(contentEl);
4154
+ return this;
4155
+ }
4156
+ setYes(handler) {
4157
+ const btn = createElement(`<button data-action="confirm">${this.ctx.$t("confirm")}</button>`);
4158
+ btn.onclick = this.onBtnClick(handler);
4159
+ this.$actions.appendChild(btn);
4160
+ return this;
4161
+ }
4162
+ setNo(handler) {
4163
+ const btn = createElement(`<button data-action="cancel">${this.ctx.$t("cancel")}</button>`);
4164
+ btn.onclick = this.onBtnClick(handler);
4165
+ this.$actions.appendChild(btn);
4166
+ return this;
4167
+ }
4168
+ onBtnClick(handler) {
4169
+ return (evt) => {
4170
+ const re = handler(evt.currentTarget, this);
4171
+ if (re === void 0 || re === true) {
4172
+ this.$el.remove();
4173
+ }
4174
+ };
4175
+ }
4176
+ }
4177
+ const CaptchaChecker = {
4178
+ request(that, ctx, inputVal) {
4179
+ return that.ctx.getApi().captchaCheck(inputVal);
4180
+ },
4181
+ body(that, ctx) {
4182
+ if (that.captchaConf.iframe) {
4183
+ const $iframeWrap = createElement(`<div class="atk-checker-iframe-wrap"></div>`);
4184
+ const $iframe = createElement(`<iframe class="atk-fade-in"></iframe>`);
4185
+ $iframe.style.display = "none";
4186
+ showLoading($iframeWrap, { transparentBg: true });
4187
+ $iframe.src = `${that.ctx.conf.server}/api/captcha/get?t=${+new Date()}`;
4188
+ $iframe.onload = () => {
4189
+ $iframe.style.display = "";
4190
+ hideLoading($iframeWrap);
4191
+ };
4192
+ $iframeWrap.append($iframe);
4193
+ const $closeBtn = createElement(`<div class="atk-close-btn"><i class="atk-icon atk-icon-close"></i></div>`);
4194
+ $iframeWrap.append($closeBtn);
4195
+ ctx.hideInteractInput();
4196
+ let stop = false;
4197
+ const sleep = (ms) => new Promise((resolve) => {
4198
+ window.setTimeout(() => {
4199
+ resolve(null);
4200
+ }, ms);
4201
+ });
4202
+ (function queryStatus() {
4203
+ return __async(this, null, function* () {
4204
+ yield sleep(1e3);
4205
+ if (stop)
4206
+ return;
4207
+ let isPass = false;
4208
+ try {
4209
+ const resp = yield that.ctx.getApi().captchaStatus();
4210
+ isPass = resp.is_pass;
4211
+ } catch (e) {
4212
+ isPass = false;
4213
+ }
4214
+ if (isPass) {
4215
+ ctx.triggerSuccess();
4216
+ } else {
4217
+ queryStatus();
4218
+ }
4219
+ });
4220
+ })();
4221
+ $closeBtn.onclick = () => {
4222
+ stop = true;
4223
+ ctx.cancel();
4224
+ };
4225
+ return $iframeWrap;
4226
+ }
4227
+ const elem = createElement(`<span><img class="atk-captcha-img" src="${that.captchaConf.imgData || ""}">${that.ctx.$t("captchaCheck")}</span>`);
4228
+ elem.querySelector(".atk-captcha-img").onclick = () => {
4229
+ const imgEl = elem.querySelector(".atk-captcha-img");
4230
+ that.ctx.getApi().captchaGet().then((imgData) => {
4231
+ imgEl.setAttribute("src", imgData);
4232
+ }).catch((err) => {
4233
+ console.error("Failed to get captcha image ", err);
4234
+ });
4235
+ };
4236
+ return elem;
4237
+ },
4238
+ onSuccess(that, ctx, data, inputVal, formEl) {
4239
+ that.captchaConf.val = inputVal;
4240
+ },
4241
+ onError(that, ctx, err, inputVal, formEl) {
4242
+ formEl.querySelector(".atk-captcha-img").click();
4243
+ }
4244
+ };
4245
+ const AdminChecker = {
4246
+ inputType: "password",
4247
+ request(that, ctx, inputVal) {
4248
+ const data = {
4249
+ name: that.ctx.user.data.nick,
4250
+ email: that.ctx.user.data.email,
4251
+ password: inputVal
4252
+ };
4253
+ return that.ctx.getApi().login(data.name, data.email, data.password);
4254
+ },
4255
+ body(that, ctx) {
4256
+ return createElement(`<span>${that.ctx.$t("adminCheck")}</span>`);
4257
+ },
4258
+ onSuccess(that, ctx, userToken, inputVal, formEl) {
4259
+ that.ctx.user.update({
4260
+ isAdmin: true,
4261
+ token: userToken
4262
+ });
4263
+ that.ctx.trigger("user-changed", that.ctx.user.data);
4264
+ that.ctx.listReload();
4265
+ },
4266
+ onError(that, ctx, err, inputVal, formEl) {
4267
+ }
4268
+ };
4269
+ class CheckerLauncher {
4270
+ constructor(ctx) {
4271
+ __publicField(this, "ctx");
4272
+ __publicField(this, "launched", []);
4273
+ __publicField(this, "captchaConf", {});
4274
+ this.ctx = ctx;
4275
+ }
4276
+ checkCaptcha(payload) {
4277
+ this.captchaConf.imgData = payload.imgData;
4278
+ this.captchaConf.iframe = payload.iframe;
4279
+ this.fire(CaptchaChecker, payload);
4280
+ }
4281
+ checkAdmin(payload) {
4282
+ this.fire(AdminChecker, payload);
4283
+ }
4284
+ fire(checker, payload) {
4285
+ if (this.launched.includes(checker))
4286
+ return;
4287
+ this.launched.push(checker);
4288
+ const layer = new Layer(this.ctx, `checker-${new Date().getTime()}`);
4289
+ layer.setMaskClickHide(false);
4290
+ layer.show();
4291
+ let hideInteractInput = false;
4292
+ const checkerCtx = {
4293
+ getLayer: () => layer,
4294
+ hideInteractInput: () => {
4295
+ hideInteractInput = true;
4296
+ },
4297
+ triggerSuccess: () => {
4298
+ this.close(checker, layer);
4299
+ if (checker.onSuccess)
4300
+ checker.onSuccess(this, checkerCtx, "", "", formEl);
4301
+ if (payload.onSuccess)
4302
+ payload.onSuccess("", dialog.$el);
3992
4303
  },
3993
4304
  cancel: () => {
3994
4305
  this.close(checker, layer);
@@ -4008,7 +4319,7 @@ class CheckerLauncher {
4008
4319
  }
4009
4320
  };
4010
4321
  let btnTextOrg;
4011
- const dialog = new Dialog(formEl);
4322
+ const dialog = new Dialog(this.ctx, formEl);
4012
4323
  dialog.setYes((btnEl) => {
4013
4324
  const inputVal = $input.value.trim();
4014
4325
  if (!btnTextOrg)
@@ -4021,7 +4332,7 @@ class CheckerLauncher {
4021
4332
  btnEl.innerText = btnTextOrg || "";
4022
4333
  btnEl.classList.remove("error");
4023
4334
  };
4024
- btnEl.innerText = "\u52A0\u8F7D\u4E2D...";
4335
+ btnEl.innerText = `${this.ctx.$t("loading")}...`;
4025
4336
  checker.request(this, checkerCtx, inputVal).then((data) => {
4026
4337
  this.close(checker, layer);
4027
4338
  if (checker.onSuccess)
@@ -4060,30 +4371,68 @@ class CheckerLauncher {
4060
4371
  }
4061
4372
  }
4062
4373
  var editor = "";
4063
- var EditorHTML = '<div class="atk-main-editor">\n <div class="atk-header">\n <input name="nick" placeholder="\u6635\u79F0" class="atk-nick" type="text" required="required">\n <input name="email" placeholder="\u90AE\u7BB1" class="atk-email" type="email" required="required">\n <input name="link" placeholder="\u7F51\u5740 (https://)" class="atk-link" type="url">\n </div>\n <div class="atk-textarea-wrap">\n <textarea class="atk-textarea" placeholder=""></textarea>\n </div>\n <div class="atk-plug-wrap" style="display: none;"></div>\n <div class="atk-bottom">\n <div class="atk-item atk-plug-btn-wrap"></div>\n <div class="atk-item">\n <button type="button" class="atk-send-btn"></button>\n </div>\n </div>\n <div class="atk-notify-wrap"></div>\n</div>\n';
4374
+ var EditorHTML = '<div class="atk-main-editor">\n <div class="atk-header">\n <input name="nick" class="atk-nick" type="text" required="required">\n <input name="email" class="atk-email" type="email" required="required">\n <input name="link" class="atk-link" type="url">\n </div>\n <div class="atk-textarea-wrap">\n <textarea class="atk-textarea"></textarea>\n </div>\n <div class="atk-plug-panel-wrap" style="display: none;"></div>\n <div class="atk-bottom">\n <div class="atk-item atk-plug-btn-wrap"></div>\n <div class="atk-item">\n <button type="button" class="atk-send-btn"></button>\n </div>\n </div>\n <div class="atk-notify-wrap"></div>\n</div>\n';
4064
4375
  var emoticonsPlug = "";
4065
4376
  class EditorPlug {
4066
4377
  constructor(editor2) {
4067
4378
  __publicField(this, "editor");
4068
- __publicField(this, "ctx");
4379
+ __publicField(this, "$panel");
4380
+ __publicField(this, "$btn");
4381
+ __publicField(this, "onHeaderInput");
4069
4382
  this.editor = editor2;
4070
- this.ctx = editor2.ctx;
4383
+ }
4384
+ get ctx() {
4385
+ return this.editor.ctx;
4386
+ }
4387
+ registerPanel(html = "<div></div>") {
4388
+ this.$panel = createElement(html);
4389
+ return this.$panel;
4390
+ }
4391
+ registerBtn(html) {
4392
+ this.$btn = createElement(`<span class="atk-plug-btn" data-plug-name="${this.constructor.name}">${html}</span>`);
4393
+ return this.$btn;
4394
+ }
4395
+ registerHeaderInputEvt(action) {
4396
+ this.onHeaderInput = action;
4397
+ }
4398
+ getPanel() {
4399
+ return this.$panel;
4400
+ }
4401
+ getBtn() {
4402
+ return this.$btn;
4071
4403
  }
4072
4404
  }
4073
4405
  __publicField(EditorPlug, "Name");
4074
- __publicField(EditorPlug, "BtnHTML");
4075
4406
  class EmoticonsPlug extends EditorPlug {
4076
4407
  constructor(editor2) {
4077
4408
  super(editor2);
4078
- __publicField(this, "$el");
4079
4409
  __publicField(this, "emoticons", []);
4410
+ __publicField(this, "loadingTask", null);
4080
4411
  __publicField(this, "$grpWrap");
4081
4412
  __publicField(this, "$grpSwitcher");
4082
- __publicField(this, "loadingTask", null);
4083
4413
  __publicField(this, "isListLoaded", false);
4084
4414
  __publicField(this, "isImgLoaded", false);
4085
4415
  this.editor = editor2;
4086
- this.$el = createElement(`<div class="atk-editor-plug-emoticons"></div>`);
4416
+ this.registerPanel(`<div class="atk-editor-plug-emoticons"></div>`);
4417
+ this.registerBtn(this.ctx.$t("emoticon"));
4418
+ window.setTimeout(() => {
4419
+ this.loadEmoticonsData();
4420
+ }, 1e3);
4421
+ }
4422
+ onPanelShow() {
4423
+ (() => __async(this, null, function* () {
4424
+ yield this.loadEmoticonsData();
4425
+ if (!this.isImgLoaded) {
4426
+ this.initEmoticonsList();
4427
+ this.isImgLoaded = true;
4428
+ }
4429
+ setTimeout(() => {
4430
+ this.changeListHeight();
4431
+ }, 30);
4432
+ }))();
4433
+ }
4434
+ onPanelHide() {
4435
+ this.$panel.parentElement.style.height = "";
4087
4436
  }
4088
4437
  loadEmoticonsData() {
4089
4438
  return __async(this, null, function* () {
@@ -4094,9 +4443,9 @@ class EmoticonsPlug extends EditorPlug {
4094
4443
  return;
4095
4444
  }
4096
4445
  this.loadingTask = (() => __async(this, null, function* () {
4097
- showLoading(this.$el);
4446
+ showLoading(this.$panel);
4098
4447
  this.emoticons = yield this.handleData(this.ctx.conf.emoticons);
4099
- hideLoading(this.$el);
4448
+ hideLoading(this.$panel);
4100
4449
  this.loadingTask = null;
4101
4450
  this.isListLoaded = true;
4102
4451
  }))();
@@ -4109,8 +4458,8 @@ class EmoticonsPlug extends EditorPlug {
4109
4458
  data = [data];
4110
4459
  }
4111
4460
  if (!Array.isArray(data)) {
4112
- setError(this.$el, "\u8868\u60C5\u5305\u6570\u636E\u5FC5\u987B\u4E3A Array/Object/String \u7C7B\u578B");
4113
- hideLoading(this.$el);
4461
+ setError(this.$panel, "\u8868\u60C5\u5305\u6570\u636E\u5FC5\u987B\u4E3A Array/Object/String \u7C7B\u578B");
4462
+ hideLoading(this.$panel);
4114
4463
  return [];
4115
4464
  }
4116
4465
  const pushGrp = (grp) => {
@@ -4163,8 +4512,8 @@ class EmoticonsPlug extends EditorPlug {
4163
4512
  const json = yield resp.json();
4164
4513
  return json;
4165
4514
  } catch (err) {
4166
- hideLoading(this.$el);
4167
- setError(this.$el, `\u8868\u60C5\u52A0\u8F7D\u5931\u8D25 ${String(err)}`);
4515
+ hideLoading(this.$panel);
4516
+ setError(this.$panel, `\u8868\u60C5\u52A0\u8F7D\u5931\u8D25 ${String(err)}`);
4168
4517
  return [];
4169
4518
  }
4170
4519
  });
@@ -4218,7 +4567,7 @@ class EmoticonsPlug extends EditorPlug {
4218
4567
  }
4219
4568
  initEmoticonsList() {
4220
4569
  this.$grpWrap = createElement(`<div class="atk-grp-wrap"></div>`);
4221
- this.$el.append(this.$grpWrap);
4570
+ this.$panel.append(this.$grpWrap);
4222
4571
  this.emoticons.forEach((grp, index) => {
4223
4572
  const $grp = createElement(`<div class="atk-grp" style="display: none;"></div>`);
4224
4573
  this.$grpWrap.append($grp);
@@ -4247,15 +4596,17 @@ class EmoticonsPlug extends EditorPlug {
4247
4596
  };
4248
4597
  });
4249
4598
  });
4250
- this.$grpSwitcher = createElement(`<div class="atk-grp-switcher"></div>`);
4251
- this.$el.append(this.$grpSwitcher);
4252
- this.emoticons.forEach((grp, index) => {
4253
- const $item = createElement("<span />");
4254
- $item.innerText = grp.name;
4255
- $item.setAttribute("data-index", String(index));
4256
- $item.onclick = () => this.openGrp(index);
4257
- this.$grpSwitcher.append($item);
4258
- });
4599
+ if (this.emoticons.length > 1) {
4600
+ this.$grpSwitcher = createElement(`<div class="atk-grp-switcher"></div>`);
4601
+ this.$panel.append(this.$grpSwitcher);
4602
+ this.emoticons.forEach((grp, index) => {
4603
+ const $item = createElement("<span />");
4604
+ $item.innerText = grp.name;
4605
+ $item.setAttribute("data-index", String(index));
4606
+ $item.onclick = () => this.openGrp(index);
4607
+ this.$grpSwitcher.append($item);
4608
+ });
4609
+ }
4259
4610
  if (this.emoticons.length > 0)
4260
4611
  this.openGrp(0);
4261
4612
  }
@@ -4273,26 +4624,8 @@ class EmoticonsPlug extends EditorPlug {
4273
4624
  (_a = this.$grpSwitcher.querySelector(`span[data-index="${index}"]`)) == null ? void 0 : _a.classList.add("active");
4274
4625
  this.changeListHeight();
4275
4626
  }
4276
- getEl() {
4277
- return this.$el;
4278
- }
4279
4627
  changeListHeight() {
4280
4628
  }
4281
- onShow() {
4282
- (() => __async(this, null, function* () {
4283
- yield this.loadEmoticonsData();
4284
- if (!this.isImgLoaded) {
4285
- this.initEmoticonsList();
4286
- this.isImgLoaded = true;
4287
- }
4288
- setTimeout(() => {
4289
- this.changeListHeight();
4290
- }, 30);
4291
- }))();
4292
- }
4293
- onHide() {
4294
- this.$el.parentElement.style.height = "";
4295
- }
4296
4629
  transEmoticonImageText(text) {
4297
4630
  if (!this.emoticons || !Array.isArray(this.emoticons))
4298
4631
  return text;
@@ -4307,262 +4640,18 @@ class EmoticonsPlug extends EditorPlug {
4307
4640
  }
4308
4641
  }
4309
4642
  __publicField(EmoticonsPlug, "Name", "emoticons");
4310
- __publicField(EmoticonsPlug, "BtnHTML", "\u8868\u60C5");
4311
- var previewPlug = "";
4312
- class PreviewPlug extends EditorPlug {
4643
+ class UploadPlug extends EditorPlug {
4313
4644
  constructor(editor2) {
4314
4645
  super(editor2);
4315
- __publicField(this, "$el");
4316
- __publicField(this, "binded", false);
4317
- this.initEl();
4318
- }
4319
- initEl() {
4320
- this.$el = createElement('<div class="atk-editor-plug-preview"></div>');
4321
- this.binded = false;
4322
- }
4323
- getEl() {
4324
- return this.$el;
4325
- }
4326
- onShow() {
4327
- this.updateContent();
4328
- if (!this.binded) {
4329
- const event = () => {
4330
- this.updateContent();
4331
- };
4332
- this.editor.$textarea.addEventListener("input", event);
4333
- this.editor.$textarea.addEventListener("change", event);
4334
- this.binded = true;
4335
- }
4336
- }
4337
- onHide() {
4338
- }
4339
- updateContent() {
4340
- if (this.$el.style.display !== "none") {
4341
- this.$el.innerHTML = this.editor.getContentMarked();
4342
- }
4343
- }
4344
- }
4345
- __publicField(PreviewPlug, "Name", "preview");
4346
- __publicField(PreviewPlug, "BtnHTML", '\u9884\u89C8 <i title="Markdown is supported"><svg class="markdown" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M14.85 3H1.15C.52 3 0 3.52 0 4.15v7.69C0 12.48.52 13 1.15 13h13.69c.64 0 1.15-.52 1.15-1.15v-7.7C16 3.52 15.48 3 14.85 3zM9 11H7V8L5.5 9.92 4 8v3H2V5h2l1.5 2L7 5h2v6zm2.99.5L9.5 8H11V5h2v3h1.5l-2.51 3.5z"></path></svg></i>');
4347
- class Editor extends Component {
4348
- constructor(ctx) {
4349
- super(ctx);
4350
- __publicField(this, "LOADABLE_PLUG_LIST", [EmoticonsPlug, PreviewPlug]);
4351
- __publicField(this, "plugList", {});
4352
- __publicField(this, "$header");
4353
- __publicField(this, "$textareaWrap");
4354
- __publicField(this, "$textarea");
4355
- __publicField(this, "$plugWrap");
4356
- __publicField(this, "$bottom");
4357
- __publicField(this, "$plugBtnWrap");
4358
- __publicField(this, "$imgUploadBtn");
4359
4646
  __publicField(this, "$imgUploadInput");
4360
- __publicField(this, "$submitBtn");
4361
- __publicField(this, "$notifyWrap");
4362
- __publicField(this, "replyComment", null);
4363
- __publicField(this, "$sendReply", null);
4364
- __publicField(this, "isTraveling", false);
4365
- __publicField(this, "queryUserInfo", {
4366
- timeout: null,
4367
- abortFunc: null
4368
- });
4369
- __publicField(this, "openedPlugName", null);
4370
4647
  __publicField(this, "allowImgExts", ["png", "jpg", "jpeg", "gif", "bmp", "svg", "webp"]);
4371
- this.$el = createElement(EditorHTML);
4372
- this.$header = this.$el.querySelector(".atk-header");
4373
- this.$textareaWrap = this.$el.querySelector(".atk-textarea-wrap");
4374
- this.$textarea = this.$el.querySelector(".atk-textarea");
4375
- this.$plugWrap = this.$el.querySelector(".atk-plug-wrap");
4376
- this.$bottom = this.$el.querySelector(".atk-bottom");
4377
- this.$plugBtnWrap = this.$el.querySelector(".atk-plug-btn-wrap");
4378
- this.$submitBtn = this.$el.querySelector(".atk-send-btn");
4379
- this.$notifyWrap = this.$el.querySelector(".atk-notify-wrap");
4380
- this.initLocalStorage();
4381
- this.initHeader();
4382
- this.initTextarea();
4383
- this.initEditorPlug();
4384
- this.initBottomPart();
4385
- this.ctx.on("editor-open", () => this.open());
4386
- this.ctx.on("editor-close", () => this.close());
4387
- this.ctx.on("editor-reply", (p) => this.setReply(p.data, p.$el, p.scroll));
4388
- this.ctx.on("editor-reply-cancel", () => this.cancelReply());
4389
- this.ctx.on("editor-show-loading", () => showLoading(this.$el));
4390
- this.ctx.on("editor-hide-loading", () => hideLoading(this.$el));
4391
- this.ctx.on("editor-notify", (f) => this.showNotify(f.msg, f.type));
4392
- this.ctx.on("editor-travel", ($el) => this.travel($el));
4393
- this.ctx.on("editor-travel-back", () => this.travelBack());
4394
- this.ctx.on("conf-updated", () => this.refreshUploadBtn());
4395
- }
4396
- get user() {
4397
- return this.ctx.user;
4398
- }
4399
- initLocalStorage() {
4400
- const localContent = window.localStorage.getItem("ArtalkContent") || "";
4401
- if (localContent.trim() !== "") {
4402
- this.showNotify("\u5DF2\u81EA\u52A8\u6062\u590D", "i");
4403
- this.setContent(localContent);
4404
- }
4405
- this.$textarea.addEventListener("input", () => {
4406
- this.saveContent();
4407
- });
4408
- }
4409
- initHeader() {
4410
- Object.keys(this.user.data).forEach((field) => {
4411
- const inputEl = this.getInputEl(field);
4412
- if (inputEl && inputEl instanceof HTMLInputElement) {
4413
- inputEl.value = this.user.data[field] || "";
4414
- inputEl.addEventListener("input", () => this.onHeaderInput(field, inputEl));
4415
- }
4416
- });
4417
- const $linkInput = this.getInputEl("link");
4418
- if ($linkInput) {
4419
- $linkInput.addEventListener("change", () => {
4420
- const link = $linkInput.value.trim();
4421
- if (!!link && !/^(http|https):\/\//.test(link)) {
4422
- $linkInput.value = `https://${link}`;
4423
- this.user.data.link = $linkInput.value;
4424
- this.saveUser();
4425
- }
4426
- });
4427
- }
4428
- }
4429
- getInputEl(field) {
4430
- const inputEl = this.$header.querySelector(`[name="${field}"]`);
4431
- return inputEl;
4432
- }
4433
- onHeaderInput(field, inputEl) {
4434
- this.user.data[field] = inputEl.value.trim();
4435
- if (field === "nick" || field === "email") {
4436
- this.user.data.token = "";
4437
- this.user.data.isAdmin = false;
4438
- if (this.queryUserInfo.timeout !== null)
4439
- window.clearTimeout(this.queryUserInfo.timeout);
4440
- if (this.queryUserInfo.abortFunc !== null)
4441
- this.queryUserInfo.abortFunc();
4442
- this.queryUserInfo.timeout = window.setTimeout(() => {
4443
- this.queryUserInfo.timeout = null;
4444
- const { req, abort } = new Api(this.ctx).userGet(this.user.data.nick, this.user.data.email);
4445
- this.queryUserInfo.abortFunc = abort;
4446
- req.then((data) => {
4447
- if (!data.is_login) {
4448
- this.user.data.token = "";
4449
- this.user.data.isAdmin = false;
4450
- }
4451
- this.ctx.trigger("unread-update", { notifies: data.unread });
4452
- if (this.user.checkHasBasicUserInfo() && !data.is_login && data.user && data.user.is_admin) {
4453
- this.showLoginDialog();
4454
- }
4455
- if (data.user && data.user.link) {
4456
- this.user.data.link = data.user.link;
4457
- this.getInputEl("link").value = data.user.link;
4458
- }
4459
- }).catch(() => {
4460
- }).finally(() => {
4461
- this.queryUserInfo.abortFunc = null;
4462
- });
4463
- }, 400);
4464
- }
4465
- this.saveUser();
4466
- }
4467
- showLoginDialog() {
4468
- this.ctx.trigger("checker-admin", {
4469
- onSuccess: () => {
4470
- }
4471
- });
4472
- }
4473
- saveUser() {
4474
- this.user.save();
4475
- this.ctx.trigger("user-changed", this.ctx.user.data);
4476
- }
4477
- saveContent() {
4478
- window.localStorage.setItem("ArtalkContent", this.getContentOriginal().trim());
4479
- }
4480
- initTextarea() {
4481
- this.$textarea.placeholder = this.ctx.conf.placeholder || "";
4482
- this.$textarea.addEventListener("keydown", (e) => {
4483
- const keyCode = e.keyCode || e.which;
4484
- if (keyCode === 9) {
4485
- e.preventDefault();
4486
- this.insertContent(" ");
4487
- }
4488
- });
4489
- this.$textarea.addEventListener("input", (evt) => {
4490
- this.adjustTextareaHeight();
4491
- });
4492
- }
4493
- adjustTextareaHeight() {
4494
- const diff = this.$textarea.offsetHeight - this.$textarea.clientHeight;
4495
- this.$textarea.style.height = "0px";
4496
- this.$textarea.style.height = `${this.$textarea.scrollHeight + diff}px`;
4497
- }
4498
- initEditorPlug() {
4499
- this.plugList = {};
4500
- this.$plugWrap.innerHTML = "";
4501
- this.$plugWrap.style.display = "none";
4502
- this.openedPlugName = null;
4503
- this.$plugBtnWrap.innerHTML = "";
4504
- this.LOADABLE_PLUG_LIST.forEach((PlugObj) => {
4505
- if (PlugObj.Name === "emoticons" && !this.conf.emoticons)
4506
- return;
4507
- const btnElem = createElement(`<span class="atk-plug-btn" data-plug-name="${PlugObj.Name}">${PlugObj.BtnHTML}</span>`);
4508
- this.$plugBtnWrap.appendChild(btnElem);
4509
- btnElem.addEventListener("click", () => {
4510
- let plug = this.plugList[PlugObj.Name];
4511
- if (!plug) {
4512
- plug = new PlugObj(this);
4513
- this.plugList[PlugObj.Name] = plug;
4514
- }
4515
- this.$plugBtnWrap.querySelectorAll(".active").forEach((item) => item.classList.remove("active"));
4516
- if (PlugObj.Name === this.openedPlugName) {
4517
- plug.onHide();
4518
- this.$plugWrap.style.display = "none";
4519
- this.openedPlugName = null;
4520
- return;
4521
- }
4522
- if (this.$plugWrap.querySelector(`[data-plug-name="${PlugObj.Name}"]`) === null) {
4523
- const plugEl = plug.getEl();
4524
- plugEl.setAttribute("data-plug-name", PlugObj.Name);
4525
- plugEl.style.display = "none";
4526
- this.$plugWrap.appendChild(plugEl);
4527
- }
4528
- Array.from(this.$plugWrap.children).forEach((plugItemEl) => {
4529
- const plugItemName = plugItemEl.getAttribute("data-plug-name");
4530
- if (plugItemName === PlugObj.Name) {
4531
- plugItemEl.style.display = "";
4532
- this.plugList[plugItemName].onShow();
4533
- } else {
4534
- plugItemEl.style.display = "none";
4535
- this.plugList[plugItemName].onHide();
4536
- }
4537
- });
4538
- this.$plugWrap.style.display = "";
4539
- this.openedPlugName = PlugObj.Name;
4540
- btnElem.classList.add("active");
4541
- });
4542
- });
4543
- if (this.conf.emoticons) {
4544
- const emoPlug = new EmoticonsPlug(this);
4545
- this.plugList[EmoticonsPlug.Name] = emoPlug;
4546
- window.setTimeout(() => {
4547
- emoPlug.loadEmoticonsData();
4548
- }, 1e3);
4549
- }
4550
- this.initImgUpload();
4551
- }
4552
- closePlug() {
4553
- this.$plugWrap.innerHTML = "";
4554
- this.$plugWrap.style.display = "none";
4555
- this.openedPlugName = null;
4556
- }
4557
- initImgUpload() {
4558
- this.$imgUploadBtn = createElement(`<span class="atk-plug-btn">\u56FE\u7247</span>`);
4559
- this.$plugBtnWrap.querySelector('[data-plug-name="preview"]').before(this.$imgUploadBtn);
4560
4648
  this.$imgUploadInput = document.createElement("input");
4561
4649
  this.$imgUploadInput.type = "file";
4562
4650
  this.$imgUploadInput.style.display = "none";
4563
4651
  this.$imgUploadInput.accept = this.allowImgExts.map((o) => `.${o}`).join(",");
4564
- this.$imgUploadBtn.after(this.$imgUploadInput);
4565
- this.$imgUploadBtn.onclick = () => {
4652
+ const $btn = this.registerBtn(`${this.ctx.$t("image")}`);
4653
+ $btn.after(this.$imgUploadInput);
4654
+ $btn.onclick = () => {
4566
4655
  const $input = this.$imgUploadInput;
4567
4656
  $input.onchange = () => {
4568
4657
  (() => __async(this, null, function* () {
@@ -4574,6 +4663,12 @@ class Editor extends Component {
4574
4663
  };
4575
4664
  $input.click();
4576
4665
  };
4666
+ this.ctx.on("conf-updated", () => {
4667
+ if (!this.ctx.conf.imgUpload) {
4668
+ this.getBtn().setAttribute("atk-only-admin-show", "");
4669
+ this.ctx.checkAdminShowEl();
4670
+ }
4671
+ });
4577
4672
  const uploadFromFileList = (files) => {
4578
4673
  if (!files)
4579
4674
  return;
@@ -4582,11 +4677,11 @@ class Editor extends Component {
4582
4677
  this.uploadImg(file);
4583
4678
  }
4584
4679
  };
4585
- this.$textarea.addEventListener("dragover", (evt) => {
4680
+ this.editor.$textarea.addEventListener("dragover", (evt) => {
4586
4681
  evt.stopPropagation();
4587
4682
  evt.preventDefault();
4588
4683
  });
4589
- this.$textarea.addEventListener("drop", (evt) => {
4684
+ this.editor.$textarea.addEventListener("drop", (evt) => {
4590
4685
  var _a;
4591
4686
  const files = (_a = evt.dataTransfer) == null ? void 0 : _a.files;
4592
4687
  if (files == null ? void 0 : files.length) {
@@ -4594,7 +4689,7 @@ class Editor extends Component {
4594
4689
  uploadFromFileList(files);
4595
4690
  }
4596
4691
  });
4597
- this.$textarea.addEventListener("paste", (evt) => {
4692
+ this.editor.$textarea.addEventListener("paste", (evt) => {
4598
4693
  var _a;
4599
4694
  const files = (_a = evt.clipboardData) == null ? void 0 : _a.files;
4600
4695
  if (files == null ? void 0 : files.length) {
@@ -4603,45 +4698,242 @@ class Editor extends Component {
4603
4698
  }
4604
4699
  });
4605
4700
  }
4606
- refreshUploadBtn() {
4607
- if (!this.$imgUploadBtn)
4608
- return;
4609
- if (!this.ctx.conf.imgUpload) {
4610
- this.$imgUploadBtn.setAttribute("atk-only-admin-show", "");
4611
- this.ctx.trigger("check-admin-show-el");
4612
- }
4613
- }
4614
4701
  uploadImg(file) {
4615
4702
  return __async(this, null, function* () {
4616
4703
  const fileExt = /[^.]+$/.exec(file.name);
4617
4704
  if (!fileExt || !this.allowImgExts.includes(fileExt[0]))
4618
4705
  return;
4619
4706
  if (!this.ctx.user.checkHasBasicUserInfo()) {
4620
- this.showNotify("\u586B\u5165\u4F60\u7684\u540D\u5B57\u90AE\u7BB1\u624D\u80FD\u4E0A\u4F20\u54E6", "w");
4707
+ this.editor.showNotify(this.ctx.$t("uploadLoginMsg"), "w");
4621
4708
  return;
4622
4709
  }
4623
4710
  let insertPrefix = "\n";
4624
- if (this.$textarea.value.trim() === "")
4711
+ if (this.editor.$textarea.value.trim() === "")
4625
4712
  insertPrefix = "";
4626
4713
  const uploadPlaceholderTxt = `${insertPrefix}![](Uploading ${file.name}...)`;
4627
- this.insertContent(uploadPlaceholderTxt);
4714
+ this.editor.insertContent(uploadPlaceholderTxt);
4628
4715
  let resp;
4629
4716
  try {
4630
- resp = yield new Api(this.ctx).imgUpload(file);
4717
+ if (!this.ctx.conf.imgUploader) {
4718
+ resp = yield this.ctx.getApi().imgUpload(file);
4719
+ } else {
4720
+ resp = { img_url: yield this.ctx.conf.imgUploader(file) };
4721
+ }
4631
4722
  } catch (err) {
4632
4723
  console.error(err);
4633
- this.showNotify(`\u56FE\u7247\u4E0A\u4F20\u5931\u8D25\uFF0C${err.msg}`, "e");
4724
+ this.editor.showNotify(`${this.ctx.$t("uploadFail")}\uFF0C${err.msg}`, "e");
4634
4725
  }
4635
4726
  if (!!resp && resp.img_url) {
4636
4727
  let imgURL = resp.img_url;
4637
4728
  if (!isValidURL(imgURL))
4638
4729
  imgURL = getURLBasedOnApi(this.ctx, imgURL);
4639
- this.setContent(this.$textarea.value.replace(uploadPlaceholderTxt, `${insertPrefix}![](${imgURL})`));
4730
+ this.editor.setContent(this.editor.$textarea.value.replace(uploadPlaceholderTxt, `${insertPrefix}![](${imgURL})`));
4640
4731
  } else {
4641
- this.setContent(this.$textarea.value.replace(uploadPlaceholderTxt, ""));
4732
+ this.editor.setContent(this.editor.$textarea.value.replace(uploadPlaceholderTxt, ""));
4642
4733
  }
4643
4734
  });
4644
4735
  }
4736
+ }
4737
+ __publicField(UploadPlug, "Name", "upload");
4738
+ var previewPlug = "";
4739
+ class PreviewPlug extends EditorPlug {
4740
+ constructor(editor2) {
4741
+ super(editor2);
4742
+ __publicField(this, "isBind", false);
4743
+ this.registerPanel(`<div class="atk-editor-plug-preview"></div>`);
4744
+ let btnText = this.editor.$t("preview");
4745
+ if (this.ctx.markedInstance)
4746
+ btnText += ` <i title="Markdown is supported"><svg class="markdown" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M14.85 3H1.15C.52 3 0 3.52 0 4.15v7.69C0 12.48.52 13 1.15 13h13.69c.64 0 1.15-.52 1.15-1.15v-7.7C16 3.52 15.48 3 14.85 3zM9 11H7V8L5.5 9.92 4 8v3H2V5h2l1.5 2L7 5h2v6zm2.99.5L9.5 8H11V5h2v3h1.5l-2.51 3.5z"></path></svg></i>`;
4747
+ this.registerBtn(btnText);
4748
+ }
4749
+ onPanelShow() {
4750
+ this.updateContent();
4751
+ if (!this.isBind) {
4752
+ const event = () => {
4753
+ this.updateContent();
4754
+ };
4755
+ this.editor.$textarea.addEventListener("input", event);
4756
+ this.editor.$textarea.addEventListener("change", event);
4757
+ this.isBind = true;
4758
+ }
4759
+ }
4760
+ updateContent() {
4761
+ if (this.$panel.style.display !== "none") {
4762
+ this.$panel.innerHTML = this.editor.getContentMarked();
4763
+ }
4764
+ }
4765
+ }
4766
+ __publicField(PreviewPlug, "Name", "preview");
4767
+ class HeaderInputPlug extends EditorPlug {
4768
+ constructor(editor2) {
4769
+ super(editor2);
4770
+ __publicField(this, "queryUserInfo", {
4771
+ timeout: null,
4772
+ abortFunc: null
4773
+ });
4774
+ this.registerHeaderInputEvt((key, $input) => {
4775
+ if (key === "nick" || key === "email") {
4776
+ this.fetchUserInfo();
4777
+ }
4778
+ });
4779
+ this.editor.$link.addEventListener("change", () => {
4780
+ const link = this.editor.$link.value.trim();
4781
+ if (!!link && !/^(http|https):\/\//.test(link)) {
4782
+ this.editor.$link.value = `https://${link}`;
4783
+ this.ctx.user.update({ link: this.editor.$link.value });
4784
+ }
4785
+ });
4786
+ }
4787
+ fetchUserInfo() {
4788
+ this.ctx.user.logout();
4789
+ if (this.queryUserInfo.timeout)
4790
+ window.clearTimeout(this.queryUserInfo.timeout);
4791
+ if (this.queryUserInfo.abortFunc)
4792
+ this.queryUserInfo.abortFunc();
4793
+ this.queryUserInfo.timeout = window.setTimeout(() => {
4794
+ this.queryUserInfo.timeout = null;
4795
+ const { req, abort } = this.ctx.getApi().userGet(this.ctx.user.data.nick, this.ctx.user.data.email);
4796
+ this.queryUserInfo.abortFunc = abort;
4797
+ req.then((data) => {
4798
+ var _a;
4799
+ if (!data.is_login) {
4800
+ this.ctx.user.logout();
4801
+ }
4802
+ this.ctx.updateNotifies(data.unread);
4803
+ if (this.ctx.user.checkHasBasicUserInfo() && !data.is_login && ((_a = data.user) == null ? void 0 : _a.is_admin)) {
4804
+ this.showLoginDialog();
4805
+ }
4806
+ if (data.user && data.user.link) {
4807
+ this.editor.$link.value = data.user.link;
4808
+ this.ctx.user.update({ link: data.user.link });
4809
+ }
4810
+ }).catch(() => {
4811
+ }).finally(() => {
4812
+ this.queryUserInfo.abortFunc = null;
4813
+ });
4814
+ }, 400);
4815
+ }
4816
+ showLoginDialog() {
4817
+ this.ctx.checkAdmin({
4818
+ onSuccess: () => {
4819
+ }
4820
+ });
4821
+ }
4822
+ }
4823
+ __publicField(HeaderInputPlug, "Name", "headerInput");
4824
+ class Editor extends Component {
4825
+ constructor(ctx) {
4826
+ super(ctx);
4827
+ __publicField(this, "$header");
4828
+ __publicField(this, "$nick");
4829
+ __publicField(this, "$email");
4830
+ __publicField(this, "$link");
4831
+ __publicField(this, "$textareaWrap");
4832
+ __publicField(this, "$textarea");
4833
+ __publicField(this, "$bottom");
4834
+ __publicField(this, "$submitBtn");
4835
+ __publicField(this, "$notifyWrap");
4836
+ __publicField(this, "replyComment", null);
4837
+ __publicField(this, "$sendReply", null);
4838
+ __publicField(this, "isTraveling", false);
4839
+ __publicField(this, "ENABLED_PLUGS", [EmoticonsPlug, UploadPlug, PreviewPlug, HeaderInputPlug]);
4840
+ __publicField(this, "plugList", {});
4841
+ __publicField(this, "openedPlugName", null);
4842
+ __publicField(this, "$plugPanelWrap");
4843
+ __publicField(this, "$plugBtnWrap");
4844
+ this.$el = createElement(EditorHTML);
4845
+ this.$header = this.$el.querySelector(".atk-header");
4846
+ this.$nick = this.$header.querySelector('[name="nick"]');
4847
+ this.$email = this.$header.querySelector('[name="email"]');
4848
+ this.$link = this.$header.querySelector('[name="link"]');
4849
+ this.$textareaWrap = this.$el.querySelector(".atk-textarea-wrap");
4850
+ this.$textarea = this.$el.querySelector(".atk-textarea");
4851
+ this.$bottom = this.$el.querySelector(".atk-bottom");
4852
+ this.$submitBtn = this.$el.querySelector(".atk-send-btn");
4853
+ this.$notifyWrap = this.$el.querySelector(".atk-notify-wrap");
4854
+ this.$plugBtnWrap = this.$el.querySelector(".atk-plug-btn-wrap");
4855
+ this.$plugPanelWrap = this.$el.querySelector(".atk-plug-panel-wrap");
4856
+ this.initLocalStorage();
4857
+ this.initHeader();
4858
+ this.initTextarea();
4859
+ this.initPlugs();
4860
+ this.initSubmitBtn();
4861
+ this.ctx.on("conf-updated", () => {
4862
+ });
4863
+ }
4864
+ get user() {
4865
+ return this.ctx.user;
4866
+ }
4867
+ get $inputs() {
4868
+ return { nick: this.$nick, email: this.$email, link: this.$link };
4869
+ }
4870
+ initLocalStorage() {
4871
+ const localContent = window.localStorage.getItem("ArtalkContent") || "";
4872
+ if (localContent.trim() !== "") {
4873
+ this.showNotify(this.$t("restoredMsg"), "i");
4874
+ this.setContent(localContent);
4875
+ }
4876
+ this.$textarea.addEventListener("input", () => this.saveToLocalStorage());
4877
+ }
4878
+ saveToLocalStorage() {
4879
+ window.localStorage.setItem("ArtalkContent", this.getContentOriginal().trim());
4880
+ }
4881
+ initHeader() {
4882
+ Object.entries(this.$inputs).forEach(([key, $input]) => {
4883
+ $input.value = this.user.data[key] || "";
4884
+ $input.addEventListener("input", () => this.onHeaderInput(key, $input));
4885
+ $input.placeholder = `${this.$t(key)}`;
4886
+ });
4887
+ }
4888
+ onHeaderInput(key, $input) {
4889
+ this.user.update({
4890
+ [key]: $input.value.trim()
4891
+ });
4892
+ Object.entries(this.plugList).forEach(([plugName, plug]) => {
4893
+ if (plug.onHeaderInput)
4894
+ plug.onHeaderInput(key, $input);
4895
+ });
4896
+ }
4897
+ initTextarea() {
4898
+ this.$textarea.placeholder = this.ctx.conf.placeholder || this.$t("placeholder");
4899
+ this.$textarea.addEventListener("keydown", (e) => {
4900
+ const keyCode = e.keyCode || e.which;
4901
+ if (keyCode === 9) {
4902
+ e.preventDefault();
4903
+ this.insertContent(" ");
4904
+ }
4905
+ });
4906
+ this.$textarea.addEventListener("input", () => {
4907
+ this.adjustTextareaHeight();
4908
+ });
4909
+ }
4910
+ initSubmitBtn() {
4911
+ this.$submitBtn.innerText = this.ctx.conf.sendBtn || this.$t("send");
4912
+ this.$submitBtn.addEventListener("click", () => this.submit());
4913
+ }
4914
+ getFinalContent() {
4915
+ let content = this.getContentOriginal();
4916
+ if (this.plugList.emoticons) {
4917
+ content = this.plugList.emoticons.transEmoticonImageText(content);
4918
+ }
4919
+ return content;
4920
+ }
4921
+ getContentOriginal() {
4922
+ return this.$textarea.value || "";
4923
+ }
4924
+ getContentMarked() {
4925
+ return marked(this.ctx, this.getFinalContent());
4926
+ }
4927
+ setContent(val) {
4928
+ this.$textarea.value = val;
4929
+ this.saveToLocalStorage();
4930
+ if (this.plugList.preview) {
4931
+ this.plugList.preview.updateContent();
4932
+ }
4933
+ window.setTimeout(() => {
4934
+ this.adjustTextareaHeight();
4935
+ }, 80);
4936
+ }
4645
4937
  insertContent(val) {
4646
4938
  if (document.selection) {
4647
4939
  this.$textarea.focus();
@@ -4661,48 +4953,21 @@ class Editor extends Component {
4661
4953
  this.$textarea.value += val;
4662
4954
  }
4663
4955
  }
4664
- setContent(val) {
4665
- this.$textarea.value = val;
4666
- this.saveContent();
4667
- if (!!this.plugList && !!this.plugList.preview) {
4668
- this.plugList.preview.updateContent();
4669
- }
4670
- window.setTimeout(() => {
4671
- this.adjustTextareaHeight();
4672
- }, 80);
4673
- }
4674
4956
  clearEditor() {
4675
4957
  this.setContent("");
4676
4958
  this.cancelReply();
4677
4959
  }
4678
- getFinalContent() {
4679
- let content = this.getContentOriginal();
4680
- if (this.plugList && this.plugList.emoticons) {
4681
- const emoticonsPlug2 = this.plugList.emoticons;
4682
- content = emoticonsPlug2.transEmoticonImageText(content);
4683
- }
4684
- return content;
4685
- }
4686
- getContentOriginal() {
4687
- return this.$textarea.value || "";
4688
- }
4689
- getContentMarked() {
4690
- return marked(this.ctx, this.getFinalContent());
4691
- }
4692
- initBottomPart() {
4693
- this.initReply();
4694
- this.initSubmit();
4695
- }
4696
- initReply() {
4697
- this.replyComment = null;
4698
- this.$sendReply = null;
4960
+ adjustTextareaHeight() {
4961
+ const diff = this.$textarea.offsetHeight - this.$textarea.clientHeight;
4962
+ this.$textarea.style.height = "0px";
4963
+ this.$textarea.style.height = `${this.$textarea.scrollHeight + diff}px`;
4699
4964
  }
4700
4965
  setReply(commentData, $comment, scroll = true) {
4701
4966
  if (this.replyComment !== null) {
4702
4967
  this.cancelReply();
4703
4968
  }
4704
4969
  if (this.$sendReply === null) {
4705
- this.$sendReply = createElement('<div class="atk-send-reply">\u56DE\u590D <span class="atk-text"></span><span class="atk-cancel" title="\u53D6\u6D88 AT">\xD7</span></div>');
4970
+ this.$sendReply = createElement(`<div class="atk-send-reply">${this.$t("reply")} <span class="atk-text"></span><span class="atk-cancel">\xD7</span></div>`);
4706
4971
  this.$sendReply.querySelector(".atk-text").innerText = `@${commentData.nick}`;
4707
4972
  this.$sendReply.addEventListener("click", () => {
4708
4973
  this.cancelReply();
@@ -4727,12 +4992,14 @@ class Editor extends Component {
4727
4992
  this.travelBack();
4728
4993
  }
4729
4994
  }
4730
- initSubmit() {
4731
- this.$submitBtn.innerText = this.ctx.conf.sendBtn || "Send";
4732
- this.$submitBtn.addEventListener("click", (evt) => {
4733
- evt.currentTarget;
4734
- this.submit();
4735
- });
4995
+ showNotify(msg, type) {
4996
+ showNotify(this.$notifyWrap, msg, type);
4997
+ }
4998
+ showLoading() {
4999
+ showLoading(this.$el);
5000
+ }
5001
+ hideLoading() {
5002
+ hideLoading(this.$el);
4736
5003
  }
4737
5004
  submit() {
4738
5005
  return __async(this, null, function* () {
@@ -4741,9 +5008,9 @@ class Editor extends Component {
4741
5008
  return;
4742
5009
  }
4743
5010
  this.ctx.trigger("editor-submit");
4744
- showLoading(this.$el);
5011
+ this.showLoading();
4745
5012
  try {
4746
- const nComment = yield new Api(this.ctx).add({
5013
+ const nComment = yield this.ctx.getApi().add({
4747
5014
  content: this.getFinalContent(),
4748
5015
  nick: this.user.data.nick,
4749
5016
  email: this.user.data.email,
@@ -4756,27 +5023,24 @@ class Editor extends Component {
4756
5023
  if (this.replyComment !== null && this.replyComment.page_key !== this.ctx.conf.pageKey) {
4757
5024
  window.open(`${this.replyComment.page_url}#atk-comment-${nComment.id}`);
4758
5025
  }
4759
- this.ctx.trigger("list-insert", nComment);
5026
+ this.ctx.insertComment(nComment);
4760
5027
  this.clearEditor();
4761
5028
  this.ctx.trigger("editor-submitted");
4762
5029
  } catch (err) {
4763
5030
  console.error(err);
4764
- this.showNotify(`\u8BC4\u8BBA\u5931\u8D25\uFF0C${err.msg || String(err)}`, "e");
5031
+ this.showNotify(`${this.$t("commentFail")}\uFF0C${err.msg || String(err)}`, "e");
4765
5032
  return;
4766
5033
  } finally {
4767
- hideLoading(this.$el);
5034
+ this.hideLoading();
4768
5035
  }
4769
5036
  });
4770
5037
  }
4771
- showNotify(msg, type) {
4772
- showNotify(this.$notifyWrap, msg, type);
4773
- }
4774
5038
  close() {
4775
5039
  if (!this.$textareaWrap.querySelector(".atk-comment-closed"))
4776
- this.$textareaWrap.prepend(createElement('<div class="atk-comment-closed">\u4EC5\u7BA1\u7406\u5458\u53EF\u8BC4\u8BBA</div>'));
5040
+ this.$textareaWrap.prepend(createElement(`<div class="atk-comment-closed">${this.$t("onlyAdminCanReply")}</div>`));
4777
5041
  if (!this.user.data.isAdmin) {
4778
5042
  this.$textarea.style.display = "none";
4779
- this.closePlug();
5043
+ this.closePlugPanel();
4780
5044
  this.$bottom.style.display = "none";
4781
5045
  } else {
4782
5046
  this.$textarea.style.display = "";
@@ -4808,12 +5072,76 @@ class Editor extends Component {
4808
5072
  if (this.replyComment !== null)
4809
5073
  this.cancelReply();
4810
5074
  }
4811
- initRemoteEmoticons() {
5075
+ initPlugs() {
5076
+ this.plugList = {};
5077
+ this.$plugPanelWrap.innerHTML = "";
5078
+ this.$plugPanelWrap.style.display = "none";
5079
+ this.openedPlugName = null;
5080
+ this.$plugBtnWrap.innerHTML = "";
5081
+ const disabledPlugs = [];
5082
+ if (!this.conf.emoticons)
5083
+ disabledPlugs.push("emoticons");
5084
+ if (!this.conf.preview)
5085
+ disabledPlugs.push("preview");
5086
+ this.ENABLED_PLUGS.forEach((Plug) => {
5087
+ const plugName = Plug.Name;
5088
+ if (disabledPlugs.includes(plugName))
5089
+ return;
5090
+ const plug = new Plug(this);
5091
+ this.plugList[plugName] = plug;
5092
+ const $btn = plug.getBtn();
5093
+ if ($btn) {
5094
+ this.$plugBtnWrap.appendChild($btn);
5095
+ $btn.onclick = $btn.onclick || (() => {
5096
+ this.$plugBtnWrap.querySelectorAll(".active").forEach((item) => item.classList.remove("active"));
5097
+ if (plugName === this.openedPlugName) {
5098
+ this.closePlugPanel();
5099
+ return;
5100
+ }
5101
+ this.openPlugPanel(plugName);
5102
+ $btn.classList.add("active");
5103
+ });
5104
+ const $panel = plug.getPanel();
5105
+ if ($panel) {
5106
+ $panel.setAttribute("data-plug-name", plugName);
5107
+ $panel.style.display = "none";
5108
+ this.$plugPanelWrap.appendChild($panel);
5109
+ }
5110
+ }
5111
+ });
5112
+ }
5113
+ openPlugPanel(plugName) {
5114
+ Object.entries(this.plugList).forEach(([aPlugName, plug]) => {
5115
+ const plugPanel = plug.getPanel();
5116
+ if (!plugPanel)
5117
+ return;
5118
+ if (aPlugName === plugName) {
5119
+ plugPanel.style.display = "";
5120
+ if (plug.onPanelShow)
5121
+ plug.onPanelShow();
5122
+ } else {
5123
+ plugPanel.style.display = "none";
5124
+ if (plug.onPanelHide)
5125
+ plug.onPanelHide();
5126
+ }
5127
+ });
5128
+ this.$plugPanelWrap.style.display = "";
5129
+ this.openedPlugName = plugName;
5130
+ }
5131
+ closePlugPanel() {
5132
+ if (!this.openedPlugName)
5133
+ return;
5134
+ const plug = this.plugList[this.openedPlugName];
5135
+ if (!plug)
5136
+ return;
5137
+ if (plug.onPanelHide)
5138
+ plug.onPanelHide();
5139
+ this.$plugPanelWrap.style.display = "none";
5140
+ this.openedPlugName = null;
4812
5141
  }
4813
5142
  }
4814
5143
  var list = "";
4815
- var ListHTML = '<div class="atk-list">\n <div class="atk-list-header">\n <div class="atk-comment-count">\n <div class="atk-text">\n <span class="atk-comment-count-num">0</span>\n \u6761\u8BC4\u8BBA\n </div>\n </div>\n <div class="atk-right-action">\n <span data-action="admin-close-comment" class="atk-hide" atk-only-admin-show>\u5173\u95ED\u8BC4\u8BBA</span>\n <span data-action="open-sidebar" class="atk-hide atk-on">\n <span class="atk-unread-badge" style="display: none;"></span>\n <div class="atk-text">\u901A\u77E5\u4E2D\u5FC3</div>\n </span>\n </div>\n </div>\n <div class="atk-list-body"></div>\n <div class="atk-list-footer">\n <div class="atk-copyright"></div>\n </div>\n</div>\n';
4816
- var comment = "";
5144
+ var ListHTML = '<div class="atk-list">\n <div class="atk-list-header">\n <div class="atk-comment-count">\n <div class="atk-text"></div>\n </div>\n <div class="atk-right-action">\n <span data-action="admin-close-comment" class="atk-hide" atk-only-admin-show></span>\n <span data-action="open-sidebar" class="atk-hide atk-on">\n <span class="atk-unread-badge" style="display: none;"></span>\n <div class="atk-text"></div>\n </span>\n </div>\n </div>\n <div class="atk-list-body"></div>\n <div class="atk-list-footer">\n <div class="atk-copyright"></div>\n </div>\n</div>\n';
4817
5145
  const win = window || {};
4818
5146
  const nav = navigator || {};
4819
5147
  function Detect(userAgent) {
@@ -5004,9 +5332,10 @@ function Detect(userAgent) {
5004
5332
  }
5005
5333
  return dest;
5006
5334
  }
5007
- var 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-item atk-badge"></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';
5335
+ var comment = "";
5008
5336
  class ActionBtn {
5009
- constructor(conf) {
5337
+ constructor(ctx, conf) {
5338
+ __publicField(this, "ctx");
5010
5339
  __publicField(this, "conf");
5011
5340
  __publicField(this, "$el");
5012
5341
  __publicField(this, "isLoading", false);
@@ -5014,6 +5343,7 @@ class ActionBtn {
5014
5343
  __publicField(this, "msgRecTimerFunc");
5015
5344
  __publicField(this, "isConfirming", false);
5016
5345
  __publicField(this, "confirmRecTimer");
5346
+ this.ctx = ctx;
5017
5347
  this.$el = createElement(`<span class="atk-common-action-btn"></span>`);
5018
5348
  this.conf = typeof conf !== "object" ? { text: conf } : conf;
5019
5349
  this.$el.innerText = this.getText();
@@ -5045,7 +5375,7 @@ class ActionBtn {
5045
5375
  if (!this.isConfirming) {
5046
5376
  this.isConfirming = true;
5047
5377
  this.$el.classList.add("atk-btn-confirm");
5048
- this.$el.innerText = this.conf.confirmText || "\u786E\u8BA4\u64CD\u4F5C";
5378
+ this.$el.innerText = this.conf.confirmText || this.ctx.$t("actionConfirm");
5049
5379
  this.confirmRecTimer = window.setTimeout(() => confirmRestore(), 5e3);
5050
5380
  return;
5051
5381
  }
@@ -5073,7 +5403,7 @@ class ActionBtn {
5073
5403
  this.isLoading = value;
5074
5404
  if (value) {
5075
5405
  this.$el.classList.add("atk-btn-loading");
5076
- this.$el.innerText = loadingText || "\u52A0\u8F7D\u4E2D...";
5406
+ this.$el.innerText = loadingText || `${this.ctx.$t("loading")}...`;
5077
5407
  } else {
5078
5408
  this.$el.classList.remove("atk-btn-loading");
5079
5409
  this.$el.innerText = this.getText();
@@ -5121,40 +5451,33 @@ class ActionBtn {
5121
5451
  this.msgRecTimerFunc = void 0;
5122
5452
  }
5123
5453
  }
5124
- class Comment extends Component {
5125
- constructor(ctx, data) {
5126
- super(ctx);
5127
- __publicField(this, "data");
5454
+ var 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';
5455
+ class CommentRender {
5456
+ constructor(comment2) {
5457
+ __publicField(this, "comment");
5458
+ __publicField(this, "$el");
5128
5459
  __publicField(this, "$main");
5129
5460
  __publicField(this, "$header");
5130
5461
  __publicField(this, "$headerNick");
5131
- __publicField(this, "$headerBadge");
5462
+ __publicField(this, "$headerBadgeWrap");
5132
5463
  __publicField(this, "$body");
5133
5464
  __publicField(this, "$content");
5134
- __publicField(this, "$children");
5465
+ __publicField(this, "$childrenWrap");
5135
5466
  __publicField(this, "$actions");
5136
5467
  __publicField(this, "voteBtnUp");
5137
5468
  __publicField(this, "voteBtnDown");
5138
- __publicField(this, "parent");
5139
- __publicField(this, "nestCurt");
5140
- __publicField(this, "nestMax");
5141
- __publicField(this, "children", []);
5142
- __publicField(this, "flatMode", false);
5143
- __publicField(this, "replyTo");
5144
5469
  __publicField(this, "$replyTo");
5145
5470
  __publicField(this, "$replyAt");
5146
- __publicField(this, "afterRender");
5147
- __publicField(this, "unread", false);
5148
- __publicField(this, "openable", false);
5149
- __publicField(this, "openURL");
5150
- __publicField(this, "openEvt");
5151
- __publicField(this, "onReplyBtnClick");
5152
- __publicField(this, "onDelete");
5153
- this.nestMax = ctx.conf.nestMax || 3;
5154
- this.data = __spreadValues({}, data);
5155
- this.data.date = this.data.date.replace(/-/g, "/");
5156
- this.parent = null;
5157
- this.nestCurt = 1;
5471
+ this.comment = comment2;
5472
+ }
5473
+ get ctx() {
5474
+ return this.comment.ctx;
5475
+ }
5476
+ get data() {
5477
+ return this.comment.getData();
5478
+ }
5479
+ get cConf() {
5480
+ return this.comment.getConf();
5158
5481
  }
5159
5482
  render() {
5160
5483
  this.$el = createElement(CommentHTML);
@@ -5163,10 +5486,7 @@ class Comment extends Component {
5163
5486
  this.$body = this.$el.querySelector(".atk-body");
5164
5487
  this.$content = this.$body.querySelector(".atk-content");
5165
5488
  this.$actions = this.$el.querySelector(".atk-actions");
5166
- this.$children = null;
5167
5489
  this.$el.setAttribute("data-comment-id", `${this.data.id}`);
5168
- this.renderCheckUnread();
5169
- this.renderCheckClickable();
5170
5490
  this.renderAvatar();
5171
5491
  this.renderHeader();
5172
5492
  this.renderContent();
@@ -5174,35 +5494,13 @@ class Comment extends Component {
5174
5494
  this.renderReplyTo();
5175
5495
  this.renderPending();
5176
5496
  this.renderActionBtn();
5177
- if (this.afterRender)
5178
- this.afterRender();
5497
+ this.recoveryChildrenWrap();
5179
5498
  return this.$el;
5180
5499
  }
5181
- renderCheckUnread() {
5182
- if (this.unread)
5183
- this.$el.classList.add("atk-unread");
5184
- else
5185
- this.$el.classList.remove("atk-unread");
5186
- }
5187
- renderCheckClickable() {
5188
- if (this.openable) {
5189
- this.$el.classList.add("atk-openable");
5190
- } else {
5191
- this.$el.classList.remove("atk-openable");
5192
- }
5193
- this.$el.addEventListener("click", (evt) => {
5194
- if (this.openable && this.openURL) {
5195
- evt.preventDefault();
5196
- window.open(this.openURL);
5197
- }
5198
- if (this.openEvt)
5199
- this.openEvt();
5200
- });
5201
- }
5202
5500
  renderAvatar() {
5203
5501
  const $avatar = this.$el.querySelector(".atk-avatar");
5204
5502
  const $avatarImg = createElement("<img />");
5205
- $avatarImg.src = this.getGravatarUrl();
5503
+ $avatarImg.src = this.comment.getGravatarURL();
5206
5504
  if (this.data.link) {
5207
5505
  const $avatarA = createElement('<a target="_blank" rel="noreferrer noopener nofollow"></a>');
5208
5506
  $avatarA.href = this.data.link;
@@ -5222,197 +5520,415 @@ class Comment extends Component {
5222
5520
  } else {
5223
5521
  this.$headerNick.innerText = this.data.nick;
5224
5522
  }
5225
- this.$headerBadge = this.$el.querySelector(".atk-badge");
5226
- if (this.data.badge_name) {
5227
- this.$headerBadge.innerText = this.data.badge_name;
5228
- if (this.data.badge_color)
5229
- this.$headerBadge.style.backgroundColor = this.data.badge_color;
5230
- } else {
5231
- this.$headerBadge.remove();
5232
- this.$headerBadge = void 0;
5523
+ this.$headerBadgeWrap = this.$el.querySelector(".atk-badge-wrap");
5524
+ this.$headerBadgeWrap.innerHTML = "";
5525
+ const badgeText = this.data.badge_name;
5526
+ const badgeColor = this.data.badge_color;
5527
+ if (badgeText) {
5528
+ const $badge = createElement(`<span class="atk-badge"></span>`);
5529
+ $badge.innerText = badgeText.replace("\u7BA1\u7406\u5458", this.ctx.$t("admin"));
5530
+ $badge.style.backgroundColor = badgeColor || "";
5531
+ this.$headerBadgeWrap.append($badge);
5233
5532
  }
5234
5533
  if (this.data.is_pinned) {
5235
- const $pinnedBadge = createElement(`<span class="atk-item atk-pinned-badge">\u7F6E\u9876</span>`);
5236
- this.$headerNick.insertAdjacentElement("afterend", $pinnedBadge);
5534
+ const $pinnedBadge = createElement(`<span class="atk-pinned-badge">${this.ctx.$t("pin")}</span>`);
5535
+ this.$headerBadgeWrap.append($pinnedBadge);
5237
5536
  }
5238
5537
  const $date = this.$el.querySelector(".atk-date");
5239
- $date.innerText = this.getDateFormatted();
5538
+ $date.innerText = this.comment.getDateFormatted();
5240
5539
  $date.setAttribute("data-atk-comment-date", String(+new Date(this.data.date)));
5241
- if (this.conf.uaBadge) {
5242
- const $uaWrap = createElement(`<span class="atk-ua-wrap"></span>`);
5540
+ if (this.ctx.conf.uaBadge) {
5541
+ let $uaWrap = this.$header.querySelector("atk-ua-wrap");
5542
+ if (!$uaWrap) {
5543
+ $uaWrap = createElement(`<span class="atk-ua-wrap"></span>`);
5544
+ this.$header.append($uaWrap);
5545
+ }
5546
+ $uaWrap.innerHTML = "";
5243
5547
  const $uaBrowser = createElement(`<span class="atk-ua ua-browser"></span>`);
5244
5548
  const $usOS = createElement(`<span class="atk-ua ua-os"></span>`);
5245
- $uaBrowser.innerText = this.getUserUaBrowser();
5246
- $usOS.innerText = this.getUserUaOS();
5549
+ $uaBrowser.innerText = this.comment.getUserUaBrowser();
5550
+ $usOS.innerText = this.comment.getUserUaOS();
5247
5551
  $uaWrap.append($uaBrowser);
5248
5552
  $uaWrap.append($usOS);
5249
- this.$header.append($uaWrap);
5250
5553
  }
5251
5554
  }
5252
5555
  renderContent() {
5253
5556
  if (!this.data.is_collapsed) {
5254
- this.$content.innerHTML = this.getContentMarked();
5557
+ this.$content.innerHTML = this.comment.getContentMarked();
5558
+ this.$content.classList.remove("atk-hide", "atk-collapsed");
5255
5559
  return;
5256
5560
  }
5257
5561
  this.$content.classList.add("atk-hide", "atk-type-collapsed");
5258
5562
  const collapsedInfoEl = createElement(`
5259
5563
  <div class="atk-collapsed">
5260
- <span class="atk-text">\u8BE5\u8BC4\u8BBA\u5DF2\u88AB\u7CFB\u7EDF\u6216\u7BA1\u7406\u5458\u6298\u53E0</span>
5261
- <span class="atk-show-btn">\u67E5\u770B\u5185\u5BB9</span>
5564
+ <span class="atk-text">${this.ctx.$t("collapsedMsg")}</span>
5565
+ <span class="atk-show-btn">${this.ctx.$t("expand")}</span>
5262
5566
  </div>`);
5263
5567
  this.$body.insertAdjacentElement("beforeend", collapsedInfoEl);
5264
5568
  const contentShowBtn = collapsedInfoEl.querySelector(".atk-show-btn");
5265
5569
  contentShowBtn.addEventListener("click", (e) => {
5266
5570
  e.stopPropagation();
5267
5571
  if (this.$content.classList.contains("atk-hide")) {
5268
- this.$content.innerHTML = this.getContentMarked();
5572
+ this.$content.innerHTML = this.comment.getContentMarked();
5269
5573
  this.$content.classList.remove("atk-hide");
5270
5574
  playFadeInAnim(this.$content);
5271
- contentShowBtn.innerHTML = "\u6536\u8D77\u5185\u5BB9";
5575
+ contentShowBtn.innerHTML = this.ctx.$t("collapse");
5272
5576
  } else {
5273
5577
  this.$content.innerHTML = "";
5274
5578
  this.$content.classList.add("atk-hide");
5275
- contentShowBtn.innerHTML = "\u67E5\u770B\u5185\u5BB9";
5579
+ contentShowBtn.innerHTML = this.ctx.$t("expand");
5276
5580
  }
5277
5581
  });
5278
5582
  }
5279
5583
  renderReplyAt() {
5280
- if (this.flatMode || this.data.rid === 0)
5281
- return;
5282
- if (this.$replyAt)
5584
+ if (this.cConf.isFlatMode || this.data.rid === 0)
5283
5585
  return;
5284
- if (!this.replyTo)
5586
+ if (!this.cConf.replyTo)
5285
5587
  return;
5286
5588
  this.$replyAt = createElement(`<span class="atk-item atk-reply-at"><span class="atk-arrow"></span><span class="atk-nick"></span></span>`);
5287
- this.$replyAt.querySelector(".atk-nick").innerText = `${this.replyTo.nick}`;
5589
+ this.$replyAt.querySelector(".atk-nick").innerText = `${this.cConf.replyTo.nick}`;
5288
5590
  this.$replyAt.onclick = () => {
5289
- this.goToReplyComment();
5591
+ this.comment.getActions().goToReplyComment();
5290
5592
  };
5291
- const $after = this.$headerBadge || this.$headerNick;
5292
- $after.insertAdjacentElement("afterend", this.$replyAt);
5593
+ this.$headerBadgeWrap.insertAdjacentElement("afterend", this.$replyAt);
5293
5594
  }
5294
5595
  renderReplyTo() {
5295
- if (!this.flatMode)
5596
+ if (!this.cConf.isFlatMode)
5296
5597
  return;
5297
- if (!this.replyTo)
5598
+ if (!this.cConf.replyTo)
5298
5599
  return;
5299
5600
  this.$replyTo = createElement(`
5300
5601
  <div class="atk-reply-to">
5301
- <div class="atk-meta">\u56DE\u590D <span class="atk-nick"></span>:</div>
5602
+ <div class="atk-meta">${this.ctx.$t("reply")} <span class="atk-nick"></span>:</div>
5302
5603
  <div class="atk-content"></div>
5303
5604
  </div>`);
5304
5605
  const $nick = this.$replyTo.querySelector(".atk-nick");
5305
- $nick.innerText = `@${this.replyTo.nick}`;
5606
+ $nick.innerText = `@${this.cConf.replyTo.nick}`;
5306
5607
  $nick.onclick = () => {
5307
- this.goToReplyComment();
5608
+ this.comment.getActions().goToReplyComment();
5308
5609
  };
5309
- let replyContent = marked(this.ctx, this.replyTo.content);
5310
- if (this.replyTo.is_collapsed)
5311
- replyContent = "[\u5DF2\u6298\u53E0]";
5610
+ let replyContent = marked(this.ctx, this.cConf.replyTo.content);
5611
+ if (this.cConf.replyTo.is_collapsed)
5612
+ replyContent = `[${this.ctx.$t("collapsed")}]`;
5312
5613
  this.$replyTo.querySelector(".atk-content").innerHTML = replyContent;
5313
5614
  this.$body.prepend(this.$replyTo);
5314
5615
  }
5315
- goToReplyComment() {
5316
- const origHash = window.location.hash;
5317
- const modifyHash = `#atk-comment-${this.data.rid}`;
5318
- window.location.hash = modifyHash;
5319
- if (modifyHash === origHash)
5320
- window.dispatchEvent(new Event("hashchange"));
5321
- }
5322
5616
  renderPending() {
5323
5617
  if (!this.data.is_pending)
5324
5618
  return;
5325
- const pendingEl = createElement(`<div class="atk-pending">\u5BA1\u6838\u4E2D\uFF0C\u4EC5\u672C\u4EBA\u53EF\u89C1\u3002</div>`);
5619
+ const pendingEl = createElement(`<div class="atk-pending">${this.ctx.$t("pendingMsg")}</div>`);
5326
5620
  this.$body.prepend(pendingEl);
5327
5621
  }
5328
5622
  renderActionBtn() {
5329
5623
  if (this.ctx.conf.vote) {
5330
- this.voteBtnUp = new ActionBtn(() => `\u8D5E\u540C (${this.data.vote_up || 0})`).appendTo(this.$actions);
5624
+ this.voteBtnUp = new ActionBtn(this.ctx, () => `${this.ctx.$t("voteUp")} (${this.data.vote_up || 0})`).appendTo(this.$actions);
5331
5625
  this.voteBtnUp.setClick(() => {
5332
- this.vote("up");
5626
+ this.comment.getActions().vote("up");
5333
5627
  });
5334
5628
  if (this.ctx.conf.voteDown) {
5335
- this.voteBtnDown = new ActionBtn(() => `\u53CD\u5BF9 (${this.data.vote_down || 0})`).appendTo(this.$actions);
5629
+ this.voteBtnDown = new ActionBtn(this.ctx, () => `${this.ctx.$t("voteDown")} (${this.data.vote_down || 0})`).appendTo(this.$actions);
5336
5630
  this.voteBtnDown.setClick(() => {
5337
- this.vote("down");
5631
+ this.comment.getActions().vote("down");
5338
5632
  });
5339
5633
  }
5340
5634
  }
5341
5635
  if (this.data.is_allow_reply) {
5342
- const replyBtn = createElement(`<span>\u56DE\u590D</span>`);
5636
+ const replyBtn = createElement(`<span>${this.ctx.$t("reply")}</span>`);
5343
5637
  this.$actions.append(replyBtn);
5344
5638
  replyBtn.addEventListener("click", (e) => {
5345
5639
  e.stopPropagation();
5346
- if (!this.onReplyBtnClick) {
5347
- this.ctx.trigger("editor-reply", { data: this.data, $el: this.$el });
5640
+ if (!this.cConf.onReplyBtnClick) {
5641
+ this.ctx.replyComment(this.data, this.$el);
5348
5642
  } else {
5349
- this.onReplyBtnClick();
5643
+ this.cConf.onReplyBtnClick();
5350
5644
  }
5351
5645
  });
5352
5646
  }
5353
- const collapseBtn = new ActionBtn({
5354
- text: () => this.data.is_collapsed ? "\u53D6\u6D88\u6298\u53E0" : "\u6298\u53E0",
5647
+ const collapseBtn = new ActionBtn(this.ctx, {
5648
+ text: () => this.data.is_collapsed ? this.ctx.$t("expand") : this.ctx.$t("collapse"),
5355
5649
  adminOnly: true
5356
5650
  });
5357
5651
  collapseBtn.appendTo(this.$actions);
5358
5652
  collapseBtn.setClick(() => {
5359
- this.adminEdit("collapsed", collapseBtn);
5653
+ this.comment.getActions().adminEdit("collapsed", collapseBtn);
5360
5654
  });
5361
- const pendingBtn = new ActionBtn({
5362
- text: () => this.data.is_pending ? "\u5F85\u5BA1" : "\u5DF2\u5BA1",
5655
+ const pendingBtn = new ActionBtn(this.ctx, {
5656
+ text: () => this.data.is_pending ? this.ctx.$t("pending") : this.ctx.$t("approved"),
5363
5657
  adminOnly: true
5364
5658
  });
5365
5659
  pendingBtn.appendTo(this.$actions);
5366
5660
  pendingBtn.setClick(() => {
5367
- this.adminEdit("pending", pendingBtn);
5661
+ this.comment.getActions().adminEdit("pending", pendingBtn);
5368
5662
  });
5369
- const delBtn = new ActionBtn({
5370
- text: "\u5220\u9664",
5663
+ const delBtn = new ActionBtn(this.ctx, {
5664
+ text: this.ctx.$t("delete"),
5371
5665
  confirm: true,
5372
- confirmText: "\u786E\u8BA4\u5220\u9664",
5666
+ confirmText: this.ctx.$t("deleteConfirm"),
5373
5667
  adminOnly: true
5374
5668
  });
5375
5669
  delBtn.appendTo(this.$actions);
5376
5670
  delBtn.setClick(() => {
5377
- this.adminDelete(delBtn);
5671
+ this.comment.getActions().adminDelete(delBtn);
5378
5672
  });
5379
- const pinnedBtn = new ActionBtn({
5380
- text: () => this.data.is_pinned ? "\u53D6\u6D88\u7F6E\u9876" : "\u7F6E\u9876",
5673
+ const pinnedBtn = new ActionBtn(this.ctx, {
5674
+ text: () => this.data.is_pinned ? this.ctx.$t("unpin") : this.ctx.$t("pin"),
5381
5675
  adminOnly: true
5382
5676
  });
5383
5677
  pinnedBtn.appendTo(this.$actions);
5384
5678
  pinnedBtn.setClick(() => {
5385
- this.adminEdit("pinned", pendingBtn);
5679
+ this.comment.getActions().adminEdit("pinned", pendingBtn);
5680
+ });
5681
+ }
5682
+ checkHeightLimit() {
5683
+ this.checkHeightLimitArea("content");
5684
+ this.checkHeightLimitArea("children");
5685
+ }
5686
+ checkHeightLimitArea(area) {
5687
+ const childrenMaxH = this.ctx.conf.heightLimit.children;
5688
+ const contentMaxH = this.ctx.conf.heightLimit.content;
5689
+ if (area === "children" && !childrenMaxH)
5690
+ return;
5691
+ if (area === "content" && !contentMaxH)
5692
+ return;
5693
+ let maxHeight;
5694
+ if (area === "children")
5695
+ maxHeight = childrenMaxH;
5696
+ if (area === "content")
5697
+ maxHeight = contentMaxH;
5698
+ const checkEl = ($el) => {
5699
+ if (!$el)
5700
+ return;
5701
+ if (getHeight($el) > maxHeight) {
5702
+ this.heightLimitAdd($el, maxHeight);
5703
+ }
5704
+ };
5705
+ if (area === "children") {
5706
+ checkEl(this.$childrenWrap);
5707
+ } else if (area === "content") {
5708
+ checkEl(this.$content);
5709
+ checkEl(this.$replyTo);
5710
+ onImagesLoaded(this.$content, () => {
5711
+ checkEl(this.$content);
5712
+ });
5713
+ if (this.$replyTo) {
5714
+ onImagesLoaded(this.$replyTo, () => {
5715
+ checkEl(this.$replyTo);
5716
+ });
5717
+ }
5718
+ }
5719
+ }
5720
+ heightLimitRemove($el) {
5721
+ if (!$el)
5722
+ return;
5723
+ if (!$el.classList.contains("atk-height-limit"))
5724
+ return;
5725
+ $el.classList.remove("atk-height-limit");
5726
+ Array.from($el.children).forEach((e) => {
5727
+ if (e.classList.contains("atk-height-limit-btn"))
5728
+ e.remove();
5729
+ });
5730
+ $el.style.height = "";
5731
+ $el.style.overflow = "";
5732
+ }
5733
+ heightLimitRemoveForChildren() {
5734
+ if (!this.$childrenWrap)
5735
+ return;
5736
+ this.heightLimitRemove(this.$childrenWrap);
5737
+ }
5738
+ heightLimitAdd($el, maxHeight) {
5739
+ if (!$el)
5740
+ return;
5741
+ if ($el.classList.contains("atk-height-limit"))
5742
+ return;
5743
+ $el.classList.add("atk-height-limit");
5744
+ $el.style.height = `${maxHeight}px`;
5745
+ $el.style.overflow = "hidden";
5746
+ const $hideMoreOpenBtn = createElement(`<div class="atk-height-limit-btn">${this.ctx.$t("readMore")}</span>`);
5747
+ $hideMoreOpenBtn.onclick = (e) => {
5748
+ e.stopPropagation();
5749
+ this.heightLimitRemove($el);
5750
+ const children = this.comment.getChildren();
5751
+ if (children.length === 1)
5752
+ children[0].getRender().heightLimitRemove(children[0].getRender().$content);
5753
+ };
5754
+ $el.append($hideMoreOpenBtn);
5755
+ }
5756
+ playFadeAnim() {
5757
+ playFadeInAnim(this.comment.getRender().$el);
5758
+ }
5759
+ playFadeAnimForBody() {
5760
+ playFadeInAnim(this.comment.getRender().$body);
5761
+ }
5762
+ getChildrenWrap() {
5763
+ return this.$childrenWrap;
5764
+ }
5765
+ renderChildrenWrap() {
5766
+ if (!this.$childrenWrap) {
5767
+ this.$childrenWrap = createElement('<div class="atk-comment-children"></div>');
5768
+ this.$main.append(this.$childrenWrap);
5769
+ }
5770
+ return this.$childrenWrap;
5771
+ }
5772
+ recoveryChildrenWrap() {
5773
+ if (this.$childrenWrap) {
5774
+ this.$main.append(this.$childrenWrap);
5775
+ }
5776
+ }
5777
+ setUnread(val) {
5778
+ if (val)
5779
+ this.$el.classList.add("atk-unread");
5780
+ else
5781
+ this.$el.classList.remove("atk-unread");
5782
+ }
5783
+ setOpenable(val) {
5784
+ if (val)
5785
+ this.$el.classList.add("atk-openable");
5786
+ else
5787
+ this.$el.classList.remove("atk-openable");
5788
+ }
5789
+ setOpenURL(url) {
5790
+ this.setOpenable(true);
5791
+ this.$el.onclick = (evt) => {
5792
+ evt.preventDefault();
5793
+ window.open(url);
5794
+ if (this.cConf.openEvt)
5795
+ this.cConf.openEvt();
5796
+ };
5797
+ }
5798
+ setOpenAction(action) {
5799
+ this.setOpenable(true);
5800
+ this.$el.onclick = (evt) => {
5801
+ evt.preventDefault();
5802
+ action();
5803
+ };
5804
+ }
5805
+ }
5806
+ class CommentActions {
5807
+ constructor(comment2) {
5808
+ __publicField(this, "comment");
5809
+ this.comment = comment2;
5810
+ }
5811
+ get ctx() {
5812
+ return this.comment.ctx;
5813
+ }
5814
+ get data() {
5815
+ return this.comment.getData();
5816
+ }
5817
+ get cConf() {
5818
+ return this.comment.getConf();
5819
+ }
5820
+ vote(type) {
5821
+ const actionBtn = type === "up" ? this.comment.getRender().voteBtnUp : this.comment.getRender().voteBtnDown;
5822
+ this.ctx.getApi().vote(this.data.id, `comment_${type}`).then((v) => {
5823
+ var _a, _b;
5824
+ this.data.vote_up = v.up;
5825
+ this.data.vote_down = v.down;
5826
+ (_a = this.comment.getRender().voteBtnUp) == null ? void 0 : _a.updateText();
5827
+ (_b = this.comment.getRender().voteBtnDown) == null ? void 0 : _b.updateText();
5828
+ }).catch((err) => {
5829
+ actionBtn == null ? void 0 : actionBtn.setError(this.ctx.$t("voteFail"));
5830
+ console.log(err);
5831
+ });
5832
+ }
5833
+ adminEdit(type, btnElem) {
5834
+ if (btnElem.isLoading)
5835
+ return;
5836
+ btnElem.setLoading(true, `${this.ctx.$t("editing")}...`);
5837
+ const modify = __spreadValues({}, this.data);
5838
+ if (type === "collapsed") {
5839
+ modify.is_collapsed = !modify.is_collapsed;
5840
+ } else if (type === "pending") {
5841
+ modify.is_pending = !modify.is_pending;
5842
+ } else if (type === "pinned") {
5843
+ modify.is_pinned = !modify.is_pinned;
5844
+ }
5845
+ this.ctx.getApi().commentEdit(modify).then((data) => {
5846
+ btnElem.setLoading(false);
5847
+ this.comment.setData(data);
5848
+ this.ctx.listRefreshUI();
5849
+ }).catch((err) => {
5850
+ console.error(err);
5851
+ btnElem.setError(this.ctx.$t("editFail"));
5852
+ });
5853
+ }
5854
+ adminDelete(btnElem) {
5855
+ if (btnElem.isLoading)
5856
+ return;
5857
+ btnElem.setLoading(true, `${this.ctx.$t("deleting")}...`);
5858
+ this.ctx.getApi().commentDel(this.data.id, this.data.site_name).then(() => {
5859
+ btnElem.setLoading(false);
5860
+ if (this.cConf.onDelete)
5861
+ this.cConf.onDelete(this.comment);
5862
+ }).catch((e) => {
5863
+ console.error(e);
5864
+ btnElem.setError(this.ctx.$t("deleteFail"));
5386
5865
  });
5387
5866
  }
5388
- refreshUI() {
5389
- const originalEl = this.$el;
5390
- const newEl = this.render();
5391
- originalEl.replaceWith(newEl);
5392
- this.playFadeInAnim();
5393
- this.eachComment(this.children, (child) => {
5394
- var _a;
5395
- (_a = child.parent) == null ? void 0 : _a.getChildrenEl().appendChild(child.render());
5396
- child.playFadeInAnim();
5397
- });
5398
- this.ctx.trigger("comments-loaded");
5867
+ goToReplyComment() {
5868
+ const origHash = window.location.hash;
5869
+ const modifyHash = `#atk-comment-${this.data.rid}`;
5870
+ window.location.hash = modifyHash;
5871
+ if (modifyHash === origHash)
5872
+ window.dispatchEvent(new Event("hashchange"));
5873
+ }
5874
+ }
5875
+ class Comment extends Component {
5876
+ constructor(ctx, data, conf) {
5877
+ super(ctx);
5878
+ __publicField(this, "renderInstance");
5879
+ __publicField(this, "actionInstance");
5880
+ __publicField(this, "data");
5881
+ __publicField(this, "cConf");
5882
+ __publicField(this, "parent");
5883
+ __publicField(this, "children", []);
5884
+ __publicField(this, "nestCurt");
5885
+ __publicField(this, "nestMax");
5886
+ this.nestMax = ctx.conf.nestMax || 3;
5887
+ this.cConf = conf;
5888
+ this.data = __spreadValues({}, data);
5889
+ this.data.date = this.data.date.replace(/-/g, "/");
5890
+ this.parent = null;
5891
+ this.nestCurt = 1;
5892
+ this.actionInstance = new CommentActions(this);
5893
+ this.renderInstance = new CommentRender(this);
5894
+ }
5895
+ render() {
5896
+ const newEl = this.renderInstance.render();
5897
+ if (this.$el)
5898
+ this.$el.replaceWith(newEl);
5899
+ this.$el = newEl;
5900
+ if (this.cConf.afterRender)
5901
+ this.cConf.afterRender();
5399
5902
  }
5400
- eachComment(commentList, action) {
5401
- if (commentList.length === 0)
5402
- return;
5403
- commentList.every((item) => {
5404
- if (action(item, commentList) === false)
5405
- return false;
5406
- this.eachComment(item.getChildren(), action);
5407
- return true;
5408
- });
5903
+ getActions() {
5904
+ return this.actionInstance;
5409
5905
  }
5410
- getIsRoot() {
5411
- return this.data.rid === 0;
5906
+ getRender() {
5907
+ return this.renderInstance;
5908
+ }
5909
+ getData() {
5910
+ return this.data;
5911
+ }
5912
+ setData(data) {
5913
+ this.data = data;
5914
+ this.render();
5915
+ this.getRender().playFadeAnimForBody();
5916
+ }
5917
+ getParent() {
5918
+ return this.parent;
5412
5919
  }
5413
5920
  getChildren() {
5414
5921
  return this.children;
5415
5922
  }
5923
+ getNestCurt() {
5924
+ return this.nestCurt;
5925
+ }
5926
+ getIsRoot() {
5927
+ return this.data.rid === 0;
5928
+ }
5929
+ getID() {
5930
+ return this.data.id;
5931
+ }
5416
5932
  putChild(childC, insertMode = "append") {
5417
5933
  childC.parent = this;
5418
5934
  childC.nestCurt = this.nestCurt + 1;
@@ -5422,22 +5938,19 @@ class Comment extends Component {
5422
5938
  $children.append(childC.getEl());
5423
5939
  else if (insertMode === "prepend")
5424
5940
  $children.prepend(childC.getEl());
5425
- childC.playFadeInAnim();
5426
- childC.checkHeightLimitArea("content");
5941
+ childC.getRender().playFadeAnim();
5942
+ childC.getRender().checkHeightLimitArea("content");
5427
5943
  }
5428
5944
  getChildrenEl() {
5429
- if (!this.$children) {
5945
+ let $children = this.getRender().getChildrenWrap();
5946
+ if (!$children) {
5430
5947
  if (this.nestCurt < this.nestMax) {
5431
- this.$children = createElement('<div class="atk-comment-children"></div>');
5432
- this.$main.append(this.$children);
5433
- } else if (this.parent) {
5434
- this.$children = this.parent.getChildrenEl();
5948
+ $children = this.getRender().renderChildrenWrap();
5949
+ } else {
5950
+ $children = this.parent.getChildrenEl();
5435
5951
  }
5436
5952
  }
5437
- return this.$children;
5438
- }
5439
- getParent() {
5440
- return this.parent;
5953
+ return $children;
5441
5954
  }
5442
5955
  getParents() {
5443
5956
  const parents = [];
@@ -5453,17 +5966,14 @@ class Comment extends Component {
5453
5966
  getEl() {
5454
5967
  return this.$el;
5455
5968
  }
5456
- getData() {
5457
- return this.data;
5458
- }
5459
- getGravatarUrl() {
5969
+ getGravatarURL() {
5460
5970
  return getGravatarURL(this.ctx, this.data.email_encrypted);
5461
5971
  }
5462
5972
  getContentMarked() {
5463
5973
  return marked(this.ctx, this.data.content);
5464
5974
  }
5465
5975
  getDateFormatted() {
5466
- return timeAgo(new Date(this.data.date));
5976
+ return timeAgo(new Date(this.data.date), this.ctx);
5467
5977
  }
5468
5978
  getUserUaBrowser() {
5469
5979
  const info = Detect(this.data.ua);
@@ -5473,143 +5983,8 @@ class Comment extends Component {
5473
5983
  const info = Detect(this.data.ua);
5474
5984
  return `${info.os} ${info.osVersion}`;
5475
5985
  }
5476
- playFadeInAnim() {
5477
- playFadeInAnim(this.$el);
5478
- }
5479
- vote(type) {
5480
- const actionBtn = type === "up" ? this.voteBtnUp : this.voteBtnDown;
5481
- new Api(this.ctx).vote(this.data.id, `comment_${type}`).then((v) => {
5482
- var _a, _b;
5483
- this.data.vote_up = v.up;
5484
- this.data.vote_down = v.down;
5485
- (_a = this.voteBtnUp) == null ? void 0 : _a.updateText();
5486
- (_b = this.voteBtnDown) == null ? void 0 : _b.updateText();
5487
- }).catch((err) => {
5488
- actionBtn == null ? void 0 : actionBtn.setError(`\u6295\u7968\u5931\u8D25`);
5489
- console.log(err);
5490
- });
5491
- }
5492
- adminEdit(type, btnElem) {
5493
- if (btnElem.isLoading)
5494
- return;
5495
- btnElem.setLoading(true, "\u4FEE\u6539\u4E2D...");
5496
- const modify = __spreadValues({}, this.data);
5497
- if (type === "collapsed") {
5498
- modify.is_collapsed = !modify.is_collapsed;
5499
- } else if (type === "pending") {
5500
- modify.is_pending = !modify.is_pending;
5501
- } else if (type === "pinned") {
5502
- modify.is_pinned = !modify.is_pinned;
5503
- }
5504
- new Api(this.ctx).commentEdit(modify).then((comment2) => {
5505
- btnElem.setLoading(false);
5506
- this.data = comment2;
5507
- this.refreshUI();
5508
- playFadeInAnim(this.$body);
5509
- this.ctx.trigger("list-refresh-ui");
5510
- }).catch((err) => {
5511
- console.error(err);
5512
- btnElem.setError("\u4FEE\u6539\u5931\u8D25");
5513
- });
5514
- }
5515
- adminDelete(btnElem) {
5516
- if (btnElem.isLoading)
5517
- return;
5518
- btnElem.setLoading(true, "\u5220\u9664\u4E2D...");
5519
- new Api(this.ctx).commentDel(this.data.id, this.data.site_name).then(() => {
5520
- btnElem.setLoading(false);
5521
- if (this.onDelete)
5522
- this.onDelete(this);
5523
- }).catch((e) => {
5524
- console.error(e);
5525
- btnElem.setError("\u5220\u9664\u5931\u8D25");
5526
- });
5527
- }
5528
- setUnread(val) {
5529
- this.unread = val;
5530
- if (this.unread)
5531
- this.$el.classList.add("atk-unread");
5532
- else
5533
- this.$el.classList.remove("atk-unread");
5534
- }
5535
- setOpenURL(url) {
5536
- if (!url) {
5537
- this.openable = false;
5538
- this.$el.classList.remove("atk-openable");
5539
- }
5540
- this.openable = true;
5541
- this.openURL = url;
5542
- this.$el.classList.add("atk-openable");
5543
- }
5544
- checkHeightLimit() {
5545
- this.checkHeightLimitArea("content");
5546
- this.checkHeightLimitArea("children");
5547
- }
5548
- checkHeightLimitArea(area) {
5549
- var _a, _b;
5550
- const childrenMaxH = (_a = this.ctx.conf.heightLimit) == null ? void 0 : _a.children;
5551
- const contentMaxH = (_b = this.ctx.conf.heightLimit) == null ? void 0 : _b.content;
5552
- if (area === "children" && !childrenMaxH)
5553
- return;
5554
- if (area === "content" && !contentMaxH)
5555
- return;
5556
- let maxHeight;
5557
- if (area === "children")
5558
- maxHeight = childrenMaxH;
5559
- if (area === "content")
5560
- maxHeight = contentMaxH;
5561
- const checkEl = ($el) => {
5562
- if (!$el)
5563
- return;
5564
- if (getHeight($el) > maxHeight) {
5565
- this.heightLimitAdd($el, maxHeight);
5566
- }
5567
- };
5568
- if (area === "children") {
5569
- checkEl(this.$children);
5570
- } else if (area === "content") {
5571
- checkEl(this.$content);
5572
- checkEl(this.$replyTo);
5573
- onImagesLoaded(this.$content, () => {
5574
- checkEl(this.$content);
5575
- });
5576
- if (this.$replyTo) {
5577
- onImagesLoaded(this.$replyTo, () => {
5578
- checkEl(this.$replyTo);
5579
- });
5580
- }
5581
- }
5582
- }
5583
- heightLimitRemove($el) {
5584
- if (!$el)
5585
- return;
5586
- if (!$el.classList.contains("atk-height-limit"))
5587
- return;
5588
- $el.classList.remove("atk-height-limit");
5589
- Array.from($el.children).forEach((e) => {
5590
- if (e.classList.contains("atk-height-limit-btn"))
5591
- e.remove();
5592
- });
5593
- $el.style.height = "";
5594
- $el.style.overflow = "";
5595
- }
5596
- heightLimitAdd($el, maxHeight) {
5597
- if (!$el)
5598
- return;
5599
- if ($el.classList.contains("atk-height-limit"))
5600
- return;
5601
- $el.classList.add("atk-height-limit");
5602
- $el.style.height = `${maxHeight}px`;
5603
- $el.style.overflow = "hidden";
5604
- const $hideMoreOpenBtn = createElement(`<div class="atk-height-limit-btn">\u9605\u8BFB\u66F4\u591A</span>`);
5605
- $hideMoreOpenBtn.onclick = (e) => {
5606
- e.stopPropagation();
5607
- this.heightLimitRemove($el);
5608
- const children = this.getChildren();
5609
- if (children.length === 1)
5610
- children[0].heightLimitRemove(children[0].$content);
5611
- };
5612
- $el.append($hideMoreOpenBtn);
5986
+ getConf() {
5987
+ return this.cConf;
5613
5988
  }
5614
5989
  }
5615
5990
  var pagination = "";
@@ -5755,11 +6130,13 @@ class ReadMoreBtn {
5755
6130
  __publicField(this, "$text");
5756
6131
  __publicField(this, "offset", 0);
5757
6132
  __publicField(this, "total", 0);
6133
+ __publicField(this, "origText", "Load More");
5758
6134
  this.conf = conf;
6135
+ this.origText = this.conf.text || this.origText;
5759
6136
  this.$el = createElement(`<div class="atk-list-read-more" style="display: none;">
5760
6137
  <div class="atk-list-read-more-inner">
5761
6138
  <div class="atk-loading-icon" style="display: none;"></div>
5762
- <span class="atk-text">\u52A0\u8F7D\u66F4\u591A</span>
6139
+ <span class="atk-text">${this.origText}</span>
5763
6140
  </div>
5764
6141
  </div>`);
5765
6142
  this.$loading = this.$el.querySelector(".atk-loading-icon");
@@ -5791,7 +6168,7 @@ class ReadMoreBtn {
5791
6168
  this.$text.innerText = errMsg;
5792
6169
  this.$el.classList.add("atk-err");
5793
6170
  window.setTimeout(() => {
5794
- this.$text.innerText = "\u67E5\u770B\u66F4\u591A";
6171
+ this.$text.innerText = this.origText;
5795
6172
  this.$el.classList.remove("atk-err");
5796
6173
  }, 2e3);
5797
6174
  }
@@ -5858,15 +6235,14 @@ function makeNestCommentNodeList(srcData, sortBy = "DATE_DESC", nestMax = 2) {
5858
6235
  })(nodeList);
5859
6236
  return nodeList;
5860
6237
  }
5861
- const backendMinVersion = "2.1.6";
6238
+ const backendMinVersion = "2.1.7";
5862
6239
  class ListLite extends Component {
5863
6240
  constructor(ctx) {
5864
6241
  super(ctx);
5865
6242
  __publicField(this, "$commentsWrap");
5866
- __publicField(this, "commentList", []);
5867
6243
  __publicField(this, "data");
5868
6244
  __publicField(this, "isLoading", false);
5869
- __publicField(this, "noCommentText", "\u65E0\u5185\u5BB9");
6245
+ __publicField(this, "noCommentText");
5870
6246
  __publicField(this, "flatMode", false);
5871
6247
  __publicField(this, "nestSortBy");
5872
6248
  __publicField(this, "pageMode", "pagination");
@@ -5885,19 +6261,23 @@ class ListLite extends Component {
5885
6261
  <div class="atk-list-comments-wrap"></div>
5886
6262
  </div>`);
5887
6263
  this.$commentsWrap = this.$el.querySelector(".atk-list-comments-wrap");
5888
- if (ctx.conf.noComment)
5889
- this.noCommentText = ctx.conf.noComment;
6264
+ this.noCommentText = ctx.conf.noComment || ctx.$t("noComment");
5890
6265
  this.nestSortBy = this.ctx.conf.nestSort || "DATE_ASC";
5891
6266
  window.setInterval(() => {
5892
6267
  this.$el.querySelectorAll("[data-atk-comment-date]").forEach((el) => {
5893
6268
  const date = el.getAttribute("data-atk-comment-date");
5894
- el.innerText = timeAgo(new Date(Number(date)));
6269
+ el.innerText = timeAgo(new Date(Number(date)), this.ctx);
5895
6270
  });
5896
6271
  }, 30 * 1e3);
5897
- this.ctx.on("unread-update", (data) => this.updateUnread(data.notifies));
5898
6272
  }
5899
- get commentDataList() {
5900
- return this.commentList.map((c) => c.data);
6273
+ getData() {
6274
+ return this.data;
6275
+ }
6276
+ clearData() {
6277
+ this.data = void 0;
6278
+ }
6279
+ getCommentsWrapEl() {
6280
+ return this.$commentsWrap;
5901
6281
  }
5902
6282
  fetchComments(offset) {
5903
6283
  return __async(this, null, function* () {
@@ -5922,13 +6302,13 @@ class ListLite extends Component {
5922
6302
  this.pagination.setLoading(false);
5923
6303
  };
5924
6304
  showLoading$1();
5925
- this.ctx.trigger("comments-load");
6305
+ this.ctx.trigger("list-load");
5926
6306
  if (this.pageMode === "read-more" && offset === 0) {
5927
- this.clearAllComments();
6307
+ this.ctx.clearAllComments();
5928
6308
  }
5929
6309
  let listData;
5930
6310
  try {
5931
- listData = yield new Api(this.ctx).get(offset, this.pageSize, this.flatMode, this.paramsEditor);
6311
+ listData = yield this.ctx.getApi().get(offset, this.pageSize, this.flatMode, this.paramsEditor);
5932
6312
  } catch (e) {
5933
6313
  this.onError(e.msg || String(e), offset, e.data);
5934
6314
  throw e;
@@ -5949,13 +6329,13 @@ class ListLite extends Component {
5949
6329
  onLoad(data, offset) {
5950
6330
  var _a, _b;
5951
6331
  if (this.pageMode === "pagination") {
5952
- this.clearAllComments();
6332
+ this.ctx.clearAllComments();
5953
6333
  }
5954
6334
  this.data = data;
5955
6335
  const feMinVersion = ((_a = data.api_version) == null ? void 0 : _a.fe_min_version) || "0.0.0";
5956
- if (this.ctx.conf.versionCheck && this.versionCheck("\u524D\u7AEF", feMinVersion, "2.2.12"))
6336
+ if (this.ctx.conf.versionCheck && this.versionCheck("frontend", feMinVersion, "2.3.2"))
5957
6337
  return;
5958
- if (this.ctx.conf.versionCheck && this.versionCheck("\u540E\u7AEF", backendMinVersion, (_b = data.api_version) == null ? void 0 : _b.version))
6338
+ if (this.ctx.conf.versionCheck && this.versionCheck("backend", backendMinVersion, (_b = data.api_version) == null ? void 0 : _b.version))
5959
6339
  return;
5960
6340
  if (data.conf && typeof data.conf.img_upload === "boolean") {
5961
6341
  this.ctx.conf.imgUpload = data.conf.img_upload;
@@ -5963,8 +6343,8 @@ class ListLite extends Component {
5963
6343
  this.importComments(data.comments);
5964
6344
  this.refreshPagination(offset, this.flatMode ? data.total : data.total_roots);
5965
6345
  this.refreshUI();
5966
- this.ctx.trigger("unread-update", { notifies: data.unread || [] });
5967
- this.ctx.trigger("comments-loaded");
6346
+ this.ctx.updateNotifies(data.unread || []);
6347
+ this.ctx.trigger("list-loaded");
5968
6348
  this.ctx.trigger("conf-updated");
5969
6349
  if (this.onAfterLoad)
5970
6350
  this.onAfterLoad(data);
@@ -5982,16 +6362,16 @@ class ListLite extends Component {
5982
6362
  this.readMoreBtn.update(offset, total);
5983
6363
  }
5984
6364
  initPagination() {
5985
- var _a;
5986
6365
  if (this.pageMode === "read-more") {
5987
6366
  this.readMoreBtn = new ReadMoreBtn({
5988
6367
  pageSize: this.pageSize,
5989
6368
  onClick: (o) => __async(this, null, function* () {
5990
6369
  yield this.fetchComments(o);
5991
- })
6370
+ }),
6371
+ text: this.ctx.$t("loadMore")
5992
6372
  });
5993
6373
  this.$el.append(this.readMoreBtn.$el);
5994
- if ((_a = this.conf.pagination) == null ? void 0 : _a.autoLoad) {
6374
+ if (this.conf.pagination.autoLoad) {
5995
6375
  const at = this.scrollListenerAt || document;
5996
6376
  if (this.autoLoadScrollEvent)
5997
6377
  at.removeEventListener("scroll", this.autoLoadScrollEvent);
@@ -6019,7 +6399,7 @@ class ListLite extends Component {
6019
6399
  pageSize: this.pageSize,
6020
6400
  onChange: (o) => __async(this, null, function* () {
6021
6401
  if (this.ctx.conf.editorTravel === true) {
6022
- this.ctx.trigger("editor-travel-back");
6402
+ this.ctx.editorTravelBack();
6023
6403
  }
6024
6404
  yield this.fetchComments(o);
6025
6405
  if (this.repositionAt) {
@@ -6039,11 +6419,11 @@ class ListLite extends Component {
6039
6419
  msg = String(msg);
6040
6420
  console.error(msg);
6041
6421
  if (offset !== 0 && this.pageMode === "read-more") {
6042
- (_a = this.readMoreBtn) == null ? void 0 : _a.showErr(`\u83B7\u53D6\u5931\u8D25`);
6422
+ (_a = this.readMoreBtn) == null ? void 0 : _a.showErr(this.$t("loadFail"));
6043
6423
  return;
6044
6424
  }
6045
- const $err = createElement(`<span>${msg}\uFF0C\u65E0\u6CD5\u83B7\u53D6\u8BC4\u8BBA\u5217\u8868\u6570\u636E<br/></span>`);
6046
- const $retryBtn = createElement('<span style="cursor:pointer;">\u70B9\u51FB\u91CD\u65B0\u83B7\u53D6</span>');
6425
+ const $err = createElement(`<span>${msg}\uFF0C${this.$t("listLoadFailMsg")}<br/></span>`);
6426
+ const $retryBtn = createElement(`<span style="cursor:pointer;">${this.$t("listRetry")}</span>`);
6047
6427
  $retryBtn.onclick = () => this.fetchComments(0);
6048
6428
  $err.appendChild($retryBtn);
6049
6429
  const adminBtn = createElement('<span atk-only-admin-show> | <span style="cursor:pointer;">\u6253\u5F00\u63A7\u5236\u53F0</span></span>');
@@ -6058,13 +6438,13 @@ class ListLite extends Component {
6058
6438
  };
6059
6439
  sidebarView = `sites|${JSON.stringify(viewLoadParam)}`;
6060
6440
  }
6061
- adminBtn.onclick = () => this.ctx.trigger("sidebar-show", {
6441
+ adminBtn.onclick = () => this.ctx.showSidebar({
6062
6442
  view: sidebarView
6063
6443
  });
6064
6444
  setError(this.$el, $err);
6065
6445
  }
6066
6446
  refreshUI() {
6067
- const isNoComment = this.commentList.length <= 0;
6447
+ const isNoComment = this.ctx.getCommentList().length <= 0;
6068
6448
  let $noComment = this.$commentsWrap.querySelector(".atk-list-no-comment");
6069
6449
  if (isNoComment) {
6070
6450
  if (!$noComment) {
@@ -6075,24 +6455,28 @@ class ListLite extends Component {
6075
6455
  } else {
6076
6456
  $noComment == null ? void 0 : $noComment.remove();
6077
6457
  }
6078
- this.ctx.trigger("check-admin-show-el");
6458
+ this.ctx.checkAdminShowEl();
6459
+ }
6460
+ reload() {
6461
+ this.fetchComments(0);
6079
6462
  }
6080
6463
  createComment(cData, ctxData) {
6081
6464
  if (!ctxData)
6082
- ctxData = this.commentDataList;
6083
- const comment2 = new Comment(this.ctx, cData);
6084
- comment2.flatMode = this.flatMode;
6085
- comment2.afterRender = () => {
6086
- if (this.renderComment)
6087
- this.renderComment(comment2);
6088
- };
6089
- comment2.onDelete = (c) => {
6090
- this.deleteComment(c);
6091
- this.refreshUI();
6092
- };
6093
- comment2.replyTo = cData.rid ? ctxData.find((c) => c.id === cData.rid) : void 0;
6465
+ ctxData = this.ctx.getCommentDataList();
6466
+ const comment2 = new Comment(this.ctx, cData, {
6467
+ isFlatMode: this.flatMode,
6468
+ afterRender: () => {
6469
+ if (this.renderComment)
6470
+ this.renderComment(comment2);
6471
+ },
6472
+ onDelete: (c) => {
6473
+ this.ctx.deleteComment(c);
6474
+ this.refreshUI();
6475
+ },
6476
+ replyTo: cData.rid ? ctxData.find((c) => c.id === cData.rid) : void 0
6477
+ });
6094
6478
  comment2.render();
6095
- this.commentList.push(comment2);
6479
+ this.ctx.getCommentList().push(comment2);
6096
6480
  return comment2;
6097
6481
  }
6098
6482
  importComments(srcData) {
@@ -6109,7 +6493,7 @@ class ListLite extends Component {
6109
6493
  rootNodes.forEach((rootNode) => {
6110
6494
  const rootC = this.createComment(rootNode.comment, srcData);
6111
6495
  this.$commentsWrap.appendChild(rootC.getEl());
6112
- rootC.playFadeInAnim();
6496
+ rootC.getRender().playFadeAnim();
6113
6497
  const that = this;
6114
6498
  (function loadChildren(parentC, parentNode) {
6115
6499
  parentNode.children.forEach((node) => {
@@ -6119,7 +6503,7 @@ class ListLite extends Component {
6119
6503
  loadChildren(childC, node);
6120
6504
  });
6121
6505
  })(rootC, rootNode);
6122
- rootC.checkHeightLimit();
6506
+ rootC.getRender().checkHeightLimit();
6123
6507
  });
6124
6508
  }
6125
6509
  putCommentFlatMode(cData, ctxData, insertMode) {
@@ -6131,9 +6515,9 @@ class ListLite extends Component {
6131
6515
  this.$commentsWrap.append(comment2.getEl());
6132
6516
  if (insertMode === "prepend")
6133
6517
  this.$commentsWrap.prepend(comment2.getEl());
6134
- comment2.playFadeInAnim();
6518
+ comment2.getRender().playFadeAnim();
6135
6519
  }
6136
- comment2.checkHeightLimit();
6520
+ comment2.getRender().checkHeightLimit();
6137
6521
  return comment2;
6138
6522
  }
6139
6523
  insertComment(commentData) {
@@ -6142,66 +6526,41 @@ class ListLite extends Component {
6142
6526
  if (commentData.rid === 0) {
6143
6527
  this.$commentsWrap.prepend(comment2.getEl());
6144
6528
  } else {
6145
- const parent = this.findComment(commentData.rid);
6529
+ const parent = this.ctx.findComment(commentData.rid);
6146
6530
  if (parent) {
6147
6531
  parent.putChild(comment2, this.nestSortBy === "DATE_ASC" ? "append" : "prepend");
6148
6532
  comment2.getParents().forEach((p) => {
6149
- if (p.$children)
6150
- p.heightLimitRemove(p.$children);
6533
+ p.getRender().heightLimitRemoveForChildren();
6151
6534
  });
6152
6535
  }
6153
6536
  }
6154
- comment2.checkHeightLimit();
6537
+ comment2.getRender().checkHeightLimit();
6155
6538
  scrollIntoView(comment2.getEl());
6156
- comment2.playFadeInAnim();
6539
+ comment2.getRender().playFadeAnim();
6157
6540
  } else {
6158
- const comment2 = this.putCommentFlatMode(commentData, this.commentDataList, "prepend");
6541
+ const comment2 = this.putCommentFlatMode(commentData, this.ctx.getCommentDataList(), "prepend");
6159
6542
  scrollIntoView(comment2.getEl());
6160
6543
  }
6161
6544
  if (this.data)
6162
6545
  this.data.total += 1;
6163
6546
  this.refreshUI();
6164
- this.ctx.trigger("comments-loaded");
6165
- }
6166
- findComment(id) {
6167
- return this.commentList.find((c) => c.data.id === id);
6168
- }
6169
- deleteComment(_comment) {
6170
- let comment2;
6171
- if (typeof _comment === "number") {
6172
- const findComment = this.findComment(_comment);
6173
- if (!findComment)
6174
- throw Error(`\u672A\u627E\u5230\u8BC4\u8BBA ${_comment}`);
6175
- comment2 = findComment;
6176
- } else
6177
- comment2 = _comment;
6178
- comment2.getEl().remove();
6179
- this.commentList.splice(this.commentList.indexOf(comment2), 1);
6180
- if (this.data)
6181
- this.data.total -= 1;
6182
- this.refreshUI();
6183
- }
6184
- clearAllComments() {
6185
- this.$commentsWrap.innerHTML = "";
6186
- this.data = void 0;
6187
- this.commentList = [];
6547
+ this.ctx.trigger("list-loaded");
6548
+ this.ctx.trigger("list-inserted", commentData);
6188
6549
  }
6189
6550
  updateUnread(notifies) {
6190
6551
  this.unread = notifies;
6191
6552
  if (this.unreadHighlight === true) {
6192
- this.commentList.forEach((comment2) => {
6193
- const notify = this.unread.find((o) => o.comment_id === comment2.data.id);
6553
+ this.ctx.getCommentList().forEach((comment2) => {
6554
+ const notify = this.unread.find((o) => o.comment_id === comment2.getID());
6194
6555
  if (notify) {
6195
- comment2.setUnread(true);
6196
- comment2.setOpenURL(notify.read_link);
6197
- comment2.openEvt = () => {
6198
- this.unread = this.unread.filter((o) => o.comment_id !== comment2.data.id);
6199
- this.ctx.trigger("unread-update", {
6200
- notifies: this.unread
6201
- });
6202
- };
6556
+ comment2.getRender().setUnread(true);
6557
+ comment2.getRender().setOpenAction(() => {
6558
+ window.open(notify.read_link);
6559
+ this.unread = this.unread.filter((o) => o.comment_id !== comment2.getID());
6560
+ this.ctx.updateNotifies(this.unread);
6561
+ });
6203
6562
  } else {
6204
- comment2.setUnread(false);
6563
+ comment2.getRender().setUnread(false);
6205
6564
  }
6206
6565
  });
6207
6566
  }
@@ -6209,7 +6568,7 @@ class ListLite extends Component {
6209
6568
  versionCheck(name, needVersion, curtVersion) {
6210
6569
  const needUpdate = versionCompare(needVersion, curtVersion) === 1;
6211
6570
  if (needUpdate) {
6212
- const errEl = createElement(`<div>Artalk ${name}\u7248\u672C\u5DF2\u8FC7\u65F6\uFF0C\u8BF7\u66F4\u65B0\u4EE5\u83B7\u5F97\u5B8C\u6574\u4F53\u9A8C<br/>\u5982\u679C\u4F60\u662F\u7BA1\u7406\u5458\uFF0C\u8BF7\u524D\u5F80 \u201C<a href="https://artalk.js.org/" target="_blank">\u5B98\u65B9\u6587\u6863</a>\u201D \u83B7\u5F97\u5E2E\u52A9<br/><br/><span style="color: var(--at-color-meta);">\u5F53\u524D${name}\u7248\u672C ${curtVersion}\uFF0C\u9700\u6C42\u7248\u672C >= ${needVersion}</span><br/><br/></div>`);
6571
+ const errEl = createElement(`<div>Artalk ${this.$t(name)}\u7248\u672C\u5DF2\u8FC7\u65F6\uFF0C\u8BF7\u66F4\u65B0\u4EE5\u83B7\u5F97\u5B8C\u6574\u4F53\u9A8C<br/>\u5982\u679C\u4F60\u662F\u7BA1\u7406\u5458\uFF0C\u8BF7\u524D\u5F80 \u201C<a href="https://artalk.js.org/" target="_blank">\u5B98\u65B9\u6587\u6863</a>\u201D \u83B7\u5F97\u5E2E\u52A9<br/><br/><span style="color: var(--at-color-meta);">\u5F53\u524D${this.$t(name)}\u7248\u672C ${curtVersion}\uFF0C\u9700\u6C42\u7248\u672C >= ${needVersion}</span><br/><br/></div>`);
6213
6572
  const ignoreBtn = createElement('<span style="cursor:pointer;">\u5FFD\u7565</span>');
6214
6573
  ignoreBtn.onclick = () => {
6215
6574
  setError(this.$el.parentElement, null);
@@ -6225,55 +6584,46 @@ class ListLite extends Component {
6225
6584
  }
6226
6585
  class List extends ListLite {
6227
6586
  constructor(ctx) {
6228
- var _a, _b;
6229
6587
  const el = createElement(ListHTML);
6230
6588
  super(ctx);
6231
6589
  __publicField(this, "$closeCommentBtn");
6232
6590
  __publicField(this, "$openSidebarBtn");
6233
6591
  __publicField(this, "$unreadBadge");
6234
6592
  __publicField(this, "$commentCount");
6593
+ __publicField(this, "$commentCountNum");
6235
6594
  __publicField(this, "$dropdownWrap");
6236
6595
  __publicField(this, "goToCommentFounded", false);
6237
6596
  __publicField(this, "goToCommentDelay", true);
6238
6597
  el.querySelector(".atk-list-body").append(this.$el);
6239
6598
  this.$el = el;
6240
- let flatMode = false;
6241
- if (this.ctx.conf.flatMode === "auto") {
6599
+ if (this.ctx.conf.flatMode === true) {
6600
+ this.flatMode = true;
6601
+ } else if (this.conf.nestMax && this.conf.nestMax <= 1) {
6602
+ this.flatMode = true;
6603
+ } else if (this.ctx.conf.flatMode === "auto") {
6242
6604
  if (window.matchMedia("(max-width: 768px)").matches)
6243
- flatMode = true;
6244
- } else if (this.ctx.conf.flatMode === true) {
6245
- flatMode = true;
6605
+ this.flatMode = true;
6246
6606
  }
6247
- this.flatMode = flatMode;
6248
- this.pageMode = ((_a = this.conf.pagination) == null ? void 0 : _a.readMore) ? "read-more" : "pagination";
6249
- this.pageSize = ((_b = this.conf.pagination) == null ? void 0 : _b.pageSize) || 20;
6607
+ this.pageMode = this.conf.pagination.readMore ? "read-more" : "pagination";
6608
+ this.pageSize = this.conf.pagination.pageSize || 20;
6250
6609
  this.repositionAt = this.$el;
6251
6610
  this.initListActionBtn();
6252
6611
  this.$commentCount = this.$el.querySelector(".atk-comment-count");
6612
+ this.$commentCount.innerHTML = this.$t("counter", {
6613
+ count: '<span class="atk-comment-count-num">0</span>'
6614
+ });
6615
+ this.$commentCountNum = this.$commentCount.querySelector(".atk-comment-count-num");
6253
6616
  if (this.ctx.conf.listSort) {
6254
6617
  this.initDropdown();
6255
6618
  }
6256
- this.$el.querySelector(".atk-copyright").innerHTML = `Powered By <a href="https://artalk.js.org" target="_blank" title="Artalk v${"2.2.12"}">Artalk</a>`;
6257
- this.ctx.on("list-reload", () => this.fetchComments(0));
6258
- this.ctx.on("list-refresh-ui", () => this.refreshUI());
6259
- this.ctx.on("list-import", (data) => this.importComments(data));
6260
- this.ctx.on("list-insert", (data) => this.insertComment(data));
6261
- this.ctx.on("list-delete", (comment2) => this.deleteComment(comment2.id));
6262
- this.ctx.on("list-update", (updateData) => {
6263
- updateData(this.data);
6264
- this.refreshUI();
6265
- });
6266
- this.ctx.on("unread-update", (data) => {
6267
- var _a2;
6268
- return this.showUnreadBadge(((_a2 = data.notifies) == null ? void 0 : _a2.length) || 0);
6269
- });
6619
+ this.$el.querySelector(".atk-copyright").innerHTML = `Powered By <a href="https://artalk.js.org" target="_blank" title="Artalk v${"2.3.2"}">Artalk</a>`;
6270
6620
  }
6271
6621
  initListActionBtn() {
6272
6622
  this.$openSidebarBtn = this.$el.querySelector('[data-action="open-sidebar"]');
6273
6623
  this.$closeCommentBtn = this.$el.querySelector('[data-action="admin-close-comment"]');
6274
6624
  this.$unreadBadge = this.$el.querySelector(".atk-unread-badge");
6275
6625
  this.$openSidebarBtn.addEventListener("click", () => {
6276
- this.ctx.trigger("sidebar-show");
6626
+ this.ctx.showSidebar();
6277
6627
  });
6278
6628
  this.$closeCommentBtn.addEventListener("click", () => {
6279
6629
  if (!this.data)
@@ -6285,20 +6635,20 @@ class List extends ListLite {
6285
6635
  refreshUI() {
6286
6636
  var _a;
6287
6637
  super.refreshUI();
6288
- this.$el.querySelector(".atk-comment-count-num").innerText = String(Number((_a = this.data) == null ? void 0 : _a.total) || 0);
6638
+ this.$commentCountNum.innerText = String(Number((_a = this.data) == null ? void 0 : _a.total) || 0);
6289
6639
  if (!!this.ctx.user.data.nick && !!this.ctx.user.data.email) {
6290
6640
  this.$openSidebarBtn.classList.remove("atk-hide");
6291
6641
  } else {
6292
6642
  this.$openSidebarBtn.classList.add("atk-hide");
6293
6643
  }
6294
- this.ctx.trigger("check-admin-show-el");
6295
- this.$openSidebarBtn.querySelector(".atk-text").innerText = !this.ctx.user.data.isAdmin ? "\u901A\u77E5\u4E2D\u5FC3" : "\u63A7\u5236\u4E2D\u5FC3";
6644
+ this.ctx.checkAdminShowEl();
6645
+ this.$openSidebarBtn.querySelector(".atk-text").innerText = !this.ctx.user.data.isAdmin ? this.$t("msgCenter") : this.$t("ctrlCenter");
6296
6646
  if (!!this.data && !!this.data.page && this.data.page.admin_only === true) {
6297
- this.ctx.trigger("editor-close");
6298
- this.$closeCommentBtn.innerHTML = "\u6253\u5F00\u8BC4\u8BBA";
6647
+ this.ctx.editorClose();
6648
+ this.$closeCommentBtn.innerHTML = this.$t("openComment");
6299
6649
  } else {
6300
- this.ctx.trigger("editor-open");
6301
- this.$closeCommentBtn.innerHTML = "\u5173\u95ED\u8BC4\u8BBA";
6650
+ this.ctx.editorOpen();
6651
+ this.$closeCommentBtn.innerHTML = this.$t("closeComment");
6302
6652
  }
6303
6653
  }
6304
6654
  onLoad(data, offset) {
@@ -6306,7 +6656,7 @@ class List extends ListLite {
6306
6656
  if (!this.goToCommentFounded)
6307
6657
  this.checkGoToCommentByUrlHash();
6308
6658
  if (this.ctx.conf.editorTravel === true) {
6309
- this.ctx.trigger("editor-travel-back");
6659
+ this.ctx.editorTravelBack();
6310
6660
  }
6311
6661
  }
6312
6662
  checkGoToCommentByUrlHash() {
@@ -6319,7 +6669,7 @@ class List extends ListLite {
6319
6669
  }
6320
6670
  if (!commentId)
6321
6671
  return;
6322
- const comment2 = this.findComment(commentId);
6672
+ const comment2 = this.ctx.findComment(commentId);
6323
6673
  if (!comment2) {
6324
6674
  if (this.readMoreBtn)
6325
6675
  this.readMoreBtn.click();
@@ -6329,16 +6679,13 @@ class List extends ListLite {
6329
6679
  }
6330
6680
  const notifyKey = getQueryParam("atk_notify_key");
6331
6681
  if (notifyKey) {
6332
- new Api(this.ctx).markRead(notifyKey).then(() => {
6682
+ this.ctx.getApi().markRead(notifyKey).then(() => {
6333
6683
  this.unread = this.unread.filter((o) => o.comment_id !== commentId);
6334
- this.ctx.trigger("unread-update", {
6335
- notifies: this.unread
6336
- });
6684
+ this.updateUnread(this.unread);
6337
6685
  });
6338
6686
  }
6339
6687
  comment2.getParents().forEach((p) => {
6340
- if (p.$children)
6341
- p.heightLimitRemove(p.$children);
6688
+ p.getRender().heightLimitRemoveForChildren();
6342
6689
  });
6343
6690
  const goTo = () => {
6344
6691
  scrollIntoView(comment2.getEl(), false);
@@ -6356,15 +6703,15 @@ class List extends ListLite {
6356
6703
  adminPageEditSave() {
6357
6704
  if (!this.data || !this.data.page)
6358
6705
  return;
6359
- this.ctx.trigger("editor-show-loading");
6360
- new Api(this.ctx).pageEdit(this.data.page).then((page) => {
6706
+ this.ctx.editorShowLoading();
6707
+ this.ctx.getApi().pageEdit(this.data.page).then((page) => {
6361
6708
  if (this.data)
6362
6709
  this.data.page = __spreadValues({}, page);
6363
6710
  this.refreshUI();
6364
6711
  }).catch((err) => {
6365
- this.ctx.trigger("editor-notify", { msg: `\u4FEE\u6539\u9875\u9762\u6570\u636E\u5931\u8D25\uFF1A${err.msg || String(err)}`, type: "e" });
6712
+ this.ctx.editorShowNotify(`${this.$t("editFail")}: ${err.msg || String(err)}`, "e");
6366
6713
  }).finally(() => {
6367
- this.ctx.trigger("editor-hide-loading");
6714
+ this.ctx.editorHideLoading();
6368
6715
  });
6369
6716
  }
6370
6717
  showUnreadBadge(count) {
@@ -6386,22 +6733,22 @@ class List extends ListLite {
6386
6733
  this.fetchComments(0);
6387
6734
  };
6388
6735
  const dropdownList = [
6389
- ["\u6700\u65B0", () => {
6736
+ [this.$t("sortLatest"), () => {
6390
6737
  reloadUseParamsEditor((p) => {
6391
6738
  p.sort_by = "date_desc";
6392
6739
  });
6393
6740
  }],
6394
- ["\u6700\u70ED", () => {
6741
+ [this.$t("sortBest"), () => {
6395
6742
  reloadUseParamsEditor((p) => {
6396
6743
  p.sort_by = "vote";
6397
6744
  });
6398
6745
  }],
6399
- ["\u6700\u65E9", () => {
6746
+ [this.$t("sortOldest"), () => {
6400
6747
  reloadUseParamsEditor((p) => {
6401
6748
  p.sort_by = "date_asc";
6402
6749
  });
6403
6750
  }],
6404
- ["\u4F5C\u8005", () => {
6751
+ [this.$t("sortAuthor"), () => {
6405
6752
  reloadUseParamsEditor((p) => {
6406
6753
  p.view_only_admin = true;
6407
6754
  });
@@ -6436,6 +6783,10 @@ class List extends ListLite {
6436
6783
  });
6437
6784
  this.$dropdownWrap.append($dropdown);
6438
6785
  }
6786
+ updateUnread(notifies) {
6787
+ super.updateUnread(notifies);
6788
+ this.showUnreadBadge((notifies == null ? void 0 : notifies.length) || 0);
6789
+ }
6439
6790
  }
6440
6791
  var sidebarLayer = "";
6441
6792
  var SidebarHTML = '<div class="atk-sidebar-layer">\n <div class="atk-sidebar-inner">\n <div class="atk-sidebar-header">\n <div class="atk-sidebar-close"><i class="atk-icon atk-icon-close"></i></div>\n </div>\n <div class="atk-sidebar-iframe-wrap"></div>\n </div>\n</div>\n';
@@ -6448,7 +6799,6 @@ class SidebarLayer extends Component {
6448
6799
  __publicField(this, "$iframeWrap");
6449
6800
  __publicField(this, "$iframe");
6450
6801
  __publicField(this, "firstShow", true);
6451
- __publicField(this, "loadingTimer", null);
6452
6802
  this.$el = createElement(SidebarHTML);
6453
6803
  this.$header = this.$el.querySelector(".atk-sidebar-header");
6454
6804
  this.$closeBtn = this.$header.querySelector(".atk-sidebar-close");
@@ -6456,20 +6806,18 @@ class SidebarLayer extends Component {
6456
6806
  this.$closeBtn.onclick = () => {
6457
6807
  this.hide();
6458
6808
  };
6459
- this.ctx.on("sidebar-show", (conf) => this.show(conf || {}));
6460
- this.ctx.on("sidebar-hide", () => this.hide());
6461
6809
  this.ctx.on("user-changed", () => {
6462
6810
  this.firstShow = true;
6463
6811
  });
6464
6812
  }
6465
- show(conf) {
6466
- return __async(this, null, function* () {
6813
+ show() {
6814
+ return __async(this, arguments, function* (conf = {}) {
6467
6815
  this.$el.style.transform = "";
6468
6816
  if (this.layer == null) {
6469
6817
  this.layer = new Layer(this.ctx, "sidebar", this.$el);
6470
6818
  this.layer.afterHide = () => {
6471
6819
  if (this.ctx.conf.editorTravel === true) {
6472
- this.ctx.trigger("editor-travel-back");
6820
+ this.ctx.editorTravelBack();
6473
6821
  }
6474
6822
  };
6475
6823
  }
@@ -6479,11 +6827,11 @@ class SidebarLayer extends Component {
6479
6827
  }, 20);
6480
6828
  (() => __async(this, null, function* () {
6481
6829
  var _a;
6482
- const resp = yield new Api(this.ctx).loginStatus();
6830
+ const resp = yield this.ctx.getApi().loginStatus();
6483
6831
  if (resp.is_admin && !resp.is_login) {
6484
6832
  (_a = this.layer) == null ? void 0 : _a.hide();
6485
6833
  this.firstShow = true;
6486
- this.ctx.trigger("checker-admin", {
6834
+ this.ctx.checkAdmin({
6487
6835
  onSuccess: () => {
6488
6836
  setTimeout(() => {
6489
6837
  this.show(conf);
@@ -6494,7 +6842,7 @@ class SidebarLayer extends Component {
6494
6842
  });
6495
6843
  }
6496
6844
  }))();
6497
- this.ctx.trigger("unread-update", { notifies: [] });
6845
+ this.ctx.updateNotifies([]);
6498
6846
  if (this.firstShow) {
6499
6847
  this.$iframeWrap.innerHTML = "";
6500
6848
  this.$iframe = createElement("<iframe></iframe>");
@@ -6520,12 +6868,14 @@ class SidebarLayer extends Component {
6520
6868
  if (!this.conf.darkMode && isIframeSrcDarkMode)
6521
6869
  this.iframeLoad(this.$iframe.src.replace("&darkMode=1", ""));
6522
6870
  }
6871
+ this.ctx.trigger("sidebar-show");
6523
6872
  });
6524
6873
  }
6525
6874
  hide() {
6526
6875
  var _a;
6527
6876
  this.$el.style.transform = "";
6528
6877
  (_a = this.layer) == null ? void 0 : _a.hide();
6878
+ this.ctx.trigger("sidebar-hide");
6529
6879
  }
6530
6880
  iframeLoad(src) {
6531
6881
  if (!this.$iframe)
@@ -6536,94 +6886,112 @@ class SidebarLayer extends Component {
6536
6886
  hideLoading(this.$iframeWrap);
6537
6887
  };
6538
6888
  }
6539
- checkReqStatus(url) {
6540
- return __async(this, null, function* () {
6541
- if (this.loadingTimer !== null)
6542
- window.clearTimeout(this.loadingTimer);
6543
- this.loadingTimer = window.setTimeout(() => __async(this, null, function* () {
6544
- try {
6545
- yield fetch(url);
6546
- } catch (err) {
6547
- console.log(err);
6548
- const $errAlert = createElement(`<div class="atk-err-alert"> <div class="atk-title">\u4FA7\u8FB9\u680F\u4F3C\u4E4E\u6253\u5F00\u5931\u8D25</div> <div class="atk-text"><span id="AtkReload">\u91CD\u65B0\u52A0\u8F7D</span> / <span id="AtkCancel">\u53D6\u6D88</span></div></div>`);
6549
- const $reloadBtn = $errAlert.querySelector("#AtkReload");
6550
- const $cancelBtn = $errAlert.querySelector("#AtkCancel");
6551
- $reloadBtn.onclick = () => {
6552
- this.iframeLoad(url);
6553
- $errAlert.remove();
6554
- };
6555
- $cancelBtn.onclick = () => {
6556
- $errAlert.remove();
6557
- };
6558
- this.$iframeWrap.append($errAlert);
6559
- }
6560
- }), 2e3);
6889
+ }
6890
+ const PvCountWidget = (ctx) => {
6891
+ initCountWidget({ ctx, pvAdd: true });
6892
+ };
6893
+ function initCountWidget(p) {
6894
+ return __async(this, null, function* () {
6895
+ const countEl = p.ctx.conf.countEl;
6896
+ if (countEl && document.querySelector(countEl)) {
6897
+ handleStatCount(p, { api: "page_comment", countEl });
6898
+ }
6899
+ const curtPagePvNum = p.pvAdd ? yield p.ctx.getApi().pv() : void 0;
6900
+ const pvEl = p.ctx.conf.pvEl;
6901
+ if (pvEl && document.querySelector(pvEl)) {
6902
+ handleStatCount(p, {
6903
+ api: "page_pv",
6904
+ countEl: pvEl,
6905
+ curtPageCount: curtPagePvNum
6906
+ });
6907
+ }
6908
+ });
6909
+ }
6910
+ function handleStatCount(p, args) {
6911
+ return __async(this, null, function* () {
6912
+ let pageCounts = {};
6913
+ const curtPageKey = p.ctx.conf.pageKey;
6914
+ if (args.curtPageCount)
6915
+ pageCounts[curtPageKey] = args.curtPageCount;
6916
+ let queryPageKeys = Array.from(document.querySelectorAll(args.countEl)).map((e) => e.getAttribute("data-page-key") || curtPageKey).filter((pageKey) => pageCounts[pageKey] === void 0);
6917
+ queryPageKeys = [...new Set(queryPageKeys)];
6918
+ if (queryPageKeys.length > 0) {
6919
+ const counts = yield p.ctx.getApi().stat(args.api, queryPageKeys);
6920
+ pageCounts = __spreadValues(__spreadValues({}, pageCounts), counts);
6921
+ }
6922
+ document.querySelectorAll(args.countEl).forEach((el) => {
6923
+ const pageKey = el.getAttribute("data-page-key") || curtPageKey;
6924
+ el.innerHTML = `${Number(pageCounts[pageKey] || 0)}`;
6561
6925
  });
6562
- }
6926
+ });
6563
6927
  }
6564
6928
  const _Artalk = class {
6565
6929
  constructor(customConf) {
6566
- __publicField(this, "ctx");
6567
6930
  __publicField(this, "conf");
6931
+ __publicField(this, "ctx");
6568
6932
  __publicField(this, "$root");
6569
6933
  __publicField(this, "checkerLauncher");
6570
6934
  __publicField(this, "editor");
6571
6935
  __publicField(this, "list");
6572
6936
  __publicField(this, "sidebarLayer");
6573
- this.conf = mergeDeep(_Artalk.defaults, customConf);
6574
- this.conf.server = this.conf.server.replace(/\/$/, "").replace(/\/api\/?$/, "");
6575
- if (!this.conf.pageKey) {
6576
- this.conf.pageKey = `${window.location.pathname}`;
6577
- }
6578
- if (!this.conf.pageTitle) {
6579
- this.conf.pageTitle = `${document.title}`;
6580
- }
6581
- if (this.conf.nestMax && this.conf.nestMax <= 1) {
6582
- this.conf.flatMode = true;
6583
- }
6584
- if (!!this.conf.el && this.conf.el instanceof HTMLElement) {
6937
+ this.conf = _Artalk.HandelBaseConf(customConf);
6938
+ if (this.conf.el instanceof HTMLElement)
6585
6939
  this.$root = this.conf.el;
6586
- } else {
6587
- try {
6588
- const $root = document.querySelector(this.conf.el);
6589
- if (!$root)
6590
- throw Error(`Sorry, target element "${this.conf.el}" was not found.`);
6591
- this.$root = $root;
6592
- } catch (e) {
6593
- console.error(e);
6594
- throw new Error("Please check your Artalk `el` config.");
6595
- }
6596
- }
6597
- this.ctx = new Context(this.$root, this.conf);
6598
- this.$root.classList.add("artalk");
6599
- this.$root.innerHTML = "";
6600
- if (this.conf.useBackendConf) {
6601
- return (() => __async(this, null, function* () {
6602
- yield this.loadConfRemote();
6603
- this.initCore();
6604
- return this;
6605
- }))();
6606
- }
6607
- this.initCore();
6940
+ this.ctx = new Context(this.conf, this.$root);
6941
+ if (this.conf.useBackendConf)
6942
+ return this.loadConfRemoteAndInitComponents();
6943
+ this.initComponents();
6608
6944
  }
6609
- initCore() {
6945
+ initComponents() {
6946
+ this.initLocale();
6610
6947
  this.initLayer();
6611
6948
  this.initDarkMode();
6612
- this.checkerLauncher = new CheckerLauncher(this.ctx);
6613
6949
  initMarked(this.ctx);
6950
+ this.checkerLauncher = new CheckerLauncher(this.ctx);
6951
+ this.ctx.setCheckerLauncher(this.checkerLauncher);
6614
6952
  this.editor = new Editor(this.ctx);
6953
+ this.ctx.setEditor(this.editor);
6615
6954
  this.$root.appendChild(this.editor.$el);
6616
6955
  this.list = new List(this.ctx);
6956
+ this.ctx.setList(this.list);
6617
6957
  this.$root.appendChild(this.list.$el);
6618
6958
  this.sidebarLayer = new SidebarLayer(this.ctx);
6959
+ this.ctx.setSidebarLayer(this.sidebarLayer);
6619
6960
  this.$root.appendChild(this.sidebarLayer.$el);
6620
6961
  this.list.fetchComments(0);
6621
6962
  this.initEventBind();
6622
- this.initPV();
6623
6963
  _Artalk.Plugins.forEach((plugin) => {
6624
- if (typeof plugin === "function") {
6964
+ if (typeof plugin === "function")
6625
6965
  plugin(this.ctx);
6966
+ });
6967
+ }
6968
+ static HandelBaseConf(customConf) {
6969
+ const conf = mergeDeep(_Artalk.defaults, customConf);
6970
+ if (typeof conf.el === "string" && !!conf.el) {
6971
+ try {
6972
+ const findEl = document.querySelector(conf.el);
6973
+ if (!findEl)
6974
+ throw Error(`Target element "${conf.el}" was not found.`);
6975
+ conf.el = findEl;
6976
+ } catch (e) {
6977
+ console.error(e);
6978
+ throw new Error("Please check your Artalk `el` config.");
6626
6979
  }
6980
+ }
6981
+ conf.server = conf.server.replace(/\/$/, "").replace(/\/api\/?$/, "");
6982
+ if (!conf.pageKey) {
6983
+ conf.pageKey = `${window.location.pathname}`;
6984
+ }
6985
+ if (!conf.pageTitle) {
6986
+ conf.pageTitle = `${document.title}`;
6987
+ }
6988
+ return conf;
6989
+ }
6990
+ loadConfRemoteAndInitComponents() {
6991
+ return __async(this, null, function* () {
6992
+ yield this.loadConfRemote();
6993
+ this.initComponents();
6994
+ return this;
6627
6995
  });
6628
6996
  }
6629
6997
  loadConfRemote() {
@@ -6631,9 +6999,9 @@ const _Artalk = class {
6631
6999
  showLoading(this.$root);
6632
7000
  let backendConf = {};
6633
7001
  try {
6634
- backendConf = yield new Api(this.ctx).conf();
7002
+ backendConf = yield this.ctx.getApi().conf();
6635
7003
  } catch (err) {
6636
- console.error("\u914D\u7F6E\u8FDC\u7A0B\u83B7\u53D6\u5931\u8D25", err);
7004
+ console.error("Load config from remote err", err);
6637
7005
  }
6638
7006
  this.ctx.conf = mergeDeep(this.ctx.conf, backendConf);
6639
7007
  hideLoading(this.$root);
@@ -6644,29 +7012,22 @@ const _Artalk = class {
6644
7012
  this.list.goToCommentDelay = false;
6645
7013
  this.list.checkGoToCommentByUrlHash();
6646
7014
  });
6647
- this.ctx.on("check-admin-show-el", () => {
6648
- const items = [];
6649
- this.$root.querySelectorAll(`[atk-only-admin-show]`).forEach((item) => items.push(item));
6650
- const { $wrap: $layerWrap } = GetLayerWrap(this.ctx);
6651
- if ($layerWrap)
6652
- $layerWrap.querySelectorAll(`[atk-only-admin-show]`).forEach((item) => items.push(item));
6653
- const $sidebarEl = document.querySelector(".atk-sidebar");
6654
- if ($sidebarEl)
6655
- $sidebarEl.querySelectorAll(`[atk-only-admin-show]`).forEach((item) => items.push(item));
6656
- items.forEach(($item) => {
6657
- if (this.ctx.user.data.isAdmin)
6658
- $item.classList.remove("atk-hide");
6659
- else
6660
- $item.classList.add("atk-hide");
6661
- });
6662
- });
6663
7015
  this.ctx.on("user-changed", () => {
6664
- this.ctx.trigger("check-admin-show-el");
6665
- this.ctx.trigger("list-refresh-ui");
7016
+ this.ctx.checkAdminShowEl();
7017
+ this.ctx.listRefreshUI();
6666
7018
  });
6667
7019
  }
6668
- reload() {
6669
- this.list.fetchComments(0);
7020
+ initLocale() {
7021
+ if (typeof this.conf.locale === "string") {
7022
+ if (this.conf.locale === "auto") {
7023
+ this.conf.locale = navigator.languages ? navigator.languages[0] : navigator.language;
7024
+ }
7025
+ this.conf.locale = this.conf.locale.replace(/^([a-zA-Z]+)(-[a-zA-Z]+)?$/, (_, p1, p2) => p1.toLowerCase() + (p2 || "").toUpperCase());
7026
+ if (!internal[this.conf.locale]) {
7027
+ console.log(`Locale "${this.conf.locale}" not found.`);
7028
+ this.conf.locale = "en";
7029
+ }
7030
+ }
6670
7031
  }
6671
7032
  initLayer() {
6672
7033
  Layer.BodyOrgOverflow = document.body.style.overflow;
@@ -6701,17 +7062,6 @@ const _Artalk = class {
6701
7062
  }
6702
7063
  }
6703
7064
  }
6704
- initPV() {
6705
- return __async(this, null, function* () {
6706
- const pvNum = yield new Api(this.ctx).pv();
6707
- if (Number.isNaN(Number(pvNum)))
6708
- return;
6709
- if (!this.conf.pvEl || !document.querySelector(this.conf.pvEl))
6710
- return;
6711
- const $pv = document.querySelector(this.conf.pvEl);
6712
- $pv.innerText = String(pvNum);
6713
- });
6714
- }
6715
7065
  on(name, handler) {
6716
7066
  this.ctx.on(name, handler, "external");
6717
7067
  }
@@ -6721,11 +7071,21 @@ const _Artalk = class {
6721
7071
  trigger(name, payload) {
6722
7072
  this.ctx.trigger(name, payload, "external");
6723
7073
  }
7074
+ reload() {
7075
+ this.ctx.listReload();
7076
+ }
6724
7077
  static Use(plugin) {
6725
7078
  this.Plugins.push(plugin);
6726
7079
  }
7080
+ static LoadCountWidget(customConf) {
7081
+ const conf = this.HandelBaseConf(customConf);
7082
+ const ctx = new Context(conf);
7083
+ initCountWidget({ ctx, pvAdd: false });
7084
+ }
6727
7085
  };
6728
7086
  let Artalk = _Artalk;
6729
- __publicField(Artalk, "defaults", defaults$3);
6730
- __publicField(Artalk, "Plugins", []);
7087
+ __publicField(Artalk, "defaults", defaults);
7088
+ __publicField(Artalk, "Plugins", [
7089
+ PvCountWidget
7090
+ ]);
6731
7091
  export { Artalk as default };