Source: modalTimer.js

"use strict";
// SPDX-License-Identifier: GPL-3.0-or-later
// myMPD (c) 2018-2024 Juergen Mang <mail@jcgames.de>
// https://github.com/jcorporation/mympd

/** @module modalTimer_js */

/**
 * Initialization function for the timer elements
 * @returns {void}
 */
function initModalTimer() {
    elGetById('modalTimerList').addEventListener('click', function(event) {
        event.stopPropagation();
        event.preventDefault();
        if (event.target.nodeName === 'A') {
            deleteTimer(event.target, getData(event.target.parentNode.parentNode, 'id'));
            return;
        }
        if (event.target.nodeName === 'BUTTON') {
            toggleTimer(event.target, getData(event.target.parentNode.parentNode, 'id'));
            return;
        }
        const target = event.target.closest('TR');
        if (checkTargetClick(target) === true) {
            showEditTimer(getData(target, 'id'));
        }
    }, false);

    const modalTimerstartHourInputEl = elGetById('modalTimerstartHourInput');
    for (let i = 0; i < 24; i++) {
        modalTimerstartHourInputEl.appendChild(
            elCreateText('option', {"value": i}, zeroPad(i, 2))
        );
    }

    const modalTimerstartMinuteInputEl = elGetById('modalTimerstartMinuteInput');
    for (let i = 0; i < 60; i = i + 5) {
        modalTimerstartMinuteInputEl.appendChild(
            elCreateText('option', {"value": i}, zeroPad(i, 2))
        );
    }

    elGetById('modalTimerVolumeInput').addEventListener('change', function() {
        elGetById('textTimerVolume').textContent = this.value + ' %';
    }, false);

    elGetById('modalTimerActionInput').addEventListener('change', function() {
        selectTimerActionChange();
    }, false);

    elGetById('modalTimerIntervalInput').addEventListener('change', function() {
        selectTimerIntervalChange();
    }, false);

    elGetById('modalTimer').addEventListener('show.bs.modal', function () {
        showListTimer();
    });

    setDataId('modalTimerPlaylistInput', 'cb-filter', 'filterPlaylistsSelect');
    setDataId('modalTimerPlaylistInput', 'cb-filter-options', [0, 'modalTimerPlaylistInput']);
}

/**
 * Deletes a timer
 * @param {EventTarget} el triggering element
 * @param {number} timerid the timer id
 * @returns {void}
 */
//eslint-disable-next-line no-unused-vars
function deleteTimer(el, timerid) {
    showConfirmInline(el.parentNode.previousSibling, tn('Do you really want to delete the timer?'), tn('Yes, delete it'), function() {
        sendAPI("MYMPD_API_TIMER_RM", {
            "timerid": timerid
        }, deleteTimerCheckError, true);
    });
}

/**
 * Handler for the MYMPD_API_TIMER_RM jsonrpc response
 * @param {object} obj jsonrpc response
 * @returns {void}
 */
function deleteTimerCheckError(obj) {
    if (modalListApply(obj) === true) {
        showListTimer();
    }
}

/**
 * Toggles the timer enabled state
 * @param {EventTarget} target triggering element
 * @param {number} timerid the timer id
 * @returns {void}
 */
//eslint-disable-next-line no-unused-vars
function toggleTimer(target, timerid) {
    if (target.classList.contains('active')) {
        target.classList.remove('active');
        sendAPI("MYMPD_API_TIMER_TOGGLE", {
            "timerid": timerid,
            "enabled": false
        }, showListTimer, false);
    }
    else {
        target.classList.add('active');
        sendAPI("MYMPD_API_TIMER_TOGGLE", {
            "timerid": timerid,
            "enabled": true
        }, showListTimer, false);
    }
}

/**
 * Saves the timer
 * @param {Element} target triggering element
 * @returns {void}
 */
//eslint-disable-next-line no-unused-vars
function saveTimer(target) {
    cleanupModalId('modalTimer');
    btnWaiting(target, true);

    let minOneDay = false;
    const weekdayBtns = ['modalTimerMonBtn', 'modalTimerTueBtn', 'modalTimerWedBtn', 'modalTimerThuBtn', 'modalTimerFriBtn', 'modalTimerSatBtn', 'modalTimerSunBtn'];
    const weekdays = [];
    for (let i = 0, j = weekdayBtns.length; i < j; i++) {
        const checked = elGetById(weekdayBtns[i]).classList.contains('active') ? true : false;
        weekdays.push(checked);
        if (checked === true) {
            minOneDay = true;
        }
    }
    if (minOneDay === false) {
        setIsInvalidId('modalTimerSunBtn');
        btnWaiting(target, false);
        return;
    }

    const args = {};
    const argEls = document.querySelectorAll('#modalTimerScriptActionArguments input');
    for (let i = 0, j = argEls.length; i < j; i++) {
        args[getData(argEls[i], 'name')] = argEls[i].value;
    }
    let interval = Number(getSelectValueId('modalTimerIntervalInput'));
    if (interval === -2) {
        //repeat
        interval = Number(elGetById('modalTimerIntervalRepeatInput').value);
        //convert interval to seconds
        const unit = Number(getSelectValueId('modalTimerIntervalRepeatUnit'));
        interval = interval * unit;
    }
    let preset = getSelectValueId('modalTimerPresetInput');
    if (preset === undefined) {
        //set to empty string, else the jsonrpc parameter is not set
        preset = '';
    }
    const modalTimerActionInput = elGetById('modalTimerActionInput');
    sendAPI("MYMPD_API_TIMER_SAVE", {
        "timerid": getDataId('modalTimerEditTab', 'id'),
        "name": elGetById('modalTimerNameInput').value,
        "interval": interval,
        "enabled": (elGetById('modalTimerEnabledInput').classList.contains('active') ? true : false),
        "startHour": Number(getSelectValueId('modalTimerstartHourInput')),
        "startMinute": Number(getSelectValueId('modalTimerstartMinuteInput')),
        "weekdays": weekdays,
        "action": getData(modalTimerActionInput.options[modalTimerActionInput.selectedIndex].parentNode, 'value'),
        "subaction": getSelectValue(modalTimerActionInput),
        "volume": Number(elGetById('modalTimerVolumeInput').value),
        "playlist": getDataId('modalTimerPlaylistInput', 'value'),
        "preset": preset,
        "arguments": args
    }, saveTimerCheckError, true);
}

/**
 * Handler for the MYMPD_API_TIMER_SAVE jsonrpc response
 * @param {object} obj jsonrpc response
 * @returns {void}
 */
function saveTimerCheckError(obj) {
    if (modalApply(obj) === true) {
        showListTimer();
    }
}

/**
 * Shows the edit timer tab
 * @param {number} timerid the timer id
 * @returns {void}
 */
//eslint-disable-next-line no-unused-vars
function showEditTimer(timerid) {
    cleanupModalId('modalTimer');
    elHideId('timerActionPlay');
    elHideId('modalTimerScriptActionGroup');
    elGetById('modalTimerListTab').classList.remove('active');
    elGetById('modalTimerEditTab').classList.add('active');
    elHideId('modalTimerListFooter');
    elShowId('modalTimerEditFooter');
    elGetById('modalTimerPlaylistInput').filterInput.value = '';

    if (timerid !== 0) {
        sendAPI("MYMPD_API_TIMER_GET", {
            "timerid": timerid
        }, parseEditTimer, false);
    }
    else {
        filterPlaylistsSelect(0, 'modalTimerPlaylistInput', '', '');
        setDataId('modalTimerEditTab', 'id', 0);
        elGetById('modalTimerNameInput').value = '';
        toggleBtnChkId('modalTimerEnabledInput', true);
        elGetById('modalTimerstartHourInput').value = '12';
        elGetById('modalTimerstartMinuteInput').value = '0';
        elGetById('modalTimerActionInput').value = 'startplay';
        elGetById('modalTimerVolumeInput').value = '50';
        elGetById('textTimerVolume').textContent = '50 %';
        selectTimerIntervalChange(86400);
        selectTimerActionChange();
        const weekdayBtns = ['modalTimerMonBtn', 'modalTimerTueBtn', 'modalTimerWedBtn', 'modalTimerThuBtn', 'modalTimerFriBtn', 'modalTimerSatBtn', 'modalTimerSunBtn'];
        for (let i = 0, j = weekdayBtns.length; i < j; i++) {
            toggleBtnId(weekdayBtns[i], false);
        }
        elShowId('timerActionPlay');
    }
    setFocusId('modalTimerNameInput');
}

/**
 * Parses the MYMPD_API_TIMER_GET response
 * @param {object} obj jsonrpc response
 * @returns {void}
 */
function parseEditTimer(obj) {
    filterPlaylistsSelect(0, 'modalTimerPlaylistInput', '', obj.result.playlist);
    setDataId('modalTimerEditTab', 'id', obj.result.timerid);
    elGetById('modalTimerNameInput').value = obj.result.name;
    toggleBtnChkId('modalTimerEnabledInput', obj.result.enabled);
    elGetById('modalTimerstartHourInput').value = obj.result.startHour;
    elGetById('modalTimerstartMinuteInput').value = obj.result.startMinute;
    elGetById('modalTimerActionInput').value = obj.result.subaction;
    selectTimerActionChange(obj.result.arguments);
    selectTimerIntervalChange(obj.result.interval);
    elGetById('modalTimerVolumeInput').value = obj.result.volume;
    elGetById('textTimerVolume').textContent = obj.result.volume + ' %';
    elGetById('modalTimerPresetInput').value = obj.result.preset;

    const weekdayBtns = ['modalTimerMonBtn', 'modalTimerTueBtn', 'modalTimerWedBtn', 'modalTimerThuBtn', 'modalTimerFriBtn', 'modalTimerSatBtn', 'modalTimerSunBtn'];
    for (let i = 0, j = weekdayBtns.length; i < j; i++) {
        toggleBtnId(weekdayBtns[i], obj.result.weekdays[i]);
    }
}

/**
 * Handler for the timer interval select change event
 * @param {number} [value] the timer interval
 * @returns {void}
 */
function selectTimerIntervalChange(value) {
    if (value === undefined) {
        //change event from select itself
        value = Number(getSelectValueId('modalTimerIntervalInput'));
    }
    else {
        if (value !== -1 &&
            value !== 0)
        {
            //repeat
            elGetById('modalTimerIntervalInput').value = '-2';
        }
        else {
            //one shot
            elGetById('modalTimerIntervalInput').value = value;
        }
    }
    if (value !== -1 &&
        value !== 0)
    {
        //repeat
        elShowId('modalTimerIntervalRepeatGroup');
        if (value === -2) {
            //default interval is one day
            value = 86400;
        }
    }
    else {
        //one shot
        elHideId('modalTimerIntervalRepeatGroup');
    }

    const modalTimerIntervalRepeatInput = elGetById('modalTimerIntervalRepeatInput');
    const modalTimerIntervalRepeatUnit = elGetById('modalTimerIntervalRepeatUnit');
    for (const unit of [604800, 86400, 3600, 60, 1]) {
        if (value >= unit &&
            value % unit === 0)
        {
            modalTimerIntervalRepeatInput.value = value / unit;
            modalTimerIntervalRepeatUnit.value = unit;
            break;
        }
    }
}

/**
 * Handler for the timer action change event
 * @param {object} [values] argument values object
 * @returns {void}
 */
function selectTimerActionChange(values) {
    const el = elGetById('modalTimerActionInput');

    if (getSelectValue(el) === 'startplay') {
        elShowId('timerActionPlay');
        elHideId('modalTimerScriptActionGroup');
    }
    else if (el.selectedIndex > -1 &&
             getData(el.options[el.selectedIndex].parentNode, 'value') === 'script')
    {
        elShowId('modalTimerScriptActionGroup');
        elHideId('timerActionPlay');
        showTimerScriptArgs(el.options[el.selectedIndex], values);
    }
    else {
        elHideId('timerActionPlay');
        elHideId('modalTimerScriptActionGroup');
    }
}

/**
 * Shows the arguments for a timer script
 * @param {HTMLElement} optionEl the selected timer script option element
 * @param {object} values argument values object
 * @returns {void}
 */
function showTimerScriptArgs(optionEl, values) {
    if (values === undefined) {
        values = {};
    }
    const args = getData(optionEl, 'arguments');
    const list = elGetById('modalTimerScriptActionArguments');
    elClear(list);
    for (let i = 0, j = args.arguments.length; i < j; i++) {
        const input = elCreateEmpty('input', {"class": ["form-control"], "type": "text", "name": "modalTimerScriptActionArguments" + i,
            "value": (values[args.arguments[i]] ? values[args.arguments[i]] : '')});
        setData(input, 'name', args.arguments[i]);
        const fg = elCreateNodes('div', {"class": ["form-group", "row", "mb-3"]}, [
            elCreateText('label', {"class": ["col-sm-4", "col-form-label"], "for": "modalTimerScriptActionArguments" + i}, args.arguments[i]),
            elCreateNode('div', {"class": ["col-sm-8"]}, input)
        ]);
        list.appendChild(fg);
    }
    if (args.arguments.length === 0) {
        list.textContent = tn('No arguments');
    }
}

/**
 * Shows the list timer tab
 * @returns {void}
 */
function showListTimer() {
    cleanupModalId('modalTimer');
    elGetById('modalTimerListTab').classList.add('active');
    elGetById('modalTimerEditTab').classList.remove('active');
    elShowId('modalTimerListFooter');
    elHideId('modalTimerEditFooter');
    sendAPI("MYMPD_API_TIMER_LIST", {}, parseListTimer, true);
}

/**
 * Parses the MYMPD_API_TIMER_LIST jsonrpc response
 * @param {object} obj jsonrpc response
 * @returns {void}
 */
function parseListTimer(obj) {
    const table = elGetById('modalTimerList');
    const tbody = table.querySelector('tbody');
    if (checkResult(obj, table, 'table') === false) {
        return;
    }
    elClear(tbody);
    const weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
    for (let i = 0; i < obj.result.returnedEntities; i++) {
        const btn = elCreateEmpty('button', {"name": "enabled", "class": ["btn", "btn-secondary", "btn-xs", "mi", "mi-sm"]});
        if (obj.result.data[i].enabled === true) {
            btn.classList.add('active');
            btn.textContent = 'check';
        }
        else {
            btn.textContent = 'radio_button_unchecked';
        }

        const days = [];
        for (let j = 0; j < 7; j++) {
            if (obj.result.data[i].weekdays[j] === true) {
                days.push(tn(weekdays[j]));
            }
        }

        let interval = '';
        switch (obj.result.data[i].interval) {
            case -1: interval = tn('One shot and delete'); break;
            case 0: interval = tn('One shot and disable'); break;
            default:
                for (const unit of [604800, 86400, 3600, 60, 1]) {
                    if (obj.result.data[i].interval >= unit && obj.result.data[i].interval % unit === 0) {
                        interval = tn('Each ' + unit, {"smartCount": obj.result.data[i].interval / unit});
                        break;
                    }
                }
        }

        const row = elCreateNodes('tr', {"title": tn('Edit')}, [
            elCreateText('td', {}, obj.result.data[i].name),
            elCreateNode('td', {}, btn),
            elCreateText('td', {}, zeroPad(obj.result.data[i].startHour, 2) + ':' + zeroPad(obj.result.data[i].startMinute, 2) +
                ' ' + tn('on') + ' ' + days.join(', ')),
            elCreateText('td', {}, interval),
            elCreateText('td', {}, prettyTimerAction(obj.result.data[i].action, obj.result.data[i].subaction)),
            elCreateNode('td', {"data-col": "Action"},
                elCreateText('a', {"class": ["mi", "color-darkgrey"], "href": "#"}, 'delete')
            )
        ]);
        setData(row, 'id', obj.result.data[i].timerid);
        tbody.append(row);
    }
}

/**
 * Pretty prints the timer action
 * @param {string} action the action
 * @param {string} subaction the sub action
 * @returns {string} the translated action
 */
function prettyTimerAction(action, subaction) {
    if (action === 'player' &&
        subaction === 'startplay')
    {
        return tn('Start playback');
    }
    if (action === 'player' &&
        subaction === 'stopplay')
    {
        return tn('Stop playback');
    }
    if (action === 'script') {
        return tn('Script') + ': ' + subaction;
    }
    return action + ': ' + subaction;
}