const ActionType = { Created: 0, Saved: 1, Sent: 2, EmailSent: 3, Delivered: 4, Seen: 5, Signed: 6, Rejected: 7, } class App { constructor(envelopeKey, envelopeReceiver, documentBytes, licenseKey, locale, container) { this.container = container ?? `#${this.constructor.name.toLowerCase()}`; this.envelopeKey = envelopeKey this.Network = new Network() this.Instance = null this.currentDocument = null this.currentReceiver = null this.signatureCount = 0; this.envelopeReceiver = envelopeReceiver; this.documentBytes = documentBytes; this.licenseKey = licenseKey; this.locale = locale; } // This function will be called from the ShowEnvelope.razor page // and will trigger loading of the Editor Interface async init() { // Load the envelope from the database this.currentDocument = this.envelopeReceiver.envelope.documents[0] this.currentReceiver = this.envelopeReceiver.receiver // Load the document from the filestore const documentResponse = this.documentBytes if (documentResponse.fatal || documentResponse.error) { return Swal.fire({ title: 'Fehler', text: 'Dokument konnte nicht geladen werden!', icon: 'error', }) } const arrayBuffer = this.documentBytes // Load PSPDFKit this.Instance = await UI.loadPSPDFKit(arrayBuffer, this.container, this.licenseKey, this.locale) UI.addToolbarItems(this.Instance, this.handleClick.bind(this)) this.Instance.addEventListener( 'annotations.load', this.handleAnnotationsLoad.bind(this) ) this.Instance.addEventListener( 'annotations.change', this.handleAnnotationsChange.bind(this) ) this.Instance.addEventListener( 'annotations.create', this.handleAnnotationsCreate.bind(this) ) this.Instance.addEventListener("annotations.willChange", _ => { Comp.ActPanel.Toggle(); }); // Load annotations into PSPDFKit try { this.signatureCount = this.currentDocument.elements.length await createAnnotations(this.currentDocument, this.Instance) } catch (e) { console.error("Error loading annotations:", e); } //add click events of external buttons [...document.getElementsByClassName('btn_refresh')].forEach(btn => btn.addEventListener('click', _ => this.handleClick('RESET'))); [...document.getElementsByClassName('btn_complete')].forEach(btn => btn.addEventListener('click', _ => this.handleClick('FINISH'))); [...document.getElementsByClassName('btn_reject')].forEach(btn => btn.addEventListener('click', _ => this.handleClick('REJECT'))); } handleAnnotationsLoad(loadedAnnotations) { loadedAnnotations.toJS() } handleAnnotationsChange() { } async handleAnnotationsCreate(createdAnnotations) { const annotation = createdAnnotations.toJS()[0] const isFormField = !!annotation.formFieldName const isSignature = !!annotation.isSignature if (isFormField === false && isSignature === true) { const left = annotation.boundingBox.left - 20 const top = annotation.boundingBox.top - 20 const width = 150 const height = 75 const timestamp = new Date() const imageUrl = await createAnnotationFrameBlob( this.envelopeReceiver.name, this.currentReceiver.signature, timestamp, width, height ) const request = await fetch(imageUrl) const blob = await request.blob() const imageAttachmentId = await this.Instance.createAttachment(blob) const frameAnnotation = createImageAnnotation( new PSPDFKit.Geometry.Rect({ left: left, top: top, width: width, height: height, }), annotation.pageIndex, imageAttachmentId ) this.Instance.create(frameAnnotation) } } async handleClick(eventType) { let result = false switch (eventType) { case 'RESET': result = await this.handleReset(null) Comp.SignatureProgress.SignedCount = 0; if (result.isConfirmed) { Swal.fire({ title: 'Erfolg', text: 'Dokument wurde zurückgesetzt', icon: 'info', }) } break; case 'FINISH': result = await this.handleFinish(null) if (result == true) { // Redirect to success page after saving to database window.location.href = `/EnvelopeKey/${this.envelopeKey}/Success` } break; case 'REJECT': Swal.fire({ title: localized.rejection, html: `
${localized.rejectionReasonQ}
`, icon: "question", input: "text", inputAttributes: { autocapitalize: "off" }, showCancelButton: true, confirmButtonColor: "#3085d6", cancelButtonColor: "#d33", confirmButtonText: localized.complete, cancelButtonText: localized.back, showLoaderOnConfirm: true, preConfirm: async (reason) => { try { var res = await rejectEnvelope(reason); return res; } catch (error) { Swal.showValidationMessage(` Request failed: ${error} `); } }, allowOutsideClick: () => !Swal.isLoading() }).then((result) => { if (!result.isConfirmed) return; const res = result.value; if (res.ok) { redirRejected() } else Swal.showValidationMessage(`Request failed: ${res.message}`); }); break; case 'COPY_URL': const url = window.location.href.replace(/\/readonly/gi, ''); navigator.clipboard.writeText(url).then(function () { bsNotify('Kopiert', { alert_type: 'success', delay: 4, icon_name: 'check_circle' }); }).catch(function (err) { bsNotify('Unerwarteter Fehler', { alert_type: 'danger', delay: 4, icon_name: 'error' }); }); break; case 'SHARE': // Show the modal Comp.ShareBackdrop.show(); break; case 'LOGOUT': await logout(); break; } } async handleFinish(event) { const iJSON = await this.Instance.exportInstantJSON() const iFormFieldValues = await iJSON.formFieldValues; //check required const iReqFields = iFormFieldValues.filter(f => isFieldRequired(f)) const hasEmptyReq = iReqFields.some(f => (f.value === undefined || f.value === null || f.value === "")) if (hasEmptyReq) { Swal.fire({ title: 'Warnung', text: 'Bitte füllen Sie alle Standortinformationen vollständig aus!', icon: 'warning', }) return false; } //check city const city_regex = new RegExp("^[a-zA-Z\\u0080-\\u024F]+(?:([\\ \\-\\']|(\\.\\ ))[a-zA-Z\\u0080-\\u024F]+)*$") const iCityFields = iFormFieldValues.filter(f => isCityField(f)) for (var f of iCityFields) if (!IS_MOBILE_DEVICE && !city_regex.test(f.value)) { Swal.fire({ title: 'Warnung', text: `Bitte überprüfen Sie die eingegebene Ortsangabe "${f.value}" auf korrekte Formatierung. Beispiele für richtige Formate sind: München, Île-de-France, Sauðárkrókur, San Francisco, St. Catharines usw.`, icon: 'warning', }) return false; } //check # of signature const validationResult = await this.validateAnnotations(this.signatureCount) if (validationResult === false) { Swal.fire({ title: 'Warnung', text: 'Es wurden nicht alle Signaturfelder ausgefüllt!', icon: 'warning', }) return false } return Swal.fire({ title: localized.confirmation, html: `
${localized.sigAgree}
`, icon: "question", showCancelButton: true, confirmButtonColor: "#3085d6", cancelButtonColor: "#d33", confirmButtonText: localized.finalize, cancelButtonText: localized.back }).then(async (result) => { if (result.isConfirmed) { //--- // Save changes before doing anything try { await this.Instance.save() } catch (e) { Swal.fire({ title: 'Fehler', text: 'Umschlag konnte nicht signiert werden!', icon: 'error', }) return false } // Export annotation data and save to database try { const res = await this.Network.postEnvelope(this.envelopeKey, await iJSON); if (!res.ok) { if (res.status === 403) { Swal.fire({ title: 'Warnung', text: 'Umschlag ist nicht mehr verfügbar.', icon: 'warning', }) return false } else { throw new Error() } } else return true } catch (e) { Swal.fire({ title: 'Fehler', text: 'Umschlag konnte nicht signiert werden!', icon: 'error', }) return false } } else return false; }); } async validateAnnotations(totalSignatures) { const annotations = await getAnnotations(this.Instance) const filtered = annotations .map(a => a.toJS()) .filter(a => a.isSignature) if (totalSignatures > filtered.length) { return false } else { return true } } async handleReset(event) { const result = await Swal.fire({ title: 'Sind sie sicher?', text: 'Wollen Sie das Dokument und alle erstellten Signaturen zurücksetzen?', icon: 'question', showCancelButton: true }) if (result.isConfirmed) { const result = await deleteAnnotations(this.Instance) } return result } }