File size: 3,356 Bytes
4463908
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f503bad
 
 
 
 
 
 
4463908
 
f503bad
 
 
 
795fd69
f503bad
 
 
 
4463908
 
f503bad
 
4463908
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/* ================================================================
   TRTOI 就地編輯(inline editing)
   登入後:按「編輯此頁」→ 頁面上的文字直接可改 → 按「儲存」寫回。
   每個可編輯元素帶 data-e="JSON 路徑";data-html="1" 者保留 HTML。
   ================================================================ */
(function () {
  "use strict";

  var nodes = [].slice.call(document.querySelectorAll("[data-e]"));
  if (!nodes.length) return;

  var fab = document.getElementById("editToggle");
  var bar = document.getElementById("editbar");
  var saveBtn = document.getElementById("saveBtn");
  var cancelBtn = document.getElementById("cancelBtn");
  var editing = false;
  var backup = {};

  function readVal(n) {
    return n.getAttribute("data-html") ? n.innerHTML.trim() : n.textContent.trim();
  }
  function writeVal(n, v) {
    if (n.getAttribute("data-html")) n.innerHTML = v; else n.textContent = v;
  }

  function enter() {
    editing = true;
    document.body.classList.add("edit-mode");
    nodes.forEach(function (n) {
      backup[n.getAttribute("data-e")] = readVal(n);
      n.setAttribute("contenteditable", "true");
    });
    bar.classList.add("show");
    fab.style.display = "none";
  }

  function leave() {
    editing = false;
    document.body.classList.remove("edit-mode");
    nodes.forEach(function (n) { n.removeAttribute("contenteditable"); });
    bar.classList.remove("show");
    fab.style.display = "";
  }

  // 編輯時避免點到連結就跳走
  document.addEventListener("click", function (e) {
    if (!editing) return;
    var a = e.target.closest ? e.target.closest("a") : null;
    if (a) e.preventDefault();
  }, true);

  fab.addEventListener("click", enter);

  cancelBtn.addEventListener("click", function () {
    nodes.forEach(function (n) {
      var k = n.getAttribute("data-e");
      if (k in backup) writeVal(n, backup[k]);
    });
    leave();
  });

  saveBtn.addEventListener("click", function () {
    // 依目標分頁分組:預設寫回目前頁面(window.SLUG),
    // 帶 data-slug 者寫回指定目標(如側欄註記 → 站台層級 "site")。
    var groups = {};
    nodes.forEach(function (n) {
      var slug = n.getAttribute("data-slug") || window.SLUG;
      (groups[slug] || (groups[slug] = {}))[n.getAttribute("data-e")] = readVal(n);
    });
    saveBtn.disabled = true;
    saveBtn.textContent = "儲存中…";
    Promise.all(Object.keys(groups).map(function (slug) {
      return fetch("/admin/save-inline/" + slug, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ patches: groups[slug], lang: window.LANG })
      }).then(function (r) { return r.json(); });
    }))
      .then(function (results) {
        if (results.every(function (res) { return res && res.ok; })) {
          location.reload();
        } else {
          var bad = results.filter(function (r) { return !(r && r.ok); })[0];
          alert("儲存失敗:" + ((bad && bad.error) || "未知錯誤"));
          saveBtn.disabled = false;
          saveBtn.textContent = "儲存";
        }
      })
      .catch(function () {
        alert("儲存失敗(無法連線)");
        saveBtn.disabled = false;
        saveBtn.textContent = "儲存";
      });
  });
})();