/*
THIS IS A GENERATED/BUNDLED FILE BY ROLLUP
if you want to view the source visit the plugins github repository
*/

'use strict';

var obsidian = require('obsidian');

/******************************************************************************
Copyright (c) Microsoft Corporation.

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */

function __awaiter(thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
}

// allows time to be optional
const ISO_DATE_REGEX = /^(\d{4}-\d{2}-\d{2})(T\d{2}:\d{2}:\d{2}(\.\d{3})?(Z|[+-]\d{2}:\d{2})?)?$/;
var CellTypes;
(function (CellTypes) {
    CellTypes[CellTypes["TEXT"] = 0] = "TEXT";
    CellTypes[CellTypes["NUMBER"] = 1] = "NUMBER";
    CellTypes[CellTypes["ISO_DATE"] = 2] = "ISO_DATE";
})(CellTypes || (CellTypes = {}));
var SortOrder;
(function (SortOrder) {
    SortOrder[SortOrder["DEFAULT"] = 0] = "DEFAULT";
    SortOrder[SortOrder["ASCENDING"] = 1] = "ASCENDING";
    SortOrder[SortOrder["DESCENDING"] = 2] = "DESCENDING";
})(SortOrder || (SortOrder = {}));
var AttributeName;
(function (AttributeName) {
    AttributeName["tableHeader"] = "sortable-style";
    AttributeName["cssAscending"] = "sortable-asc";
    AttributeName["cssDescending"] = "sortable-desc";
})(AttributeName || (AttributeName = {}));
class TableState {
    constructor() {
        this.columnIdx = null;
        this.sortOrder = SortOrder.DEFAULT;
        this.defaultOrdering = null;
    }
}
function shouldSort(htmlEl) {
    // dataview table: parent must be a "dataview" HTMLTableElement
    const p = htmlEl.matchParent(".dataview");
    if (p instanceof HTMLTableElement)
        return true;
    // reading mode, i.e. non-editing
    return null !== htmlEl.matchParent(".markdown-reading-view");
}
function onHeadClick(evt, tableStates) {
    // check if the clicked element is a table header
    const htmlEl = evt.target;
    if (!shouldSort(htmlEl)) {
        return;
    }
    const th = htmlEl.closest("thead th");
    if (th === null) {
        return;
    }
    const table = htmlEl.closest("table");
    const tableBody = table.querySelector("tbody");
    const thArray = Array.from(th.parentNode.children);
    const thIdx = thArray.indexOf(th);
    // create a new table state if does not previously exist
    if (!tableStates.has(table)) {
        tableStates.set(table, new TableState());
    }
    const tableState = tableStates.get(table);
    thArray.forEach((th, i) => {
        if (i !== thIdx) {
            th.removeAttribute(AttributeName.tableHeader);
        }
    });
    if (tableState.defaultOrdering === null) {
        tableState.defaultOrdering = Array.from(tableBody.rows);
    }
    // sorting the same column, again
    if (tableState.columnIdx === thIdx) {
        tableState.sortOrder = (tableState.sortOrder + 1) % 3;
    }
    // sorting a new column
    else {
        tableState.columnIdx = thIdx;
        tableState.sortOrder = SortOrder.ASCENDING;
    }
    sortTable(tableState, tableBody);
    switch (tableState.sortOrder) {
        case SortOrder.ASCENDING:
            th.setAttribute(AttributeName.tableHeader, AttributeName.cssAscending);
            break;
        case SortOrder.DESCENDING:
            th.setAttribute(AttributeName.tableHeader, AttributeName.cssDescending);
            break;
    }
    // if the current state is now the default one, then forget about this table
    if (tableState.sortOrder === SortOrder.DEFAULT) {
        tableStates.delete(table);
        th.removeAttribute(AttributeName.tableHeader);
    }
}
function sortTable(tableState, tableBody) {
    emptyTable(tableBody, tableState.defaultOrdering);
    if (tableState.sortOrder === SortOrder.DEFAULT) {
        fillTable(tableBody, tableState.defaultOrdering);
        return;
    }
    const xs = [...tableState.defaultOrdering];
    const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" });
    xs.sort((a, b) => compareRows(a, b, tableState.columnIdx, tableState.sortOrder, collator));
    fillTable(tableBody, xs);
}
function resetTable(tableState, tableBody) {
    emptyTable(tableBody, tableState.defaultOrdering);
    fillTable(tableBody, tableState.defaultOrdering);
}
function compareRows(a, b, index, order, collator) {
    if (order === SortOrder.DESCENDING) {
        [a, b] = [b, a];
    }
    const [valueA, typeA] = valueFromCell(a.cells[index]);
    const [valueB, typeB] = valueFromCell(b.cells[index]);
    if (typeA !== typeB) {
        return collator.compare(valueA.toString(), valueB.toString());
    }
    switch (typeA) {
        case CellTypes.NUMBER:
        case CellTypes.ISO_DATE:
            return valueA === valueB ? 0 : valueA < valueB ? -1 : 1;
        case CellTypes.TEXT:
            return collator.compare(valueA.toString(), valueB.toString());
        default:
            // unreachable
            return 0;
    }
}
function valueFromCell(element) {
    // TODO: extend to other data-types.
    const text = element.textContent;
    if (ISO_DATE_REGEX.test(text)) {
        return [new Date(text), CellTypes.ISO_DATE];
    }
    // use `Number` in favour of `parseFloat` to disallow "false positives"
    const value = Number(text);
    if (!isNaN(value)) {
        return [value, CellTypes.NUMBER];
    }
    // fallback: text value, will use collator for comparison
    return [text, CellTypes.TEXT];
}
function emptyTable(tableBody, rows) {
    rows.forEach(() => tableBody.deleteRow(-1));
}
function fillTable(tableBody, rows) {
    rows.forEach((row) => tableBody.appendChild(row));
}

class SortablePlugin extends obsidian.Plugin {
    onload() {
        return __awaiter(this, void 0, void 0, function* () {
            console.log("Sortable: loading plugin...");
            this.tableStates = new WeakMap();
            this.registerDomEvent(window, "click", (ev) => onHeadClick(ev, this.tableStates));
            console.log("Sortable: loaded plugin.");
        });
    }
    onunload() {
        // get all HTMLTableElements present in the map and reset their state
        const tables = Array.from(document.querySelectorAll("table"));
        for (const table of tables) {
            if (this.tableStates.has(table)) {
                const state = this.tableStates.get(table);
                resetTable(state, table.querySelector("tbody"));
                const th = table.querySelector(`thead th:nth-child(${state.columnIdx + 1})`);
                th.removeAttribute(AttributeName.tableHeader);
                this.tableStates.delete(table);
            }
        }
        delete this.tableStates;
        console.log("Sortable: unloaded plugin.");
    }
}

module.exports = SortablePlugin;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,[encoded data, 10052 characters]
