Skip to content
quarry Artifact [c50e9b0e77]
Login

Artifact [c50e9b0e77]

Artifact c50e9b0e776f9cce0a0fe301a852ec51fe8172fbc98fe1644998706f81729848:


// meld.js — draws what a conflicting landing's three panes cannot draw
// for themselves: the bands joining each marked block in ours or theirs
// to its place in the result, the overview strip down the right edge,
// and stepping from one conflict to the next. The panes are complete
// without it; the server already marked every block (meld.go).
(function () {
  "use strict";

  var colour = { cf: "var(--meld-cf)", chg: "var(--meld-chg)", ins: "var(--meld-ins)" };
  function kindOf(el) {
    if (el.classList.contains("cf")) { return "cf"; }
    if (el.classList.contains("chg")) { return "chg"; }
    if (el.classList.contains("ins")) { return "ins"; }
    return "";
  }

  function mount(m) {
    var file = m.closest(".meld-file");
    var pane = function (p) { return m.querySelector('[data-pane="' + p + '"]'); };
    var seg = function (p, id) { return pane(p).querySelector('.seg[data-id="' + id + '"]'); };
    var conflicts = Array.prototype.slice.call(pane("M").querySelectorAll(".seg.cf"));
    var cur = -1;

    function bands() {
      ["L", "R"].forEach(function (side) {
        var g = m.querySelector('[data-gut="' + side + '"]'), svg = g.querySelector("svg");
        if (getComputedStyle(g).display === "none") { svg.innerHTML = ""; return; }
        var gr = g.getBoundingClientRect(), w = gr.width, h = w / 2, out = "";
        pane(side).querySelectorAll(".seg.cf, .seg.chg, .seg.ins").forEach(function (a) {
          var mid = seg("M", a.dataset.id);
          if (!mid) { return; }
          var ar = a.getBoundingClientRect(), mr = mid.getBoundingClientRect();
          var x0 = side === "L" ? 0 : w, x1 = side === "L" ? w : 0;
          var ya = ar.top - gr.top, yb = Math.max(ar.bottom - gr.top, ya + 1);
          var ma = mr.top - gr.top, mb = Math.max(mr.bottom - gr.top, ma + 1);
          var c = colour[kindOf(a)];
          out += '<path d="M' + x0 + "," + ya + " C" + h + "," + ya + " " + h + "," + ma + " " + x1 + "," + ma +
            " L" + x1 + "," + mb + " C" + h + "," + mb + " " + h + "," + yb + " " + x0 + "," + yb +
            ' Z" fill="' + c + '" fill-opacity=".28" stroke="' + c + '" stroke-opacity=".7"/>';
        });
        svg.innerHTML = out;
      });
      overview();
    }

    // The strip maps the whole result: each marked block at its place,
    // the current conflict outlined; a mark jumps there.
    function overview() {
      var o = m.querySelector(".ovr");
      if (!o || getComputedStyle(o).display === "none") { return; }
      o.innerHTML = "";
      var inner = pane("M").querySelector(".inner"), ir = inner.getBoundingClientRect();
      var head = pane("M").querySelector(".phead").getBoundingClientRect().height;
      var span = o.getBoundingClientRect().height - head;
      if (ir.height <= 0 || span <= 0) { return; }
      pane("M").querySelectorAll(".seg.cf, .seg.chg, .seg.ins").forEach(function (s) {
        var r = s.getBoundingClientRect(), i = document.createElement("i");
        i.style.top = (head + (r.top - ir.top) / ir.height * span) + "px";
        i.style.height = Math.max(3, r.height / ir.height * span) + "px";
        i.style.background = colour[kindOf(s)];
        if (conflicts[cur] === s) { i.className = "cur"; }
        i.title = kindOf(s) === "cf" ? "conflict" : "change";
        i.addEventListener("click", function () {
          var k = conflicts.indexOf(s);
          if (k >= 0) { go(k); } else { s.scrollIntoView({ block: "center", behavior: "smooth" }); }
        });
        o.appendChild(i);
      });
    }

    function go(k) {
      if (!conflicts.length) { return; }
      cur = (k + conflicts.length) % conflicts.length;
      conflicts.forEach(function (c, i) { c.classList.toggle("cur", i === cur); });
      var pos = file.querySelector("[data-meld-pos]");
      if (pos) { pos.textContent = "conflict " + (cur + 1) + " of " + conflicts.length; }
      conflicts[cur].scrollIntoView({ block: "center", behavior: "smooth" });
      overview();
    }
    file.querySelectorAll("[data-meld-step]").forEach(function (b) {
      b.addEventListener("click", function () { go(cur < 0 ? (b.dataset.meldStep > 0 ? 0 : -1) : cur + Number(b.dataset.meldStep)); });
    });

    // A long line scrolls its own pane sideways; the conflict label
    // stays as wide as the pane it sits in.
    function fit() {
      var w = pane("M").clientWidth;
      pane("M").querySelectorAll(".tagline").forEach(function (t) { t.style.width = w + "px"; });
      bands();
    }
    ["L", "M", "R"].forEach(function (p) { pane(p).addEventListener("scroll", bands, { passive: true }); });
    window.addEventListener("resize", fit);
    if (document.fonts && document.fonts.ready) { document.fonts.ready.then(fit); }
    fit();
  }

  document.querySelectorAll("[data-meld]").forEach(mount);
})();