__  __    __   __  _____      _            _          _____ _          _ _ 
 |  \/  |   \ \ / / |  __ \    (_)          | |        / ____| |        | | |
 | \  / |_ __\ V /  | |__) | __ ___   ____ _| |_ ___  | (___ | |__   ___| | |
 | |\/| | '__|> <   |  ___/ '__| \ \ / / _` | __/ _ \  \___ \| '_ \ / _ \ | |
 | |  | | |_ / . \  | |   | |  | |\ V / (_| | ||  __/  ____) | | | |  __/ | |
 |_|  |_|_(_)_/ \_\ |_|   |_|  |_| \_/ \__,_|\__\___| |_____/|_| |_|\___V 2.1
 if you need WebShell for Seo everyday contact me on Telegram
 Telegram Address : @jackleet
        
        
For_More_Tools: Telegram: @jackleet | Bulk Smtp support mail sender | Business Mail Collector | Mail Bouncer All Mail | Bulk Office Mail Validator | Html Letter private



Upload:

Command:

[email protected]: ~ $
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2024 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */
"use strict";

// Object initialisation
if (typeof akeebabackup === "undefined")
{
    var akeebabackup = {};
}

if (typeof akeebabackup.Configuration === "undefined")
{
    akeebabackup.Configuration = {
        GUI:            {
            /**
             * Renders the label of a configuration option, appending it to the container element
             *
             * @param   {string}   current_id  The input name, e.g. var[something.or.another]
             * @param   {Object}   defdata     The option definition data
             * @param   {Element}  row_div    The element which contains the option itself (the DIV of the current row)
             */
            renderOptionLabel:
                function (current_id, defdata, row_div)
                {
                    const noLabel = (defdata['nolabel'] ?? 0) != 0;

                    // No interface is rendered for 'hidden' and 'none' option types
                    if (noLabel || (defdata["type"] === "hidden") || (defdata["type"] === "none"))
                    {
                        return;
                    }

                    // Create label
                    var label       = document.createElement("label");
                    label.className = "col-sm-3 col-form-label";
                    label.setAttribute("for", current_id);
                    label.innerHTML = defdata["title"];

                    if (defdata["description"])
                    {
                        label.setAttribute("rel", "popover");
                        label.setAttribute("title", defdata["title"]);
                        label.setAttribute("data-bs-content", defdata["description"]);
                    }

                    if (defdata["bold"])
                    {
                        label.className += ' fw-bold';
                    }

                    row_div.appendChild(label);
                },

            /**
             * Renders an option of type "none". A do-not-display field. It doesn't render any input element at all.
             *
             * @param   {string}  current_id       The input name, e.g. var[something.or.another]
             * @param   {Object}  defdata          The option definition data
             * @param   {Element} controlWrapper  The element which contains the option's input object
             * @param   {Element} row_div         The element which contains the option itself (the DIV of the current
             *     row)
             * @param   {Element} container       The element which contains the row_div (option group container)
             */
            renderOptionTypeNone:
                function (current_id, defdata, controlWrapper, row_div, container)
                {
                    // Nothing to render
                },

            /**
             * Renders an option of type "hidden". A hidden field.
             *
             * @param   {string}  current_id      The input name, e.g. var[something.or.another]
             * @param   {Object}  defdata         The option definition data
             * @param   {Element} controlWrapper  The element which contains the option's input object
             * @param   {Element} row_div         The element which contains the option itself (the DIV of the current
             *     row)
             * @param   {Element} container       The element which contains the row_div (option group container)
             */
            renderOptionTypeHidden:
                function (current_id, defdata, controlWrapper, row_div, container)
                {
                    var hiddenfield = document.createElement("input");
                    hiddenfield.id  = current_id;
                    hiddenfield.setAttribute("type", "hidden");
                    hiddenfield.setAttribute("name", current_id);
                    hiddenfield.value = defdata["default"];

                    container.appendChild(hiddenfield);
                },

            /**
             * Renders an option of type "separator". A GUI row separator.
             *
             * @param   {string}  current_id      The input name, e.g. var[something.or.another]
             * @param   {Object}  defdata         The option definition data
             * @param   {Element} controlWrapper  The element which contains the option's input object
             * @param   {Element} row_div         The element which contains the option itself (the DIV of the current
             *     row)
             * @param   {Element} container       The element which contains the row_div (option group container)
             */
            renderOptionTypeSeparator:
                function (current_id, defdata, controlWrapper, row_div, container)
                {
                    const element   = defdata["element"] ?? "div";
                    const content   = defdata["content"] ?? null;
                    const separator = document.createElement(element);

                    if (content)
                    {
                        separator.innerHTML = content;
                    }

                    container.appendChild(separator);
                },

            /**
             * Renders an option of type "installer". An installer selection.
             *
             * @param   {string}  current_id      The input name, e.g. var[something.or.another]
             * @param   {Object}  defdata         The option definition data
             * @param   {Element} controlWrapper  The element which contains the option's input object
             * @param   {Element} row_div         The element which contains the option itself (the DIV of the current
             *     row)
             * @param   {Element} container       The element which contains the row_div (option group container)
             */
            renderOptionTypeInstaller:
                function (current_id, defdata, controlWrapper, row_div, container)
                {
                    // Create the select element
                    var editor       = document.createElement("select");
                    editor.className = "form-select akeeba-configuration-select-installer";
                    editor.id        = current_id;
                    editor.setAttribute("name", current_id);

                    for (var key in akeebabackup.Configuration.installers)
                    {
                        if (!akeebabackup.Configuration.installers.hasOwnProperty(key))
                        {
                            continue;
                        }

                        var element = akeebabackup.Configuration.installers[key];

                        var option       = document.createElement("option");
                        option.value     = key;
                        option.innerHTML = element.name;

                        if (defdata["default"] === key)
                        {
                            option.setAttribute("selected", 1);
                        }

                        editor.appendChild(option);
                    }

                    controlWrapper.appendChild(editor);
                    row_div.appendChild(controlWrapper);
                },

            /**
             * Renders an option of type "engine". An engine selection.
             *
             * @param   {string}  current_id      The input name, e.g. var[something.or.another]
             * @param   {Object}  defdata         The option definition data
             * @param   {Element} controlWrapper  The element which contains the option's input object
             * @param   {Element} row_div         The element which contains the option itself (the DIV of the current
             *     row)
             * @param   {Element} container       The element which contains the row_div (option group container)
             */
            renderOptionTypeEngine:
                function (current_id, defdata, controlWrapper, row_div, container)
                {
                    var engine_type = defdata["subtype"];

                    if (akeebabackup.Configuration.engines[engine_type] == null)
                    {
                        return;
                    }

                    var config_key = current_id.substr(4, current_id.length - 5);

                    // Container for engine parameters, initially hidden
                    var engine_config_container       = document.createElement("div");
                    engine_config_container.id        = config_key + "_config";
                    engine_config_container.className = "akeeba-engine-options";

                    // Create the select element
                    var editor = document.createElement("select");
                    editor.className = 'form-select';
                    editor.id  = current_id;
                    editor.setAttribute("name", current_id);

                    var engineOptions = akeebabackup.Configuration.engines[engine_type];

                    for (var key in engineOptions)
                    {
                        if (!engineOptions.hasOwnProperty(key))
                        {
                            continue;
                        }

                        var element = engineOptions[key];

                        var option       = document.createElement("option");
                        option.value     = key;
                        option.innerHTML = element.information.title;

                        if (defdata["default"] == key)
                        {
                            option.setAttribute("selected", "selected");
                        }

                        editor.appendChild(option);
                    }

                    akeebabackup.System.addEventListener(editor, "change", function (e)
                    {
                        // When the selection changes, we have to repopulate the config container
                        // First, save any changed values
                        var old_values = {};

                        var allElements = [
                            document.getElementById(config_key + "_config").querySelectorAll("input"),
                            document.getElementById(config_key + "_config").querySelectorAll("select")
                        ];

                        var allInputs = null;
                        var input     = null;
                        var id        = null;

                        for (var i = 0; i < allElements.length; i++)
                        {
                            allInputs = allElements[i];

                            if (!allInputs.length)
                            {
                                continue;
                            }

                            for (j = 0; j < allInputs.length; j++)
                            {
                                input = allInputs[j];
                                id    = input.id;

                                old_values[id] = input.value;

                                if ((input.getAttribute("type") === "checkbox") || (input.getAttribute("type") === "radio"))
                                {
                                    old_values[id] = input.checked;
                                }
                                else if (input.getAttribute("type") === "select")
                                {
                                    old_values[id] = input.options[input.selectedIndex].value;
                                }
                            }

                        }

                        // Create the new interface
                        var new_engine        = editor.value;
                        var enginedef         = akeebabackup.Configuration.engines[engine_type][new_engine];
                        var enginetitle       = enginedef.information.title;
                        var new_data          = {};
                        new_data[enginetitle] = enginedef.parameters;

                        akeebabackup.Configuration.parseGuiData(new_data, engine_config_container);

                        var elCardBody = engine_config_container.querySelector("div.card-body");
                        if (elCardBody instanceof Element)
                        {
                            elCardBody.insertAdjacentHTML(
                                "afterbegin",
                                "<p class=\"alert alert-info\">" + enginedef.information.description + "</p>"
                            );
                        }

                        // Reapply changed values
                        allElements = [
                            document.getElementById(config_key + "_config").querySelectorAll("input"),
                            document.getElementById(config_key + "_config").querySelectorAll("select")
                        ];

                        for (i = 0; i < allElements.length; i++)
                        {
                            allInputs = allElements[i];

                            if (!allInputs.length)
                            {
                                continue;
                            }

                            for (var j = 0; j < allInputs.length; j++)
                            {
                                input = allInputs[j];
                                id    = input.id;

                                var old = old_values[id];

                                if ((old == null))
                                {
                                    continue;
                                }

                                if ((input.getAttribute("type") === "checkbox") || (input.getAttribute("type") === "radio"))
                                {
                                    if (old)
                                    {
                                        input.setAttribute("checked", "checked");
                                    }
                                    else
                                    {
                                        input.removeAttribute("checked");
                                    }
                                }
                                else
                                {
                                    input.value = old;
                                }

                                // Trigger the change event for drop-downs
                                if (i === 1)
                                {
                                    akeebabackup.System.triggerEvent(input, "change");
                                }
                            }
                        }

                        // Initialise the new popovers
                        akeebabackup.Configuration.initialisePopovers();

                        // Finally, run the activation_callback
                        if (typeof enginedef.information.activation_callback !== "undefined")
                        {
                            window[enginedef.information.activation_callback](enginedef.parameters);
                        }
                    });

                    // Add a configuration show/hide button
                    var button       = document.createElement("button");
                    button.className = "btn btn-secondary btn-sm";

                    var icon       = document.createElement("span");
                    icon.className = "fa fa-wrench";
                    button.appendChild(icon);

                    var btnText       = document.createElement("span");
                    btnText.innerHTML = Joomla.Text._("COM_AKEEBABACKUP_CONFIG_UI_CONFIG");
                    button.appendChild(btnText);

                    akeebabackup.System.addEventListener(button, "click", function (e)
                    {
                        e.preventDefault();

                        var bsCollapse = new bootstrap.Collapse(engine_config_container);
                        bsCollapse.toggle();
                    });

                    var inputGroupWrapper = document.createElement('div');
                    inputGroupWrapper.className = 'input-group';
                    inputGroupWrapper.appendChild(editor);
                    // inputGroupWrapper.appendChild(button);

                    controlWrapper.appendChild(inputGroupWrapper);
                    controlWrapper.appendChild(engine_config_container);

                    row_div.appendChild(controlWrapper);

                    // Populate config container with the default engine data
                    akeebabackup.System.triggerEvent(editor, 'change');
                },

            /**
             * Renders an option of type "browsedir". A text box with an option to launch a browser.
             *
             * @param   {string}  current_id      The input name, e.g. var[something.or.another]
             * @param   {Object}  defdata         The option definition data
             * @param   {Element} controlWrapper  The element which contains the option's input object
             * @param   {Element} row_div         The element which contains the option itself (the DIV of the current
             *     row)
             * @param   {Element} container       The element which contains the row_div (option group container)
             */
            renderOptionTypeBrowsedir:
                function (current_id, defdata, controlWrapper, row_div, container)
                {
                    var editor = document.createElement("input");
                    editor.className = 'form-control';
                    editor.setAttribute("type", "text");
                    editor.setAttribute("name", current_id);
                    editor.setAttribute("size", "30");
                    editor.id    = current_id;
                    editor.value = defdata["default"];

                    var button       = document.createElement("button");
                    button.className = "btn btn-secondary";
                    button.setAttribute("title", Joomla.Text._("COM_AKEEBABACKUP_CONFIG_UI_BROWSE"));

                    var icon       = document.createElement("span");
                    icon.className = "fa fa-folder-open";
                    button.appendChild(icon);

                    akeebabackup.System.addEventListener(button, "click", function (e)
                    {
                        e.preventDefault();

                        if (akeebabackup.Configuration.onBrowser != null)
                        {
                            akeebabackup.Configuration.onBrowser(editor.value, editor);
                        }

                        return false;
                    });

                    var containerDiv       = document.createElement("div");
                    containerDiv.className = "input-group";

                    containerDiv.appendChild(editor);
                    containerDiv.appendChild(button);

                    controlWrapper.appendChild(containerDiv);
                    row_div.appendChild(controlWrapper);
                },

            /**
             * Renders an option of type "enum". A drop-down list.
             *
             * @param   {string}  current_id      The input name, e.g. var[something.or.another]
             * @param   {Object}  defdata         The option definition data
             * @param   {Element} controlWrapper  The element which contains the option's input object
             * @param   {Element} row_div         The element which contains the option itself (the DIV of the current
             *     row)
             * @param   {Element} container       The element which contains the row_div (option group container)
             */
            renderOptionTypeEnum:
                function (current_id, defdata, controlWrapper, row_div, container)
                {
                    var editor       = document.createElement("select");
                    editor.className = "form-select akeeba-configuration-select-enum";
                    editor.id        = current_id;
                    editor.setAttribute("name", current_id);

                    // Create and append options
                    var enumvalues = defdata["enumvalues"].split("|");
                    var enumkeys   = defdata["enumkeys"].split("|");

                    for (var counter = 0; counter < enumvalues.length; counter++)
                    {
                        var value = enumvalues[counter];

                        var item_description = enumkeys[counter];
                        var option           = document.createElement("option");
                        option.value         = value;
                        option.innerHTML     = item_description;

                        if (value === defdata["default"])
                        {
                            option.setAttribute("selected", "selected");
                        }

                        editor.appendChild(option);
                    }

                    if (typeof defdata["onchange"] !== "undefined")
                    {
                        akeebabackup.System.addEventListener(editor, "change", function ()
                        {
                            var callback_onchange = defdata["onchange"];
                            callback_onchange(editor);
                        });
                    }

                    controlWrapper.appendChild(editor);
                    row_div.appendChild(controlWrapper);
                },

            /**
             * Renders an option of type "string". A simple single-line, unvalidated text box.
             *
             * @param   {string}  current_id      The input name, e.g. var[something.or.another]
             * @param   {Object}  defdata         The option definition data
             * @param   {Element} controlWrapper  The element which contains the option's input object
             * @param   {Element} row_div         The element which contains the option itself (the DIV of the current
             *     row)
             * @param   {Element} container       The element which contains the row_div (option group container)
             */
            renderOptionTypeString:
                function (current_id, defdata, controlWrapper, row_div, container)
                {
                    var editor       = document.createElement("input");
                    editor.className = "form-control akeeba-configuration-string";
                    editor.setAttribute("type", "text");
                    editor.id = current_id;
                    editor.setAttribute("name", current_id);
                    editor.value = defdata["default"];

                    controlWrapper.appendChild(editor);
                    row_div.appendChild(controlWrapper);
                },

            /**
             * Renders an option of type "password". A simple single-line, unvalidated password box.
             *
             * @param   {string}  current_id      The input name, e.g. var[something.or.another]
             * @param   {Object}  defdata         The option definition data
             * @param   {Element} controlWrapper  The element which contains the option's input object
             * @param   {Element} row_div         The element which contains the option itself (the DIV of the current
             *     row)
             * @param   {Element} container       The element which contains the row_div (option group container)
             */
            renderOptionTypePassword:
                function (current_id, defdata, controlWrapper, row_div, container)
                {
                    akeebabackup.Configuration.passwordFields[current_id] = defdata["default"];

                    var editor       = document.createElement("input");
                    editor.className = "form-control akeeba-configuration-password";
                    editor.setAttribute("type", "password");
                    editor.id = current_id;
                    editor.setAttribute("name", current_id);
                    editor.setAttribute("size", 40);
                    editor.value = defdata["default"];
                    editor.setAttribute("autocomplete", "off");

                    controlWrapper.appendChild(editor);
                    row_div.appendChild(controlWrapper);
                },

            /**
             * Renders an option of type "integer". Hidden form element with the real value.
             *
             * @param   {string}  current_id      The input name, e.g. var[something.or.another]
             * @param   {Object}  defdata         The option definition data
             * @param   {Element} controlWrapper  The element which contains the option's input object
             * @param   {Element} row_div         The element which contains the option itself (the DIV of the current
             *     row)
             * @param   {Element} container       The element which contains the row_div (option group container)
             */
            renderOptionTypeInteger:
                function (current_id, defdata, controlWrapper, row_div, container)
                {
                    var config_key = current_id.substr(4, current_id.length - 5);

                    // Hidden input field. Holds the actual value saved to the configuration.
                    var elHiddenInput = document.createElement("input");
                    elHiddenInput.id  = config_key;
                    elHiddenInput.setAttribute("name", current_id);
                    elHiddenInput.setAttribute("type", "hidden");
                    elHiddenInput.value = defdata["default"];

                    // Custom value input box
                    var minValue = defdata["min"] / defdata["scale"];
                    var maxValue = defdata["max"] / defdata["scale"];
                    var stepValue = 1 / defdata["scale"];

                    if (defdata["scale"] > 1)
                    {
                        minValue = minValue.toFixed(2);
                        maxValue = maxValue.toFixed(2);
                        stepValue = stepValue.toFixed(2);
                    }
                    else
                    {
                        minValue = Math.trunc(minValue);
                        maxValue = Math.trunc(maxValue);
                        stepValue = Math.trunc(stepValue);
                    }

                    var elCustomValue = document.createElement("input");
                    elCustomValue.className = 'form-control';
                    elCustomValue.setAttribute("type", "number");
                    elCustomValue.id            = config_key + "_custom";
                    elCustomValue.style.display = "none";
                    elCustomValue.setAttribute("min", minValue);
                    elCustomValue.setAttribute("max", maxValue);
                    elCustomValue.setAttribute("step", stepValue);

                    akeebabackup.System.addEventListener(elCustomValue, "blur", function ()
                    {
                        var value = parseFloat(elCustomValue.value);
                        value     = value * defdata["scale"];

                        if (value < defdata["min"])
                        {
                            value = defdata["min"];
                        }
                        else if (value > defdata["max"])
                        {
                            value = defdata["max"];
                        }

                        elHiddenInput.value = value;

                        var newValue = value / defdata["scale"];

                        if (defdata["scale"] > 1)
                        {
                            elCustomValue.value = newValue.toFixed(2);
                        }
                        else
                        {
                            elCustomValue.value = Math.trunc(newValue);
                        }


                    });

                    // Select element with preset options
                    var elDropdown = document.createElement("select");
                    elDropdown.id  = config_key + "_dropdown";
                    elDropdown.setAttribute("name", config_key + "_dropdown");
                    elDropdown.className = "form-select";

                    // Create and append the preset options to the select element
                    var enumvalues     = defdata["shortcuts"].split("|");
                    var quantizer      = defdata["scale"];
                    var isPresetOption = false;

                    for (var counter = 0; counter < enumvalues.length; counter++)
                    {
                        var value = enumvalues[counter];

                        var item_description       = value / quantizer;
                        var elDropdownOption       = document.createElement("option");
                        elDropdownOption.value     = value;
                        elDropdownOption.innerHTML = (quantizer > 1) ? item_description.toFixed(2) : Math.trunc(item_description);

                        if (value == defdata["default"])
                        {
                            elDropdownOption.setAttribute("selected", "selected");
                            isPresetOption = true;
                        }

                        elDropdown.appendChild(elDropdownOption);
                    }

                    // Create one last option called "Custom"
                    var option       = document.createElement("option");
                    option.value     = -1;
                    option.innerHTML = Joomla.Text._('COM_AKEEBABACKUP_CONFIG_UI_CUSTOM');

                    if (!isPresetOption)
                    {
                        option.setAttribute("selected", "selected");
                        elCustomValue.value         = (defdata["default"] / defdata["scale"]).toFixed(2);
                        elCustomValue.style.display = "block";
                    }

                    elDropdown.appendChild(option);

                    // Add actions to the dropdown
                    akeebabackup.System.addEventListener(elDropdown, "change", function ()
                    {
                        var value = elDropdown.value;

                        if (value === '-1')
                        {
                            elCustomValue.value         = (defdata["default"] / defdata["scale"]).toFixed(2);
                            elCustomValue.style.display = "block";
                            akeebabackup.System.triggerEvent(elCustomValue, "focus");

                            return;
                        }

                        elHiddenInput.value         = value;
                        elCustomValue.style.display = "none";
                    });

                    var inputGroupWrapper = document.createElement('div');
                    inputGroupWrapper.className = 'input-group';

                    // Label
                    var uom = defdata["uom"];

                    inputGroupWrapper.appendChild(elDropdown);
                    inputGroupWrapper.appendChild(elCustomValue);

                    if ((typeof (uom) === "string") && (uom.length !== 0))
                    {
                        var label         = document.createElement("span");
                        label.className = 'input-group-text';
                        label.textContent = uom;

                        inputGroupWrapper.appendChild(label);
                    }

                    controlWrapper.appendChild(inputGroupWrapper);
                    controlWrapper.appendChild(elHiddenInput);

                    row_div.appendChild(controlWrapper);
                },

            /**
             * Renders an option of type "bool". A toggle button.
             *
             * @param   {string}  current_id      The input name, e.g. var[something.or.another]
             * @param   {Object}  defdata         The option definition data
             * @param   {Element} controlWrapper  The element which contains the option's input object
             * @param   {Element} row_div         The element which contains the option itself (the DIV of the current
             *     row)
             * @param   {Element} container       The element which contains the row_div (option group container)
             */
            renderOptionTypeBool:
                function (current_id, defdata, controlWrapper, row_div, container)
                {
                    var newId = current_id.match(/[a-z0-9]/ig).join('');

                    var wrap_div       = document.createElement("div");
                    wrap_div.className = "btn-group";
                    wrap_div.setAttribute('role', 'group');

                    var elYesInput = document.createElement('input');
                    elYesInput.type = 'radio';
                    elYesInput.className = 'btn-check';
                    elYesInput.name = current_id;
                    elYesInput.setAttribute('autocomplete', 'off');
                    elYesInput.id = newId + '_1';
                    elYesInput.value = '1';

                    var elYesLabel = document.createElement('label');
                    elYesLabel.className = 'btn btn-outline-success';
                    elYesLabel.setAttribute('for', newId + '_1');
                    elYesLabel.innerText = Joomla.Text._('JYES');

                    var elNoInput = document.createElement('input');
                    elNoInput.type = 'radio';
                    elNoInput.className = 'btn-check';
                    elNoInput.name = current_id;
                    elNoInput.setAttribute('autocomplete', 'off');
                    elNoInput.id = newId + '_0';
                    elNoInput.value = 0;

                    var elNoLabel = document.createElement('label');
                    elNoLabel.className = 'btn btn-outline-danger';
                    elNoLabel.setAttribute('for', newId + '_0');
                    elNoLabel.innerText = Joomla.Text._('JNO');

                    if (defdata["default"] != 0)
                    {
                        elYesInput.setAttribute("checked", "checked");
                    }
                    else
                    {
                        elNoInput.setAttribute("checked", "checked");
                    }

                    wrap_div.appendChild(elYesInput);
                    wrap_div.appendChild(elYesLabel);
                    wrap_div.appendChild(elNoInput);
                    wrap_div.appendChild(elNoLabel);
                    controlWrapper.appendChild(wrap_div);
                    row_div.appendChild(controlWrapper);
                },

            /**
             * Renders an option of type "button". Button with a custom hook function.
             *
             * @param   {string}  current_id      The input name, e.g. var[something.or.another]
             * @param   {Object}  defdata         The option definition data
             * @param   {Element} controlWrapper  The element which contains the option's input object
             * @param   {Element} row_div         The element which contains the option itself (the DIV of the current
             *     row)
             * @param   {Element} container       The element which contains the row_div (option group container)
             */
            renderOptionTypeButton:
                function (current_id, defdata, controlWrapper, row_div, container)
                {
                    // Create the button
                    var label        = row_div.querySelector("label");
                    var hook         = defdata["hook"];
                    var labeltext    = label.innerHTML;
                    var editor       = document.createElement("button");
                    editor.id        = current_id;
                    editor.innerHTML = labeltext;
                    editor.className = "btn btn-secondary";
                    label.innerHTML  = "&nbsp;";

                    akeebabackup.System.addEventListener(editor, "click", function (e)
                    {
                        e.preventDefault();

                        try
                        {
                            window[hook]();
                        }
                        catch (err)
                        {
                        }
                    });

                    controlWrapper.appendChild(editor);
                    row_div.appendChild(controlWrapper);
                },

            /**
             * Renders an option of an unknown type (an extension is used).
             *
             * @param   {string}  current_id      The input name, e.g. var[something.or.another]
             * @param   {Object}  defdata         The option definition data+
             * @param   {Element} controlWrapper  The element which contains the option's input object
             * @param   {Element} row_div         The element which contains the option itself (the DIV of the current
             *     row)
             * @param   {Element} container       The element which contains the row_div (option group container)
             */
            renderOptionTypeUnknown:
                function (current_id, defdata, controlWrapper, row_div, container)
                {
                    var config_key = current_id.substr(4, current_id.length - 5);

                    var method = "akeeba_render_" + defdata["type"];
                    var fn     = window[method];

                    if (typeof fn == "function")
                    {
                        fn(config_key, defdata, label, row_div);
                    }
                    else
                    {
                        try
                        {
                            window[method](config_key, defdata, label, row_div);
                        }
                        catch (e)
                        {

                        }
                    }
                }
        },
        engines:        {},
        installers:     {},
        URLs:           {},
        FtpTest:        {
            testConnection:
                function (buttonKey, configKey, isCurl)
                {
                    var button = document.getElementById("var[" + buttonKey + "]");

                    akeebabackup.Configuration.FtpTest.buttonKey = "var[" + buttonKey + "]";

                    if (button === null)
                    {
                        button = document.getElementById(buttonKey);

                        akeebabackup.Configuration.FtpTest.buttonKey = buttonKey;
                    }

                    if (button === null)
                    {
                        console.warn("Button " + akeebabackup.Configuration.FtpTest.buttonKey + " not found");

                        return;
                    }

                    button.setAttribute("disabled", "disabled");

                    var data = {};

                    try
                    {
                        var ssl_key = "var[" + configKey + ".ftps]";
                        var passive_key = "var[" + configKey + ".passive_mode]"

                        data = {
                            isCurl:                  (isCurl ? 1 : 0),
                            host:                    document.getElementById("var[" + configKey + ".host]").value,
                            port:                    document.getElementById("var[" + configKey + ".port]").value,
                            user:                    document.getElementById("var[" + configKey + ".user]").value,
                            pass:                    document.getElementById("var[" + configKey + ".pass]").value,
                            initdir:                 document.getElementById("var[" + configKey + ".initial_directory]").value,
                            usessl:                  document.querySelector('input[name="' + ssl_key +'"]:checked').value,
                            passive:                 document.querySelector('input[name="' + passive_key +'"]:checked').value,
                            passive_mode_workaround: 0
                        };
                    }
                    catch (e)
                    {
                        data = {
                            isCurl:                  (isCurl ? 1 : 0),
                            host:                    document.getElementById(configKey + "_host").value,
                            port:                    document.getElementById(configKey + "_port").value,
                            user:                    document.getElementById(configKey + "_user").value,
                            pass:                    document.getElementById(configKey + "_pass").value,
                            initdir:                 document.getElementById(configKey + "_initial_directory").value,
                            usessl:                  document.getElementById(configKey + "_ftps").checked,
                            passive:                 document.getElementById(configKey + "_passive_mode").checked,
                            passive_mode_workaround: 0
                        };
                    }

                    // The passive_mode_workaround input is only defined for cURL
                    if (isCurl)
                    {
                        try
                        {
                            data.passive_mode_workaround =
                                document.querySelector('input[name="var[' + configKey + '.passive_mode_workaround]"]:checked').value;
                        }
                        catch (e)
                        {
                            data.passive_mode_workaround =
                                document.getElementById(configKey + "_passive_mode_workaround").checked;
                        }
                    }

                    // Construct the query
                    akeebabackup.System.params.AjaxURL = akeebabackup.Configuration.URLs.testFtp;

                    akeebabackup.System.doAjax(
                        data,
                        function (res)
                        {
                            var button = document.getElementById(akeebabackup.Configuration.FtpTest.buttonKey);
                            button.removeAttribute("disabled");

                            var elTestFTPBodyOK   = document.getElementById("testFtpDialogBodyOk");
                            var elTestFTPBodyFail = document.getElementById("testFtpDialogBodyFail");
                            var elTestFTPLabel    = document.getElementById("testFtpDialogLabel");

                            elTestFTPBodyOK.style.display   = "none";
                            elTestFTPBodyFail.style.display = "none";

                            if (res?.status === true)
                            {
                                elTestFTPLabel.textContent      =
                                    Joomla.Text._("COM_AKEEBABACKUP_CONFIG_DIRECTFTP_TEST_OK");
                                elTestFTPBodyOK.textContent     =
                                    Joomla.Text._("COM_AKEEBABACKUP_CONFIG_DIRECTFTP_TEST_OK");
                                elTestFTPBodyOK.style.display   = "block";
                                elTestFTPBodyFail.style.display = "none";
                            }
                            else
                            {
                                elTestFTPLabel.textContent      =
                                    Joomla.Text._("COM_AKEEBABACKUP_CONFIG_DIRECTFTP_TEST_FAIL");
                                elTestFTPBodyFail.textContent   = res?.status ?? '???';
                                elTestFTPBodyOK.style.display   = "none";
                                elTestFTPBodyFail.style.display = "block";
                            }

                            // Use Bootstrap to open the modal
                            var myModal = new bootstrap.Modal(document.getElementById('testFtpDialog'), {
                                keyboard: true,
                                backdrop: true
                            });
                            myModal.show();

                        }, null, false, 15000
                    )
                }
        },
        SftpTest:       {
            testConnection:
                function (buttonKey, configKey, isCurl)
                {
                    var button                              = document.getElementById("var[" + buttonKey + "]");
                    akeebabackup.Configuration.SftpTest.buttonKey = "var[" + buttonKey + "]";

                    button.setAttribute("disabled", "disabled");

                    var data = {
                        isCurl:  (isCurl ? 1 : 0),
                        host:    document.getElementById("var[" + configKey + ".host]").value,
                        port:    document.getElementById("var[" + configKey + ".port]").value,
                        user:    document.getElementById("var[" + configKey + ".user]").value,
                        pass:    document.getElementById("var[" + configKey + ".pass]").value,
                        initdir: document.getElementById("var[" + configKey + ".initial_directory]").value,
                        privkey: document.getElementById("var[" + configKey + ".privkey]").value,
                        pubkey:  document.getElementById("var[" + configKey + ".pubkey]").value
                    };

                    // Construct the query
                    akeebabackup.System.params.AjaxURL = akeebabackup.Configuration.URLs.testSftp;

                    akeebabackup.System.doAjax(
                        data,
                        function (res)
                        {
                            var button = document.getElementById(akeebabackup.Configuration.SftpTest.buttonKey);
                            button.removeAttribute("disabled");

                            var elTestFTPBodyOK   = document.getElementById("testFtpDialogBodyOk");
                            var elTestFTPBodyFail = document.getElementById("testFtpDialogBodyFail");
                            var elTestFTPLabel    = document.getElementById("testFtpDialogLabel");

                            elTestFTPBodyOK.style.display   = "none";
                            elTestFTPBodyFail.style.display = "none";

                            if (res?.status === true)
                            {
                                elTestFTPLabel.textContent      =
                                    Joomla.Text._("COM_AKEEBABACKUP_CONFIG_DIRECTSFTP_TEST_OK");
                                elTestFTPBodyOK.textContent     =
                                    Joomla.Text._("COM_AKEEBABACKUP_CONFIG_DIRECTSFTP_TEST_OK");
                                elTestFTPBodyOK.style.display   = "block";
                                elTestFTPBodyFail.style.display = "none";
                            }
                            else
                            {
                                elTestFTPLabel.textContent      =
                                    Joomla.Text._("COM_AKEEBABACKUP_CONFIG_DIRECTSFTP_TEST_FAIL");
                                elTestFTPBodyFail.textContent   = res?.status ?? '???';
                                elTestFTPBodyOK.style.display   = "none";
                                elTestFTPBodyFail.style.display = "block";
                            }

                            // Use Bootstrap to open the modal
                            var myModal = new bootstrap.Modal(document.getElementById('testFtpDialog'), {
                                keyboard: true,
                                backdrop: true
                            });
                            myModal.show();
                        }, null, false, 15000
                    )
                }
        },
        FtpModal:       null,
        passwordFields: {},
        fsBrowser:      {
            params:      {
                dialogId:     "folderBrowserDialog",
                dialogBodyId: "folderBrowserDialogBody"
            },
            modalObject: null
        },

        /**
         * Parses the JSON decoded data object defining engine and GUI parameters for the
         * configuration page
         *
         * @param  data  The nested objects of engine and GUI definitions
         */
        parseConfigData:
            function (data)
            {
                akeebabackup.Configuration.engines    = data.engines;
                akeebabackup.Configuration.installers = data.installers;
                akeebabackup.Configuration.parseGuiData(data.gui);
            },

        /**
         * Restores the contents of the password fields after brain-dead browsers with broken password managers try to
         * auto-fill the wrong password to the wrong field without warning you or asking you.
         */
        restoreDefaultPasswords:
            function ()
            {
                for (var curid in akeebabackup.Configuration.passwordFields)
                {
                    if (!akeebabackup.Configuration.passwordFields.hasOwnProperty(curid))
                    {
                        continue;
                    }

                    var defvalue = akeebabackup.Configuration.passwordFields[curid];

                    var myElement = document.getElementById(curid);

                    if (!myElement)
                    {
                        continue;
                    }

                    // Do not remove this line. It's required when defvalue is empty. Why? BECAUSE BROWSERS ARE BRAIN
                    // DEAD!
                    myElement.value = "WORKAROUND FOR NAUGHTY BROWSERS";
                    // This line finally sets the fields back to its default value.
                    myElement.value = defvalue;
                }
            },

        /**
         * Opens a filesystem folder browser
         *
         * @param  folder   The folder to start browsing from
         * @param  element  The element whose value we'll modify when this browser returns
         */
        onBrowser:
            function (folder, element)
            {
                // Close dialog callback (user confirmed the new folder)
                akeebabackup.Configuration.onBrowserCallback = function (myFolder)
                {
                    element.value = myFolder;

                    if ((typeof akeebabackup.Configuration.fsBrowser.modalObject == "object") && akeebabackup.Configuration.fsBrowser.modalObject.close)
                    {
                        akeebabackup.Configuration.fsBrowser.modalObject.close()
                    }
                    else if ((typeof akeebabackup.Configuration.fsBrowser.modalObject == "object") && akeebabackup.Configuration.fsBrowser.modalObject.hide)
                    {
                        akeebabackup.Configuration.fsBrowser.modalObject.hide()
                    }
                };

                // URL to load the browser
                var browserSrc = akeebabackup.Configuration.URLs["browser"] + encodeURIComponent(folder);

                var dialogBody = document.getElementById(akeebabackup.Configuration.fsBrowser.params.dialogBodyId);

                dialogBody.innerHTML = "";

                var iFrame = document.createElement("iframe");
                iFrame.setAttribute("src", browserSrc);
                iFrame.setAttribute("width", "100%");
                iFrame.setAttribute("height", 400);
                iFrame.setAttribute("frameborder", 0);
                iFrame.setAttribute("allowtransparency", "true");

                dialogBody.appendChild(iFrame);

                // Use Bootstrap to open the modal
                akeebabackup.Configuration.fsBrowser.modalObject = new bootstrap.Modal(
                    document.getElementById(akeebabackup.Configuration.fsBrowser.params.dialogId), {
                        keyboard: false,
                        backdrop: 'static'
                    });
                akeebabackup.Configuration.fsBrowser.modalObject.show();
            },

        /**
         * Parses the main configuration GUI definition, generating the on-page widgets
         *
         * @param  data      The nested objects of the GUI definition ('gui' key of JSON data)
         * @param  rootnode  The jroot DOM element in which to create the widgets
         */
        parseGuiData:
            function (data, rootnode)
            {
                if (rootnode == null)
                {
                    // The default root node is the form itself
                    rootnode = document.getElementById("akeebagui");
                }

                // Begin by slashing contents of the akeebagui DIV
                rootnode.innerHTML = "";

                // This is the workhorse, looping through groupdefs and creating HTML elements
                var group_id = 0;

                for (var headertext in data)
                {
                    if (!data.hasOwnProperty(headertext))
                    {
                        continue;
                    }

                    var groupdef = data[headertext];

                    // Loop for each group definition
                    group_id++;

                    if (!groupdef)
                    {
                        continue;
                    }

                    // Each group is a Bootstrap card. The outer container which is appended to the root node.
                    var cardOuterWrapper = document.createElement('div');
                    cardOuterWrapper.className = 'card mt-3 rounded-top';
                    rootnode.appendChild(cardOuterWrapper);

                    // The card has a header which is appended to the outer wrapper
                    var header = document.createElement('h3');
                    header.className = 'card-header';
                    header.innerHTML = headertext;
                    header.id = "auigrp_" + rootnode.id + "_" + group_id;
                    cardOuterWrapper.appendChild(header);

                    // All the options are rendered inside the card body
                    var container       = document.createElement("div");
                    container.className = "card-body";
                    cardOuterWrapper.appendChild(container);

                    // Loop each element, rendering a row that gets appended to the container.
                    for (var config_key in groupdef)
                    {
                        if (!groupdef.hasOwnProperty(config_key))
                        {
                            continue;
                        }

                        var defdata = groupdef[config_key];

                        // Parameter ID
                        var current_id = "var[" + config_key + "]";

                        // Option row DIV
                        var showOn        = defdata['showon'] ?? null;
                        var row_div       = document.createElement("div");
                        row_div.className = "row mb-3";
                        row_div.id        = "akconfigrow." + config_key;

                        if (showOn)
                        {
                            row_div.dataset.showon = JSON.stringify(showOn);
                        }
                        /**
                         * We must append the option row to the container only if the option type is NOT 'hidden' or
                         * 'none'. These two option types are non-GUI elements. We only render a hidden field for them.
                         * The hidden field is rendered without a row container so that we don't create an empty row in
                         * the interface.
                         */
                        if ((defdata["type"] != "hidden") && (defdata["type"] != "none"))
                        {
                            container.appendChild(row_div);
                        }

                        // Render the label, if applicable
                        akeebabackup.Configuration.GUI.renderOptionLabel(current_id, defdata, row_div);

                        // Create GUI representation based on type
                        var controlWrapper       = document.createElement("div");
                        controlWrapper.className = "col-sm-9";

                        var ucfirstType  = defdata["type"][0].toUpperCase() + defdata["type"].slice(1);
                        var renderMethod = "renderOptionType" + ucfirstType;

                        if (typeof akeebabackup.Configuration.GUI[renderMethod] === "function")
                        {
                            akeebabackup.Configuration.GUI[renderMethod](
                                current_id, defdata, controlWrapper, row_div, container);
                        }
                        else
                        {
                            akeebabackup.Configuration.GUI.renderOptionTypeUnknown(current_id, defdata, controlWrapper,
                                row_div,
                                container
                            );
                        }
                    }
                }

                // Re-initialise the ShowOn JavaScript
                Joomla.Showon.initialise(rootnode);
            },

        onChangeScriptType:
            function (selectElement)
            {
                // Currently selected value
                var value             = selectElement.options[selectElement.selectedIndex].value;
                var possibleInstaller = (value === "joomla") ? "angie" : ("angie-" + value);

                // All possible installers
                var installerSelect   = document.getElementById("var[akeeba.advanced.embedded_installer]");
                var installerElements = installerSelect.children;

                for (var i = 0; i < installerElements.length; i++)
                {
                    var element = installerElements[i];

                    if (element.value === possibleInstaller)
                    {
                        installerSelect.value = possibleInstaller;

                        return;
                    }
                }
            },

        initialisePopovers:
            function()
            {
                var popovers = Joomla.getOptions('bootstrap.popover');

                if (typeof popovers !== 'object' || popovers === null)
                {
                    return;
                }

                Object.keys(popovers).forEach(function (popover)
                {
                    var opt     = popovers[popover];
                    var options = {
                        animation:         opt.animation ? opt.animation : true,
                        container:         opt.container ? opt.container : false,
                        //content:           opt.content ? opt.content : "",
                        delay:             opt.delay ? opt.delay : 0,
                        html:              opt.html ? opt.html : false,
                        placement:         opt.placement ? opt.placement : "top",
                        selector:          opt.selector ? opt.selector : false,
                        //title:             opt.title ? opt.title : "",
                        trigger:           opt.trigger ? opt.trigger : "click",
                        offset:            opt.offset ? opt.offset : 0,
                        fallbackPlacement: opt.fallbackPlacement ? opt.fallbackPlacement : "flip",
                        boundary:          opt.boundary ? opt.boundary : "scrollParent",
                        customClass:       opt.customClass ? opt.customClass : "",
                        sanitize:          opt.sanitize ? opt.sanitize : true,
                        sanitizeFn:        opt.sanitizeFn ? opt.sanitizeFn : null,
                        popperConfig:      opt.popperConfig ? opt.popperConfig : null,
                    };

                    if (opt.template)
                    {
                        options.template = opt.template;
                    }
                    if (opt.allowList)
                    {
                        options.allowList = opt.allowList;
                    }

                    var elements = Array.from(document.querySelectorAll(popover));
                    if (elements.length)
                    {
                        elements.map(function (el)
                        {
                            new window.bootstrap.Popover(el, options)
                        });
                    }
                });
            },

        onFormSubmit: (event) => {
            const adminForm = document.getElementById("adminForm");

            // Old browsers: submit the form with 100+ parameters.
            if (
                !adminForm || typeof window.FormData === "undefined"
                || typeof Object.fromEntries === "undefined" || typeof JSON.stringify === "undefined"
            )
            {
                return true;
            }

            // Newer browsers: submit as a single JSON document
            event.preventDefault();

            akeebabackup.Configuration.submitJsonForm(
                JSON.stringify(Object.fromEntries(new FormData(adminForm)))
            );
        },

        submitJsonForm: async function(json) {
            // Set up our virtual form
            const formData = new FormData();
            formData.set('jsonForm', json);
            formData.set(Joomla.getOptions('csrf.token'), 1);
            formData.set('task', document.forms['adminForm']['task'].value);

            // Initialise the redirection detection code.
            let url = null;

            try {
                // Do an async POST to the profile config save endpoint
                const response = await fetch(
                    document.forms['adminForm'].action,
                    {
                        method: "POST",
                        body: formData,
                    });

                // Redirections will be respected by using their URL as the ultimate redirection target.
                if (response.redirected)
                {
                    url = response.url;
                }

                // Something went wrong. Reload the configuration page.
                if (!response.ok) {
                    throw new Error('Error found in network response');
                }
            } catch (error) {
                location.reload();

                return;
            }

            // No redirection URL detected; reload the page.
            if (!url)
            {
                location.reload();

                return;
            }

            // We have a redirection URL; follow it.
            window.location = url;
        }
    };
}


// =====================================================================================================================
// Initialise hooks used by the engine definitions INI files
// =====================================================================================================================

function directftp_test_connection()
{
    akeebabackup.Configuration.FtpTest.testConnection("engine.archiver.directftp.ftp_test", "engine.archiver.directftp", 0);
}

function postprocftp_test_connection()
{
    akeebabackup.Configuration.FtpTest.testConnection("engine.postproc.ftp.ftp_test", "engine.postproc.ftp", 0);
}

function directftpcurl_test_connection()
{
    akeebabackup.Configuration.FtpTest.testConnection("engine.archiver.directftpcurl.ftp_test",
        "engine.archiver.directftpcurl", 1
    );
}

function postprocftpcurl_test_connection()
{
    akeebabackup.Configuration.FtpTest.testConnection("engine.postproc.ftpcurl.ftp_test", "engine.postproc.ftpcurl", 1);
}

function directsftp_test_connection()
{
    akeebabackup.Configuration.SftpTest.testConnection("engine.archiver.directsftp.sftp_test", "engine.archiver.directsftp",
        0
    );
}

function postprocsftp_test_connection()
{
    akeebabackup.Configuration.SftpTest.testConnection("engine.postproc.sftp.sftp_test", "engine.postproc.sftp", 0);
}

function directsftpcurl_test_connection()
{
    akeebabackup.Configuration.SftpTest.testConnection("engine.archiver.directsftpcurl.sftp_test",
        "engine.archiver.directsftpcurl", 1
    );
}

function postprocsftpcurl_test_connection()
{
    akeebabackup.Configuration.SftpTest.testConnection("engine.postproc.sftpcurl.sftp_test", "engine.postproc.sftpcurl", 1);
}

function akconfig_dropbox_openoauth()
{
    var url = akeebabackup.Configuration.URLs.dpeauthopen;

    if (url.indexOf("?") == -1)
    {
        url = url + "?";
    }
    else
    {
        url = url + "&";
    }

    window.open(url + "engine=dropbox", "akeeba_dropbox_window", "width=1010,height=500,opener");
}

function akconfig_dropbox_gettoken()
{
    akeebabackup.System.AjaxURL = akeebabackup.Configuration.URLs["dpecustomapi"];

    var data = {
        engine: "dropbox",
        method: "getauth"
    };

    akeebabackup.System.doAjax(
        data,
        function (res)
        {
            if (res["error"] != "")
            {
                alert("ERROR: Could not complete authentication; please retry");
            }
            else
            {
                document.getElementById("var[engine.postproc.dropbox.token]").value        = res.token.oauth_token;
                document.getElementById("var[engine.postproc.dropbox.token_secret]").value =
                    res.token.oauth_token_secret;
                document.getElementById("var[engine.postproc.dropbox.uid]").value          = res.token.uid;
                alert("Authentication successful!");
            }
        }, function (errorMessage)
        {
            alert("ERROR: Could not complete authentication; please retry" + "\n" + errorMessage);
        }, false, 15000
    );
}

function akconfig_dropbox2_openoauth()
{
    var url = akeebabackup.Configuration.URLs.dpeauthopen;

    if (url.indexOf("?") == -1)
    {
        url = url + "?";
    }
    else
    {
        url = url + "&";
    }

    window.open(url + "engine=dropbox2", "akeeba_dropbox2_window", "width=1010,height=500,opener");
}

function akeeba_dropbox2_oauth_callback(data)
{
    // Update the tokens
    document.getElementById("var[engine.postproc.dropbox2.access_token]").value  = data.access_token;
    document.getElementById("var[engine.postproc.dropbox2.refresh_token]").value = data.refresh_token;

    // Close the window
    var myWindow = window.open("", "akeeba_dropbox2_window");
    myWindow.close();
}

function akconfig_onedrive_openoauth()
{
    var url = akeebabackup.Configuration.URLs.dpeauthopen;

    if (url.indexOf("?") == -1)
    {
        url = url + "?";
    }
    else
    {
        url = url + "&";
    }

    window.open(url + "engine=onedrive", "akeeba_onedrive_window", "width=1010,height=500,opener");
}

function akeeba_onedrive_oauth_callback(data)
{
    // Update the tokens
    document.getElementById("var[engine.postproc.onedrive.access_token]").value  = data.access_token;
    document.getElementById("var[engine.postproc.onedrive.refresh_token]").value = data.refresh_token;

    // Close the window
    var myWindow = window.open("", "akeeba_onedrive_window");
    myWindow.close();
}

function akconfig_onedrivebusiness_openoauth()
{
    var url = akeebabackup.Configuration.URLs.dpeauthopen;

    if (url.indexOf("?") == -1)
    {
        url = url + "?";
    }
    else
    {
        url = url + "&";
    }

    window.open(url + "engine=onedrivebusiness", "akeeba_onedrivebusiness_window", "width=1010,height=500,opener");
}

function akeeba_onedrivebusiness_oauth_callback(data)
{
    // Update the tokens
    document.getElementById("var[engine.postproc.onedrivebusiness.access_token]").value  = data.access_token;
    document.getElementById("var[engine.postproc.onedrivebusiness.refresh_token]").value = data.refresh_token;

    // Close the window
    var myWindow = window.open("", "akeeba_onedrivebusiness_window");
    myWindow.close();
}

function akeeba_onedrivebusiness_refreshdrives(params)
{
    if (document.getElementById('var[akeeba.advanced.postproc_engine]').value !== 'onedrivebusiness')
    {
        return;
    }

    params = params || {};

    if (typeof params["engine.postproc.onedrivebusiness.drive"] === "undefined")
    {
        params["engine.postproc.onedrivebusiness.drive"] = {
            "default": document.getElementById("var[engine.postproc.googledrive.team_drive]").value
        };
    }

    akeebabackup.System.AjaxURL = akeebabackup.Configuration.URLs["dpecustomapi"];

    var data = {
        engine: "onedrivebusiness",
        method: "getDrives",
        params: {
            "engine.postproc.onedrivebusiness.access_token":  document.getElementById(
                "var[engine.postproc.googledrive.access_token]").value,
            "engine.postproc.onedrivebusiness.refresh_token": document.getElementById(
                "var[engine.postproc.googledrive.refresh_token]").value
        }
    };

    akeebabackup.System.doAjax(
        data,
        function (res)
        {
            if (!res?.list?.length)
            {
                alert("ERROR: Could not retrieve list of OneDrive Drives.");

                return;
            }

            var dropDown       = document.getElementById("var[engine.postproc.onedrivebusiness.drive]");
            dropDown.innerHTML = "";

            res.list.forEach(item => {
                var elOption   = document.createElement("option");
                elOption.value = item[0];
                elOption.text  = item[1];

                if (params["engine.postproc.onedrivebusiness.drive"]["default"] === elOption.value)
                {
                    elOption.selected = true;
                }

                dropDown.appendChild(elOption);
            })
        }, function (errorMessage)
        {
            alert("ERROR: Could not retrieve list of OneDrive Drives. Error: " + "\n" + errorMessage);
        }, false, 15000
    );
}

function akconfig_onedriveapp_openoauth()
{
    var url = akeebabackup.Configuration.URLs.dpeauthopen;

    if (url.indexOf("?") == -1)
    {
        url = url + "?";
    }
    else
    {
        url = url + "&";
    }

    window.open(url + "engine=onedriveapp", "akeeba_onedriveapp_window", "width=1010,height=500,opener");
}

function akeeba_onedriveapp_oauth_callback(data)
{
    // Update the tokens
    document.getElementById("var[engine.postproc.onedriveapp.access_token]").value  = data.access_token;
    document.getElementById("var[engine.postproc.onedriveapp.refresh_token]").value = data.refresh_token;

    // Close the window
    var myWindow = window.open("", "akeeba_onedriveapp_window");
    myWindow.close();
}

function akconfig_googledrive_openoauth()
{
    var url = akeebabackup.Configuration.URLs.dpeauthopen;

    if (url.indexOf("?") == -1)
    {
        url = url + "?";
    }
    else
    {
        url = url + "&";
    }

    window.open(url + "engine=googledrive", "akeeba_googledrive_window", "width=1010,height=500,opener");
}

function akeeba_googledrive_oauth_callback(data)
{
    // Update the tokens
    document.getElementById("var[engine.postproc.googledrive.access_token]").value  = data.access_token;
    document.getElementById("var[engine.postproc.googledrive.refresh_token]").value = data.refresh_token;

    // Close the window
    var myWindow = window.open("", "akeeba_googledrive_window");
    myWindow.close();

    // Refresh the list of drives
    akeeba_googledrive_refreshdrives();
}

function akeeba_googledrive_refreshdrives(params)
{
    if (document.getElementById('var[akeeba.advanced.postproc_engine]').value !== 'googledrive')
    {
        return;
    }

    params = params || {};

    if (typeof params["engine.postproc.googledrive.team_drive"] === "undefined")
    {
        params["engine.postproc.googledrive.team_drive"] = {
            "default": document.getElementById("var[engine.postproc.googledrive.team_drive]").value
        };
    }

    akeebabackup.System.AjaxURL = akeebabackup.Configuration.URLs["dpecustomapi"];

    var data = {
        engine: "googledrive",
        method: "getDrives",
        params: {
            "engine.postproc.googledrive.access_token":  document.getElementById(
                "var[engine.postproc.googledrive.access_token]").value,
            "engine.postproc.googledrive.refresh_token": document.getElementById(
                "var[engine.postproc.googledrive.refresh_token]").value
        }
    };

    akeebabackup.System.doAjax(
        data,
        function (res)
        {
            console.log(res)

            if (!res?.list?.length)
            {
                alert("ERROR: Could not retrieve list of Google Drives.");

                return;
            }

            var dropDown       = document.getElementById("var[engine.postproc.googledrive.team_drive]");
            dropDown.innerHTML = "";

            res.list.forEach(item => {
                var elOption   = document.createElement("option");
                elOption.value = item[0];
                elOption.text  = item[1];

                if (params["engine.postproc.googledrive.team_drive"]["default"] === elOption.value)
                {
                    elOption.selected = true;
                }

                dropDown.appendChild(elOption);
            })
        }, function (errorMessage)
        {
            alert("ERROR: Could not retrieve list of Google Drives. Error: " + "\n" + errorMessage);
        }, false, 15000
    );
}

function akconfig_box_openoauth()
{
    var url = akeebabackup.Configuration.URLs.dpeauthopen;

    if (url.indexOf("?") == -1)
    {
        url = url + "?";
    }
    else
    {
        url = url + "&";
    }

    window.open(url + "engine=box", "akeeba_box_window", "width=1010,height=500,opener");
}

function akconfig_box_oauth_callback(data)
{
    // Update the tokens
    document.getElementById("var[engine.postproc.box.access_token]").value  = data.access_token;
    document.getElementById("var[engine.postproc.box.refresh_token]").value = data.refresh_token;

    // Close the window
    var myWindow = window.open("", "akeeba_box_window");
    myWindow.close();
}

// Initialisation
akeebabackup.System.documentReady(function() {
    // Get the configured URLs
    akeebabackup.Configuration.URLs = Joomla.getOptions("akeebabackup.Configuration.URLs", {});

    // Configuration page: we will be doing AJAX calls to the Data Processing Engine Custom API URL
    if (typeof akeebabackup.Configuration.URLs["dpecustomapi"] !== "undefined")
    {
        akeebabackup.System.params.AjaxURL = akeebabackup.Configuration.URLs["dpecustomapi"];
    }

    /**
     * The rest of the code only applies to the Configuration GUI.
     *
     * Therefore, if we have no Configuration GUI data (e.g. this file was included from a different view) we have to
     * return without trying to do anything else.
     */
    var guiData = Joomla.getOptions("akeebabackup.Configuration.GUIData", null);

    if (guiData !== null)
    {
        // Load the configuration UI data in a timeout to prevent Safari from auto-filling the password fields
        setTimeout(function ()
        {
            // Work around browsers which blatantly ignore autocomplete=off
            setTimeout(akeebabackup.Configuration.restoreDefaultPasswords, 1000);

            // Render the configuration UI in the timeout to prevent Safari from autofilling the password fields
            akeebabackup.Configuration.parseConfigData(Joomla.getOptions("akeebabackup.Configuration.GUIData", {}));

            akeebabackup.Configuration.initialisePopovers();

            // Reload drive lists if applicable
            akeeba_googledrive_refreshdrives(akeebabackup.Configuration.engines.postproc.googledrive.parameters);
            akeeba_onedrivebusiness_refreshdrives(akeebabackup.Configuration.engines.postproc.onedrivebusiness.parameters);

            // Post the form as a big JSON blob if possible
            const adminForm = document.getElementById('adminForm');

            if (adminForm) {
                adminForm.addEventListener('submit', akeebabackup.Configuration.onFormSubmit);
            }

        }, 10);
    }
});

Filemanager

Name Type Size Permission Actions
Backup.js File 18.73 KB 0664
Backup.min.js File 10.27 KB 0664
Browser.js File 1018 B 0664
Browser.min.js File 647 B 0664
Configuration.js File 75.16 KB 0664
Configuration.min.js File 33.87 KB 0664
ConfigurationWizard.js File 13.3 KB 0664
ConfigurationWizard.min.js File 7.78 KB 0664
ControlPanel.js File 2.92 KB 0664
ControlPanel.min.js File 1.51 KB 0664
DatabaseFilters.js File 16.6 KB 0664
DatabaseFilters.min.js File 7.96 KB 0664
Discover.js File 614 B 0664
Discover.min.js File 326 B 0664
FileFilters.js File 31.18 KB 0664
FileFilters.min.js File 14.23 KB 0664
IncludeFolders.js File 13.03 KB 0664
IncludeFolders.min.js File 6.27 KB 0664
Log.js File 1.06 KB 0664
Log.min.js File 702 B 0664
Manage.js File 5.55 KB 0664
Manage.min.js File 3.39 KB 0664
MultipleDatabases.js File 16.75 KB 0664
MultipleDatabases.min.js File 8.19 KB 0664
RegExDatabaseFilters.js File 10.87 KB 0664
RegExDatabaseFilters.min.js File 5.49 KB 0664
RegExFileFilters.js File 11.11 KB 0664
RegExFileFilters.min.js File 5.67 KB 0664
RemoteFiles.js File 1.75 KB 0664
RemoteFiles.min.js File 876 B 0664
Restore.js File 14.35 KB 0664
Restore.min.js File 6.28 KB 0664
S3Import.js File 2.9 KB 0664
S3Import.min.js File 1.69 KB 0664
System.js File 43.92 KB 0664
System.min.js File 11.09 KB 0664
Transfer.js File 18.97 KB 0664
Transfer.min.js File 12.98 KB 0664
Upload.js File 1.11 KB 0664
Upload.min.js File 567 B 0664
WebPush.js File 11.81 KB 0664
WebPush.min.js File 7.77 KB 0664
WebPushWorker.js File 479 B 0664
WebPushWorker.min.js File 256 B 0664
Filemanager