git @ Cat's Eye Technologies REDGREEN / master demo / dam / dam-saveable.js
master

Tree @master (Download .tar.gz)

dam-saveable.js @masterraw · history · blame

// SPDX-FileCopyrightText: Chris Pressey, the creator of this work, has dedicated it to the public domain.
// For more information, please refer to <https://unlicense.org/>
// SPDX-License-Identifier: Unlicense

(function() {
    function makeSaveable(config) {
        config = config || {};
        var editor = config.editor;
        var onUpdate = config.onUpdate;
        var nameSpacePrefix = config.nameSpacePrefix || "";

        var input = DAM.maker('input');
        var select = DAM.maker('select');
        var option = DAM.maker('option');
        var button = DAM.maker('button');
        var div = DAM.maker('div');

        var unsavedChanges = false;
        var saveAreaVisible = false;

        function stripNameSpacePrefix(key) {
            return key.slice(nameSpacePrefix.length);
        }

        function updateSaveSlotOptions() {
            saveSlotSelect.innerHTML = '';
            saveSlotSelect.appendChild(option({ value: '' }, 'Select a save slot to load from'));

            // Get all keys and sort them alphabetically
            var keys = [];
            for (var i = 0; i < localStorage.length; i++) {
                var key = localStorage.key(i);
                if (key.startsWith(nameSpacePrefix)) {
                    keys.push(key);
                }
            }
            keys.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));

            // Add sorted options to select
            for (var i = 0; i < keys.length; i++) {
                saveSlotSelect.appendChild(option({ value: keys[i] }, stripNameSpacePrefix(keys[i])));
            }
        }

        function resetDropdownToDefault() {
            saveSlotSelect.value = '';
        }

        function toggleSaveArea() {
            saveAreaVisible = !saveAreaVisible;
            saveArea.style.display = saveAreaVisible ? "inline-block" : "none";
            toggleSaveAreaButton.textContent = saveAreaVisible ? "Save <<" : "Save >>";
        }

        function saveText() {
            var name = nameInput.value.trim();
            var text = editor.value;
            
            if (!name) {
                alert('Please enter a name before saving.');
                return;
            }

            // Check if name already exists in localStorage
            var key = nameSpacePrefix + name;
            if (localStorage.getItem(key) !== null) {
                if (!confirm('A save slot with this name already exists. Do you want to overwrite it?')) {
                    return; // User cancelled overwrite
                }
            }

            localStorage.setItem(key, text);
            updateSaveSlotOptions();
            unsavedChanges = false;
            toggleSaveArea(); // Hide save area after successful save
            // Select the newly saved slot in the dropdown
            saveSlotSelect.value = key;
        }

        function loadText(key) {
            var savedText = localStorage.getItem(key);
            if (savedText !== null) {
                editor.value = savedText;
                if (typeof onUpdate === "function") {
                    onUpdate(savedText);
                }
                nameInput.value = stripNameSpacePrefix(key);
                unsavedChanges = false;
            }
        }

        var saveSlotSelect = select(
            option({ value: '' }, 'Select a save slot to load')
        );

        var nameInput = input({
            type: 'text',
            placeholder: 'Enter name'
        });

        var saveButton = button(
            { onclick: saveText },
            'Save'
        );

        var saveArea = div(
            { style: "display: none" },
            nameInput,
            saveButton
        );

        var toggleSaveAreaButton = button(
            { onclick: toggleSaveArea },
            'Save >>'
        );

        editor.addEventListener('input', function() {
            unsavedChanges = true;
        });

        saveSlotSelect.addEventListener('change', function(event) {
            var key = event.target.value;  // key of selected save slot
            if (key) {
                if (unsavedChanges) {
                    if (confirm('You have unsaved changes. Are you sure you want to load?')) {
                        loadText(key);
                    } else {
                        // Reset dropdown to default when cancelled
                        resetDropdownToDefault();
                    }
                } else {
                    loadText(key);
                }
            }
        });

        // Set up any presets (pre-set save slots) that aren't already in storage
        if (Array.isArray(config.presets)) {
            for (var i = 0; i < config.presets.length; i++) {
                var preset = config.presets[i];
                if (!Array.isArray(preset) || preset.length !== 2) {
                    throw new Error('Each preset must be a [name, content] pair');
                }
                var [name, content] = preset;
                key = nameSpacePrefix + name;
                if (localStorage.getItem(key) === null) {
                    localStorage.setItem(key, content);
                }
            }
        }

        updateSaveSlotOptions();

        return div(
            { class: 'dam-saveable-widget' },
            saveSlotSelect,
            toggleSaveAreaButton,
            saveArea
        );
    }

    if (typeof module !== 'undefined') {
        module.exports = { makeSaveable: makeSaveable };
    } else if (typeof window !== 'undefined') {
        window.DAM = window.DAM || {};
        DAM.makeSaveable = makeSaveable;
    }
})();