Greasy Fork is available in English.

KameSame Open Framework - Settings module

Settings module for KameSame Open Framework

Ajankohdalta 5.11.2022. Katso uusin versio.

Tätä skriptiä ei tulisi asentaa suoraan. Se on kirjasto muita skriptejä varten sisällytettäväksi metadirektiivillä // @require https://update.greasyfork.org/scripts/451521/1113605/KameSame%20Open%20Framework%20-%20Settings%20module.js.

"use strict";
// ==UserScript==
// @name        KameSame Open Framework - Settings module
// @namespace   timberpile
// @description Settings module for KameSame Open Framework
// @version     0.3
// @copyright   2022+, Robin Findley, Timberpile
// @license     MIT http://opensource.org/licenses/MIT
// ==/UserScript==
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
    if (kind === "m") throw new TypeError("Private method is not writable");
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
    return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
    return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};

((async (global) => {
    var _KSOFSettings_instances, _KSOFSettings_openDialog, _KSOFSettings_settingChanged;
    const ksof = global.ksof;
    const backgroundFuncs = () => {
        return {
            open: () => {
                const anchor = installAnchor();
                let bkgd = anchor.find('> #ksofs_bkgd');
                if (bkgd.length === 0) {
                    bkgd = $('<div id="ksofs_bkgd" refcnt="0"></div>');
                    anchor.prepend(bkgd);
                }
                const refcnt = Number(bkgd.attr('refcnt'));
                bkgd.attr('refcnt', refcnt + 1);
            },
            close: () => {
                const bkgd = $('#ksof_ds > #ksofs_bkgd');
                if (bkgd.length === 0)
                    return;
                const refcnt = Number(bkgd.attr('refcnt'));
                if (refcnt <= 0)
                    return;
                bkgd.attr('refcnt', refcnt - 1);
            },
        };
    };
    //########################################################################
    //------------------------------
    // Constructor
    //------------------------------
    class KSOFSettings {
        constructor(config) {
            _KSOFSettings_instances.add(this);
            _KSOFSettings_openDialog.set(this, void 0);
            this.cfg = config;
            this.configList = {};
            __classPrivateFieldSet(this, _KSOFSettings_openDialog, $(), "f");
            this.background = backgroundFuncs();
        }
        //------------------------------
        // Open the settings dialog.
        //------------------------------
        static save(context) {
            const scriptId = ((typeof context === 'string') ? context : context.cfg.scriptId);
            const settings = ksof.settings[scriptId];
            if (!settings)
                return Promise.resolve('');
            return ksof.fileCache.save(`ksof.settings.${scriptId}`, settings);
        }
        save() {
            return KSOFSettings.save(this);
        }
        //------------------------------
        // Open the settings dialog.
        //------------------------------
        static async load(context, defaults) {
            const scriptId = ((typeof context === 'string') ? context : context.cfg.scriptId);
            const finish = (settings) => {
                if (defaults)
                    ksof.settings[scriptId] = deepMerge(defaults, settings);
                else
                    ksof.settings[scriptId] = settings;
                return ksof.settings[scriptId];
            };
            try {
                const settings = await ksof.fileCache.load(`ksof.settings.${scriptId}`);
                return finish(settings);
            }
            catch (error) {
                return finish.call(null, {});
            }
        }
        load(defaults) {
            return KSOFSettings.load(this, defaults);
        }
        //------------------------------
        // Save button handler.
        //------------------------------
        saveBtn() {
            const scriptId = this.cfg.scriptId;
            const settings = ksof.settings[scriptId];
            if (settings) {
                const activeTabs = __classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").find('.ui-tabs-active').toArray()
                    .map((tab) => { return `#${tab.attributes.getNamedItem('id')?.value || ''}`; });
                if (activeTabs.length > 0)
                    settings.ksofActiveTabs = activeTabs;
            }
            if (this.cfg.autosave === undefined || this.cfg.autosave === true) {
                this.save();
            }
            if (this.cfg.onSave) {
                this.cfg.onSave(ksof.settings[this.cfg.scriptId]);
            }
            ksof.trigger('ksof.settings.save');
            this.keepSettings = true;
            __classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").dialog('close');
        }
        //------------------------------
        // Cancel button handler.
        //------------------------------
        cancel() {
            __classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").dialog('close');
            if (typeof this.cfg.onCancel === 'function')
                this.cfg.onCancel(ksof.settings[this.cfg.scriptId]);
        }
        //------------------------------
        // Open the settings dialog.
        //------------------------------
        open() {
            if (!ready)
                return;
            if (__classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").length > 0)
                return;
            installAnchor();
            if (this.cfg.background !== false)
                this.background.open();
            __classPrivateFieldSet(this, _KSOFSettings_openDialog, $(`<div id="ksofs_${this.cfg.scriptId}" class="ksof_settings" style="display:none"></div>`), "f");
            __classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").html(configToHTML(this));
            const resize = (event, ui) => {
                const isNarrow = __classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").hasClass('narrow');
                ui;
                if (isNarrow && ui.size.width >= 510) {
                    __classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").removeClass('narrow');
                }
                else if (!isNarrow && ui.size.width < 490) {
                    __classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").addClass('narrow');
                }
            };
            const tabActivated = () => {
                const wrapper = $(__classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").dialog('widget'));
                if ((wrapper.outerHeight() || 0) + wrapper.position().top > document.body.clientHeight) {
                    __classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").dialog('option', 'maxHeight', document.body.clientHeight);
                }
            };
            let width = 500;
            if (window.innerWidth < 510) {
                width = 280;
                __classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").addClass('narrow');
            }
            __classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").dialog({
                title: this.cfg.title,
                buttons: [
                    {
                        text: 'Save',
                        click: this.saveBtn.bind(this),
                    },
                    {
                        text: 'Cancel',
                        click: this.cancel.bind(this),
                    },
                ],
                width,
                maxHeight: document.body.clientHeight,
                modal: false,
                autoOpen: false,
                appendTo: '#ksof_ds',
                resize: resize.bind(this),
                close: () => {
                    this.close(false);
                },
            });
            $(__classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").dialog('widget')).css('position', 'fixed');
            __classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").parent().addClass('ksof_settings_dialog');
            $('.ksof_stabs').tabs({ activate: tabActivated.bind(null) });
            const settings = ksof.settings[this.cfg.scriptId];
            if (settings && settings.ksofActiveTabs instanceof Array) {
                const activeTabs = settings.ksofActiveTabs;
                for (let tabIndex = 0; tabIndex < activeTabs.length; tabIndex++) {
                    const tab = $(activeTabs[tabIndex]);
                    tab.closest('.ui-tabs').tabs({ active: tab.index() });
                }
            }
            const toggleMulti = (e) => {
                if (e.button != 0)
                    return true;
                const multi = $(e.currentTarget);
                const scroll = e.currentTarget.scrollTop;
                e.target.selected = !e.target.selected;
                setTimeout(() => {
                    e.currentTarget.scrollTop = scroll;
                    multi.focus(); // TODO what should this do? it's deprecated
                }, 0);
                return __classPrivateFieldGet(this, _KSOFSettings_instances, "m", _KSOFSettings_settingChanged).call(this, e);
            };
            const settingButtonClicked = (e) => {
                const name = e.target.attributes.name.value;
                const _item = this.configList[name];
                if (_item.type == 'button') {
                    const item = _item;
                    item.onClick.call(e, name, item, __classPrivateFieldGet(this, _KSOFSettings_instances, "m", _KSOFSettings_settingChanged).bind(this, e));
                }
            };
            __classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").dialog('open');
            __classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").find('.setting[multiple]').on('mousedown', toggleMulti.bind(this));
            __classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").find('.setting').on('change', __classPrivateFieldGet(this, _KSOFSettings_instances, "m", _KSOFSettings_settingChanged).bind(this));
            __classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").find('form').on('submit', () => { return false; });
            __classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").find('button.setting').on('click', settingButtonClicked.bind(this));
            if (typeof this.cfg.preOpen === 'function')
                this.cfg.preOpen(__classPrivateFieldGet(this, _KSOFSettings_openDialog, "f"));
            this.reversions = deepMerge({}, ksof.settings[this.cfg.scriptId]);
            this.refresh();
        }
        //------------------------------
        // Close and destroy the dialog.
        //------------------------------
        close(keepSettings) {
            if (!this.keepSettings && keepSettings !== true) {
                // Revert settings
                ksof.settings[this.cfg.scriptId] = deepMerge({}, this.reversions || {});
                delete this.reversions;
            }
            delete this.keepSettings;
            __classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").dialog('destroy');
            __classPrivateFieldSet(this, _KSOFSettings_openDialog, $(), "f");
            if (this.cfg.background !== false)
                this.background.close();
            if (typeof this.cfg.onClose === 'function')
                this.cfg.onClose(ksof.settings[this.cfg.scriptId]);
        }
        //------------------------------
        // Update the dialog to reflect changed settings.
        //------------------------------
        refresh() {
            const scriptId = this.cfg.scriptId;
            const settings = ksof.settings[scriptId];
            for (const name in this.configList) {
                const elem = __classPrivateFieldGet(this, _KSOFSettings_openDialog, "f").find(`#${scriptId}_${name}`);
                const _config = this.configList[name];
                const value = getValue(this, settings, name);
                if (_config.type == 'dropdown') {
                    elem.find(`option[name="${value}"]`).prop('selected', true);
                }
                else if (_config.type == 'list') {
                    const config = _config;
                    if (config.multi === true) {
                        elem.find('option').each((i, e) => {
                            const optionName = e.getAttribute('name') || `#${e.index}`;
                            e.selected = value[optionName];
                        });
                    }
                    else {
                        elem.find(`option[name="${value}"]`).prop('selected', true);
                    }
                }
                else if (_config.type == 'checkbox') {
                    elem.prop('checked', value);
                }
                else {
                    elem.val(value);
                }
            }
            if (typeof this.cfg.onRefresh === 'function')
                this.cfg.onRefresh(ksof.settings[this.cfg.scriptId]);
        }
    }
    _KSOFSettings_openDialog = new WeakMap(), _KSOFSettings_instances = new WeakSet(), _KSOFSettings_settingChanged = function _KSOFSettings_settingChanged(event) {
        const elem = $(event.currentTarget);
        const name = elem.attr('name');
        if (!name)
            return false;
        const _item = this.configList[name];
        // Extract the value
        let value;
        if (_item.type == 'dropdown') {
            value = elem.find(':checked').attr('name');
        }
        else if (_item.type == 'list') {
            const item = _item;
            if (item.multi === true) {
                value = {};
                elem.find('option').each((i, e) => {
                    const optionName = e.getAttribute('name') || `#${e.index}`;
                    value[optionName] = e.selected;
                });
            }
            else {
                value = elem.find(':checked').attr('name');
            }
        }
        else if (_item.type == 'input') {
            const item = _item;
            if (item.subtype === 'number') {
                value = Number(elem.val());
            }
        }
        else if (_item.type == 'checkbox') {
            value = elem.is(':checked');
        }
        else if (_item.type == 'number') {
            value = Number(elem.val());
        }
        else {
            value = elem.val();
        }
        // Validation
        let valid = { valid: true, msg: '' };
        {
            const item = _item;
            if (item.validate) {
                const _valid = item.validate.call(event.target, value, item);
                if (typeof _valid === 'boolean')
                    valid = { valid: _valid, msg: '' };
                else if (typeof _valid === 'string')
                    valid = { valid: false, msg: _valid };
            }
        }
        if (_item.type == 'number') {
            const item = _item;
            if (item.min && Number(value) < item.min) {
                valid.valid = false;
                if (valid.msg.length === 0) {
                    if (typeof item.max === 'number')
                        valid.msg = `Must be between ${item.min} and ${item.max}`;
                    else
                        valid.msg = `Must be ${item.min} or higher`;
                }
            }
            else if (item.max && Number(value) > item.max) {
                valid.valid = false;
                if (valid.msg.length === 0) {
                    if (typeof item.min === 'number')
                        valid.msg = `Must be between ${item.min} and ${item.max}`;
                    else
                        valid.msg = `Must be ${item.max} or lower`;
                }
            }
        }
        else if (_item.type == 'text') {
            const item = _item;
            if (item.match !== undefined && value.match(item.match) === null) {
                valid.valid = false;
                if (valid.msg.length === 0)
                    // valid.msg = item.error_msg || 'Invalid value' // TODO no item has a error_msg?
                    valid.msg = 'Invalid value';
            }
        }
        // Style for valid/invalid
        const parent = elem.closest('.right');
        parent.find('.note').remove();
        if (typeof valid.msg === 'string' && valid.msg.length > 0)
            parent.append(`<div class="note${valid.valid ? '' : ' error'}">${valid.msg}</div>`);
        if (!valid.valid) {
            elem.addClass('invalid');
        }
        else {
            elem.removeClass('invalid');
        }
        const scriptId = this.cfg.scriptId;
        const settings = ksof.settings[scriptId];
        if (valid.valid) {
            const item = _item;
            // if (item.no_save !== true) set_value(this, settings, name, value) // TODO what is no_save supposed to do?
            setValue(this, settings, name, value);
            if (item.onChange)
                item.onChange.call(event.target, name, value, item);
            if (this.cfg.onChange)
                this.cfg.onChange.call(event.target, name, value, item);
            if (item.refreshOnChange === true)
                this.refresh();
        }
        return false;
    };
    const createSettings = () => {
        const settingsObj = (config) => {
            return new KSOFSettings(config);
        };
        settingsObj.save = (context) => { return KSOFSettings.save(context); };
        settingsObj.load = (context, defaults) => { return KSOFSettings.load(context, defaults); };
        settingsObj.background = backgroundFuncs();
        return settingsObj;
    };
    ksof.Settings = createSettings();
    ksof.settings = {};
    //########################################################################
    let ready = false;
    //========================================================================
    const deepMerge = (...objects) => {
        const merged = {};
        const recursiveMerge = (dest, src) => {
            for (const prop in src) {
                if (typeof src[prop] === 'object' && src[prop] !== null) {
                    const srcProp = src[prop];
                    if (Array.isArray(srcProp)) {
                        dest[prop] = srcProp.slice();
                    }
                    else {
                        dest[prop] = dest[prop] || {};
                        recursiveMerge(dest[prop], srcProp);
                    }
                }
                else {
                    dest[prop] = src[prop];
                }
            }
            return dest;
        };
        for (const obj in objects) {
            recursiveMerge(merged, objects[obj]);
        }
        return merged;
    };
    //------------------------------
    // Convert a config object to html dialog.
    //------------------------------
    /* eslint-disable no-case-declarations */
    const configToHTML = (context) => {
        context.configList = {};
        if (!ksof.settings) {
            return '';
        }
        const assemblePages = (id, tabs, pages) => { return `<div id="${id}" class="ksof_stabs"><ul>${tabs.join('')}</ul>${pages.join('')}</div>`; };
        const wrapRow = (html, full, hoverTip) => { return `<div class="row${full ? ' full' : ''}"${toTitle(hoverTip)}>${html}</div>`; };
        const wrapLeft = (html) => { return `<div class="left">${html}</div>`; };
        const wrapRight = (html) => { return `<div class="right">${html}</div>`; };
        const escapeText = (text) => {
            return text.replace(/[<>]/g, (ch) => {
                if (ch == '<')
                    return '&lt';
                if (ch == '>')
                    return '&gt';
                return '';
            });
        };
        const escapeAttr = (text) => { return text.replace(/"/g, '&quot'); };
        const toTitle = (tip) => { if (!tip)
            return ''; return ` title="${tip.replace(/"/g, '&quot')}"`; };
        const parseItem = (name, _item, passback) => {
            if (typeof _item.type !== 'string')
                return '';
            const id = `${context.cfg.scriptId}_${name}`;
            let cname, html = '', childPassback, nonPage = '';
            const makeLabel = (item) => {
                if (typeof item.label !== 'string')
                    return '';
                return wrapLeft(`<label for="${id}">${item.label}</label>`);
            };
            const _type = _item.type;
            if (_type == 'tabset') {
                const item = _item;
                childPassback = {};
                for (cname in item.content) {
                    nonPage += parseItem(cname, item.content[cname], childPassback);
                }
                if (childPassback.tabs && childPassback.pages) {
                    html = assemblePages(id, childPassback.tabs, childPassback.pages);
                }
            }
            else if (_type == 'page') {
                const item = _item;
                if (typeof item.content !== 'object')
                    item.content = {};
                if (!passback.tabs) {
                    passback.tabs = [];
                }
                if (!passback.pages) {
                    passback.pages = [];
                }
                passback.tabs.push(`<li id="${id}_tab"${toTitle(item.hoverTip)}><a href="#${id}">${item.label}</a></li>`);
                childPassback = {};
                for (cname in item.content)
                    nonPage += parseItem(cname, item.content[cname], childPassback);
                if (childPassback.tabs && childPassback.pages)
                    html = assemblePages(id, childPassback.tabs, childPassback.pages);
                passback.pages.push(`<div id="${id}">${html}${nonPage}</div>`);
                passback.isPage = true;
                html = '';
            }
            else if (_type == 'group') {
                const item = _item;
                if (typeof item.content !== 'object')
                    item.content = {};
                childPassback = {};
                for (cname in item.content)
                    nonPage += parseItem(cname, item.content[cname], childPassback);
                if (childPassback.tabs && childPassback.pages)
                    html = assemblePages(id, childPassback.tabs, childPassback.pages);
                html = `<fieldset id="${id}" class="ksof_group"><legend>${item.label}</legend>${html}${nonPage}</fieldset>`;
            }
            else if (_type == 'dropdown') {
                const item = _item;
                context.configList[name] = item;
                let value = getValue(context, base, name);
                if (value === undefined) {
                    if (item.default !== undefined) {
                        value = item.default;
                    }
                    else {
                        value = Object.keys(item.content)[0];
                    }
                    setValue(context, base, name, value);
                }
                html = `<select id="${id}" name="${name}" class="setting"${toTitle(item.hoverTip)}>`;
                for (cname in item.content)
                    html += `<option name="${cname}">${escapeText(item.content[cname])}</option>`;
                html += '</select>';
                html = makeLabel(item) + wrapRight(html);
                html = wrapRow(html, item.fullWidth, item.hoverTip);
            }
            else if (_type == 'list') {
                const item = _item;
                context.configList[name] = item;
                let value = getValue(context, base, name);
                if (value === undefined) {
                    if (item.default !== undefined) {
                        value = item.default;
                    }
                    else {
                        if (item.multi === true) {
                            value = {};
                            Object.keys(item.content).forEach((key) => {
                                value[key] = false;
                            });
                        }
                        else {
                            value = Object.keys(item.content)[0];
                        }
                    }
                    setValue(context, base, name, value);
                }
                let attribs = ` size="${item.size || Object.keys(item.content).length || 4}"`;
                if (item.multi === true)
                    attribs += ' multiple';
                html = `<select id="${id}" name="${name}" class="setting list"${attribs}${toTitle(item.hoverTip)}>`;
                for (cname in item.content)
                    html += `<option name="${cname}">${escapeText(item.content[cname])}</option>`;
                html += '</select>';
                html = makeLabel(item) + wrapRight(html);
                html = wrapRow(html, item.fullWidth, item.hoverTip);
            }
            else if (_type == 'checkbox') {
                const item = _item;
                context.configList[name] = item;
                html = makeLabel(item);
                let value = getValue(context, base, name);
                if (value === undefined) {
                    value = (item.default || false);
                    setValue(context, base, name, value);
                }
                html += wrapRight(`<input id="${id}" class="setting" type="checkbox" name="${name}">`);
                html = wrapRow(html, item.fullWidth, item.hoverTip);
            }
            else if (_type == 'input') {
                const item = _item;
                const itype = item.subtype || 'text';
                context.configList[name] = item;
                html += makeLabel(item);
                let value = getValue(context, base, name);
                if (value === undefined) {
                    const isNumber = (item.subtype === 'number');
                    value = (item.default || (isNumber ? 0 : ''));
                    setValue(context, base, name, value);
                }
                html += wrapRight(`<input id="${id}" class="setting" type="${itype}" name="${name}"${(item.placeholder ? ` placeholder="${escapeAttr(item.placeholder)}"` : '')}>`);
                html = wrapRow(html, item.fullWidth, item.hoverTip);
            }
            else if (_type == 'number') {
                const item = _item;
                const itype = item.type;
                context.configList[name] = item;
                html += makeLabel(item);
                let value = getValue(context, base, name);
                if (value === undefined) {
                    const isNumber = (item.type === 'number');
                    value = (item.default || (isNumber ? 0 : ''));
                    setValue(context, base, name, value);
                }
                html += wrapRight(`<input id="${id}" class="setting" type="${itype}" name="${name}"${(item.placeholder ? ` placeholder="${escapeAttr(item.placeholder)}"` : '')}>`);
                html = wrapRow(html, item.fullWidth, item.hoverTip);
            }
            else if (_type == 'text') {
                const item = _item;
                const itype = item.type;
                context.configList[name] = item;
                html += makeLabel(item);
                let value = getValue(context, base, name);
                if (value === undefined) {
                    value = (item.default || '');
                    setValue(context, base, name, value);
                }
                html += wrapRight(`<input id="${id}" class="setting" type="${itype}" name="${name}"${(item.placeholder ? ` placeholder="${escapeAttr(item.placeholder)}"` : '')}>`);
                html = wrapRow(html, item.fullWidth, item.hoverTip);
            }
            else if (_type == 'color') {
                const item = _item;
                context.configList[name] = item;
                html += makeLabel(item);
                let value = getValue(context, base, name);
                if (value === undefined) {
                    value = (item.default || '#000000');
                    setValue(context, base, name, value);
                }
                html += wrapRight(`<input id="${id}" class="setting" type="color" name="${name}">`);
                html = wrapRow(html, item.fullWidth, item.hoverTip);
            }
            else if (_type == 'button') {
                const item = _item;
                context.configList[name] = item;
                html += makeLabel(item);
                const text = escapeText(item.text || 'Click');
                html += wrapRight(`<button type="button" class="setting" name="${name}">${text}</button>`);
                html = wrapRow(html, item.fullWidth, item.hoverTip);
            }
            else if (_type == 'divider') {
                html += '<hr>';
            }
            else if (_type == 'section') {
                const item = _item;
                html += `<section>${item.label || ''}</section>`;
            }
            else if (_type == 'html') {
                const item = _item;
                html += makeLabel(item);
                html += item.html;
                switch (item.wrapper) {
                    case 'row':
                        html = wrapRow(html, undefined, item.hoverTip);
                        break;
                    case 'left':
                        html = wrapLeft(html);
                        break;
                    case 'right':
                        html = wrapRight(html);
                        break;
                }
            }
            return html;
        };
        let base = ksof.settings[context.cfg.scriptId];
        if (base === undefined)
            ksof.settings[context.cfg.scriptId] = base = {};
        let html = '';
        const childPassback = {};
        const id = `${context.cfg.scriptId}_dialog`;
        for (const name in context.cfg.content) {
            html += parseItem(name, context.cfg.content[name], childPassback);
        }
        if (childPassback.tabs && childPassback.pages)
            html = assemblePages(id, childPassback.tabs, childPassback.pages) + html;
        return `<form>${html}</form>`;
    };
    const getValue = (context, base, name) => {
        const item = context.configList[name];
        const evaluate = (item.path !== undefined);
        const path = (item.path || name);
        try {
            if (!evaluate)
                return base[path];
            return eval(path.replace(/@/g, 'base.'));
        }
        catch (e) {
            return;
        }
    };
    const setValue = (context, base, name, value) => {
        const item = context.configList[name];
        const evaluate = (item.path !== undefined);
        const path = (item.path || name);
        try {
            if (!evaluate)
                return base[path] = value;
            let depth = 0;
            let newPath = '';
            let param = '';
            let c;
            for (let idx = 0; idx < path.length; idx++) {
                c = path[idx];
                if (c === '[') {
                    if (depth++ === 0) {
                        newPath += '[';
                        param = '';
                    }
                    else {
                        param += '[';
                    }
                }
                else if (c === ']') {
                    if (--depth === 0) {
                        newPath += `${JSON.stringify(eval(param))}]`;
                    }
                    else {
                        param += ']';
                    }
                }
                else {
                    if (c === '@')
                        c = 'base.';
                    if (depth === 0)
                        newPath += c;
                    else
                        param += c;
                }
            }
            eval(`${newPath}=value`);
        }
        catch (e) {
            return;
        }
    };
    const installAnchor = () => {
        let anchor = $('#ksof_ds');
        if (anchor.length === 0) {
            anchor = $('<div id="ksof_ds"></div></div>');
            $('body').prepend(anchor);
            $('#ksof_ds').on('keydown keyup keypress', '.ksof_settings_dialog', (e) => {
                // Stop keys from bubbling beyond the background overlay.
                e.stopPropagation();
            });
        }
        return anchor;
    };
    //------------------------------
    // Load jquery UI and the appropriate CSS based on location.
    //------------------------------
    const cssUrl = ksof.supportFiles['jqui_ksmain.css'];
    ksof.include('Jquery');
    await ksof.ready('document, Jquery');
    await Promise.all([
        ksof.loadScript(ksof.supportFiles['jquery_ui.js'], true /* cache */),
        ksof.loadCSS(cssUrl, true /* cache */),
    ]);
    ready = true;
    // Workaround...  https://community.wanikani.com/t/19984/55
    try {
        const temp = $.fn;
        delete temp.autocomplete;
    }
    catch (e) {
        // do nothing
    }
    // Notify listeners that we are ready.
    // Delay guarantees include() callbacks are called before ready() callbacks.
    setTimeout(() => { ksof.setState('ksof.Settings', 'ready'); }, 0);
})(window));