//
// Tabellen-Sortierer
// Author: Stefan Bion
// 
// Anwendung:
// - Einmalig die Funktion InitTableSorter() aufrufen (z.B. im body-Tag per onload)
// - Den zu sortierenden Tabellen (table) die Klasse "sorted" zuweisen.
// - Spalten (th) mit numerischen Werten die Klasse "sortNumeric" zuweisen.
// - Spalten (th) mit Datumswerten (TT.MM.JJJJ) die Klasse "sortDate" zuweisen.
// - Optional: Zellen (td) das Attribut "data-sort-value" mit einen Sortierwert zuweisen.
//   Dieser wird dann anstelle des Textes in der Zelle (innerText) verwendet.
// Ein Klick auf die Spaltenüberschrift sortiert dann diese Spalte
// (1. Klick = aufsteigend, 2. Klick = absteigend, 3. Klick = Original-Reihenfolge).
// Ein Symbol neben der Spaltenüberschrift zeigt die aktuelle Sortierrichtung an.
// Werden die Zeilen (TD) einer Tabelle dynamisch neu aufgebaut, sollte InitTableSorter(table)
// aufgerufen werden, wobei table das betreffende Tabellen-Element ist.
// Wird auch der Header (TH) einer Tabelle dynamisch neu aufgebaut, sollte
// InitTableSorter(table, true) aufgerufen werden.
//

'use strict';

function InitTableSorter(table, rebuild)
{
	// Merkvariablen für Sortierspalte und -richtung je Tabelle
	var lastRow = {}; // Hash für letzte Sortierspalte pro Tabelle
	var lastDir = {}; // Hash für letzte Sortierrichtung pro Tabelle

	// Sortier-Icons für Spalten-Titel
	var strAsc  = '\u{25B2}'; // Dreieck mit Spitze oben
	var strDesc = '\u{25BC}'; // Dreieck mit Spitze unten
	var strNone = '\u{25C6}'; // Rhombus

	var sortAscending; // Sortierrichtung: undefined = unsortiert (original); true = aufsteigend; false = absteigend
	var rowId = 0; // globale Zeilen-ID für Original-Reihenfolge der Zeilen

	function SortTable(event)
	{
		event.currentTarget.addEventListener('click', SortTable, { once: true });

		// Nur sortieren, wenn auch tatsächlich die Spaltenüberschrift angeklickt wurde und nicht
		// irgend ein anderes Control wie z.B. eine Select-Box innerhalb des Tabellen-Headers!
		if (event.currentTarget !== event.target)
			return;

		var table = event.currentTarget.parTable; // die zu sortierende Tabelle
		var n = event.currentTarget.parColumn; // der Index der zu sortierenden Spalte

		// true, wenn Spalte numerisch sortiert werden soll
		var sortNumeric = table.getElementsByTagName('TH')[n].classList.contains('sortNumeric');
		var sortDate = table.getElementsByTagName('TH')[n].classList.contains('sortDate');

		// Ermittlung der Child-Nummer der Tabelle
		var tableNo = 0;
		for (var child = table; (child = child.previousSibling) !== null; tableNo++ );
  
		// Ermittlung der Tabellenzeilen
		var rows = table.getElementsByTagName('TR');

		// Jeder Klick auf eine Tabellenspalte kehrt die Sortierung um
		var nOld = lastRow[tableNo];
		sortAscending = lastDir[tableNo];
		sortAscending = n !== nOld ? true : sortAscending ? false : sortAscending === false ? undefined : true;
		lastRow[tableNo] = n;
		lastDir[tableNo] = sortAscending;

		// Den Wert jeder Zelle der Tabellenzeile zusammen mit der Zeilennummer in ein Array kopieren
		var rowInfo = [];
		var rowInfoNoSort = [];

		if (sortAscending === undefined)
		{
			// Original-Reihenfolge der Tabellenzeilen wiederherstellen:
			for (var iRow = 1; iRow < rows.length; iRow++)
			{
				var row = rows[iRow];
				rowInfo.push({ iRow: iRow, value: row.getAttribute('data-orig-order') });
			}
			rowInfo.sort(function(a, b) { return a.value - b.value; });
		}
		else
		{
			var bNoSort = false;
			for (var iRow = 1; iRow < rows.length; iRow++)
			{
				var row = rows[iRow];
				if (row.classList.contains('nosort')) bNoSort = true;
				if (bNoSort) { rowInfoNoSort.push({ iRow: iRow }); continue; }
				var elemTableCell = row.getElementsByTagName('TD')[n];
				var value = elemTableCell.getAttribute('data-sort-value');
				if (!value)
				{
					value = elemTableCell.innerText.toLowerCase();
					if (sortNumeric)
					{
						value = Number(value.replace(',', '.').replace(/[^0-9.-]/g, ''));
					}
					else if (sortDate)
					{
						var [date, time] = value.split(' ');
						if (date === '') date = '00.00.0000';
						if (time === '') time = '00:00:00';
						var [dd, mm, yyyy] = date.split('.');
						var [HH, MM, SS] = time.split(':');
						// yyyymmddHHMMSS
						value = yyyy * 10000000000 + mm * 100000000 + dd * 1000000 + HH * 10000 + MM * 100 + SS;
					}
				}
				var cellInfo = { iRow: iRow, value: value };
				rowInfo.push(cellInfo);
			}

			// Das Array nach dem Zellenwert sortieren
			rowInfo.sort(function(a, b)
			{
				if (sortNumeric || sortDate)
					return sortAscending ? a.value - b.value : b.value - a.value;
				else
					return sortAscending ? a.value.localeCompare(b.value) : b.value.localeCompare(a.value); 
			});
		}

		// In der Reihenfolge der Zeilen in dem nach Wert sortierten Array
		// die Zeilen aus der Originaltabelle in die temporäre Tabelle kopieren
		// (nicht verschieben, weil sich sonst die Zeilennummern ändern):
		var tmpTable = document.createElement("TABLE");
		for (var iRowInfo = 0; iRowInfo < rowInfo.length; iRowInfo++)
		{
			var iRow = rowInfo[iRowInfo].iRow;
			var row = rows[iRow];
			tmpTable.appendChild(row.cloneNode(true));
		}

		// In der Reihenfolge der Zeilen in dem unsortierten Array
		// die Zeilen aus der Originaltabelle in die temporäre Tabelle kopieren
		// (nicht verschieben, weil sich sonst die Zeilennummern ändern):
		for (var iRowInfo = 0; iRowInfo < rowInfoNoSort.length; iRowInfo++)
		{
			var iRow = rowInfoNoSort[iRowInfo].iRow;
			var row = rows[iRow];
			tmpTable.appendChild(row.cloneNode(true));
		}

		// Die Zeilen der Originaltabelle löschen
		var parent = rows[1].parentNode;
		while (table.getElementsByTagName('TR').length > 1)
			parent.removeChild(parent.lastChild);

		// Die Zeilen aus der temporären Tabelle in die Originaltabelle verschieben
		while (tmpTable.getElementsByTagName('TR').length > 0)
			parent.appendChild(tmpTable.getElementsByTagName('TR')[0]);

		// Die temporäre Tabelle wieder löschen
		tmpTable.remove(); // IE11 unterstützt remove() nicht :-(

		// Ein Sortier-Icon rechts neben dem Spaltentitel anzeigen
		var cols = table.getElementsByTagName('TR')[0].getElementsByTagName('TH');
		for (var iCol = 0; iCol < cols.length; iCol++)
		{
			var col = cols[iCol];
			var colText = col.innerHTML;
			colText = colText.replace(strAsc, '');
			colText = colText.replace(strDesc, '');
			colText = colText.replace(strNone, '');
			colText += iCol !== n ? strNone : sortAscending === true ? strAsc : sortAscending === false ? strDesc : strNone;
			col.innerHTML = colText;
		}
	}

	function SaveOriginalOrder(table)
	{
		// Ermittlung der Tabellenzeilen
		var rows = table.getElementsByTagName('TR');

		// Original-Reihenfolge der Tabellenzeilen sichern
		for (var iRow = 1; iRow < rows.length; iRow++)
		{
			var row = rows[iRow];
			row.setAttribute('data-orig-order', rowId++);
		}
	}

	function InitColumns(table)
	{
		if (table.classList.contains('sorted'))
		{
			var cols = table.getElementsByTagName('TR')[0].getElementsByTagName('TH');
			for (var iCol = 0; iCol < cols.length; iCol++)
			{
				var col = cols[iCol];
				if (col.parentElement.classList.contains('nosort')) continue;

				col.parTable = table;
				col.parColumn = iCol;
				col.addEventListener('click', SortTable, { once: true });
				col.style.cursor = 'pointer';
				col.style.userSelect = 'none';
				col.title = 'Nach ' + col.innerText + ' sortieren';

				var colText = col.innerHTML;
				if (colText !== '') colText += '&nbsp;';
				colText += strNone;
				col.innerHTML = colText;
			}
			SaveOriginalOrder(table);
		}
	}

	if (table === undefined)
	{
		// Initialisierung: Alle Tabellen mit der Klasse "sorted" ermitteln
		// und zu den Spalten-Titeln einen Klick-Handler hinzufügen, etc.
		var tables = document.getElementsByTagName('TABLE');
		for (var iTable = 0; iTable < tables.length; iTable++)
		{
			var table = tables[iTable];
			InitColumns(table);
		}
	}
	else
	{
		// Reset-Funktion zum Entfernen der Icons beim Neuaufbau der Tabelle

		// Ermittlung der Child-Nummer der Tabelle
		var tableNo = 0;
		for (var child = table; (child = child.previousSibling) !== null; tableNo++);

		lastRow[tableNo] = undefined;
		lastDir[tableNo] = undefined;

		if (rebuild === true)
		{
			InitColumns(table);
		}
		else
		{
			var cols = table.getElementsByTagName('TR')[0].getElementsByTagName('TH');
			for (var iCol = 0; iCol < cols.length; iCol++)
			{
				var col = cols[iCol];
				var colText = col.innerHTML;
				colText = colText.replace(strAsc, '');
				colText = colText.replace(strDesc, '');
				colText = colText.replace(strNone, '');
				colText += strNone;
				col.innerHTML = colText;
			}
			SaveOriginalOrder(table);
		}
	}
}

