WME Reselect

Utility to redo & save selections

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name           WME Reselect
// @description    Utility to redo & save selections
// @namespace      [email protected]
// @grant          none
// @grant          GM_info
// @version        1.0.0
// @include 	   /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor.*$/
// @exclude        https://www.waze.com/user/*editor/*
// @exclude        https://www.waze.com/*/user/*editor/*
// @author         GyllieGyllie
// @license        MIT/BSD/X11
// @require        https://greatest.deepsurf.us/scripts/24851-wazewrap/code/WazeWrap.js
// ==/UserScript==
const undoIcon = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" style="display: block;" viewBox="0 0 2048 2048" width="1024" height="1024" preserveAspectRatio="none">\n' +
    '<path transform="translate(0,0)" d="M 213.002 213.403 C 228.525 218.435 248.707 227.047 264.234 233.218 L 351.22 267.837 L 638.58 382.721 L 1537.2 741.473 L 1685.47 800.661 C 1695.64 804.734 1759.44 828.913 1761.86 833.366 C 1753.38 835.548 1725.44 849.168 1715.09 853.835 L 1622.35 895.07 L 1440.87 975.89 C 1401.71 993.174 1357.58 1011.46 1319.55 1030.19 L 1386.8 1097.24 C 1395.6 1106.02 1411.11 1122.47 1420.27 1129.92 C 1408.69 1132.86 1394.01 1134.85 1381.74 1137.65 C 1356.64 1143.38 1337.75 1150.51 1314.33 1160.62 C 1278.63 1125.18 1243.11 1089.55 1207.79 1053.74 C 1192.29 1038 1168.91 1015.65 1154.75 999.313 C 1163.34 994.807 1176.37 989.511 1185.65 985.408 L 1246.95 958.262 C 1327.53 922.92 1407.84 886.952 1487.86 850.36 C 1493.91 847.541 1515.9 839.276 1518.81 836.421 C 1517.39 835.033 1409.15 792.354 1401.59 789.319 L 753.978 530.505 L 487.209 423.795 C 449.017 408.633 409.085 391.639 370.72 377.514 L 379.053 400.577 L 644.758 1163.87 L 733.874 1418.56 C 748.676 1460.91 767.156 1519.21 783.988 1559.51 C 804.546 1522.09 826.039 1475.01 845.529 1436.02 L 972.711 1182.92 C 984.466 1192.7 1006.82 1216.4 1018.31 1227.93 L 1107.12 1316.94 C 1114.96 1324.71 1135.92 1347.54 1143.93 1353 C 1133.22 1375.06 1106.98 1417.98 1093.62 1439.23 C 1061.93 1408.64 1028.98 1374.34 997.985 1342.84 C 983.253 1370.86 969.064 1400.92 954.878 1429.37 L 871.055 1597.26 L 805.824 1727.86 C 793.162 1753.24 779.518 1779.65 767.78 1805.37 C 757.433 1772.86 744.498 1737.95 733.175 1705.5 L 656.77 1486.62 L 353.086 613.804 L 261.573 352.523 C 245.456 306.526 228.412 259.537 213.002 213.403 z"/>\n' +
    '<path transform="translate(0,0)" d="M 1200.72 1383.49 C 1206.06 1386.66 1229.79 1412.57 1236.1 1419.16 L 1349.04 1538.68 C 1325.04 1539.11 1288.49 1543.4 1263.86 1545.85 C 1267.17 1567.95 1280.78 1597.1 1292.59 1615.76 C 1321.14 1661.07 1366.55 1693.16 1418.8 1704.92 C 1468.3 1715.59 1520.01 1706.13 1562.52 1678.63 C 1610.9 1647.78 1642.17 1598.64 1654.27 1543.05 C 1671.2 1561.35 1699.25 1588.73 1717.4 1606.17 C 1725.03 1594.86 1730.7 1582.08 1738.25 1570.55 C 1733.3 1586.84 1729.49 1598.41 1723.04 1614.05 C 1684.42 1702.73 1613.12 1767.35 1517.11 1787.42 C 1443.82 1802.58 1367.5 1787.98 1304.97 1746.84 C 1235.75 1701.07 1194.13 1634.04 1178.04 1553.61 L 1091.1 1562.33 C 1127.87 1504.44 1164.62 1442.21 1200.72 1383.49 z"/>\n' +
    '<path transform="translate(0,0)" d="M 1445.34 1192.38 C 1578.58 1187.45 1689.08 1264.06 1730.43 1391.61 C 1759.24 1386.99 1789.22 1385.2 1817.99 1381.02 C 1784.29 1441.52 1745.33 1500.56 1711.5 1561.91 C 1704.75 1556.32 1692.11 1542.9 1685.37 1536.1 C 1668.06 1518.74 1650.87 1501.25 1633.82 1483.64 C 1612.21 1461.36 1580.99 1431.96 1561.43 1408.93 C 1574.02 1407.45 1586.63 1406.07 1599.24 1404.78 C 1612.3 1403.12 1631.19 1401.23 1643.98 1400.66 L 1643.58 1399.81 C 1617.54 1345.62 1580.19 1306.38 1522.6 1286.43 C 1455.49 1263.18 1379.64 1281.11 1328.49 1329.74 C 1301.17 1355.72 1285.3 1383.57 1273.19 1418.78 C 1251.28 1396.08 1230.17 1373.69 1207.93 1351.2 C 1258.18 1254.82 1336.91 1201.6 1445.34 1192.38 z"/>\n' +
    '</svg>';
const ScriptName = GM_info.script.name;
const ScriptVersion = GM_info.script.version;
let ChangeLog = "WME Reselect has been updated to " + ScriptVersion + "<br />";
ChangeLog = ChangeLog + "<br /><b>New: </b>";
ChangeLog = ChangeLog + "<br />" + "- Ability to redo your recent selections using the redo icon in the header";
ChangeLog = ChangeLog + "<br />" + "- Ability to redo your recent selections from your selection history in the script tab";
ChangeLog = ChangeLog + "<br />" + "- Ability to save a recent selection and reselect it later, even in new browser sessions";
let wmeSDK;
let DB = null;
const options = loadOptions();
const selectionHistory = [];
let rollbackCount = 0;
let rollbackPending = undefined;
let selectionId = 0;
// Now validate the options are ok
validateOptions(options);
function log(message) {
    if (typeof message === 'string') {
        console.log('Reselect: ' + message);
    }
    else {
        console.log('Reselect: ', message);
    }
}
// the sdk init function will be available after the WME is initialized
function WMEReselect_bootstrap() {
    if (!wmeSDK.DataModel.Countries.getTopCountry() || !WazeWrap.Ready) {
        setTimeout(WMEReselect_bootstrap, 250);
        return;
    }
    if (wmeSDK.State.isReady()) {
        WMEReselect_init();
    }
    else {
        wmeSDK.Events.once({ eventName: "wme-ready" }).then(WMEReselect_init);
    }
}
async function WMEReselect_init() {
    log("Start");
    await initDatabase();
    constructSettings();
    displayChangelog();
    addHotbarIcon();
    wmeSDK.Events.on({
        eventName: 'wme-selection-changed',
        eventHandler: addSelection
    });
    wmeSDK.Events.on({
        eventName: 'wme-map-move-end',
        eventHandler: trySelecting
    });
    /*wmeSDK.Shortcuts.createShortcut({
      callback: doRollback,
      description: 'Rollback your selection',
      shortcutId: 'wme-reselect-rollback',
      shortcutKeys: 'CS+82'
    })*/
    log("Done");
}
let usableWindow = window;
if (window.unsafeWindow) {
    // Check if unsafeWindow is available, if so use that
    usableWindow = window.unsafeWindow;
}
if (usableWindow) {
    usableWindow.SDK_INITIALIZED.then(() => {
        // initialize the sdk with your script id and script name
        wmeSDK = getWmeSdk({ scriptId: "wme-reselect", scriptName: "Reselect", version: ScriptVersion });
        WMEReselect_bootstrap();
    });
}
function displayChangelog() {
    if (!WazeWrap.Interface) {
        setTimeout(displayChangelog, 1000);
        return;
    }
    // Alert the user version updates
    if (options.lastAnnouncedVersion === ScriptVersion) {
        log('Version: ' + ScriptVersion);
    }
    else {
        WazeWrap.Interface.ShowScriptUpdate(ScriptName, ScriptVersion, ChangeLog + "<br /><br />", "https://github.com/wazers/wme-reselect");
        // @ts-ignore
        const updateName = "#wmereselect" + ScriptVersion.replaceAll(".", "");
        $(updateName + " .WWSUFooter a").text("Github");
        options.lastAnnouncedVersion = ScriptVersion;
        saveOptions(options);
    }
}
function addSelection() {
    const selection = wmeSDK.Editing.getSelection();
    if (!selection) {
        if (!rollbackPending) {
            // Only reset when no pending rollback
            // Sometimes the set selection does an unselect first?
            rollbackCount = 0;
        }
        updateSelectionOptions();
        return;
    }
    if (rollbackPending && selection.objectType === rollbackPending.objectType && selection.ids.length === rollbackPending.ids.length) {
        rollbackPending = undefined;
        return;
    }
    // We don't track this selection type
    if (options.activeSelectionTypes.indexOf(selection.objectType) === -1) {
        rollbackCount = 0;
        updateSelectionOptions();
        return;
    }
    // New selection so remove all rolled back selections
    if (options.clearRolledBackOnNew && rollbackCount > 1) {
        selectionHistory.splice(selectionHistory.length - rollbackCount + 1);
        rollbackCount = 0;
    }
    selectionHistory.push({
        ...selection,
        id: (selectionId++),
        selectTime: new Date(Date.now()),
        center: wmeSDK.Map.getMapCenter(),
        zoom: wmeSDK.Map.getZoomLevel(),
    });
    //log("count: " + selectionHistory.length);
    //log("rollback: " + rollbackCount);
    // History is becoming to large so remove oldest element
    if (selectionHistory.length > options.maxHistory) {
        selectionHistory.splice(0, 1);
    }
    updateSelectionOptions();
}
function doRollback() {
    const currentSelection = wmeSDK.Editing.getSelection();
    rollbackCount += 1;
    if (rollbackCount > selectionHistory.length) {
        rollbackCount = selectionHistory.length;
    }
    rollbackPending = selectionHistory[selectionHistory.length - rollbackCount];
    if (rollbackCount === 1 && !!currentSelection && currentSelection.objectType === rollbackPending.objectType) {
        // Selection was still active so we need to ignore current selection for rollback
        rollbackCount = 2;
        rollbackPending = selectionHistory[selectionHistory.length - rollbackCount];
    }
    log("rollback: " + rollbackCount);
    log(rollbackPending);
    restoreSelection();
    updateSelectionOptions();
}
function rollbackToId(selectionId) {
    const length = selectionHistory.length;
    rollbackCount = 1;
    while (rollbackCount < length && selectionHistory[length - rollbackCount].id !== selectionId) {
        rollbackCount += 1;
    }
    rollbackPending = selectionHistory[length - rollbackCount];
    restoreSelection();
    updateSelectionOptions();
}
function rollbackToSelection(selection) {
    rollbackCount = 0;
    rollbackPending = selection;
    restoreSelection();
    updateSelectionOptions();
}
function restoreSelection() {
    if (!rollbackPending)
        return;
    const selection = rollbackPending;
    const currentCenter = wmeSDK.Map.getMapCenter();
    if (selection.zoom !== wmeSDK.Map.getZoomLevel() || selection.center.lon !== currentCenter.lon || selection.center.lat !== currentCenter.lat) {
        wmeSDK.Map.setMapCenter({
            lonLat: selection.center,
            zoomLevel: selection.zoom
        });
    }
    else {
        wmeSDK.Editing.setSelection({
            selection: rollbackPending
        });
    }
}
function trySelecting() {
    if (rollbackPending) {
        try {
            wmeSDK.Editing.setSelection({
                selection: rollbackPending
            });
        }
        catch (e) { }
        setTimeout(trySelecting, 100);
    }
}
function addHotbarIcon() {
    const icon = $("#wme-reselect-undo");
    if (options.showHotbarIcon) {
        if (icon && icon.length > 0) {
            return;
        }
        const button = document.createElement('div');
        button.className = 'wme-reselect-undo-button';
        button.id = "wme-reselect-undo";
        button.innerHTML = undoIcon;
        button.title = 'Go back 1 selection';
        button.dataset.placement = 'bottom';
        $(button).tooltip({
            trigger: 'hover',
            position: 'down'
        });
        button.onclick = () => doRollback();
        $('.secondary-toolbar-actions:not(.user-toolbar)').prepend(button);
    }
    else {
        icon.remove();
    }
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
////
//// Database Logic
////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
async function initDatabase() {
    const request = window.indexedDB.open("WME-Reselect", 1);
    request.onerror = () => {
        console.error("[WME-Reselect] Why didn't you allow my web app to use IndexedDB?!");
    };
    request.onupgradeneeded = (event) => {
        const db = event.target.result;
        if (event.newVersion >= 1 && event.oldVersion < 1) {
            const savedSelects = db.createObjectStore("saved-selects", { keyPath: "name" });
            savedSelects.transaction.oncomplete = () => {
                log("Created Saved Selects store");
            };
        }
    };
    request.onsuccess = (event) => {
        log("DB ready");
        DB = event.target.result;
    };
    while (!DB) {
        await sleep(10);
    }
    if ("openDatabase" in window) {
        try {
            let created = false;
            window.openDatabase("WME-Reselect", // actual database name.  Opens existing or creates.
            "1", // version number.  *Must* be correct.
            "Database to store WME Reselect data", //
            10 * 1024 * 1024, // Size of the DB
            () => {
                created = true;
            });
            if (!created) {
                log("Migrating");
                //await migrateDb(oldDb);
                log("Migration over");
            }
        }
        catch (e) { }
    }
}
function saveSelection(selection) {
    let name = prompt("Enter the name for this selection");
    if (!name) {
        return;
    }
    if (!DB) {
        alert("Database not initialized");
        return;
    }
    // Insert into the DB
    const objectStore = DB
        .transaction("saved-selects", "readwrite")
        .objectStore("saved-selects");
    const current = objectStore.get(name);
    current.onerror = (event) => {
        console.log(event);
        alert("Failed to save selection, please try again");
    };
    current.onsuccess = (event) => {
        const current = event.target.result;
        if (current) {
            alert("Name already exists, please choose a different name");
            return;
        }
        const save = objectStore.add({
            ...selection,
            name: name,
            id: Date.now().toString(36) + Math.random().toString(36).substring(2, 12).padStart(12, '0'),
            selectTime: new Date(Date.now()),
        });
        save.onerror = (event) => {
            console.log(event);
            alert("Failed to save selection, please try again");
        };
        save.onsuccess = () => {
            alert("Saved selection: " + name);
            loadSavedSelection();
        };
    };
}
function deleteSelection(selection) {
    if (!DB) {
        alert("Database not initialized");
        return;
    }
    // Insert into the DB
    const objectStore = DB
        .transaction("saved-selects", "readwrite")
        .objectStore("saved-selects");
    objectStore.delete(selection.name);
    alert("Deleted selection: " + selection.name);
    loadSavedSelection();
}
function loadSavedSelection() {
    if (!DB)
        return;
    const request = DB
        .transaction("saved-selects")
        .objectStore("saved-selects")
        .getAll();
    request.onsuccess = (event) => {
        if (event.target.result) {
            const results = [...event.target.result];
            const holder = $('#sidepanel-reselect-bookmarks');
            holder.empty();
            results.forEach(selection => {
                const selectionTab = $(`
        <div class="wme-reselect-selection-tab" data-id="resel-hist-${selection.id}">
            <div>
                <b>${selection.name}</b>
                <br />
                ${selection.selectTime.toLocaleDateString()} ${selection.selectTime.toLocaleTimeString()}
            </div>
            <div class="bookmark-holder">
                <i id="resel-saved-${selection.id}" class="w-icon-trash w-icon"></i>
            </div>
        </div>`);
                selectionTab.on('click', () => rollbackToSelection(selection));
                holder.append(selectionTab);
                $('#resel-saved-' + selection.id).on('click', (e) => {
                    e.stopPropagation();
                    deleteSelection(selection);
                });
            });
        }
    };
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
////
//// Option Logic
////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
function constructSettings() {
    let g = '#reselect-settings { padding: 10px; }';
    g = g + ' .wme-reselect-selection-tab { padding: 5px; cursor: pointer; border: 1px solid #dadada; border-radius: 8px; margin-bottom: 5px; display: flex; justify-content: space-between }';
    g = g + ' .wme-reselect-selection-tab:hover { background-color: #eee; }';
    g = g + ' .wme-reselect-selection-tab.active { background-color: #b1edaf; border-color: green }';
    g = g + ' .wme-reselect-selection-tab .bookmark-holder { margin: auto 0; }';
    g = g + ' .wme-reselect-selection-tab .w-icon-saved-fill { font-size: 25px; cursor: pointer; }';
    g = g + ' .wme-reselect-selection-tab .w-icon-trash { font-size: 25px; cursor: pointer; }';
    g = g + ' .wme-reselect-undo-button, { width: 25px; height: 25px; cursor: pointer; }';
    g = g + ' .wme-reselect-undo-button svg, { width: 25px; height: 25px; }';
    g = g + ' .wme-reselect-options-title { display: flex; gap: 5px; align-items: center; }';
    g = g + ' .wme-reselect-options-header { display: flex; gap: 20px; }';
    g = g + ' .wme-reselect-options-header svg { width: 40px; height: 40px; }';
    g = g + ' .wme-reselect-undo-button svg, .wme-reselect-options-title svg { width: 20px; height: 20px; }';
    $("head").append($('<style id="wme-reselect-css" type="text/css">' + g + '</style>'));
    // -- Set up the tab for the script
    wmeSDK.Sidebar.registerScriptTab().then(({ tabLabel, tabPane }) => {
        tabLabel.innerHTML = '<div class="wme-reselect-options-title">' + undoIcon + ' <span>Reselect</span></div>';
        tabLabel.title = 'Reselect';
        tabPane.innerHTML = '<div id="reselect-settings"></div>';
        const scriptContentPane = $('#reselect-settings');
        // Add the content to the settings pane
        const tabs = $('<wz-tabs fixed="true"></wz-tabs>');
        const selectionTab = $('<wz-tab is-active label="History" tooltip="History"></wz-tab>');
        const selectionTabContent = $('<div id="sidepanel-reselect-selections"></div>');
        selectionTab.append(selectionTabContent);
        const savedSelectionTab = $('<wz-tab label="Bookmarked" tooltip="Bookmarked"></wz-tab>');
        const savedSelectionTabContent = $('<div id="sidepanel-reselect-bookmarks"></div>');
        savedSelectionTab.append(savedSelectionTabContent);
        const settingsTab = $('<wz-tab label="Settings" tooltip="Settings"></wz-tab>');
        const settingsTabContent = $('<div id="sidepanel-reselect-settings"></div>');
        settingsTab.append(settingsTabContent);
        tabs.append(selectionTab);
        tabs.append(savedSelectionTab);
        tabs.append(settingsTab);
        scriptContentPane.append(tabs);
        // Setup settings
        const header = $('<div class="wme-reselect-options-header"></div>');
        settingsTabContent.append(header);
        header.append(undoIcon);
        header.append(`<h2 style="margin-top: 0;">Reselect</h2>`);
        settingsTabContent.append(`<span>Current Version: <b>${ScriptVersion}</b></span>`);
        addBooleanSettingsCallback(settingsTabContent, 'Add an icon in the top hotbar to quickly rollback a selection', 'Show Hotbar Icon', 'showHotbarIcon', (e) => {
            toggleBoolean(e);
            addHotbarIcon();
        });
        addBooleanSettings(settingsTabContent, 'Should undone selections be deleted when continuing a new selection', 'Clear undone selections on select', 'clearRolledBackOnNew');
        addTextNumberSettings(settingsTabContent, 'To avoid memory issues during long edit sessions do not make this too large', 'Max history', 'maxHistory');
        settingsTabContent.append(`<h5>Selection Types To Track:</h5>`);
        addBooleanListSettingsCallback(settingsTabContent, '', 'Segments', 'activeSelectionTypes', 'segment');
        loadSavedSelection();
    });
}
function updateSelectionOptions() {
    const holder = $('#sidepanel-reselect-selections');
    holder.empty();
    const selectionList = [...selectionHistory].reverse();
    const selectedIndex = rollbackCount - 1;
    selectionList.forEach((selection, index) => {
        const selectionTab = $(`
        <div class="wme-reselect-selection-tab ${selectedIndex === index ? 'active' : ''}" data-id="resel-hist-${selection.id}">
            <div>
                <b>${selection.ids.length}x ${selection.localizedTypeName ?? selection.objectType}</b>
                <br />
                ${selection.selectTime.toLocaleDateString()} ${selection.selectTime.toLocaleTimeString()}
            </div>
            <div class="bookmark-holder">
                <i id="resel-save-${selection.id}" class="w-icon-saved-fill w-icon"></i>
            </div>
        </div>`);
        selectionTab.on('click', () => rollbackToId(selection.id));
        holder.append(selectionTab);
        $('#resel-save-' + selection.id).on('click', (e) => {
            e.stopPropagation();
            saveSelection(selection);
        });
    });
}
function toggleTrackOptions(value) {
    const current = options['activeSelectionTypes'].indexOf(value);
    if (current >= 0) {
        options['activeSelectionTypes'].splice(current, 1);
    }
    else {
        options['activeSelectionTypes'].push(value);
    }
    saveOptions(options);
}
function getDefaultOptions() {
    return {
        lastAnnouncedVersion: '',
        maxHistory: 100,
        showHotbarIcon: true,
        clearRolledBackOnNew: false,
        activeSelectionTypes: [
            'segment'
        ]
    };
}
function loadOptions() {
    let text = localStorage.getItem("Reselect-Options");
    let options;
    if (text) {
        options = JSON.parse(text);
    }
    else {
        options = getDefaultOptions();
    }
    return options;
}
function validateOptions(options) {
    const defaultOptions = getDefaultOptions();
    // Add missing options
    for (let key in defaultOptions) {
        if (!(key in options)) {
            options[key] = defaultOptions[key];
        }
    }
}
function saveOptions(options) {
    const optionsJson = JSON.stringify(options);
    localStorage.setItem("Reselect-Options", optionsJson);
}
function toggleBoolean(event) {
    const target = event.target;
    options[target.id] = target?.checked;
    saveOptions(options);
}
function changeText(event) {
    const target = event.target;
    options[target.id] = target?.value;
    saveOptions(options);
}
function addTextNumberSettings(container, title, label, name, step = 1) {
    const currentValue = options[name];
    const textInput = $('<wz-text-input type="number" min="0" max="999" step="' + step + '" id="' + name + '" value="' + currentValue + '"></wz-text-input>');
    const optionHtml = $('<div style="margin-top: 10px;"><span Title="' + title + '">' + label + '</span></div>').append(textInput);
    container.append(optionHtml);
    textInput.on('change', changeText);
}
function addBooleanSettings(container, title, label, name) {
    addBooleanSettingsCallback(container, title, label, name, toggleBoolean);
}
function addBooleanSettingsCallback(container, title, label, name, clickHandler) {
    const currentValue = options[name];
    const checkbox = $('<wz-checkbox id="' + name + '" Title="' + title + '" name="types" disabled="false" checked="' + currentValue + '">' + label + '</wz-checkbox>');
    const optionHtml = $('<div class="urcom-option"></div>').append(checkbox);
    container.append(optionHtml);
    checkbox.on('click', clickHandler);
}
function addBooleanListSettingsCallback(container, title, label, name, value, clickHandler) {
    const currentlyActive = options[name].indexOf(value) >= 0;
    const checkbox = $('<wz-checkbox id="' + name + '" Title="' + title + '" name="types" disabled="false" checked="' + currentlyActive + '">' + label + '</wz-checkbox>');
    const optionHtml = $('<div class="reselect-option"></div>').append(checkbox);
    container.append(optionHtml);
    checkbox.on('click', clickHandler ?? (() => toggleTrackOptions(value)));
}
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}