artalk 2.1.7 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/Artalk.es.js CHANGED
@@ -68,6 +68,7 @@ class Context {
68
68
  __publicField(this, "conf");
69
69
  __publicField(this, "user");
70
70
  __publicField(this, "eventList", []);
71
+ __publicField(this, "markedInstance");
71
72
  this.cid = +new Date();
72
73
  this.$root = rootEl;
73
74
  this.conf = conf;
@@ -102,6 +103,7 @@ const defaults$3 = {
102
103
  vote: true,
103
104
  voteDown: false,
104
105
  uaBadge: true,
106
+ listSort: true,
105
107
  pvEl: "#ArtalkPV",
106
108
  flatMode: "auto",
107
109
  maxNesting: 3,
@@ -118,6 +120,7 @@ const defaults$3 = {
118
120
  content: 300,
119
121
  children: 400
120
122
  },
123
+ imgUpload: true,
121
124
  reqTimeout: 15e3,
122
125
  versionCheck: true
123
126
  };
@@ -2654,37 +2657,36 @@ function versionCompare(a, b) {
2654
2657
  }
2655
2658
  return 0;
2656
2659
  }
2657
- let markedInstance;
2660
+ function initMarked(ctx) {
2661
+ const renderer = new marked$1.Renderer();
2662
+ const linkRenderer = renderer.link;
2663
+ renderer.link = (href, title, text) => {
2664
+ const localLink = href == null ? void 0 : href.startsWith(`${window.location.protocol}//${window.location.hostname}`);
2665
+ const html = linkRenderer.call(renderer, href, title, text);
2666
+ return html.replace(/^<a /, `<a target="_blank" ${!localLink ? `rel="noreferrer noopener nofollow"` : ""} `);
2667
+ };
2668
+ const nMarked = marked$1;
2669
+ marked$1.setOptions({
2670
+ renderer,
2671
+ highlight: (code) => hanabi(code),
2672
+ pedantic: false,
2673
+ gfm: true,
2674
+ breaks: true,
2675
+ smartLists: true,
2676
+ smartypants: true,
2677
+ xhtml: false,
2678
+ sanitize: true,
2679
+ sanitizer: (html) => insane_1(html, __spreadProps(__spreadValues({}, insane_1.defaults), {
2680
+ allowedAttributes: __spreadProps(__spreadValues({}, insane_1.defaults.allowedAttributes), {
2681
+ img: ["src", "atk-emoticon"]
2682
+ })
2683
+ })),
2684
+ silent: true
2685
+ });
2686
+ ctx.markedInstance = nMarked;
2687
+ }
2658
2688
  function marked(ctx, src) {
2659
- if (!markedInstance) {
2660
- const renderer = new marked$1.Renderer();
2661
- const linkRenderer = renderer.link;
2662
- renderer.link = (href, title, text) => {
2663
- const localLink = href == null ? void 0 : href.startsWith(`${window.location.protocol}//${window.location.hostname}`);
2664
- const html = linkRenderer.call(renderer, href, title, text);
2665
- return html.replace(/^<a /, `<a target="_blank" ${!localLink ? `rel="noreferrer noopener nofollow"` : ""} `);
2666
- };
2667
- const nMarked = marked$1;
2668
- marked$1.setOptions({
2669
- renderer,
2670
- highlight: (code) => hanabi(code),
2671
- pedantic: false,
2672
- gfm: true,
2673
- breaks: true,
2674
- smartLists: true,
2675
- smartypants: true,
2676
- xhtml: false,
2677
- sanitize: true,
2678
- sanitizer: (html) => insane_1(html, __spreadProps(__spreadValues({}, insane_1.defaults), {
2679
- allowedAttributes: __spreadProps(__spreadValues({}, insane_1.defaults.allowedAttributes), {
2680
- img: ["src", "atk-emoticon"]
2681
- })
2682
- })),
2683
- silent: true
2684
- });
2685
- markedInstance = nMarked;
2686
- }
2687
- return markedInstance.parse(src);
2689
+ return ctx.markedInstance.parse(src);
2688
2690
  }
2689
2691
  function getCorrectUserAgent() {
2690
2692
  return __async(this, null, function* () {
@@ -2710,7 +2712,19 @@ function getCorrectUserAgent() {
2710
2712
  return uaRaw;
2711
2713
  });
2712
2714
  }
2713
- function showLoading(parentElem) {
2715
+ function isValidURL(urlRaw) {
2716
+ let url;
2717
+ try {
2718
+ url = new URL(urlRaw);
2719
+ } catch (_) {
2720
+ return false;
2721
+ }
2722
+ return url.protocol === "http:" || url.protocol === "https:";
2723
+ }
2724
+ function getURLBasedOnApi(ctx, path) {
2725
+ return `${ctx.conf.server.replace(/\/api\/?$/, "")}/${path.replace(/^\//, "")}`;
2726
+ }
2727
+ function showLoading(parentElem, conf) {
2714
2728
  if (parentElem instanceof Context)
2715
2729
  parentElem = parentElem.$root;
2716
2730
  let $loading = parentElem.querySelector(".atk-loading");
@@ -2720,6 +2734,8 @@ function showLoading(parentElem) {
2720
2734
  <svg viewBox="25 25 50 50"><circle cx="50" cy="50" r="20" fill="none" stroke-width="2" stroke-miterlimit="10"></circle></svg>
2721
2735
  </div>
2722
2736
  </div>`);
2737
+ if (conf == null ? void 0 : conf.transparentBg)
2738
+ $loading.style.background = "transparent";
2723
2739
  parentElem.appendChild($loading);
2724
2740
  }
2725
2741
  $loading.style.display = "";
@@ -2886,11 +2902,7 @@ const _Layer = class extends Component {
2886
2902
  if (this.maskClickHideEnable)
2887
2903
  this.hide();
2888
2904
  };
2889
- this.bodyStyleOrgOverflow = document.body.style.overflow;
2890
- this.bodyStyleOrgPaddingRight = document.body.style.paddingRight;
2891
- document.body.style.overflow = "hidden";
2892
- const bpr = parseInt(window.getComputedStyle(document.body, null).getPropertyValue("padding-right"), 10);
2893
- document.body.style.paddingRight = `${getScrollBarWidth() + bpr || 0}px`;
2905
+ this.pageBodyScrollBarHide();
2894
2906
  }
2895
2907
  hide() {
2896
2908
  if (this.afterHide)
@@ -2899,8 +2911,7 @@ const _Layer = class extends Component {
2899
2911
  this.$el.style.display = "none";
2900
2912
  this.newActionTimer(() => {
2901
2913
  this.$wrap.style.display = "none";
2902
- document.body.style.overflow = this.bodyStyleOrgOverflow;
2903
- document.body.style.paddingRight = this.bodyStyleOrgPaddingRight;
2914
+ this.pageBodyScrollBarShow();
2904
2915
  this.checkCleanLayer();
2905
2916
  }, 450);
2906
2917
  this.newActionTimer(() => {
@@ -2911,6 +2922,17 @@ const _Layer = class extends Component {
2911
2922
  setMaskClickHide(enable) {
2912
2923
  this.maskClickHideEnable = enable;
2913
2924
  }
2925
+ pageBodyScrollBarHide() {
2926
+ this.bodyStyleOrgOverflow = document.body.style.overflow;
2927
+ this.bodyStyleOrgPaddingRight = document.body.style.paddingRight;
2928
+ document.body.style.overflow = "hidden";
2929
+ const bpr = parseInt(window.getComputedStyle(document.body, null).getPropertyValue("padding-right"), 10);
2930
+ document.body.style.paddingRight = `${getScrollBarWidth() + bpr || 0}px`;
2931
+ }
2932
+ pageBodyScrollBarShow() {
2933
+ document.body.style.overflow = this.bodyStyleOrgOverflow;
2934
+ document.body.style.paddingRight = this.bodyStyleOrgPaddingRight;
2935
+ }
2914
2936
  newActionTimer(func, delay) {
2915
2937
  const act = () => {
2916
2938
  func();
@@ -2928,6 +2950,7 @@ const _Layer = class extends Component {
2928
2950
  disposeNow() {
2929
2951
  document.body.style.overflow = "";
2930
2952
  this.$el.remove();
2953
+ this.pageBodyScrollBarShow();
2931
2954
  this.checkCleanLayer();
2932
2955
  }
2933
2956
  dispose() {
@@ -3018,6 +3041,7 @@ function Fetch(ctx, input, init, timeout) {
3018
3041
  json = yield new Promise((resolve, reject) => {
3019
3042
  ctx.trigger("checker-captcha", {
3020
3043
  imgData: json.data.img_data,
3044
+ iframe: json.data.iframe,
3021
3045
  onSuccess: () => {
3022
3046
  recall(resolve, reject);
3023
3047
  },
@@ -3182,6 +3206,12 @@ class Api {
3182
3206
  }
3183
3207
  };
3184
3208
  }
3209
+ loginStatus() {
3210
+ return __async(this, null, function* () {
3211
+ const data = yield GET(this.ctx, `${this.baseURL}/login-status`);
3212
+ return data || { is_login: false };
3213
+ });
3214
+ }
3185
3215
  pageGet(siteName, offset, limit) {
3186
3216
  return __async(this, null, function* () {
3187
3217
  const params = {
@@ -3300,6 +3330,25 @@ class Api {
3300
3330
  return p.pv;
3301
3331
  });
3302
3332
  }
3333
+ imgUpload(file) {
3334
+ return __async(this, null, function* () {
3335
+ const params = {
3336
+ name: this.ctx.user.data.nick,
3337
+ email: this.ctx.user.data.email,
3338
+ page_key: this.ctx.conf.pageKey
3339
+ };
3340
+ if (this.ctx.conf.site)
3341
+ params.site_name = this.ctx.conf.site;
3342
+ const form = ToFormData(params);
3343
+ form.set("file", file);
3344
+ const init = {
3345
+ method: "POST",
3346
+ body: form
3347
+ };
3348
+ const json = yield Fetch(this.ctx, `${this.baseURL}/img-upload`, init);
3349
+ return json.data || {};
3350
+ });
3351
+ }
3303
3352
  captchaGet() {
3304
3353
  return __async(this, null, function* () {
3305
3354
  const data = yield GET(this.ctx, `${this.baseURL}/captcha/refresh`);
@@ -3312,13 +3361,64 @@ class Api {
3312
3361
  return data.img_data || "";
3313
3362
  });
3314
3363
  }
3364
+ captchaStatus() {
3365
+ return __async(this, null, function* () {
3366
+ const data = yield GET(this.ctx, `${this.baseURL}/captcha/status`);
3367
+ return data || { is_pass: false };
3368
+ });
3369
+ }
3315
3370
  }
3316
3371
  const CaptchaChecker = {
3317
- request(that, inputVal) {
3372
+ request(that, ctx, inputVal) {
3318
3373
  return new Api(that.ctx).captchaCheck(inputVal);
3319
3374
  },
3320
- body(that) {
3321
- const elem = createElement(`<span><img class="atk-captcha-img" src="${that.submitCaptchaImgData || ""}" alt="\u9A8C\u8BC1\u7801">\u6572\u5165\u9A8C\u8BC1\u7801\u7EE7\u7EED\uFF1A</span>`);
3375
+ body(that, ctx) {
3376
+ if (that.captchaConf.iframe) {
3377
+ const $iframeWrap = createElement(`<div class="atk-checker-iframe-wrap"></div>`);
3378
+ const $iframe = createElement(`<iframe class="atk-fade-in"></iframe>`);
3379
+ $iframe.style.display = "none";
3380
+ showLoading($iframeWrap, { transparentBg: true });
3381
+ $iframe.src = `${that.ctx.conf.server}/captcha/get`;
3382
+ $iframe.onload = () => {
3383
+ $iframe.style.display = "";
3384
+ hideLoading($iframeWrap);
3385
+ };
3386
+ $iframeWrap.append($iframe);
3387
+ const $closeBtn = createElement(`<div class="atk-close-btn"><i class="atk-icon atk-icon-close"></i></div>`);
3388
+ $iframeWrap.append($closeBtn);
3389
+ ctx.hideInteractInput();
3390
+ let stop = false;
3391
+ const sleep = (ms) => new Promise((resolve) => {
3392
+ window.setTimeout(() => {
3393
+ resolve(null);
3394
+ }, ms);
3395
+ });
3396
+ (function queryStatus() {
3397
+ return __async(this, null, function* () {
3398
+ yield sleep(1e3);
3399
+ if (stop)
3400
+ return;
3401
+ let isPass = false;
3402
+ try {
3403
+ const resp = yield new Api(that.ctx).captchaStatus();
3404
+ isPass = resp.is_pass;
3405
+ } catch (e) {
3406
+ isPass = false;
3407
+ }
3408
+ if (isPass) {
3409
+ ctx.triggerSuccess();
3410
+ } else {
3411
+ queryStatus();
3412
+ }
3413
+ });
3414
+ })();
3415
+ $closeBtn.onclick = () => {
3416
+ stop = true;
3417
+ ctx.cancel();
3418
+ };
3419
+ return $iframeWrap;
3420
+ }
3421
+ 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>`);
3322
3422
  elem.querySelector(".atk-captcha-img").onclick = () => {
3323
3423
  const imgEl = elem.querySelector(".atk-captcha-img");
3324
3424
  new Api(that.ctx).captchaGet().then((imgData) => {
@@ -3329,16 +3429,16 @@ const CaptchaChecker = {
3329
3429
  };
3330
3430
  return elem;
3331
3431
  },
3332
- onSuccess(that, data, inputVal, formEl) {
3333
- that.submitCaptchaVal = inputVal;
3432
+ onSuccess(that, ctx, data, inputVal, formEl) {
3433
+ that.captchaConf.val = inputVal;
3334
3434
  },
3335
- onError(that, err, inputVal, formEl) {
3435
+ onError(that, ctx, err, inputVal, formEl) {
3336
3436
  formEl.querySelector(".atk-captcha-img").click();
3337
3437
  }
3338
3438
  };
3339
3439
  const AdminChecker = {
3340
3440
  inputType: "password",
3341
- request(that, inputVal) {
3441
+ request(that, ctx, inputVal) {
3342
3442
  const data = {
3343
3443
  name: that.ctx.user.data.nick,
3344
3444
  email: that.ctx.user.data.email,
@@ -3346,33 +3446,33 @@ const AdminChecker = {
3346
3446
  };
3347
3447
  return new Api(that.ctx).login(data.name, data.email, data.password);
3348
3448
  },
3349
- body() {
3449
+ body(that, ctx) {
3350
3450
  return createElement("<span>\u6572\u5165\u5BC6\u7801\u6765\u9A8C\u8BC1\u7BA1\u7406\u5458\u8EAB\u4EFD\uFF1A</span>");
3351
3451
  },
3352
- onSuccess(that, userToken, inputVal, formEl) {
3452
+ onSuccess(that, ctx, userToken, inputVal, formEl) {
3353
3453
  that.ctx.user.data.isAdmin = true;
3354
3454
  that.ctx.user.data.token = userToken;
3355
3455
  that.ctx.user.save();
3356
3456
  that.ctx.trigger("user-changed", that.ctx.user.data);
3357
3457
  that.ctx.trigger("list-reload");
3358
3458
  },
3359
- onError(that, err, inputVal, formEl) {
3459
+ onError(that, ctx, err, inputVal, formEl) {
3360
3460
  }
3361
3461
  };
3362
3462
  class CheckerLauncher {
3363
3463
  constructor(ctx) {
3364
3464
  __publicField(this, "ctx");
3365
3465
  __publicField(this, "launched", []);
3366
- __publicField(this, "submitCaptchaVal");
3367
- __publicField(this, "submitCaptchaImgData");
3466
+ __publicField(this, "captchaConf", {});
3368
3467
  this.ctx = ctx;
3369
3468
  this.initEventBind();
3370
3469
  }
3371
3470
  initEventBind() {
3372
3471
  this.ctx.on("checker-captcha", (conf) => {
3373
- if (conf.imgData) {
3374
- this.submitCaptchaImgData = conf.imgData;
3375
- }
3472
+ if (conf.imgData)
3473
+ this.captchaConf.imgData = conf.imgData;
3474
+ if (conf.iframe)
3475
+ this.captchaConf.iframe = conf.iframe;
3376
3476
  this.fire(CaptchaChecker, conf);
3377
3477
  });
3378
3478
  this.ctx.on("checker-admin", (conf) => {
@@ -3386,12 +3486,31 @@ class CheckerLauncher {
3386
3486
  const layer = new Layer(this.ctx, `checker-${new Date().getTime()}`);
3387
3487
  layer.setMaskClickHide(false);
3388
3488
  layer.show();
3489
+ let hideInteractInput = false;
3490
+ const checkerCtx = {
3491
+ getLayer: () => layer,
3492
+ hideInteractInput: () => {
3493
+ hideInteractInput = true;
3494
+ },
3495
+ triggerSuccess: () => {
3496
+ this.close(checker, layer);
3497
+ if (checker.onSuccess)
3498
+ checker.onSuccess(this, checkerCtx, "", "", formEl);
3499
+ if (payload.onSuccess)
3500
+ payload.onSuccess("", dialog.$el);
3501
+ },
3502
+ cancel: () => {
3503
+ this.close(checker, layer);
3504
+ if (payload.onCancel)
3505
+ payload.onCancel();
3506
+ }
3507
+ };
3389
3508
  const formEl = createElement();
3390
- formEl.appendChild(checker.body(this));
3391
- const input = createElement(`<input id="check" type="${checker.inputType || "text"}" autocomplete="off" required placeholder="">`);
3392
- formEl.appendChild(input);
3393
- setTimeout(() => input.focus(), 80);
3394
- input.onkeyup = (evt) => {
3509
+ formEl.appendChild(checker.body(this, checkerCtx));
3510
+ const $input = createElement(`<input id="check" type="${checker.inputType || "text"}" autocomplete="off" required placeholder="">`);
3511
+ formEl.appendChild($input);
3512
+ setTimeout(() => $input.focus(), 80);
3513
+ $input.onkeyup = (evt) => {
3395
3514
  if (evt.key === "Enter" || evt.keyCode === 13) {
3396
3515
  evt.preventDefault();
3397
3516
  layer.getEl().querySelector('button[data-action="confirm"]').click();
@@ -3400,7 +3519,7 @@ class CheckerLauncher {
3400
3519
  let btnTextOrg;
3401
3520
  const dialog = new Dialog(formEl);
3402
3521
  dialog.setYes((btnEl) => {
3403
- const inputVal = input.value.trim();
3522
+ const inputVal = $input.value.trim();
3404
3523
  if (!btnTextOrg)
3405
3524
  btnTextOrg = btnEl.innerText;
3406
3525
  const btnTextSet = (btnText) => {
@@ -3412,18 +3531,18 @@ class CheckerLauncher {
3412
3531
  btnEl.classList.remove("error");
3413
3532
  };
3414
3533
  btnEl.innerText = "\u52A0\u8F7D\u4E2D...";
3415
- checker.request(this, inputVal).then((data) => {
3416
- this.done(checker, layer);
3534
+ checker.request(this, checkerCtx, inputVal).then((data) => {
3535
+ this.close(checker, layer);
3417
3536
  if (checker.onSuccess)
3418
- checker.onSuccess(this, data, inputVal, formEl);
3537
+ checker.onSuccess(this, checkerCtx, data, inputVal, formEl);
3419
3538
  if (payload.onSuccess)
3420
3539
  payload.onSuccess(inputVal, dialog.$el);
3421
3540
  }).catch((err) => {
3422
3541
  btnTextSet(String(err.msg || String(err)));
3423
3542
  if (checker.onError)
3424
- checker.onError(this, err, inputVal, formEl);
3543
+ checker.onError(this, checkerCtx, err, inputVal, formEl);
3425
3544
  const tf = setTimeout(() => btnTextRestore(), 3e3);
3426
- input.onfocus = () => {
3545
+ $input.onfocus = () => {
3427
3546
  btnTextRestore();
3428
3547
  clearTimeout(tf);
3429
3548
  };
@@ -3431,16 +3550,20 @@ class CheckerLauncher {
3431
3550
  return false;
3432
3551
  });
3433
3552
  dialog.setNo(() => {
3434
- this.done(checker, layer);
3553
+ this.close(checker, layer);
3435
3554
  if (payload.onCancel)
3436
3555
  payload.onCancel();
3437
3556
  return false;
3438
3557
  });
3558
+ if (hideInteractInput) {
3559
+ $input.style.display = "none";
3560
+ dialog.$el.querySelector(".atk-layer-dialog-actions").style.display = "none";
3561
+ }
3439
3562
  layer.getEl().append(dialog.$el);
3440
3563
  if (payload.onMount)
3441
3564
  payload.onMount(dialog.$el);
3442
3565
  }
3443
- done(checker, layer) {
3566
+ close(checker, layer) {
3444
3567
  layer.disposeNow();
3445
3568
  this.launched = this.launched.filter((c) => c !== checker);
3446
3569
  }
@@ -3723,6 +3846,7 @@ class Editor extends Component {
3723
3846
  __publicField(this, "$plugWrap");
3724
3847
  __publicField(this, "$bottom");
3725
3848
  __publicField(this, "$plugBtnWrap");
3849
+ __publicField(this, "$imgUploadBtn");
3726
3850
  __publicField(this, "$submitBtn");
3727
3851
  __publicField(this, "$notifyWrap");
3728
3852
  __publicField(this, "replyComment", null);
@@ -3733,6 +3857,7 @@ class Editor extends Component {
3733
3857
  abortFunc: null
3734
3858
  });
3735
3859
  __publicField(this, "openedPlugName", null);
3860
+ __publicField(this, "allowImgExts", ["png", "jpg", "jpeg", "gif", "bmp", "svg", "webp"]);
3736
3861
  this.$el = createElement(EditorHTML);
3737
3862
  this.$header = this.$el.querySelector(".atk-header");
3738
3863
  this.$textareaWrap = this.$el.querySelector(".atk-textarea-wrap");
@@ -3756,6 +3881,7 @@ class Editor extends Component {
3756
3881
  this.ctx.on("editor-notify", (f) => this.showNotify(f.msg, f.type));
3757
3882
  this.ctx.on("editor-travel", ($el) => this.travel($el));
3758
3883
  this.ctx.on("editor-travel-back", () => this.travelBack());
3884
+ this.ctx.on("conf-updated", () => this.refreshUploadBtn());
3759
3885
  }
3760
3886
  get user() {
3761
3887
  return this.ctx.user;
@@ -3854,7 +3980,7 @@ class Editor extends Component {
3854
3980
  this.openedPlugName = null;
3855
3981
  this.$plugBtnWrap.innerHTML = "";
3856
3982
  this.LOADABLE_PLUG_LIST.forEach((PlugObj) => {
3857
- const btnElem = createElement(`<span class="atk-plug-btn">${PlugObj.BtnHTML}</span>`);
3983
+ const btnElem = createElement(`<span class="atk-plug-btn" data-plug-name="${PlugObj.Name}">${PlugObj.BtnHTML}</span>`);
3858
3984
  this.$plugBtnWrap.appendChild(btnElem);
3859
3985
  btnElem.addEventListener("click", () => {
3860
3986
  let plug = this.plugList[PlugObj.Name];
@@ -3890,12 +4016,98 @@ class Editor extends Component {
3890
4016
  btnElem.classList.add("active");
3891
4017
  });
3892
4018
  });
4019
+ this.initImgUpload();
3893
4020
  }
3894
4021
  closePlug() {
3895
4022
  this.$plugWrap.innerHTML = "";
3896
4023
  this.$plugWrap.style.display = "none";
3897
4024
  this.openedPlugName = null;
3898
4025
  }
4026
+ initImgUpload() {
4027
+ this.$imgUploadBtn = createElement(`<span class="atk-plug-btn">\u56FE\u7247</span>`);
4028
+ this.$plugBtnWrap.querySelector('[data-plug-name="preview"]').before(this.$imgUploadBtn);
4029
+ this.$imgUploadBtn.onclick = () => {
4030
+ const $input = document.createElement("input");
4031
+ $input.type = "file";
4032
+ $input.accept = this.allowImgExts.map((o) => `.${o}`).join(",");
4033
+ $input.onchange = () => {
4034
+ (() => __async(this, null, function* () {
4035
+ if (!$input.files || $input.files.length === 0)
4036
+ return;
4037
+ const file = $input.files[0];
4038
+ this.uploadImg(file);
4039
+ }))();
4040
+ };
4041
+ $input.click();
4042
+ };
4043
+ const uploadFromFileList = (files) => {
4044
+ if (!files)
4045
+ return;
4046
+ for (let i = 0; i < files.length; i++) {
4047
+ const file = files[i];
4048
+ this.uploadImg(file);
4049
+ }
4050
+ };
4051
+ this.$textarea.addEventListener("dragover", (evt) => {
4052
+ evt.stopPropagation();
4053
+ evt.preventDefault();
4054
+ });
4055
+ this.$textarea.addEventListener("drop", (evt) => {
4056
+ var _a;
4057
+ const files = (_a = evt.dataTransfer) == null ? void 0 : _a.files;
4058
+ if (files == null ? void 0 : files.length) {
4059
+ evt.preventDefault();
4060
+ uploadFromFileList(files);
4061
+ }
4062
+ });
4063
+ this.$textarea.addEventListener("paste", (evt) => {
4064
+ var _a;
4065
+ const files = (_a = evt.clipboardData) == null ? void 0 : _a.files;
4066
+ if (files == null ? void 0 : files.length) {
4067
+ evt.preventDefault();
4068
+ uploadFromFileList(files);
4069
+ }
4070
+ });
4071
+ }
4072
+ refreshUploadBtn() {
4073
+ if (!this.$imgUploadBtn)
4074
+ return;
4075
+ if (!this.ctx.conf.imgUpload) {
4076
+ this.$imgUploadBtn.setAttribute("atk-only-admin-show", "");
4077
+ this.ctx.trigger("check-admin-show-el");
4078
+ }
4079
+ }
4080
+ uploadImg(file) {
4081
+ return __async(this, null, function* () {
4082
+ const fileExt = /[^.]+$/.exec(file.name);
4083
+ if (!fileExt || !this.allowImgExts.includes(fileExt[0]))
4084
+ return;
4085
+ if (!this.ctx.user.checkHasBasicUserInfo()) {
4086
+ this.showNotify("\u586B\u5165\u4F60\u7684\u540D\u5B57\u90AE\u7BB1\u624D\u80FD\u4E0A\u4F20\u54E6", "w");
4087
+ return;
4088
+ }
4089
+ let insertPrefix = "\n";
4090
+ if (this.$textarea.value.trim() === "")
4091
+ insertPrefix = "";
4092
+ const uploadPlaceholderTxt = `${insertPrefix}![](Uploading ${file.name}...)`;
4093
+ this.insertContent(uploadPlaceholderTxt);
4094
+ let resp;
4095
+ try {
4096
+ resp = yield new Api(this.ctx).imgUpload(file);
4097
+ } catch (err) {
4098
+ console.error(err);
4099
+ this.showNotify(`\u56FE\u7247\u4E0A\u4F20\u5931\u8D25\uFF0C${err.msg}`, "e");
4100
+ }
4101
+ if (!!resp && resp.img_url) {
4102
+ let imgURL = resp.img_url;
4103
+ if (!isValidURL(imgURL))
4104
+ imgURL = getURLBasedOnApi(this.ctx, imgURL);
4105
+ this.setContent(this.$textarea.value.replace(uploadPlaceholderTxt, `${insertPrefix}![](${imgURL})`));
4106
+ } else {
4107
+ this.setContent(this.$textarea.value.replace(uploadPlaceholderTxt, ""));
4108
+ }
4109
+ });
4110
+ }
3899
4111
  insertContent(val) {
3900
4112
  if (document.selection) {
3901
4113
  this.$textarea.focus();
@@ -3927,7 +4139,7 @@ class Editor extends Component {
3927
4139
  this.setContent("");
3928
4140
  this.cancelReply();
3929
4141
  }
3930
- getContent() {
4142
+ getFinalContent() {
3931
4143
  let content = this.getContentOriginal();
3932
4144
  if (this.plugList && this.plugList.emoticons) {
3933
4145
  const emoticonsPlug2 = this.plugList.emoticons;
@@ -3939,7 +4151,7 @@ class Editor extends Component {
3939
4151
  return this.$textarea.value || "";
3940
4152
  }
3941
4153
  getContentMarked() {
3942
- return marked(this.ctx, this.getContent());
4154
+ return marked(this.ctx, this.getFinalContent());
3943
4155
  }
3944
4156
  initBottomPart() {
3945
4157
  this.initReply();
@@ -3988,7 +4200,7 @@ class Editor extends Component {
3988
4200
  }
3989
4201
  submit() {
3990
4202
  return __async(this, null, function* () {
3991
- if (this.getContent().trim() === "") {
4203
+ if (this.getFinalContent().trim() === "") {
3992
4204
  this.$textarea.focus();
3993
4205
  return;
3994
4206
  }
@@ -3996,7 +4208,7 @@ class Editor extends Component {
3996
4208
  showLoading(this.$el);
3997
4209
  try {
3998
4210
  const nComment = yield new Api(this.ctx).add({
3999
- content: this.getContent(),
4211
+ content: this.getFinalContent(),
4000
4212
  nick: this.user.data.nick,
4001
4213
  email: this.user.data.email,
4002
4214
  link: this.user.data.link,
@@ -5071,6 +5283,7 @@ class ReadMoreBtn {
5071
5283
  this.hide();
5072
5284
  }
5073
5285
  }
5286
+ const backendMinVersion = "2.1.1";
5074
5287
  class ListLite extends Component {
5075
5288
  constructor(ctx) {
5076
5289
  super(ctx);
@@ -5152,17 +5365,25 @@ class ListLite extends Component {
5152
5365
  });
5153
5366
  }
5154
5367
  onLoad(data, offset) {
5368
+ var _a, _b;
5155
5369
  if (this.pageMode === "pagination") {
5156
5370
  this.clearAllComments();
5157
5371
  }
5158
5372
  this.data = data;
5159
- if (this.ctx.conf.versionCheck && this.versionCheck(data.api_version))
5373
+ const feMinVersion = ((_a = data.api_version) == null ? void 0 : _a.fe_min_version) || "0.0.0";
5374
+ if (this.ctx.conf.versionCheck && this.versionCheck("\u524D\u7AEF", feMinVersion, "2.2.0"))
5375
+ return;
5376
+ if (this.ctx.conf.versionCheck && this.versionCheck("\u540E\u7AEF", backendMinVersion, (_b = data.api_version) == null ? void 0 : _b.version))
5160
5377
  return;
5378
+ if (data.conf && typeof data.conf.img_upload === "boolean") {
5379
+ this.ctx.conf.imgUpload = data.conf.img_upload;
5380
+ }
5161
5381
  this.importComments(data.comments);
5162
5382
  this.refreshPagination(offset, this.flatMode ? data.total : data.total_roots);
5163
5383
  this.refreshUI();
5164
5384
  this.ctx.trigger("unread-update", { notifies: data.unread || [] });
5165
5385
  this.ctx.trigger("comments-loaded");
5386
+ this.ctx.trigger("conf-updated");
5166
5387
  if (this.onAfterLoad)
5167
5388
  this.onAfterLoad(data);
5168
5389
  }
@@ -5427,19 +5648,19 @@ class ListLite extends Component {
5427
5648
  });
5428
5649
  }
5429
5650
  }
5430
- versionCheck(versionData) {
5431
- const needVersion = (versionData == null ? void 0 : versionData.fe_min_version) || "0.0.0";
5432
- const needUpdate = versionCompare(needVersion, "2.1.7") === 1;
5651
+ versionCheck(name, needVersion, curtVersion) {
5652
+ const needUpdate = versionCompare(needVersion, curtVersion) === 1;
5433
5653
  if (needUpdate) {
5434
- const errEl = createElement(`<div>\u524D\u7AEF Artalk \u7248\u672C\u5DF2\u8FC7\u65F6\uFF0C\u8BF7\u66F4\u65B0\u4EE5\u83B7\u5F97\u5B8C\u6574\u4F53\u9A8C<br/>\u82E5\u60A8\u662F\u7AD9\u70B9\u7BA1\u7406\u5458\uFF0C\u8BF7\u524D\u5F80 \u201C<a href="https://artalk.js.org/" target="_blank">\u5B98\u65B9\u6587\u6863</a>\u201D \u83B7\u53D6\u5E2E\u52A9<br/><br/><span style="color: var(--at-color-meta);">\u524D\u7AEF\u7248\u672C ${"2.1.7"}\uFF0C\u9700\u6C42\u7248\u672C >= ${needVersion}</span><br/><br/></div>`);
5654
+ 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>`);
5435
5655
  const ignoreBtn = createElement('<span style="cursor:pointer;">\u5FFD\u7565</span>');
5436
5656
  ignoreBtn.onclick = () => {
5437
- setError(this.ctx, null);
5657
+ setError(this.$el.parentElement, null);
5438
5658
  this.ctx.conf.versionCheck = false;
5659
+ this.ctx.trigger("conf-updated");
5439
5660
  this.fetchComments(0);
5440
5661
  };
5441
5662
  errEl.append(ignoreBtn);
5442
- setError(this.ctx, errEl, '<span class="atk-warn-title">Artalk Warn</span>');
5663
+ setError(this.$el.parentElement, errEl, '<span class="atk-warn-title">Artalk Warn</span>');
5443
5664
  }
5444
5665
  return needUpdate;
5445
5666
  }
@@ -5469,8 +5690,10 @@ class List extends ListLite {
5469
5690
  this.repositionAt = this.$el;
5470
5691
  this.initListActionBtn();
5471
5692
  this.$commentCount = this.$el.querySelector(".atk-comment-count");
5472
- this.initDropdown();
5473
- this.$el.querySelector(".atk-copyright").innerHTML = `Powered By <a href="https://artalk.js.org" target="_blank" title="Artalk v${"2.1.7"}">Artalk</a>`;
5693
+ if (this.ctx.conf.listSort) {
5694
+ this.initDropdown();
5695
+ }
5696
+ this.$el.querySelector(".atk-copyright").innerHTML = `Powered By <a href="https://artalk.js.org" target="_blank" title="Artalk v${"2.2.0"}">Artalk</a>`;
5474
5697
  this.ctx.on("list-reload", () => this.fetchComments(0));
5475
5698
  this.ctx.on("list-refresh-ui", () => this.refreshUI());
5476
5699
  this.ctx.on("list-import", (data) => this.importComments(data));
@@ -5576,7 +5799,7 @@ class List extends ListLite {
5576
5799
  initDropdown() {
5577
5800
  this.$dropdownWrap = this.$commentCount;
5578
5801
  this.$commentCount.classList.add("atk-dropdown-wrap");
5579
- this.$commentsWrap.append(createElement(`<span class="atk-arrow-down-icon"></span>`));
5802
+ this.$dropdownWrap.append(createElement(`<span class="atk-arrow-down-icon"></span>`));
5580
5803
  const reloadUseParamsEditor = (func) => {
5581
5804
  this.paramsEditor = (p) => {
5582
5805
  func(p);
@@ -5676,11 +5899,31 @@ class SidebarLayer extends Component {
5676
5899
  this.$el.style.transform = "translate(0, 0)";
5677
5900
  }, 20);
5678
5901
  if (this.firstShow) {
5902
+ if (this.ctx.user.data.isAdmin) {
5903
+ const resp = yield new Api(this.ctx).loginStatus();
5904
+ if (!resp.is_login) {
5905
+ yield new Promise((resolve, reject) => {
5906
+ this.ctx.trigger("checker-admin", {
5907
+ onSuccess: () => {
5908
+ resolve(null);
5909
+ setTimeout(() => {
5910
+ this.show();
5911
+ }, 500);
5912
+ },
5913
+ onCancel: () => {
5914
+ var _a;
5915
+ (_a = this.layer) == null ? void 0 : _a.hide();
5916
+ }
5917
+ });
5918
+ });
5919
+ }
5920
+ }
5679
5921
  this.$iframeWrap.innerHTML = "";
5680
5922
  this.$iframe = createElement("<iframe></iframe>");
5681
- const baseURL = `${this.conf.server.replace(/\/$/, "")}/../sidebar/`;
5923
+ const baseURL = getURLBasedOnApi(this.ctx, "sidebar/");
5682
5924
  const userData = encodeURIComponent(JSON.stringify(this.ctx.user.data));
5683
- this.iframeLoad(`${baseURL}?pageKey=${encodeURIComponent(this.conf.pageKey)}&site=${encodeURIComponent(this.conf.site || "")}&user=${userData}${this.conf.darkMode ? `&darkMode=1` : ``}`);
5925
+ const location = window.location;
5926
+ this.iframeLoad(`${baseURL}?pageKey=${encodeURIComponent(this.conf.pageKey)}&site=${encodeURIComponent(this.conf.site || "")}&user=${userData}&referer=${encodeURIComponent(`${location.protocol}//${location.host}${location.pathname}`)}${this.conf.darkMode ? `&darkMode=1` : ``}`);
5684
5927
  this.$iframeWrap.append(this.$iframe);
5685
5928
  this.firstShow = false;
5686
5929
  } else {
@@ -5760,6 +6003,7 @@ const _Artalk = class {
5760
6003
  this.$root.innerHTML = "";
5761
6004
  this.initDarkMode();
5762
6005
  this.checkerLauncher = new CheckerLauncher(this.ctx);
6006
+ initMarked(this.ctx);
5763
6007
  this.editor = new Editor(this.ctx);
5764
6008
  this.$root.appendChild(this.editor.$el);
5765
6009
  this.list = new List(this.ctx);
@@ -5769,6 +6013,11 @@ const _Artalk = class {
5769
6013
  this.list.fetchComments(0);
5770
6014
  this.initEventBind();
5771
6015
  this.initPV();
6016
+ _Artalk.Plugins.forEach((plugin) => {
6017
+ if (typeof plugin === "function") {
6018
+ plugin(this.ctx);
6019
+ }
6020
+ });
5772
6021
  }
5773
6022
  initEventBind() {
5774
6023
  window.addEventListener("hashchange", () => {
@@ -5816,6 +6065,7 @@ const _Artalk = class {
5816
6065
  }
5817
6066
  setDarkMode(darkMode) {
5818
6067
  this.ctx.conf.darkMode = darkMode;
6068
+ this.ctx.trigger("conf-updated");
5819
6069
  this.initDarkMode();
5820
6070
  }
5821
6071
  initPV() {
@@ -5838,7 +6088,11 @@ const _Artalk = class {
5838
6088
  trigger(name, payload) {
5839
6089
  this.ctx.trigger(name, payload, "external");
5840
6090
  }
6091
+ static Use(plugin) {
6092
+ this.Plugins.push(plugin);
6093
+ }
5841
6094
  };
5842
6095
  let Artalk = _Artalk;
5843
6096
  __publicField(Artalk, "defaults", defaults$3);
6097
+ __publicField(Artalk, "Plugins", []);
5844
6098
  export { Artalk as default };