Source: index.js

import "regenerator-runtime/runtime.js";
import Gun from "gun/gun";
import SEA from "gun/sea"
import 'gun/lib/promise.js';

/**
 * Main FormSaver class
*/
class FormSaver {
    _gun;
    _keys;
    _user;
    _isUserAvailable = false;

    static newGunInstance = Gun;

    /**
     * FormSaver constructor
     * @param {object=} gun - An optional existing GUN instance
     */
    constructor (gun = Gun(["https://gun-eu.herokuapp.com/gun"])) {
        this._gun = gun;
    };

    /**
     * Submit an existing backup key
     * @param {string} backup - Encoded backup key
     * @returns {Promise<string>} Confirmation of submission
     */
    submitBackup(backup) {
        return new Promise (async (resolve, reject) => {
            this._keys = JSON.parse(atob(backup));
            this._user = this._gun.user();
            this._user.auth(this._keys);
            this._user.pair = this._keys;
            this._isUserAvailable = true;
            setTimeout(() => {resolve("Submitted");}, 500);
        });
    };

    async _createUser() {
        const pair = await SEA.pair();
        this._keys = pair;
        this._user = this._gun.user();
        this._user.auth(pair);
        this._user.pair = pair;
        this._isUserAvailable = true;
    };

    /**
     * Get raw backup data
     * @returns {object} - Raw backup data
     */
    getRawBackup() {
        return new Promise (async (resolve, reject) => {
            if (!this._isUserAvailable) {reject("No user available"); return};
            const data = await SEA.decrypt((await this._user.get("formOptions").promOnce()).data, this._keys);
            if (data === null || data === undefined) {reject("Data is null");} else {resolve(data);};
        });

    };

    /**
     * Set raw backup data
     * @param {object} data - Raw data object to save
     * @returns {Promise<string>} If resolved, your backup key
     */
    setRawBackup(data) {
        return new Promise (async (resolve, reject) => {
            if (!this._isUserAvailable) this._createUser();
            setTimeout(async () => {
                const sent = await this._user.get("formOptions").promPut(await SEA.encrypt(data, this._keys));
                resolve(btoa(JSON.stringify(this._keys)));
            }, 500)
        });
    };

    /**
     * Fetches your backup data and displays them on the form
     * @param {string | HTMLFormElement} form - Form name or form element
     * @returns {Promise<object>} Fetched raw backup data
     */
    getBackup(form) {
        return new Promise((resolve, reject) => {
            if (!this._isUserAvailable) {reject("No user available"); return};
            setTimeout(async () => {
                var data = await SEA.decrypt((await this._user.get("formOptions").promOnce()).data, this._user.pair);
                var elem = {};
                if (typeof form === "string") {
                    elem = document.querySelector(`form[name=${form}]`) || {}
                } else {
                    element = form || {}
                };
                if (elem === {}) {
                    reject("Form element not found");
                    return
                };
                if (typeof data !== "object") {
                    reject("Fetched data isn't an object");
                    return
                };
                elem.querySelectorAll("input, select").forEach(function (element) {
                    if (Object.keys(data).includes(element.name)) {
                        if (["text", "tel", "date", "month", "email", "number", "datetime", "datetime-local", "week", "url", "time", "color"].includes(data[element.name].type)) { 
                            element.value = data[element.name].val;
                        } else if (data[element.name].type === "radio" && data[element.name].val === element.value) {
                            element.checked = true;
                        } else if (data[element.name].type === "select") {
                            element.value = data[element.name].val;
                        } else if (data[element.name].type === "check") {
                            element.checked = data[element.name].val;
                        };
                    };
                });
                resolve(data);
            }, 500);
        });
    };
    /**
     * Fetches your current form data and saves it to the backup
     * @param {string | HTMLFormElement} form - Form name or form element
     * @returns {Promise<string>} If resolved, your backup key
     */
    setBackup(form) {
        return new Promise (async (resolve, reject) => {
            if (!this._isUserAvailable) this._createUser();
            var elem = {};
            if (typeof form === "string") {elem = document.querySelector(`form[name=${form}]`) || {}} else {element = form || {}};
            if (elem === {}) {reject("Form element not found"); return};
            var formToSend = {};
            elem.querySelectorAll("input, select").forEach(function (element) {
                if (["text", "tel", "date", "month", "email", "number", "datetime", "datetime-local", "week", "url", "time", "color"].includes(element.type)) {
                    formToSend[element.name] = {
                        type: element.type,
                        val: element.value
                    };
                } else if (element.type === "radio") {
                    if (element.checked) {
                        formToSend[element.name] = {
                            type: "radio",
                            val: element.value
                        };
                    } else {
                        if (!formToSend[element.name]) {
                            formToSend[element.name] = {
                                type: "radio",
                                val: ""
                            };
                        };
                    };
                } else if (element.tagName.toLowerCase() === "select") {
                    if (element.value.length > 0) {
                        formToSend[element.name] = {
                            type: "select",
                            val: element.value
                        };
                    } else {
                        if (!formToSend[element.name]) {
                            formToSend[element.name] = {
                                type: "select",
                                val: ""
                            };
                        };
                    };
                } else if (element.type === "checkbox") {
                    if (element.checked) {
                        formToSend[element.name] = {
                            type: "check",
                            val: true
                        };
                    } else {
                        if (!formToSend[element.name]) {
                            formToSend[element.name] = {
                                type: "check",
                                val: false
                            };
                        };
                    };
                };
            });
            setTimeout(async () => {
                const sent = await this._user.get("formOptions").promPut(await SEA.encrypt(formToSend, this._keys));
                resolve(btoa(JSON.stringify(this._keys)));
            }, 500);
        })
    };

    /**
     * Discards saved data to clean up memory
     */
    async discardBackup () {
        if (!this._isUserAvailable) return;
        await this._user.get("formOptions").promPut(null);
        this._user.delete();
        this._user = this._gun.user();
        this._isUserAvailable = false;
        this._keys = {};
        localStorage["gun/"] = {};
        localStorage["gap/gun/"] = {};
    };
};

if (window && !window.FormSaver) window.FormSaver = FormSaver;

export default FormSaver;