MediaWiki:Compare.js

From Psalms: Layer by Layer
Jump to: navigation, search

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
function fetchRenderedHTML(title, callback) {
  var url = mw.util.wikiScript() + '?title=' + encodeURIComponent(title) + '&action=render';

  fetch(url)
    .then(function (response) {
      if (!response.ok) {
        throw new Error("Network response was not ok");
      }
      return response.text();
    })
    .then(function (html) {
      console.log("Fetched HTML snippet:", html.slice(0, 500)); // limit log size
      var doc = document.implementation.createHTMLDocument("Comparison");
      doc.body.innerHTML = html;
      callback(doc);
    })
    .catch(function (err) {
      console.error("Error fetching page: " + title, err);
      document.getElementById("compare-output").textContent =
        "❌ Error fetching page: " + title + "\n" + err;
    });
}

function runTableComparison() {
  var pageA = document.getElementById("input_1").value.trim();
  var selectorA = document.getElementById("input_2").value.trim();
  var pageB = document.getElementById("input_3").value.trim();
  var selectorB = document.getElementById("input_4").value.trim();
  var output = document.getElementById("compare-output");
  output.innerHTML = "Running comparison…";

  console.log("Page A:", pageA, "Selector A:", selectorA);
  console.log("Page B:", pageB, "Selector B:", selectorB);

  fetchRenderedHTML(pageA, function(domA) {
    var tableA = domA.querySelector(selectorA);
    if (!tableA) {
      output.innerHTML = "❌ Table A not found using selector: " + escapeHTML(selectorA);
      return;
    }

    fetchRenderedHTML(pageB, function(domB) {
      var tableB = domB.querySelector(selectorB);
      if (!tableB) {
        output.innerHTML = "❌ Table B not found using selector: " + escapeHTML(selectorB);
        return;
      }

      compareTables(tableA, tableB, output);
    });
  });
}

function fetchRenderedHTML(title, callback) {
  var apiUrl = mw.util.wikiScript("api");
  var params = {
    action: "parse",
    page: title,
    format: "json",
    prop: "text"
  };

  new mw.Api().get(params).done(function(data) {
    if (data && data.parse && data.parse.text) {
      var html = data.parse.text["*"];
      var dom = document.createElement("div");
      dom.innerHTML = html;
      callback(dom);
    } else {
      alert("Error fetching page: " + title);
    }
  }).fail(function(err) {
    alert("API error fetching: " + title);
    console.error(err);
  });
}

function compareTables(tableA, tableB, output) {
  var rowsA = tableA.querySelectorAll("tr");
  var rowsB = tableB.querySelectorAll("tr");
  var maxRows = Math.max(rowsA.length, rowsB.length);
  var diffs = [];

  for (var i = 0; i < maxRows; i++) {
    var rowA = rowsA[i];
    var rowB = rowsB[i];

    if (!rowA || !rowB) {
      diffs.push("Row " + i + " missing in " + (!rowA ? "A" : "B"));
      continue;
    }

    var cellsA = rowA.querySelectorAll("td, th");
    var cellsB = rowB.querySelectorAll("td, th");
    var maxCells = Math.max(cellsA.length, cellsB.length);

    for (var j = 0; j < maxCells; j++) {
      var cellA = cellsA[j];
      var cellB = cellsB[j];

      if (!cellA || !cellB) {
        diffs.push("Row " + i + " Cell " + j + " missing in " + (!cellA ? "A" : "B"));
        continue;
      }

      var styleA = window.getComputedStyle(cellA);
      var styleB = window.getComputedStyle(cellB);

      ["backgroundColor", "color", "fontWeight", "textAlign"].forEach(function(prop) {
        if (styleA[prop] !== styleB[prop]) {
          var visualA = styleA[prop];
          var visualB = styleB[prop];

          if (prop === "backgroundColor" || prop === "color") {
            visualA += ' ' + createColorSwatch(styleA[prop]);
            visualB += ' ' + createColorSwatch(styleB[prop]);
          }

          diffs.push(
            "Row " + i + " Cell " + j + " style '" + prop + "' mismatch:<br>" +
            "<strong>Text A:</strong> " + escapeHTML(cellA.textContent.trim()) + "<br>" +
            "<strong>Style A:</strong> " + visualA + "<br>" +
            "<strong>Text B:</strong> " + escapeHTML(cellB.textContent.trim()) + "<br>" +
            "<strong>Style B:</strong> " + visualB
          );
        }
      });
    }
  }

  output.innerHTML = diffs.length
    ? "⚠️ Differences found:<br><br>" + diffs.join("<br><br>")
    : "✅ Tables match.";
}

function createColorSwatch(color) {
  return '<span style="display:inline-block;width:12px;height:12px;border:1px solid #ccc;margin-left:4px;vertical-align:middle;background:' + color + ';"></span>';
}

function escapeHTML(str) {
  return str.replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;");
}