Well let me go ahead and get this out of the way, this system was not finished and there are still bugs/issues (that I will describe), but I wanted to put this out there for any other people that may want to connect a signature pad to their Knack app. When I first started this project I tried researching and looking for anyone that may have attempted this before, but I couldn’t find anything, so I figured putting out this information couldn’t hurt anything.
So let’s get to it. First off, every signature pad is different and will have different requirements in order for it to connect to the app properly. The first signature pad I used was a Topaz model T-S460. Later I was given a Wacom signature pad that I didn’t get a chance to mess with very much. The Wacom pad was supposed to make the system easier as it simulates the movement of a mouse instead of being it’s own input system like Topaz. If I had more time with the Wacom pad, I feel like it would have been usable for the setup I was creating.
I want to share the code I created for the Topaz system and explain what was wrong with it and give some potential ideas incase another person comes along and wants to attempt it. I will not explain all of the code, however I did go through some of it and add some extra comments to potentially help explain what is happening. I am still a beginner at Javascript though, so take everything in there with a grain of salt because some parts I’m just not sure exactly what is happening.
Requirements:
- Topaz signature pad
- SigPlusExtLite Browser SDK
- A lot of patience
Issues with the code:
- The biggest issue with this code is that it will not store the signature within the signature field. Instead this will overlay the signature field in the app and put a new image on top of the field.
- The Topaz system will only output in PNG or JPG while the signature field saves as an SVG
- The SigPlusExtLite is necessary when working within a web browser, I used Chrome while I worked on it
- Trying to get the timing lined up with the Knack system time was a challenge because accessing the elements requires the code to wait until everything is loaded fully on the page before it can access anything. This is why I included “setTimeout” within the code.
- $(window).load , $(window).on(‘load’, function(){}) , and $(window).addEventListener(“load”, function(){}) all had issues with accessing the data needed, not sure entirely why, I just know the 1 second delay worked at the time of making it.
- You will need to update the view and field numbers to connect to your specific fields. This may require just changing the numbers or reworking the way it accesses the fields: whether by class, id, tag name, etc.
Code:
$(document).on('knack-view-render.view_##', function(event, view, data) {
var imgWidth;
var imgHeight;
// Testing for change to the signature
$('.jSignature').change(function() {
console.log(this);
});
// Button creation for the signature pad
var canvas = document.getElementById('view_##-field_##');
canvas.insertAdjacentHTML('afterend', '<button id="sigButton" type="button">Sign</button>');
// Event listener for each time the button is pressed
document.getElementById('sigButton').addEventListener("click", function() {
console.log("click");
/** Start signing process */
setTimeout(function() {
var canvasObj = document.getElementById('view_##-field_##').getElementsByClassName('jSignature')[0];
canvasObj.getContext('2d').clearRect(0, 0, canvasObj.width, canvasObj.height);
imgWidth = canvasObj.width;
imgHeight = canvasObj.height;
var message = {"firstName": "", "lastName": "", "eMail": "", "location": "", "imageFormat": 2, "imageX": imgWidth, "imageY": imgHeight, "imageTransparency": false, "imageScaling": false, "maxUpScalPercent": 0.0, "rawDataFormat": "ENC", "minSigPoints": 25 };
top.document.addEventListener('SignResponse', SignResponse, false);
var messageData = JSON.stringify(message);
var element = document.createElement("MyExtensionDataElement");
element.setAttribute("messageAttribute", messageData);
document.documentElement.appendChild(element);
var evt = document.createEvent("Events");
evt.initEvent("SignStartEvent", true, false);
element.dispatchEvent(evt);
console.log("event dispatched");
}, 1000)
})
function SetValues(objResponse, imageWidth, imageHeight) {
var obj = null; //used to access image data
if(typeof(objResponse) === 'string') {
obj = JSON.parse(objResponse);
} else {
obj = JSON.parse(JSON.stringify(objResponse));
}
// canvas access
var ctx = document.getElementById('view_##-field_##').getElementsByClassName('jSignature')[0].getContext('2d');
if(obj.errorMsg != null && obj.errorMsg != "" && obj.errorMsg != "undefined")
{
alert(obj.errorMsg);
} else {
if(obj.isSigned)
{
/* This is where they print out all of the data for the images */
var imgData = obj.imageData;
var img = new Image();
img.onload = function () {
ctx.drawImage(img, 0, 0, imageWidth, imageHeight);
ctx.save();
console.log(ctx);
//uploadImage();
// locate and send image data to the image input field via its id field
var imgInput = document.getElementById('field_##_upload');
imgInput.src = "data:image/png;base64," + obj.imageData;
//console.log(imgInput);
console.log(data);
}
img.src = "data:image/png;base64," + obj.imageData;
}
}
}
function SignResponse(event) {
var str = event.target.getAttribute("msgAttribute");
var obj = JSON.parse(str);
SetValues(obj, imgWidth, imgHeight);
}
})
function uploadImage() {
var app_id = Knack.application_id;
console.log("uploading");
var form = new FormData();
// change #myfileinput to select your <input type="file" /> element
var file = $(`#file`).prop('files')[0];
form.append(`files`, file);
var url = `https://api.knack.com/v1/applications/${app_id}/assets/file/upload`;
var headers = {
'X-Knack-Application-ID': app_id,
'X-Knack-REST-API-Key': `knack`,
};
Knack.showSpinner();
// Make the AJAX call
$.ajax({
url: url,
type: `POST`,
headers: headers,
processData: false,
contentType: false,
mimeType: `multipart/form-data`,
data: form,
}).done(function(responseData) {
console.log(responseData);
Knack.hideSpinner();
});
}