commit: 16cfec4fba0837841406f546cd71612274ad7c9c
parent b34a16de39cdb2ba60ed7fb887a109fc935b68fc
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Thu, 6 Jun 2024 06:08:47 +0200
sorttable: Import
Diffstat:
3 files changed, 165 insertions(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
@@ -57,7 +57,7 @@ dist:
rm -rf ${NAME}-${VERSION}
mkdir -p ${NAME}-${VERSION}
cp -f ${MAN1} ${HDR} ${SRC} ${COMPATSRC} ${DOC} \
- Makefile favicon.png logo.png style.css \
+ Makefile favicon.png logo.png style.css sorttable.css sorttable.js \
example_create.sh example_post-receive.sh \
${NAME}-${VERSION}
# make tarball
@@ -84,6 +84,8 @@ install: all
# installing example files.
mkdir -p ${DESTDIR}${DOCPREFIX}
cp -f style.css\
+ sorttable.css\
+ sorttable.js\
favicon.png\
logo.png\
example_create.sh\
@@ -101,6 +103,8 @@ uninstall:
# removing example files.
rm -f \
${DESTDIR}${DOCPREFIX}/style.css\
+ ${DESTDIR}${DOCPREFIX}/sorttable.css\
+ ${DESTDIR}${DOCPREFIX}/sorttable.js\
${DESTDIR}${DOCPREFIX}/favicon.png\
${DESTDIR}${DOCPREFIX}/logo.png\
${DESTDIR}${DOCPREFIX}/example_create.sh\
diff --git a/sorttable.css b/sorttable.css
@@ -0,0 +1,13 @@
+table.sortable th.made_sortable {
+ cursor: pointer;
+}
+table.sortable th.made_sortable:not(.sorttable_sorted):not(.sorttable_sorted_reverse):not(.sorttable_nosort)::after {
+ content: "⭥";
+ opacity: 0.5;
+}
+table.sortable th.made_sortable.sorttable_sorted:not(.sorttable_sorted_reverse):not(.sorttable_nosort)::after {
+ content: "⭣";
+}
+table.sortable th.made_sortable.sorttable_sorted_reverse:not(.sorttable_sorted):not(.sorttable_nosort)::after {
+ content: "⭡";
+}
diff --git a/sorttable.js b/sorttable.js
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2007 Stuart Langridge http://www.kryogenix.org/code/browser/sorttable/
+ * Copyright 2018, 2023 Haelwenn (lanodan) Monnier <contact@hacktivis.me>
+ * SPDX-License-Identifier: X11
+ */
+
+/* Usage:
+ * Add <script src="sorttable.js"></script> to your (x)HTML pages
+ * Add class="sortable" to any table you'd like to make sortable
+ * See sorttable.css for an example of styling
+ */
+
+sorttable = {
+ done : false
+};
+
+sorttable.init = function() {
+ // quit if this function has already been called
+ if(sorttable.done) return;
+ // flag this function so we don't do the same thing twice
+ sorttable.done = true;
+
+ var sortableElements = document.getElementsByClassName("sortable");
+ Array.prototype.filter.call(sortableElements, function(sortableElement) {
+ if(sortableElement.nodeName == "TABLE" || sortableElement.nodeName == "table")
+ {
+ sorttable.makeSortable(sortableElement);
+ }
+ });
+};
+
+sorttable.makeSortable = function(table) {
+ // Only one thead row is supported
+ if(table.tHead.rows.length != 1) return;
+ // work through each column and calculate its type
+ headrow = table.tHead.rows[0].cells;
+ for(var i = 0; i < headrow.length; i++)
+ {
+ // skip this col
+ if(!headrow[i].classList.contains("sorttable_nosort"))
+ {
+ // make it clickable to sort
+ headrow[i].sorttable_columnindex = i;
+ headrow[i].sorttable_tbody = table.tBodies[0];
+ headrow[i].addEventListener("click", event => {
+ if(event.target)
+ {
+ sorttable.innerSortFunction(event.target)
+ }
+ });
+ headrow[i].addEventListener("keydown", event => {
+ if(event.target && (event.key && event.key == "Enter"))
+ {
+ sorttable.innerSortFunction(event.target);
+ }
+ });
+ headrow[i].setAttribute("tabindex", "0");
+ headrow[i].classList.add('made_sortable');
+ }
+ }
+};
+
+sorttable.compareFn = function(a, b) {
+ // Note: Returns are reversed
+ if(a[0]<b[0]) {return 1};
+ if(a[0]>b[0]) {return -1};
+ return 0;
+};
+
+sorttable.innerSortFunction = function(tr) {
+ if(tr.classList.contains("sorttable_sorted"))
+ {
+ sorttable.reverse(tr.sorttable_tbody);
+ tr.classList.replace('sorttable_sorted', 'sorttable_sorted_reverse');
+ }
+ else if(tr.classList.contains("sorttable_sorted_reverse"))
+ {
+ sorttable.reverse(tr.sorttable_tbody);
+ tr.classList.replace('sorttable_sorted_reverse', 'sorttable_sorted');
+ }
+ else
+ {
+ // remove sorttable_sorted classes
+ theadRow = tr.parentNode;
+ theadRow.childNodes.forEach(function(
+ cell) { cell.classList.remove('sorttable_sorted_reverse', 'sorttable_sorted'); });
+ // build an array to sort. This is a Schwartzian transform thing,
+ // i.e., we "decorate" each row with the actual sort key,
+ // sort based on the sort keys, and then put the rows back in order
+ // which is a lot faster because you only do getInnerText once per row
+ row_array = [];
+ col = tr.sorttable_columnindex;
+ rows = tr.sorttable_tbody.rows;
+ for(var j = 0; j < rows.length; j++)
+ {
+ row_array[row_array.length] =
+ [ sorttable.getElementValue(rows[j].cells[col]), rows[j] ];
+ }
+ row_array.sort(sorttable.compareFn);
+ tb = tr.sorttable_tbody;
+ row_array.forEach(function(row) { tb.appendChild(row[1]); });
+ tr.classList.add('sorttable_sorted');
+ }
+};
+
+// - Use data-value="" attribute if you want to override innerHTML
+// - Use data-type="" attribute if you want to use non-lexical sorting
+// Example: <td data-value="1337" data-type="int">Leet</td>
+sorttable.getElementValue = function(element) {
+ if(element === undefined)
+ {
+ return "\u0000"; // null
+ console.log("Warning: Empty cells found");
+ };
+
+ let value = element.dataset.value ? element.dataset.value : element.innerText;
+ switch(element.dataset.type)
+ {
+ case undefined:
+ return value;
+ case "int":
+ var v = parseInt(value);
+ return v != NaN ? v : value;
+ case "float":
+ var v = parseFloat(value);
+ return v != NaN ? v : value;
+ default:
+ console.log(`Warning: Unhandled type ${element.dataset.type}`);
+ return value;
+ };
+};
+
+sorttable.reverse = function(tbody) {
+ // reverse the rows in a tbody
+ newrows = [];
+ for(var i = 0; i < tbody.rows.length; i++)
+ {
+ newrows[newrows.length] = tbody.rows[i];
+ }
+ for(var i = newrows.length - 1; i >= 0; i--)
+ {
+ tbody.appendChild(newrows[i]);
+ }
+ delete newrows;
+};
+
+sorttable.init()