sortable.js 91 regels JavaScript
/**
 * Make any HTML table sortable by clicking column headers.
 *
 * @param {string|HTMLTableElement} table - table element or its ID
 * @param {Object} [opts]
 * @param {string} [opts.ascClass]  - CSS class added to ascending header  (default "sort-asc")
 * @param {string} [opts.descClass] - CSS class added to descending header (default "sort-desc")
 * @example
 *     <script src="/js/sortable.js"></script>
 *     <script>new BonarooSortable("my-table");</script>
 */
function BonarooSortable(table, opts) {
  if (typeof table === "string") {
    table = document.getElementById(table);
  }
  if (!table || table.tagName !== "TABLE") {
    throw new Error("BonarooSortable: element is not a <table>");
  }

  this.table = table;
  this.ascClass = (opts && opts.ascClass) || "sort-asc";
  this.descClass = (opts && opts.descClass) || "sort-desc";
  this.currentCol = -1;
  this.ascending = true;

  var headers = table.querySelectorAll("thead th");
  for (var i = 0; i < headers.length; i++) {
    headers[i].style.cursor = "pointer";
    headers[i].setAttribute("data-sort-col", i);
    headers[i].addEventListener("click", this._onHeaderClick.bind(this, i));
  }
}

BonarooSortable.prototype._onHeaderClick = function _onHeaderClick(colIndex) {
  if (this.currentCol === colIndex) {
    this.ascending = !this.ascending;
  } else {
    this.currentCol = colIndex;
    this.ascending = true;
  }
  this._sort(colIndex, this.ascending);
  this._updateClasses(colIndex, this.ascending);
};

BonarooSortable.prototype._sort = function _sort(colIndex, asc) {
  var tbody = this.table.querySelector("tbody") || this.table;
  var rows = Array.prototype.slice.call(tbody.querySelectorAll("tr"));

  rows.sort(function (a, b) {
    var cellA = (a.children[colIndex] || {}).textContent || "";
    var cellB = (b.children[colIndex] || {}).textContent || "";

    var numA = parseFloat(cellA.replace(/[^\d.\-]/g, ""));
    var numB = parseFloat(cellB.replace(/[^\d.\-]/g, ""));

    var result;
    if (!isNaN(numA) && !isNaN(numB)) {
      result = numA - numB;
    } else {
      result = cellA.localeCompare(cellB, undefined, { sensitivity: "base" });
    }
    return asc ? result : -result;
  });

  for (var i = 0; i < rows.length; i++) {
    tbody.appendChild(rows[i]);
  }
};

BonarooSortable.prototype._updateClasses = function _updateClasses(colIndex, asc) {
  var headers = this.table.querySelectorAll("thead th");
  for (var i = 0; i < headers.length; i++) {
    headers[i].classList.remove(this.ascClass, this.descClass);
  }
  if (headers[colIndex]) {
    headers[colIndex].classList.add(asc ? this.ascClass : this.descClass);
  }
};

/**
 * Write the table before the current executing script element.
 * Handy for inline usage, similar to BonarooTimer.
 */
BonarooSortable.prototype.write = function write(id) {
  if (!this.table.id && id) {
    this.table.id = id;
  }
  if (document.currentScript) {
    document.currentScript.insertAdjacentElement("beforebegin", this.table);
  }
};