add prettier & format

This commit is contained in:
Jonathan Jenne 2023-11-20 10:59:28 +01:00
parent ea35ed0e29
commit 624266a971
6 changed files with 253 additions and 191 deletions

View File

@ -4,7 +4,7 @@
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "format": "npx prettier wwwroot/js --write"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "",

View File

@ -1,58 +1,84 @@
class Annotation { class Annotation {
createAnnotations(document) { createAnnotations(document) {
const annotations = []; const annotations = []
document.elements.forEach((element) => { 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) const [annotation, formField] =
annotations.push(annotation); this.createAnnotationFromElement(element)
annotations.push(formField); annotations.push(annotation)
annotations.push(formField)
}) })
return annotations; return annotations
} }
async deleteAnnotations(instance) { async deleteAnnotations(instance) {
let pageAnnotations = ( let pageAnnotations = (
await Promise.all(Array.from({ length: instance.totalPageCount }).map((_, pageIndex) => await Promise.all(
instance.getAnnotations(pageIndex) 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"); )
.flatMap((annotations) =>
annotations.reduce(
(acc, annotation) => acc.concat(annotation),
[]
)
)
.filter(
(annotation) =>
!!annotation.isSignature ||
annotation.description == 'FRAME'
)
//deleting all Annotations //deleting all Annotations
return await instance.delete(pageAnnotations); return await instance.delete(pageAnnotations)
} }
async validateAnnotations(instance) { async validateAnnotations(instance) {
let pageAnnotations = ( let pageAnnotations = (
await Promise.all(Array.from({ length: instance.totalPageCount }).map((_, pageIndex) => await Promise.all(
instance.getAnnotations(pageIndex) 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()); .flatMap((annotations) =>
return annotation; annotations.reduce(
}); (acc, annotation) => acc.concat(annotation),
[]
)
)
.map((annotation) => {
console.log(annotation.toJS())
return annotation
})
return true; return true
} }
createAnnotationFromElement(element) { createAnnotationFromElement(element) {
const id = PSPDFKit.generateInstantId() const id = PSPDFKit.generateInstantId()
const width = this.inchToPoint(element.width) const width = this.inchToPoint(element.width)
const height = this.inchToPoint(element.height) const height = this.inchToPoint(element.height)
const top = this.inchToPoint(element.top) - (height / 2) const top = this.inchToPoint(element.top) - height / 2
const left = this.inchToPoint(element.left) - (width / 2) const left = this.inchToPoint(element.left) - width / 2
const page = element.page - 1 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) console.log(annotation)
const formField = new PSPDFKit.FormFields.SignatureFormField({ const formField = new PSPDFKit.FormFields.SignatureFormField({
name: id, name: id,
annotationIds: PSPDFKit.Immutable.List([annotation.id]) annotationIds: PSPDFKit.Immutable.List([annotation.id]),
}) })
console.log(formField) console.log(formField)
@ -64,23 +90,32 @@
id: id, id: id,
pageIndex: pageIndex, pageIndex: pageIndex,
formFieldName: id, formFieldName: id,
boundingBox: new PSPDFKit.Geometry.Rect({ width, height, top, left }) boundingBox: new PSPDFKit.Geometry.Rect({
width,
height,
top,
left,
}),
}) })
return annotation return annotation
} }
async createFrameAnnotation(annotation, receiver) { async createFrameAnnotation(annotation, receiver) {
const left = annotation.boundingBox.left - 25; const left = annotation.boundingBox.left - 25
const top = annotation.boundingBox.top - 25; const top = annotation.boundingBox.top - 25
const width = 150; const width = 150
const height = 75; const height = 75
const imageUrl = await this.Annotation.createAnnotationFrameBlob(receiver.name, width, height); const imageUrl = await this.Annotation.createAnnotationFrameBlob(
const request = await fetch(imageUrl); receiver.name,
const blob = await request.blob(); 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({ const frameAnnotation = new PSPDFKit.Annotations.ImageAnnotation({
pageIndex: annotation.pageIndex, pageIndex: annotation.pageIndex,
isSignature: false, isSignature: false,
@ -96,53 +131,52 @@
width: width, width: width,
height: height, height: height,
}), }),
}); })
} }
async createAnnotationFrameBlob(receiverName, width, height) { async createAnnotationFrameBlob(receiverName, width, height) {
const canvas = document.createElement("canvas"); const canvas = document.createElement('canvas')
canvas.width = width; canvas.width = width
canvas.height = height; canvas.height = height
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext('2d')
const date = new Date(); const date = new Date()
const dateString = date.toLocaleDateString("de-DE"); const dateString = date.toLocaleDateString('de-DE')
const signatureLength = 100; const signatureLength = 100
ctx.beginPath(); ctx.beginPath()
ctx.moveTo(30, 10); ctx.moveTo(30, 10)
ctx.lineTo(signatureLength, 10); ctx.lineTo(signatureLength, 10)
ctx.moveTo(30, 10); ctx.moveTo(30, 10)
ctx.arcTo(10, 10, 10, 30, 20); ctx.arcTo(10, 10, 10, 30, 20)
ctx.moveTo(10, 30); ctx.moveTo(10, 30)
ctx.arcTo(10, 50, 30, 50, 20); ctx.arcTo(10, 50, 30, 50, 20)
ctx.moveTo(30, 50); ctx.moveTo(30, 50)
ctx.lineTo(signatureLength, 50); ctx.lineTo(signatureLength, 50)
ctx.strokeStyle = "darkblue"; ctx.strokeStyle = 'darkblue'
ctx.stroke(); ctx.stroke()
ctx.fillStyle = "black"; ctx.fillStyle = 'black'
ctx.font = "10px serif"; ctx.font = '10px serif'
ctx.fillText("Signed by", 30, 10) ctx.fillText('Signed by', 30, 10)
ctx.fillText(receiverName + ", " + dateString, 15, 60) ctx.fillText(receiverName + ', ' + dateString, 15, 60)
return new Promise(resolve => { return new Promise((resolve) => {
canvas.toBlob((blob) => { canvas.toBlob((blob) => {
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob)
resolve(url) resolve(url)
}) })
}) })
} }
inchToPoint(inch) { inchToPoint(inch) {
return inch * 72; return inch * 72
} }
} }

View File

@ -6,153 +6,171 @@ const ActionType = {
Delivered: 4, Delivered: 4,
Seen: 5, Seen: 5,
Signed: 6, Signed: 6,
Rejected: 7 Rejected: 7,
} }
class App { class App {
constructor(container, envelopeKey) { constructor(container, envelopeKey) {
this.container = container; this.container = container
this.envelopeKey = envelopeKey; this.envelopeKey = envelopeKey
// Initialize classes // Initialize classes
console.debug("Initializing classes..") console.debug('Initializing classes..')
this.UI = new UI(); this.UI = new UI()
this.Network = new Network(); this.Network = new Network()
this.Annotation = new Annotation(); this.Annotation = new Annotation()
this.Instance = null; this.Instance = null
this.currentDocument = null; this.currentDocument = null
this.currentReceiver = null; this.currentReceiver = null
} }
// This function will be called in the ShowEnvelope.razor page // This function will be called in the ShowEnvelope.razor page
// and will trigger loading of the Editor Interface // and will trigger loading of the Editor Interface
async init() { async init() {
// Load the envelope from the database // Load the envelope from the database
console.debug("Loading envelope from database..") console.debug('Loading envelope from database..')
const envelopeObject = await this.Network.getEnvelope(this.envelopeKey); const envelopeObject = await this.Network.getEnvelope(this.envelopeKey)
this.currentDocument = envelopeObject.envelope.documents[0]; this.currentDocument = envelopeObject.envelope.documents[0]
this.currentReceiver = envelopeObject.receiver; this.currentReceiver = envelopeObject.receiver
console.log(envelopeObject) console.log(envelopeObject)
// Load the document from the filestore // Load the document from the filestore
console.debug("Loading document from filestore") console.debug('Loading document from filestore')
let arrayBuffer let arrayBuffer
try { try {
arrayBuffer = await this.Network.getDocument(this.envelopeKey, this.currentDocument.id); arrayBuffer = await this.Network.getDocument(
this.envelopeKey,
this.currentDocument.id
)
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }
// Load PSPDFKit // Load PSPDFKit
console.debug("Loading PSPDFKit..") console.debug('Loading PSPDFKit..')
this.Instance = await this.UI.loadPSPDFKit(arrayBuffer, this.container) this.Instance = await this.UI.loadPSPDFKit(arrayBuffer, this.container)
this.UI.configurePSPDFKit(this.Instance, this.handleClick.bind(this)) this.UI.configurePSPDFKit(this.Instance, this.handleClick.bind(this))
this.Instance.addEventListener("annotations.load", this.handleAnnotationsLoad) this.Instance.addEventListener(
this.Instance.addEventListener("annotations.change", this.handleAnnotationsChange) 'annotations.load',
this.Instance.addEventListener("annotations.create", this.handleAnnotationsCreate.bind(this)) this.handleAnnotationsLoad
)
this.Instance.addEventListener(
'annotations.change',
this.handleAnnotationsChange
)
this.Instance.addEventListener(
'annotations.create',
this.handleAnnotationsCreate.bind(this)
)
// Load annotations into PSPDFKit // Load annotations into PSPDFKit
console.debug("Loading annotations..") console.debug('Loading annotations..')
try { try {
const annotations = this.Annotation.createAnnotations(this.currentDocument) const annotations = this.Annotation.createAnnotations(
this.currentDocument
)
const createdAnnotations = await this.Instance.create(annotations) const createdAnnotations = await this.Instance.create(annotations)
await this.Network.postHistory(this.envelopeKey, ActionType.Seen) await this.Network.postHistory(this.envelopeKey, ActionType.Seen)
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }
} }
handleAnnotationsLoad(loadedAnnotations) { handleAnnotationsLoad(loadedAnnotations) {
console.log("annotations loaded", loadedAnnotations.toJS()); console.log('annotations loaded', loadedAnnotations.toJS())
} }
handleAnnotationsChange() {} handleAnnotationsChange() {}
async handleAnnotationsCreate(createdAnnotations) { async handleAnnotationsCreate(createdAnnotations) {
const annotation = createdAnnotations.toJS()[0]; const annotation = createdAnnotations.toJS()[0]
const isFormField = !!annotation.formFieldName; const isFormField = !!annotation.formFieldName
const isSignature = !!annotation.isSignature; const isSignature = !!annotation.isSignature
if (isFormField === false && isSignature === true) { if (isFormField === false && isSignature === true) {
await this.Annotation.createFrameAnnotation(annotation, this.currentReceiver) await this.Annotation.createFrameAnnotation(
} annotation,
this.currentReceiver
)
}
} }
async handleClick(eventType) { async handleClick(eventType) {
let result = false; let result = false
switch (eventType) { switch (eventType) {
case "RESET": case 'RESET':
result = await this.handleReset(null) result = await this.handleReset(null)
if (result == true) { if (result == true) {
alert("Dokument zurückgesetzt!"); alert('Dokument zurückgesetzt!')
} else { } 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) result = await this.handleFinish(null)
if (result == true) { if (result == true) {
// TODO: Redirect to success page // TODO: Redirect to success page
alert("Dokument erfolgreich signiert!") alert('Dokument erfolgreich signiert!')
} else { } else {
alert("Fehler beim Abschließen des Dokuments!") alert('Fehler beim Abschließen des Dokuments!')
} }
break; break
} }
} }
async handleFinish(event) { async handleFinish(event) {
// Save changes before doing anything // Save changes before doing anything
try { try {
await this.Instance.save(); await this.Instance.save()
} catch (e) { } catch (e) {
console.error(e); console.error(e)
return false; return false
} }
// Export annotation data and save to database // Export annotation data and save to database
try { try {
const json = await this.Instance.exportInstantJSON() 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) { if (postEnvelopeResult === false) {
return false; return false
} }
// Redirect to success page after saving to database // Redirect to success page after saving to database
window.location.href = `/EnvelopeKey/${this.envelopeKey}/Success` window.location.href = `/EnvelopeKey/${this.envelopeKey}/Success`
} catch (e) { } catch (e) {
console.error(e); console.error(e)
return false; return false
} }
return true
return true;
} }
async handleReset(event) { async handleReset(event) {
if (confirm("Wollen Sie das Dokument und alle erstellten Signaturen zurücksetzen?")) { if (
confirm(
'Wollen Sie das Dokument und alle erstellten Signaturen zurücksetzen?'
)
) {
const result = this.Annotation.deleteAnnotations(this.Instance) const result = this.Annotation.deleteAnnotations(this.Instance)
return true; return true
} else { } else {
return true; return true
} }
} }
} }

View File

@ -1,66 +1,72 @@
class Network { class Network {
getEnvelope(envelopeKey) { getEnvelope(envelopeKey) {
return fetch(`/api/envelope/${envelopeKey}`, this.withCSRFToken({ credentials: "include" })) return fetch(
.then(res => res.json()); `/api/envelope/${envelopeKey}`,
this.withCSRFToken({ credentials: 'include' })
).then((res) => res.json())
} }
getDocument(envelopeKey, documentId) { getDocument(envelopeKey, documentId) {
return fetch(`/api/document/${envelopeKey}?index=${documentId}`, this.withCSRFToken({ credentials: "include" })) return fetch(
.then(res => res.arrayBuffer()); `/api/document/${envelopeKey}?index=${documentId}`,
this.withCSRFToken({ credentials: 'include' })
).then((res) => res.arrayBuffer())
} }
postEnvelope(envelopeKey, documentId, jsonString) { postEnvelope(envelopeKey, documentId, jsonString) {
const url = `/api/envelope/${envelopeKey}?index=${documentId}`; const url = `/api/envelope/${envelopeKey}?index=${documentId}`
const options = { const options = {
credentials: "include", credentials: 'include',
method: "POST", method: 'POST',
body: jsonString body: jsonString,
} }
console.debug("PostEnvelope/Calling url: " + url) console.debug('PostEnvelope/Calling url: ' + url)
return fetch(url, this.withCSRFToken(options)) return fetch(url, this.withCSRFToken(options))
.then(this.handleResponse) .then(this.handleResponse)
.then((res) => { .then((res) => {
if (!res.ok) { if (!res.ok) {
return false; return false
}; }
return true; return true
}); })
} }
postHistory(envelopeKey, actionType) { postHistory(envelopeKey, actionType) {
const url = `/api/history/${envelopeKey}`; const url = `/api/history/${envelopeKey}`
const data = { const data = {
actionType: actionType actionType: actionType,
} }
const options = { const options = {
credentials: "include", credentials: 'include',
method: "POST", method: 'POST',
headers: { 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)) return fetch(url, this.withCSRFToken(options))
.then(this.handleResponse) .then(this.handleResponse)
.then((res) => { .then((res) => {
if (!res.ok) { if (!res.ok) {
return false; return false
}; }
return true; return true
}); })
} }
withCSRFToken(options) { withCSRFToken(options) {
const token = (document.getElementsByName("__RequestVerificationToken")[0]).value; const token = document.getElementsByName(
let headers = options.headers; '__RequestVerificationToken'
options.headers = { ...headers, 'X-XSRF-TOKEN': token }; )[0].value
let headers = options.headers
options.headers = { ...headers, 'X-XSRF-TOKEN': token }
return options; return options
} }
handleResponse(res) { handleResponse(res) {
@ -72,4 +78,3 @@
} }
} }
} }

View File

@ -1,15 +1,15 @@
class UI { class UI {
allowedToolbarItems = [ allowedToolbarItems = [
"sidebar-thumbnails", 'sidebar-thumbnails',
"sidebar-document-ouline", 'sidebar-document-ouline',
"sidebar-bookmarks", 'sidebar-bookmarks',
"pager", 'pager',
"pan", 'pan',
"zoom-out", 'zoom-out',
"zoom-in", 'zoom-in',
"zoom-mode", 'zoom-mode',
"spacer", 'spacer',
"search" 'search',
] ]
// Load the PSPDFKit UI by setting a target element as the container to render in // Load the PSPDFKit UI by setting a target element as the container to render in
@ -19,24 +19,27 @@
styleSheets: ['/css/site.css'], styleSheets: ['/css/site.css'],
container: container, container: container,
document: arrayBuffer, document: arrayBuffer,
autoSaveMode: "DISABLED", autoSaveMode: 'DISABLED',
annotationPresets: this.getPresets(), annotationPresets: this.getPresets(),
electronicSignatures: { electronicSignatures: {
creationModes: ["DRAW", "TYPE"] creationModes: ['DRAW', 'TYPE'],
}, },
isEditableAnnotation: function (annotation) { isEditableAnnotation: function (annotation) {
// Check if the annotation is a signature // Check if the annotation is a signature
// This will allow new signatures, but not allow edits. // This will allow new signatures, but not allow edits.
console.log(annotation.isSignature, annotation.description) console.log(annotation.isSignature, annotation.description)
if (annotation.isSignature || annotation.description == "FRAME") { if (
return false; annotation.isSignature ||
annotation.description == 'FRAME'
) {
return false
} }
return true; return true
//return !annotation.isSignature; //return !annotation.isSignature;
} },
}) })
} }
@ -44,7 +47,7 @@
const toolbarItems = this.getToolbarItems(instance, handler) const toolbarItems = this.getToolbarItems(instance, handler)
instance.setToolbarItems(toolbarItems) instance.setToolbarItems(toolbarItems)
console.debug("PSPDFKit configured!"); console.debug('PSPDFKit configured!')
} }
getToolbarItems(instance, handler) { getToolbarItems(instance, handler) {
@ -63,46 +66,48 @@
getCustomItems = function (callback) { getCustomItems = function (callback) {
return [ return [
{ {
type: "custom", type: 'custom',
id: "button-reset", id: 'button-reset',
className: "button-reset", className: 'button-reset',
title: "Zurücksetzen", title: 'Zurücksetzen',
onPress() { onPress() {
console.log("RESET") console.log('RESET')
callback("RESET") callback('RESET')
}, },
icon: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-arrow-counterclockwise" viewBox="0 0 16 16"> icon: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-arrow-counterclockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z"/> <path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z"/>
<path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z"/> <path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z"/>
</svg>` </svg>`,
}, },
{ {
type: "custom", type: 'custom',
id: "button-finish", id: 'button-finish',
className: "button-finish", className: 'button-finish',
title: "Abschließen", title: 'Abschließen',
onPress() { onPress() {
console.log("FINISH") console.log('FINISH')
callback("FINISH") callback('FINISH')
} },
} },
] ]
} }
getDefaultItems(items) { getDefaultItems(items) {
return items.filter((item) => this.allowedToolbarItems.includes(item.type)) return items.filter((item) =>
this.allowedToolbarItems.includes(item.type)
)
} }
getPresets() { getPresets() {
const annotationPresets = PSPDFKit.defaultAnnotationPresets; const annotationPresets = PSPDFKit.defaultAnnotationPresets
annotationPresets.ink = { annotationPresets.ink = {
lineWidth: 10 lineWidth: 10,
};
annotationPresets.widget = {
readOnly: true
} }
return annotationPresets; annotationPresets.widget = {
readOnly: true,
}
return annotationPresets
} }
} }