PC Gaming Shelter
An archive dedicated to preserving PC Gaming history and more

MediaWiki:Datatables.js: Difference between revisions

From PC Gaming Shelter
No edit summary
No edit summary
Line 283: Line 283:
           {
           {
             title: "Developer",
             title: "Developer",
             visible: false,
             visible: true,
             searchPanes: {
             searchPanes: {
               show: true,
               show: true,
Line 290: Line 290:
           {
           {
             title: "Publisher",
             title: "Publisher",
             visible: false,
             visible: true,
             searchPanes: {
             searchPanes: {
               show: true,
               show: true,
Line 297: Line 297:
           {
           {
             title: "Genre",
             title: "Genre",
             visible: false,
             visible: true,
             searchPanes: {
             searchPanes: {
               show: true,
               show: true,
Line 304: Line 304:
           {
           {
             title: "Platform",
             title: "Platform",
             visible: false,
             visible: true,
             searchPanes: {
             searchPanes: {
               show: true,
               show: true,
Line 311: Line 311:
           {
           {
             title: "Mode",
             title: "Mode",
             visible: false,
             visible: true,
             render: {
             render: {
               display: function (data) {
               display: function (data) {
Line 327: Line 327:
           {
           {
             title: "Released",
             title: "Released",
             visible: false,
             visible: true,
             searchPanes: {
             searchPanes: {
               show: true,
               show: true,
Line 340: Line 340:
               cascadePanes: true,
               cascadePanes: true,
               initCollapsed: true,
               initCollapsed: true,
              layout: "columns-4",
               columns: [1, 2, 3, 4, 5, 6],
               columns: [1, 2, 3, 4, 5, 6],
             },
             },

Revision as of 09:07, 17 June 2026

var selectors = [
	"#games > table"
];

var found = false;
for (var i = 0; i < selectors.length; i++) {
  if (document.querySelector(selectors[i])) {
    found = true;
    break;
  }
}

/* ------------- DataTables Loader and initialisation ---------------- */

if (found === true) {
  /* Load CSS. */
  const dtsource =
    "https://cdn.datatables.net/v/dt/moment-2.29.4/dt-2.3.7/b-3.2.6/b-colvis-3.2.6/b-html5-3.2.6/b-print-3.2.6/cc-1.2.0/date-1.6.3/fh-4.0.5/r-3.0.8/sc-2.4.3/sb-1.8.4/sp-2.3.5/sl-3.1.3/sr-1.4.3/datatables.min.css";

  if (!document.querySelector('link[href*="datatables.min.css"]')) {
    $("<link/>", { rel: "stylesheet", href: dtsource }).appendTo("head");
  }

  if (!document.querySelector("#datatable-styles")) {
    $("<style/>", {
      id: "datatable-styles",
      text: `
        table.dataTable > tbody > tr > td {
          padding-top: 0.25rem;
          padding-bottom: 0.25rem;
        }

        .dt-loading {
          display: block !important;
          min-height: 4rem;
          position: relative;
        }

        .dt-loading > * {
          display: none !important;
        }

        .dt-loading::before {
          animation: dt-loading 0.8s linear infinite;
          border: 0.25rem solid #dee2e6;
          border-radius: 50%;
          border-top-color: #6c757d;
          content: "";
          height: 2rem;
          left: calc(50% - 1rem);
          position: absolute;
          top: 1rem;
          width: 2rem;
        }

        @keyframes dt-loading {
          to {
            transform: rotate(360deg);
          }
        }

        .dtsp-searchPane {
          border: 1px solid #bbb;
        }

        .dtsp-paneButton.clearButton {
          font-size: 1.5rem;
          line-height: 1;
        }

        .dtsp-paneButton.clearButton:not(:disabled) {
          color: var(--bs-danger, #dc3545);
        }
      `,
    }).appendTo("head");
  }

  $("#games > table").addClass("dt-loading").attr("aria-busy", "true");

  /* Load JavaScript. */
  $.when(
    mw.loader.getScript(
      "https://cdn.datatables.net/v/dt/moment-2.29.4/dt-2.3.7/b-3.2.6/b-colvis-3.2.6/b-html5-3.2.6/b-print-3.2.6/cc-1.2.0/date-1.6.3/fh-4.0.5/r-3.0.8/sc-2.4.3/sb-1.8.4/sp-2.3.5/sl-3.1.3/sr-1.4.3/datatables.min.js",
    ),
  ).then(
    () => {
      window.datatablesLoaded = true;
      initDataTable();
    },
    (e) => mw.log.error(e.message),
  );
}

/* ------------- DataTables configuration ---------------------------- */

function initDataTable() {
  function getDataTableColumnCount(table) {
    var headerRow =
      table.tHead && table.tHead.rows.length
        ? table.tHead.rows[table.tHead.rows.length - 1]
        : null;

    if (headerRow) {
      return headerRow.cells.length;
    }

    return table.rows.length ? table.rows[0].cells.length : 0;
  }

  function normaliseDataTableRows(table, expectedColumnCount) {
    $("tbody tr", table).each(function () {
      var row = this;
      var cellCount = row.cells.length;

      if (cellCount === 1 && row.cells[0].colSpan > 1) {
        return;
      }

      while (cellCount < expectedColumnCount) {
        row.appendChild(document.createElement("td"));
        cellCount++;
      }
    });
  }

  function waitForTableToSettle(table, callback) {
    var settleTimer;
    var timeoutTimer;
    var observer;
    var settled = false;

    function finish() {
      if (settled) return;

      settled = true;
      clearTimeout(settleTimer);
      clearTimeout(timeoutTimer);

      if (observer) {
        observer.disconnect();
      }

      callback();
    }

    function scheduleFinish() {
      clearTimeout(settleTimer);
      settleTimer = setTimeout(finish, 500);
    }

    if (window.MutationObserver) {
      observer = new MutationObserver(scheduleFinish);
      observer.observe(table.tBodies[0] || table, {
        childList: true,
        subtree: true,
      });
    }

    timeoutTimer = setTimeout(finish, 30000);
    scheduleFinish();
  }

  function getAcceptsValues(html) {
    return (html || "")
      .split(/<br\s*\/?>/i)
      .map((value) => value.trim())
      .filter(Boolean);
  }

  function getTableData(table, columnCount) {
    return Array.from(table.tBodies[0] ? table.tBodies[0].rows : [])
      .filter((row) => !(row.cells.length === 1 && row.cells[0].colSpan > 1))
      .map((row) => {
        var data = Array.from(row.cells, (cell) => cell.innerHTML.trim());

        while (data.length < columnCount) {
          data.push("");
        }

        return data.slice(0, columnCount);
      });
  }

  function buildTableLayout(table, titles) {
    var headerRow = document.createElement("tr");
    var tableHead = document.createElement("thead");

    titles.forEach((title) => {
      var headerCell = document.createElement("th");
      headerCell.textContent = title;
      headerRow.appendChild(headerCell);
    });

    table.replaceChildren(tableHead, document.createElement("tbody"));
    tableHead.appendChild(headerRow);
  }

  function suppressSearchPaneBorders(container) {
    function removeBorderClass() {
      container.querySelectorAll(".dtsp-bordered").forEach(function (element) {
        element.classList.remove("dtsp-bordered");
      });
    }

    removeBorderClass();

    new MutationObserver(removeBorderClass).observe(container, {
      attributes: true,
      attributeFilter: ["class"],
      childList: true,
      subtree: true,
    });
  }

  function openFellowshipLinksInNewTab(dataTable) {
    dataTable
      .column(0)
      .nodes()
      .toArray()
      .forEach(function (cell) {
        cell.querySelectorAll("a").forEach(function (link) {
          link.target = "_blank";
        });
      });
  }

  $(".dataTable")
    .each(function () {
      var table = this;

      waitForTableToSettle(table, function () {
        if ($.fn.dataTable.isDataTable(table)) {
          return;
        }

        normaliseDataTableRows(table, getDataTableColumnCount(table));

        $(table).DataTable({
          dom: "f",
          retrieve: true,
          order: [[0, "asc"]],
          pageLength: 1000,
        });
      });
    });

// ----- GAMES ------

$("#games > table").each(function () {
    var table = this;

    waitForTableToSettle(table, function () {
      if ($.fn.dataTable.isDataTable(table)) {
        return;
      }

      var titles = [
      	"Game", "Developer", "Publisher", "Genre", "Platform", "Mode", "Released"
      ];
      var data = getTableData(table, titles.length);

      buildTableLayout(table, titles);

      var dataTable = new DataTable(table, {
        columns: [
          {
            title: "Game",
            render: function (data, type) {
              if (type === "display") {
                return (
                  '<span style="display: list-item; margin-left: 1.25rem;">' +
                  data +
                  "</span>"
                );
              }

              return data;
            },
            searchPanes: {
              show: false,
            },
          },
          {
            title: "Developer",
            visible: true,
            searchPanes: {
              show: true,
            },
          },
          {
            title: "Publisher",
            visible: true,
            searchPanes: {
              show: true,
            },
          },
          {
            title: "Genre",
            visible: true,
            searchPanes: {
              show: true,
            },
          },
          {
            title: "Platform",
            visible: true,
            searchPanes: {
              show: true,
            },
          },          
          {
            title: "Mode",
            visible: true,
            render: {
              display: function (data) {
                return data;
              },
              sp: function (data) {
                return getAcceptsValues(data);
              },
            },
            searchPanes: {
              show: true,
              orthogonal: "sp",
            },
          },
          {
            title: "Released",
            visible: true,
            searchPanes: {
              show: true,
            },
          }, 
        ],
        layout: {
          topStart: null,
          topEnd: null,
          top2: {
            searchPanes: {
              cascadePanes: true,
              initCollapsed: true,
              columns: [1, 2, 3, 4, 5, 6],
            },
          },
          top1: {
            search: {
              className: "mx-0 w-100",
            },
          },
        },
        order: [[0, "asc"]],
        paging: false,
        searchDelay: 400,
      });

      var lastGlobalSearch = dataTable.search();
      var paneIndexes = [1, 2, 3, 4, 5, 6];

      dataTable.on("draw.dt", function () {
        var globalSearch = dataTable.search();

        openFellowshipLinksInNewTab(dataTable);

        if (globalSearch === lastGlobalSearch) {
          return;
        }

        lastGlobalSearch = globalSearch;

        paneIndexes.forEach(function (paneIndex) {
          dataTable.searchPanes.rebuildPane(paneIndex, true);
        });
      });

      dataTable.rows.add(data).draw();
      $(".dataTable")
        .removeClass("dt-loading")
        .removeAttr("aria-busy")
        .show();
      dataTable.searchPanes.rebuildPane();
      suppressSearchPaneBorders(document.querySelector(".dataTable"));
      dataTable.columns.adjust().draw(false);
    });
  });
}