diff --git a/EnvelopeGenerator.Web/package.json b/EnvelopeGenerator.Web/package.json index 09eb9352..52b6b0aa 100644 --- a/EnvelopeGenerator.Web/package.json +++ b/EnvelopeGenerator.Web/package.json @@ -4,7 +4,7 @@ "description": "", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "format": "npx prettier wwwroot/js --write" }, "keywords": [], "author": "", diff --git a/EnvelopeGenerator.Web/prettier.config.js b/EnvelopeGenerator.Web/prettier.config.mjs similarity index 100% rename from EnvelopeGenerator.Web/prettier.config.js rename to EnvelopeGenerator.Web/prettier.config.mjs diff --git a/EnvelopeGenerator.Web/wwwroot/js/annotation.js b/EnvelopeGenerator.Web/wwwroot/js/annotation.js index 8adc01c3..7d696793 100644 --- a/EnvelopeGenerator.Web/wwwroot/js/annotation.js +++ b/EnvelopeGenerator.Web/wwwroot/js/annotation.js @@ -1,58 +1,84 @@ class Annotation { createAnnotations(document) { - const annotations = []; + const annotations = [] document.elements.forEach((element) => { - console.log("Creating annotation for element", element.id) + console.log('Creating annotation for element', element.id) - const [annotation, formField] = this.createAnnotationFromElement(element) - annotations.push(annotation); - annotations.push(formField); + const [annotation, formField] = + this.createAnnotationFromElement(element) + annotations.push(annotation) + annotations.push(formField) }) - return annotations; + return annotations } async deleteAnnotations(instance) { let pageAnnotations = ( - await Promise.all(Array.from({ length: instance.totalPageCount }).map((_, pageIndex) => - instance.getAnnotations(pageIndex) - )) - ).flatMap((annotations) => - annotations.reduce((acc, annotation) => acc.concat(annotation), []) - ).filter((annotation) => !!annotation.isSignature || annotation.description == "FRAME"); + await Promise.all( + Array.from({ length: instance.totalPageCount }).map( + (_, pageIndex) => instance.getAnnotations(pageIndex) + ) + ) + ) + .flatMap((annotations) => + annotations.reduce( + (acc, annotation) => acc.concat(annotation), + [] + ) + ) + .filter( + (annotation) => + !!annotation.isSignature || + annotation.description == 'FRAME' + ) //deleting all Annotations - return await instance.delete(pageAnnotations); + return await instance.delete(pageAnnotations) } async validateAnnotations(instance) { let pageAnnotations = ( - await Promise.all(Array.from({ length: instance.totalPageCount }).map((_, pageIndex) => - instance.getAnnotations(pageIndex) - )) - ).flatMap((annotations) => - annotations.reduce((acc, annotation) => acc.concat(annotation), []) - ).map((annotation) => { - console.log(annotation.toJS()); - return annotation; - }); + await Promise.all( + Array.from({ length: instance.totalPageCount }).map( + (_, pageIndex) => instance.getAnnotations(pageIndex) + ) + ) + ) + .flatMap((annotations) => + annotations.reduce( + (acc, annotation) => acc.concat(annotation), + [] + ) + ) + .map((annotation) => { + console.log(annotation.toJS()) + return annotation + }) - return true; + return true } createAnnotationFromElement(element) { const id = PSPDFKit.generateInstantId() const width = this.inchToPoint(element.width) const height = this.inchToPoint(element.height) - const top = this.inchToPoint(element.top) - (height / 2) - const left = this.inchToPoint(element.left) - (width / 2) + const top = this.inchToPoint(element.top) - height / 2 + const left = this.inchToPoint(element.left) - width / 2 const page = element.page - 1 - const annotation = this.createSignatureAnnotation(id, width, height, top, left, page) + const annotation = this.createSignatureAnnotation( + id, + width, + height, + top, + left, + page + ) console.log(annotation) const formField = new PSPDFKit.FormFields.SignatureFormField({ name: id, - annotationIds: PSPDFKit.Immutable.List([annotation.id]) + annotationIds: PSPDFKit.Immutable.List([annotation.id]), }) console.log(formField) @@ -64,23 +90,32 @@ id: id, pageIndex: pageIndex, formFieldName: id, - boundingBox: new PSPDFKit.Geometry.Rect({ width, height, top, left }) + boundingBox: new PSPDFKit.Geometry.Rect({ + width, + height, + top, + left, + }), }) return annotation } async createFrameAnnotation(annotation, receiver) { - const left = annotation.boundingBox.left - 25; - const top = annotation.boundingBox.top - 25; - const width = 150; - const height = 75; + const left = annotation.boundingBox.left - 25 + const top = annotation.boundingBox.top - 25 + const width = 150 + const height = 75 - const imageUrl = await this.Annotation.createAnnotationFrameBlob(receiver.name, width, height); - const request = await fetch(imageUrl); - const blob = await request.blob(); + const imageUrl = await this.Annotation.createAnnotationFrameBlob( + receiver.name, + width, + height + ) + const request = await fetch(imageUrl) + const blob = await request.blob() - const imageAttachmentId = await this.Instance.createAttachment(blob); + const imageAttachmentId = await this.Instance.createAttachment(blob) const frameAnnotation = new PSPDFKit.Annotations.ImageAnnotation({ pageIndex: annotation.pageIndex, isSignature: false, @@ -96,53 +131,52 @@ width: width, height: height, }), - }); + }) } async createAnnotationFrameBlob(receiverName, width, height) { - const canvas = document.createElement("canvas"); - canvas.width = width; - canvas.height = height; + const canvas = document.createElement('canvas') + canvas.width = width + canvas.height = height - const ctx = canvas.getContext("2d"); + const ctx = canvas.getContext('2d') - const date = new Date(); - const dateString = date.toLocaleDateString("de-DE"); + const date = new Date() + const dateString = date.toLocaleDateString('de-DE') - const signatureLength = 100; + const signatureLength = 100 - ctx.beginPath(); + ctx.beginPath() - ctx.moveTo(30, 10); - ctx.lineTo(signatureLength, 10); + ctx.moveTo(30, 10) + ctx.lineTo(signatureLength, 10) - ctx.moveTo(30, 10); - ctx.arcTo(10, 10, 10, 30, 20); + ctx.moveTo(30, 10) + ctx.arcTo(10, 10, 10, 30, 20) - ctx.moveTo(10, 30); - ctx.arcTo(10, 50, 30, 50, 20); + ctx.moveTo(10, 30) + ctx.arcTo(10, 50, 30, 50, 20) - ctx.moveTo(30, 50); - ctx.lineTo(signatureLength, 50); + ctx.moveTo(30, 50) + ctx.lineTo(signatureLength, 50) - ctx.strokeStyle = "darkblue"; - ctx.stroke(); + ctx.strokeStyle = 'darkblue' + ctx.stroke() - ctx.fillStyle = "black"; - ctx.font = "10px serif"; - ctx.fillText("Signed by", 30, 10) - ctx.fillText(receiverName + ", " + dateString, 15, 60) + ctx.fillStyle = 'black' + ctx.font = '10px serif' + ctx.fillText('Signed by', 30, 10) + ctx.fillText(receiverName + ', ' + dateString, 15, 60) - return new Promise(resolve => { + return new Promise((resolve) => { canvas.toBlob((blob) => { - const url = URL.createObjectURL(blob); - resolve(url) + const url = URL.createObjectURL(blob) + resolve(url) }) }) } inchToPoint(inch) { - return inch * 72; + return inch * 72 } } - diff --git a/EnvelopeGenerator.Web/wwwroot/js/app.js b/EnvelopeGenerator.Web/wwwroot/js/app.js index e4ce77bb..921f317e 100644 --- a/EnvelopeGenerator.Web/wwwroot/js/app.js +++ b/EnvelopeGenerator.Web/wwwroot/js/app.js @@ -6,153 +6,171 @@ const ActionType = { Delivered: 4, Seen: 5, Signed: 6, - Rejected: 7 + Rejected: 7, } class App { constructor(container, envelopeKey) { - this.container = container; - this.envelopeKey = envelopeKey; + this.container = container + this.envelopeKey = envelopeKey // Initialize classes - console.debug("Initializing classes..") - this.UI = new UI(); - this.Network = new Network(); - this.Annotation = new Annotation(); + console.debug('Initializing classes..') + this.UI = new UI() + this.Network = new Network() + this.Annotation = new Annotation() - this.Instance = null; - this.currentDocument = null; - this.currentReceiver = null; + this.Instance = null + this.currentDocument = null + this.currentReceiver = null } // This function will be called in the ShowEnvelope.razor page // and will trigger loading of the Editor Interface async init() { - // Load the envelope from the database - console.debug("Loading envelope from database..") - const envelopeObject = await this.Network.getEnvelope(this.envelopeKey); - this.currentDocument = envelopeObject.envelope.documents[0]; - this.currentReceiver = envelopeObject.receiver; + console.debug('Loading envelope from database..') + const envelopeObject = await this.Network.getEnvelope(this.envelopeKey) + this.currentDocument = envelopeObject.envelope.documents[0] + this.currentReceiver = envelopeObject.receiver console.log(envelopeObject) // Load the document from the filestore - console.debug("Loading document from filestore") + console.debug('Loading document from filestore') let arrayBuffer try { - arrayBuffer = await this.Network.getDocument(this.envelopeKey, this.currentDocument.id); + arrayBuffer = await this.Network.getDocument( + this.envelopeKey, + this.currentDocument.id + ) } catch (e) { console.error(e) } // Load PSPDFKit - console.debug("Loading PSPDFKit..") + console.debug('Loading PSPDFKit..') this.Instance = await this.UI.loadPSPDFKit(arrayBuffer, this.container) this.UI.configurePSPDFKit(this.Instance, this.handleClick.bind(this)) - this.Instance.addEventListener("annotations.load", this.handleAnnotationsLoad) - this.Instance.addEventListener("annotations.change", this.handleAnnotationsChange) - this.Instance.addEventListener("annotations.create", this.handleAnnotationsCreate.bind(this)) + this.Instance.addEventListener( + 'annotations.load', + this.handleAnnotationsLoad + ) + this.Instance.addEventListener( + 'annotations.change', + this.handleAnnotationsChange + ) + this.Instance.addEventListener( + 'annotations.create', + this.handleAnnotationsCreate.bind(this) + ) // Load annotations into PSPDFKit - console.debug("Loading annotations..") + console.debug('Loading annotations..') try { - const annotations = this.Annotation.createAnnotations(this.currentDocument) + const annotations = this.Annotation.createAnnotations( + this.currentDocument + ) const createdAnnotations = await this.Instance.create(annotations) await this.Network.postHistory(this.envelopeKey, ActionType.Seen) } catch (e) { console.error(e) } - } handleAnnotationsLoad(loadedAnnotations) { - console.log("annotations loaded", loadedAnnotations.toJS()); + console.log('annotations loaded', loadedAnnotations.toJS()) } handleAnnotationsChange() {} async handleAnnotationsCreate(createdAnnotations) { - const annotation = createdAnnotations.toJS()[0]; - const isFormField = !!annotation.formFieldName; - const isSignature = !!annotation.isSignature; + const annotation = createdAnnotations.toJS()[0] + const isFormField = !!annotation.formFieldName + const isSignature = !!annotation.isSignature if (isFormField === false && isSignature === true) { - await this.Annotation.createFrameAnnotation(annotation, this.currentReceiver) - } + await this.Annotation.createFrameAnnotation( + annotation, + this.currentReceiver + ) + } } async handleClick(eventType) { - let result = false; + let result = false switch (eventType) { - case "RESET": + case 'RESET': result = await this.handleReset(null) if (result == true) { - alert("Dokument zurückgesetzt!"); + alert('Dokument zurückgesetzt!') } else { - alert("Fehler beim Zurücksetzen des Dokuments!") + alert('Fehler beim Zurücksetzen des Dokuments!') } - break; + break - case "FINISH": + case 'FINISH': result = await this.handleFinish(null) if (result == true) { // TODO: Redirect to success page - alert("Dokument erfolgreich signiert!") + alert('Dokument erfolgreich signiert!') } else { - alert("Fehler beim Abschließen des Dokuments!") + alert('Fehler beim Abschließen des Dokuments!') } - break; + break } } async handleFinish(event) { - // Save changes before doing anything try { - await this.Instance.save(); + await this.Instance.save() } catch (e) { - console.error(e); - return false; + console.error(e) + return false } // Export annotation data and save to database try { const json = await this.Instance.exportInstantJSON() - const postEnvelopeResult = await this.Network.postEnvelope(this.envelopeKey, this.currentDocument.id, JSON.stringify(json)) + const postEnvelopeResult = await this.Network.postEnvelope( + this.envelopeKey, + this.currentDocument.id, + JSON.stringify(json) + ) if (postEnvelopeResult === false) { - return false; + return false } // Redirect to success page after saving to database window.location.href = `/EnvelopeKey/${this.envelopeKey}/Success` - } catch (e) { - console.error(e); - return false; + console.error(e) + return false } - - return true; - + return true } - async handleReset(event) { - if (confirm("Wollen Sie das Dokument und alle erstellten Signaturen zurücksetzen?")) { + async handleReset(event) { + if ( + confirm( + 'Wollen Sie das Dokument und alle erstellten Signaturen zurücksetzen?' + ) + ) { const result = this.Annotation.deleteAnnotations(this.Instance) - return true; + return true } else { - return true; + return true } } } - diff --git a/EnvelopeGenerator.Web/wwwroot/js/network.js b/EnvelopeGenerator.Web/wwwroot/js/network.js index 7a83a3dd..aaf47479 100644 --- a/EnvelopeGenerator.Web/wwwroot/js/network.js +++ b/EnvelopeGenerator.Web/wwwroot/js/network.js @@ -1,66 +1,72 @@ class Network { getEnvelope(envelopeKey) { - return fetch(`/api/envelope/${envelopeKey}`, this.withCSRFToken({ credentials: "include" })) - .then(res => res.json()); + return fetch( + `/api/envelope/${envelopeKey}`, + this.withCSRFToken({ credentials: 'include' }) + ).then((res) => res.json()) } getDocument(envelopeKey, documentId) { - return fetch(`/api/document/${envelopeKey}?index=${documentId}`, this.withCSRFToken({ credentials: "include" })) - .then(res => res.arrayBuffer()); + return fetch( + `/api/document/${envelopeKey}?index=${documentId}`, + this.withCSRFToken({ credentials: 'include' }) + ).then((res) => res.arrayBuffer()) } postEnvelope(envelopeKey, documentId, jsonString) { - const url = `/api/envelope/${envelopeKey}?index=${documentId}`; + const url = `/api/envelope/${envelopeKey}?index=${documentId}` const options = { - credentials: "include", - method: "POST", - body: jsonString + credentials: 'include', + method: 'POST', + body: jsonString, } - console.debug("PostEnvelope/Calling url: " + url) + console.debug('PostEnvelope/Calling url: ' + url) return fetch(url, this.withCSRFToken(options)) .then(this.handleResponse) .then((res) => { if (!res.ok) { - return false; - }; - return true; - }); + return false + } + return true + }) } postHistory(envelopeKey, actionType) { - const url = `/api/history/${envelopeKey}`; + const url = `/api/history/${envelopeKey}` const data = { - actionType: actionType + actionType: actionType, } const options = { - credentials: "include", - method: "POST", + credentials: 'include', + method: 'POST', headers: { - 'Content-Type': "application/json; charset=utf-8" + 'Content-Type': 'application/json; charset=utf-8', }, - body: JSON.stringify(data) + body: JSON.stringify(data), } - console.debug("PostHistory/Calling url: " + url) + console.debug('PostHistory/Calling url: ' + url) return fetch(url, this.withCSRFToken(options)) .then(this.handleResponse) .then((res) => { if (!res.ok) { - return false; - }; - return true; - }); + return false + } + return true + }) } withCSRFToken(options) { - const token = (document.getElementsByName("__RequestVerificationToken")[0]).value; - let headers = options.headers; - options.headers = { ...headers, 'X-XSRF-TOKEN': token }; + const token = document.getElementsByName( + '__RequestVerificationToken' + )[0].value + let headers = options.headers + options.headers = { ...headers, 'X-XSRF-TOKEN': token } - return options; + return options } handleResponse(res) { @@ -72,4 +78,3 @@ } } } - diff --git a/EnvelopeGenerator.Web/wwwroot/js/ui.js b/EnvelopeGenerator.Web/wwwroot/js/ui.js index a710d42d..2ddc1cf0 100644 --- a/EnvelopeGenerator.Web/wwwroot/js/ui.js +++ b/EnvelopeGenerator.Web/wwwroot/js/ui.js @@ -1,15 +1,15 @@ class UI { allowedToolbarItems = [ - "sidebar-thumbnails", - "sidebar-document-ouline", - "sidebar-bookmarks", - "pager", - "pan", - "zoom-out", - "zoom-in", - "zoom-mode", - "spacer", - "search" + 'sidebar-thumbnails', + 'sidebar-document-ouline', + 'sidebar-bookmarks', + 'pager', + 'pan', + 'zoom-out', + 'zoom-in', + 'zoom-mode', + 'spacer', + 'search', ] // Load the PSPDFKit UI by setting a target element as the container to render in @@ -19,24 +19,27 @@ styleSheets: ['/css/site.css'], container: container, document: arrayBuffer, - autoSaveMode: "DISABLED", + autoSaveMode: 'DISABLED', annotationPresets: this.getPresets(), electronicSignatures: { - creationModes: ["DRAW", "TYPE"] + creationModes: ['DRAW', 'TYPE'], }, isEditableAnnotation: function (annotation) { // Check if the annotation is a signature // This will allow new signatures, but not allow edits. console.log(annotation.isSignature, annotation.description) - if (annotation.isSignature || annotation.description == "FRAME") { - return false; + if ( + annotation.isSignature || + annotation.description == 'FRAME' + ) { + return false } - return true; + return true //return !annotation.isSignature; - } + }, }) } @@ -44,7 +47,7 @@ const toolbarItems = this.getToolbarItems(instance, handler) instance.setToolbarItems(toolbarItems) - console.debug("PSPDFKit configured!"); + console.debug('PSPDFKit configured!') } getToolbarItems(instance, handler) { @@ -63,46 +66,48 @@ getCustomItems = function (callback) { return [ { - type: "custom", - id: "button-reset", - className: "button-reset", - title: "Zurücksetzen", + type: 'custom', + id: 'button-reset', + className: 'button-reset', + title: 'Zurücksetzen', onPress() { - console.log("RESET") - callback("RESET") + console.log('RESET') + callback('RESET') }, icon: ` - ` + `, }, { - type: "custom", - id: "button-finish", - className: "button-finish", - title: "Abschließen", + type: 'custom', + id: 'button-finish', + className: 'button-finish', + title: 'Abschließen', onPress() { - console.log("FINISH") - callback("FINISH") - } - } + console.log('FINISH') + callback('FINISH') + }, + }, ] } getDefaultItems(items) { - return items.filter((item) => this.allowedToolbarItems.includes(item.type)) + return items.filter((item) => + this.allowedToolbarItems.includes(item.type) + ) } getPresets() { - const annotationPresets = PSPDFKit.defaultAnnotationPresets; + const annotationPresets = PSPDFKit.defaultAnnotationPresets annotationPresets.ink = { - lineWidth: 10 - }; - - annotationPresets.widget = { - readOnly: true + lineWidth: 10, } - return annotationPresets; + annotationPresets.widget = { + readOnly: true, + } + + return annotationPresets } -} \ No newline at end of file +}