"use strict"; // // UI ELEMENTS // class SaveButton { constructor(div) { this.root = div this.root.className = "savebutton" this.root.style.display = "block" this.element = { root: this.root, button: this.root.querySelector('.savebutton--button'), icon: this.root.querySelector('.savebutton--icon'), details: this.root.querySelector('.savebutton--details') } this.switchStateInitial() } changeState(data) { var data = data || {} this.element.button.innerText = data.buttonText || "Save" this.element.button.disabled = data.buttonClickable === false ? true : false this.element.icon.className = 'savebutton--icon ' + (data.iconClasses || "") this.element.details.innerText = data.detailsText || "" } switchStateInitial() { this.changeState({ buttonClickable: false }) } switchStateUnsaved() { this.changeState({ detailsText: django.gettext("You have unsaved changes. Click here to save a draft, which you can access next time you are here.") }) } switchStateSaving() { this.changeState({ buttonText: django.gettext("Saving..."), iconClasses: "fa fa-spinner fa-spin" }) } switchStateSaveSuccess() { this.changeState({ buttonText: django.gettext("Saved"), detailsText: django.gettext("Saved successfully."), iconClasses: "fa fa-check", buttonClickable: false }) } switchStateSaveFailed(reason = "") { this.changeState({ detailsText: django.gettext("Save failed! ") + reason, iconClasses: "fa fa-exclamation-triangle savebutton--icon-failed" }) } } class DraftPrompt { constructor(opts) { this.restoreDraft = opts.restoreFn this.deleteDraft = opts.deleteFn this.root = opts.root this.element = { root: this.root, restore: this.root.querySelector('.draftprompt--restore'), delete: this.root.querySelector('.draftprompt--delete'), details: this.root.querySelector('.draftprompt--details') } // Restore should restore, then hide the prompt this.element.restore.addEventListener('click', () => { this.restoreDraft() this.switchStateHidden() }) // Delete button will delete the draft this.element.delete.addEventListener('click', () => { if (window.confirm(django.gettext('Are you sure you want to delete your draft?'))) { this.switchStateDeleting() this.deleteDraft().then(ok => ok ? this.switchStateHidden() : this.switchStateDeleteFailed() ) } }) this.switchStateShown() } changeState(data) { var data = data || {} this.element.root.style.display = data.visible ? "block" : "none" this.element.details.innerHTML = data.detailsText || "" } switchStateHidden() { this.changeState({ visible: false }) } switchStateShown() { this.changeState({ visible: true }) } switchStateDeleting() { this.changeState({ visible: true, details: '' }) } switchStateDeleteFailed() { this.changeState({ details: django.gettext("Delete failed!") }) } } // // API UTILITIES // function apiGetDraft() { return fetch('/en-gb/case-study/draft', { method: 'GET', credentials: "same-origin", headers: { "X-CSRFToken": jQuery("[name=csrfmiddlewaretoken]").val(), } }) } function apiPutDraft(formSaver) { if (!formSaver) { throw new Error("apiPutDraft: parameter not provided") } return fetch('/en-gb/case-study/draft', { method: 'PUT', credentials: "same-origin", headers: { "X-CSRFToken": jQuery("[name=csrfmiddlewaretoken]").val(), "Accept": "application/json", "Content-Type": "application/json" }, body: JSON.stringify({ version: 1, data: formSaver.serialise() }) }) } function apiDeleteDraft() { return fetch('/en-gb/case-study/draft', { method: 'DELETE', credentials: "same-origin", headers: { "X-CSRFToken": jQuery("[name=csrfmiddlewaretoken]").val(), } }) } // // GNARLY BITS TYING API & UI STUFF TOGETHER // function showDraftPrompt(formSaver, serialisedForm) { let prompt = new DraftPrompt({ root: document.querySelector('.draftprompt'), restoreFn: () => formSaver.deserialise(serialisedForm), deleteFn: () => apiDeleteDraft().then(response => response.ok) }) } function initDrafts() { const formSaver = new FormSaver({ formId: 'case-study-form', except: [ 'csrfmiddlewaretoken' ] }) // Use whether the form has errors as a proxy for whether the server has // returned us data in the form. In this case, don't show a draft. const formHasErrors = document.getElementById('form_errors').value === 'true' ? true : false if (!formHasErrors) { // Get the previous draft, if any apiGetDraft() .then(response => response.json()) .then(json => { // Handle the case where we didn't get a response if (!json) return if (json.version !== 1) { throw new Error("Bad JSON response version") } showDraftPrompt(formSaver, json.data.form) }).catch(err => { console.error(err); }) } // Init the button controller var button = new SaveButton(document.querySelector('.savebutton')) // Dirty forms set up $('#case-study-form').dirtyForms(); $('#case-study-form').on('dirty.dirtyforms', ev => { button.switchStateUnsaved() }); // Save button button.element.button.addEventListener('click', evt => { button.switchStateSaving() apiPutDraft(formSaver).then(response => { if (response.ok) { button.switchStateSaveSuccess(); $('#case-study-form').dirtyForms('setClean'); document.querySelector('.draftprompt').style.display = "none" } else { button.switchStateSaveFailed(); } }).catch(err => { console.error(err); button.switchStateSaveFailed(); }) }) } // https://github.com/snikch/jquery.dirtyforms