class RemoteSigningWidget { constructor() { this.carousel = null; this.signaturePad = null; this.initEventListeners(); } initEventListeners() { window.addEventListener('resize', this.resizeCanvas.bind(this)); document.addEventListener('click', this.resendEmail.bind(this)); document.addEventListener('click', this.clickSign.bind(this)); document.addEventListener('click', this.checkAnswers.bind(this)); document.addEventListener('input', this.onInput.bind(this)); document.addEventListener("DOMContentLoaded", () => { this.initSignature(); this.checkAnswers(); let carouselDiv = document.querySelector('[data-signing-carousel]'); if (carouselDiv) { this.attachCarousel( new bootstrap.Carousel(carouselDiv, { interval: 0, wrap: false, touch: false, keyboard: false, ride: false } )); } }); document.addEventListener('slid.bs.carousel', () => { this.resizeCanvas(); }) } /** * @param {bootstrap.Carousel} carousel */ attachCarousel(carousel) { this.carousel = carousel; } sign() { let container = document.querySelector('[data-remote-signature]'); container.querySelectorAll('button').forEach(btn => btn.disabled = true); Commons.setSpinner(container); app.setRefreshingState(true); if (this.signaturePad) { const dataUrl = this.signaturePad.toDataURL(); this.getSignatureDiv().querySelector("[data-input-type=signature]") .value = dataUrl.split(';base64,')[1]; } document.getElementById("remoteSigningForm").submit(); } /** * @param {PointerEvent} event */ clickSign(event) { let element = event.target; let action = element.dataset.action; if (!action) { return; } if (action === 'sign-modal') { Commons.showModal('sign') } else if (action === 'next') { this.carousel?.next(); } else if (action === 'previous') { this.carousel?.prev(); } else if (action === 'clear-signature-pad') { this.clearSignaturePad() } else if (action === 'submit-sign') { this.sign() } } /** * @param {PointerEvent} event */ resendEmail(event) { let target = event.target; let element; if (app.getOperationInProgress()) { return; } if (element = target.closest('.rs-resend-email')) { let icon = element.querySelector('[data-icon="envelope"]'); let spinner = element.querySelector('[data-icon="loader"]'); console.log(icon, spinner); icon.classList.add('d-none'); spinner.classList.remove('d-none'); Commons.postBody(element.dataset.url, null, () => { element.classList.add('text-muted'); Commons.unsetSpinner(element); icon.classList.remove('d-none'); spinner.classList.add('d-none'); console.log(icon, spinner); }); } } /** * @param {KeyboardEvent} event */ onInput(event) { let el = event.target; if (el.required) { if (el.value.trim() !== '') { el.classList.remove('is-invalid'); } else { el.classList.add('is-invalid'); } } this.checkAnswers(); } checkInlineAnswers() { let total = this.currentWrapper.querySelectorAll("[data-inline-answer]").length; let checked = this.currentWrapper.querySelectorAll("[data-inline-answer] :checked").length; let invalid = this.currentWrapper.querySelectorAll("[data-inline-answer] .is-invalid").length; this.valid &= total === checked; this.valid &= invalid === 0; } checkExclusiveAnswers() { let total = this.currentWrapper.querySelectorAll("[data-exlusive-answer]").length; let checked = this.currentWrapper.querySelectorAll("[data-exlusive-answer] :checked").length; this.valid &= total === checked; } checkMultipleAnswers() { this.currentWrapper.querySelectorAll("[data-multiple-answer]") .forEach(el => { let min = el.dataset.minChoice; let max = el.dataset.maxChoice; let nbChecked = el.querySelectorAll(':checked').length; let currentValid = nbChecked >= min && nbChecked <= max; this.valid &= currentValid; el.querySelectorAll('input').forEach(el => { if (!currentValid) { el.classList.add('is-invalid'); } else { el.classList.remove('is-invalid'); } }); }) } checkAnswers(event) { if (!event) { return; } this.currentWrapper = event.target.closest('.questionnaireWrapper'); if (this.currentWrapper === null) { return; } this.valid = true; this.checkInlineAnswers(); this.checkExclusiveAnswers(); this.checkMultipleAnswers(); let next = this.currentWrapper.querySelector(".question-buttons [data-action=next]"); let sign = this.currentWrapper.querySelector(".question-buttons [data-action=sign]"); if (!this.valid) { this.currentWrapper.querySelector("[data-questions-not-filled]")?.classList.remove('d-none'); if (next) next.disabled = true; if (sign) sign.disabled = true; } else { this.currentWrapper.querySelector("[data-questions-not-filled]")?.classList.add('d-none'); if (next) next.disabled = false; if (sign) sign.disabled = false; } } getSignatureDiv() { return document.querySelector("[data-remote-signature-container]"); } initSignature() { let div = this.getSignatureDiv(); if (div) { let canvas = div.querySelector("canvas"); this.signaturePad = new SignaturePad(canvas); this.signaturePad.addEventListener("afterUpdateStroke", () => { div.querySelector("[data-action='submit-sign']").disabled = false; }); this.resizeCanvas(); } } clearSignaturePad() { this.getSignatureDiv() .querySelector("[data-action='submit-sign']").disabled = true; this.signaturePad.clear(); } resizeCanvas() { if (!this.signaturePad) { return; } let canvas = this.getSignatureDiv().querySelector("canvas"); const ratio = Math.max(window.devicePixelRatio || 1, 1); canvas.width = canvas.offsetWidth * ratio; canvas.height = canvas.offsetHeight * ratio; canvas.getContext("2d").scale(ratio, ratio); this.signaturePad.clear(); } } let remoteSigning = new RemoteSigningWidget(); app.addWidget('RemoteSigning', remoteSigning);