827 lines
28 KiB
JavaScript
Executable File
827 lines
28 KiB
JavaScript
Executable File
//
|
|
// stu_capture.js
|
|
//
|
|
// Displays a form with 3 buttons on the STU pad and on the browser allowing user to input a signature.
|
|
// The final signature is then reproduced on a second window on the PC screen
|
|
//
|
|
// Copyright (c) 2021 Wacom GmbH. All rights reserved.
|
|
//
|
|
//
|
|
|
|
var signatureForm;
|
|
|
|
function captureFromSTU(sigObj, integrityType, hash, extraData) {
|
|
if (!signatureForm) {
|
|
signatureForm = new SignatureForm(sigObj, integrityType, hash, extraData);
|
|
}
|
|
signatureForm.connect();
|
|
}
|
|
|
|
class Point {
|
|
constructor(x, y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
}
|
|
|
|
// In order to simulate buttons, we have our own Button class that stores the bounds and event handler.
|
|
// Using an array of these makes it easy to add or remove buttons as desired.
|
|
// delegate void ButtonClick();
|
|
function Button() {
|
|
this.Bounds; // in Screen coordinates
|
|
this.Text;
|
|
this.Click;
|
|
}
|
|
|
|
function Rectangle(x, y, width, height) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.width = width;
|
|
this.height = height;
|
|
|
|
this.Contains = function (pt) {
|
|
if (((pt.x >= this.x) && (pt.x <= (this.x + this.width))) &&
|
|
((pt.y >= this.y) && (pt.y <= (this.y + this.height)))) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
class SignatureForm {
|
|
|
|
constructor(sigObj, integrityType, hash, extraData) {
|
|
this.sigObj = sigObj;
|
|
this.integrityType = integrityType;
|
|
this.hash = hash;
|
|
this.extraData = extraData;
|
|
|
|
// The mIsDown flag is used like this:
|
|
// 0 = up
|
|
// +ve = down, pressed on button number
|
|
// -1 = down, inking
|
|
// -2 = down, ignoring
|
|
this.mIsDown = 0;
|
|
|
|
this.mPenData = new Array(); // Array of data being stored. This can be subsequently used as desired.
|
|
this.currentDevice = null;
|
|
|
|
this.onClick = false;
|
|
}
|
|
|
|
// Connect to the first device
|
|
async connect() {
|
|
if (!this.currentDevice) {
|
|
let devices = await com.WacomGSS.STU.UsbDevice.requestDevices();
|
|
if (devices.length > 0) {
|
|
this.currentDevice = devices[0];
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
this.mTablet = new com.WacomGSS.STU.Tablet();
|
|
this.mTablet.setEncryptionHandler(new MyEncryptionHandler());
|
|
this.mTablet.setEncryptionHandler2(new MyEncryptionHandler2());
|
|
|
|
try {
|
|
await this.mTablet.usbConnect(this.currentDevice);
|
|
this.mCapability = await this.mTablet.getCapability();
|
|
this.mInformation = await this.mTablet.getInformation();
|
|
this.mInkThreshold = await this.mTablet.getInkThreshold();
|
|
|
|
try {
|
|
await this.mTablet.setPenDataOptionMode(com.WacomGSS.STU.Protocol.PenDataOptionMode.TimeCountSequence);
|
|
} catch (e) {
|
|
}
|
|
|
|
this.mTablet.addTabletHandler(this);
|
|
|
|
//if (this.mTablet.isSupported(com.WacomGSS.STU.Protocol.ReportId.OperationMode_$LI$())) {
|
|
//this.mSignatureMode = true;
|
|
//}
|
|
this.mSignatureMode = false;
|
|
|
|
const pixelWidth = (96*this.mCapability.tabletMaxX*0.01)/25.4;
|
|
const pixelHeight = (96*this.mCapability.tabletMaxY*0.01)/25.4;
|
|
//this.createModalWindow(this.mCapability.screenWidth, this.mCapability.screenHeight);
|
|
this.createModalWindow(pixelWidth, pixelHeight);
|
|
|
|
this.mScaleX = this.canvas.width / this.mCapability.tabletMaxX;
|
|
this.mScaleY = this.canvas.height / this.mCapability.tabletMaxY;
|
|
|
|
this.mBtns = new Array(3);
|
|
this.mBtns[0] = new Button();
|
|
this.mBtns[1] = new Button();
|
|
this.mBtns[2] = new Button();
|
|
|
|
if (this.mSignatureMode) {
|
|
|
|
// LCD is 800x480; Button positions and sizes are fixed
|
|
this.mBtns[0].Bounds = new Rectangle( 0, 431, 265, 48);
|
|
this.mBtns[1].Bounds = new Rectangle(266, 431, 265, 48);
|
|
this.mBtns[2].Bounds = new Rectangle(532, 431, 265, 48);
|
|
} else if (this.mInformation.modelName != "STU-300") {
|
|
|
|
// Place the buttons across the bottom of the screen.
|
|
const w2 = this.canvas.width / 3;
|
|
const w3 = this.canvas.width / 3;
|
|
const w1 = this.canvas.width - w2 - w3;
|
|
const y = this.canvas.height * 6 / 7;
|
|
const h = this.canvas.height - y;
|
|
|
|
this.mBtns[0].Bounds = new Rectangle(0, y, w1, h);
|
|
this.mBtns[1].Bounds = new Rectangle(w1, y, w2, h);
|
|
this.mBtns[2].Bounds = new Rectangle(w1 + w2, y, w3, h);
|
|
|
|
} else {
|
|
// The STU-300 is very shallow, so it is better to utilise
|
|
// the buttons to the side of the display instead.
|
|
|
|
const x = this.mCapability.screenWidth * 3 / 4;
|
|
const w = this.mCapability.screenWidth - x;
|
|
|
|
const h2 = this.mCapability.screenHeight / 3;
|
|
const h3 = this.mCapability.screenHeight / 3;
|
|
const h1 = this.mCapability.screenHeight - h2 - h3;
|
|
|
|
this.mBtns[0].Bounds = new Rectangle(x, 0, w, h1);
|
|
this.mBtns[1].Bounds = new Rectangle(x, h1, w, h2);
|
|
this.mBtns[2].Bounds = new Rectangle(x, h1 + h2, w, h3);
|
|
}
|
|
|
|
this.mBtns[0].Text = "Clear";
|
|
this.mBtns[0].Click = this.btnClearClick.bind(this);
|
|
this.mBtns[1].Text = "Cancel";
|
|
this.mBtns[1].Click = this.btnCancelClick.bind(this);
|
|
this.mBtns[2].Text = "OK";
|
|
this.mBtns[2].Click = this.btnOkClick.bind(this);
|
|
|
|
// This application uses the same bitmap for both the screen and client (window).
|
|
this.ctx.lineWidth = 1;
|
|
this.ctx.strokeStyle = 'black';
|
|
this.ctx.font = "30px Arial";
|
|
|
|
this.ctx.fillStyle = "white";
|
|
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
|
|
let encodingFlag = com.WacomGSS.STU.Protocol.ProtocolHelper.simulateEncodingFlag(this.mTablet.getProductId(), this.mCapability.ecodingFlag);
|
|
// Disable color if the bulk driver isn't installed (supportsWrite())
|
|
if ((encodingFlag & com.WacomGSS.STU.Protocol.EncodingFlag.EncodingFlag_24bit) != 0) {
|
|
this.mEncodingMode = this.mTablet.supportsWrite() ? com.WacomGSS.STU.Protocol.EncodingMode.EncodingMode_24bit_Bulk : com.WacomGSS.STU.Protocol.EncodingMode.EncodingMode_24bit;
|
|
} else if ((encodingFlag & com.WacomGSS.STU.Protocol.EncodingFlag.EncodingFlag_16bit) != 0) {
|
|
this.mEncodingMode = this.mTablet.supportsWrite() ? com.WacomGSS.STU.Protocol.EncodingMode.EncodingMode_16bit_Bulk : com.WacomGSS.STU.Protocol.EncodingMode.EncodingMode_16bit;
|
|
} else {
|
|
// assumes 1bit is available
|
|
this.mEncodingMode = com.WacomGSS.STU.Protocol.EncodingMode.EncodingMode_1bit;
|
|
}
|
|
|
|
if (this.mSignatureMode && !await this.initializeSigMode()) {
|
|
alert("Exception initializing Signature Mode, reverting to normal operation");
|
|
this.mSignatureMode = false;
|
|
}
|
|
|
|
if (!this.mSignatureMode) {
|
|
let btnsColors = ["white", "white", "white"];
|
|
if((this.mEncodingMode & (com.WacomGSS.STU.Protocol.EncodingMode.EncodingMode_16bit_Bulk | com.WacomGSS.STU.Protocol.EncodingMode.EncodingMode_24bit_Bulk)) != 0)
|
|
btnsColors = ["lightgrey", "lightgrey", "lightgrey"];
|
|
|
|
let newCanvas = this.createScreenImage(btnsColors, "black", null);
|
|
|
|
//store the background image in order to be reuse it when clear the screen
|
|
this.mCanvasBackgroundImage = newCanvas.toDataURL("image/jpeg");
|
|
this.mDeviceBackgroundImage = com.WacomGSS.STU.Protocol.ProtocolHelper.resizeAndFlatten(newCanvas, 0, 0, newCanvas.width, newCanvas.height,
|
|
this.mCapability.screenWidth, this.mCapability.screenHeight, this.mEncodingMode,0, "white", false, 0);
|
|
|
|
// If you wish to further optimize image transfer, you can compress the image using
|
|
// the Zlib algorithm.
|
|
const useZlibCompression = false;
|
|
|
|
if (this.mEncodingMode == com.WacomGSS.STU.Protocol.EncodingMode.EncodingMode_1bit && useZlibCompression) {
|
|
this.mDeviceBackgroundImage = compress_using_zlib(this.mDeviceBackgroundImage); // insert compression here!
|
|
this.mEncodingMode = com.WacomGSS.STU.Protocol.EncodingMode.EncodingMode_1bit_ZLib;
|
|
}
|
|
|
|
// Initialize the screen
|
|
await this.clearScreen();
|
|
}
|
|
|
|
if ((this.mTablet.isSupported(com.WacomGSS.STU.Protocol.ReportId.EncryptionStatus)) ||
|
|
(await com.WacomGSS.STU.Protocol.ProtocolHelper.supportsEncryption(this.mTablet.getProtocol()))) {
|
|
|
|
await this.mTablet.startCapture(0xc0ffee);
|
|
this.mIsEncrypted = true;
|
|
}
|
|
|
|
// Enable the pen data on the screen (if not already)
|
|
await this.mTablet.setInkingMode(com.WacomGSS.STU.Protocol.InkingMode.On);
|
|
|
|
this.willCanvas = document.createElement("canvas");
|
|
this.willCanvas.id = "willCanvas";
|
|
this.willCanvas.style.position = "absolute";
|
|
this.willCanvas.style.top = this.canvas.style.top;
|
|
this.willCanvas.style.left = this.canvas.style.left;
|
|
this.willCanvas.height = this.canvas.height;
|
|
this.willCanvas.width = this.canvas.width;
|
|
this.mFormDiv.appendChild(this.willCanvas);
|
|
|
|
if (this.willCanvas.addEventListener) {
|
|
this.willCanvas.addEventListener("click", this.onCanvasClick.bind(this), false);
|
|
}
|
|
|
|
await this.initInkController(this.willCanvas);
|
|
|
|
} catch (e) {
|
|
alert(e);
|
|
}
|
|
}
|
|
|
|
async disconnect() {
|
|
// Ensure that you correctly disconnect from the tablet, otherwise you are
|
|
// likely to get errors when wanting to connect a second time.
|
|
if (this.mTablet != null) {
|
|
if (this.mIsEncrypted) {
|
|
await this.mTablet.endCapture();
|
|
this.mIsEncrypted = false;
|
|
}
|
|
|
|
await this.mTablet.setInkingMode(com.WacomGSS.STU.Protocol.InkingMode.Off);
|
|
await this.mTablet.setClearScreen();
|
|
await this.mTablet.disconnect();
|
|
}
|
|
|
|
this.closeModalWindow();
|
|
}
|
|
|
|
showLoadingScreen(value) {
|
|
if (value) {
|
|
//this.canvas.style.display = "none";
|
|
this.mLoadingImageDiv.style.display = "block";
|
|
} else {
|
|
//this.canvas.style.display = "block";
|
|
this.mLoadingImageDiv.style.display = "none";
|
|
}
|
|
}
|
|
|
|
createModalWindow(width, height) {
|
|
this.mModalBackground = document.createElement('div');
|
|
this.mModalBackground.id = "modal-background";
|
|
this.mModalBackground.className = "active";
|
|
this.mModalBackground.style.width = window.innerWidth;
|
|
this.mModalBackground.style.height = window.innerHeight;
|
|
document.getElementsByTagName('body')[0].appendChild(this.mModalBackground);
|
|
|
|
let titleBarHeight = 25;
|
|
let margin = 2;
|
|
this.mSignatureWindow = document.createElement('div');
|
|
this.mSignatureWindow.id = "signatureWindow";
|
|
this.mSignatureWindow.style.position = "absolute";
|
|
this.mSignatureWindow.style.backgroundColor = "#0097d4";
|
|
this.mSignatureWindow.style.top = (window.innerHeight / 2) - (height / 2) + "px";
|
|
this.mSignatureWindow.style.left = (window.innerWidth / 2) - (width / 2) + "px";
|
|
this.mSignatureWindow.style.width = (width + margin + margin) + "px";
|
|
this.mSignatureWindow.style.height = (height+titleBarHeight + margin + margin) + "px";
|
|
document.getElementsByTagName('body')[0].appendChild(this.mSignatureWindow);
|
|
|
|
this.mTitleBar = document.createElement("div");
|
|
this.mTitleBar.id = "titleBar";
|
|
this.mTitleBar.style.width = "100%";
|
|
this.mTitleBar.style.height = (titleBarHeight-5)+"px";
|
|
this.mTitleBar.innerHTML = this.mInformation.modelName;
|
|
this.mSignatureWindow.appendChild(this.mTitleBar);
|
|
|
|
this.mFormDiv = document.createElement('div');
|
|
//this.mFormDiv.id = "signatureWindow";
|
|
//this.mFormDiv.className = "active";
|
|
this.mFormDiv.style.position = "absolute";
|
|
this.mFormDiv.style.margin = "2px 2px 2px 2px";
|
|
this.mFormDiv.style.top = titleBarHeight;//(window.innerHeight / 2) - (height / 2) + "px";
|
|
//this.mFormDiv.style.left = "10px";//(window.innerWidth / 2) - (width / 2) + "px";
|
|
this.mFormDiv.style.width = width + "px";
|
|
this.mFormDiv.style.height = height + "px";
|
|
this.mSignatureWindow.appendChild(this.mFormDiv);
|
|
//document.getElementsByTagName('body')[0].appendChild(this.mFormDiv);
|
|
|
|
this.canvas = document.createElement("canvas");
|
|
this.canvas.id = "myCanvas";
|
|
this.canvas.style.position = "absolute";
|
|
this.canvas.height = this.mFormDiv.offsetHeight;
|
|
this.canvas.width = this.mFormDiv.offsetWidth;
|
|
this.ctx = this.canvas.getContext("2d");
|
|
this.mFormDiv.appendChild(this.canvas);
|
|
//this.canvas.style.display = "none";
|
|
|
|
//if (this.canvas.addEventListener) {
|
|
//this.canvas.addEventListener("click", this.onCanvasClick.bind(this), false);
|
|
//}
|
|
|
|
this.mLoadingImageDiv = document.createElement('div');
|
|
this.mLoadingImageDiv.style.position = "absolute";
|
|
this.mLoadingImageDiv.style.backgroundColor="white";
|
|
this.mLoadingImageDiv.style.width = "100%";
|
|
this.mLoadingImageDiv.style.height = "100%";
|
|
this.mLoadingImageDiv.innerHTML = '<div id="loadingDiv"><table><tr><td><img src="../common/stu_capture/loading.gif"></td><td>Loading the image, this could take a few seconds...</td></tr></div>';
|
|
this.mFormDiv.appendChild(this.mLoadingImageDiv);
|
|
|
|
$("#signatureWindow").draggable({handle:"#titleBar"});
|
|
}
|
|
|
|
// Initialize Signature Mode (STU-540 only)
|
|
async initializeSigMode() {
|
|
// Buttons on bitmaps sent to the tablet must be in the order Cancel / OK / Clear. The tablet will then
|
|
// reorder button images displayed according to parameters passed to it in OperationMode_Signature
|
|
// This application uses Clear / Cancel / OK
|
|
const btnOrder = [2, 0, 1];
|
|
const btnsUpColors = ["blue", "red", "green"];
|
|
const btnsDownColors = ["darkblue", "darkred", "darkgreen"];
|
|
|
|
let canvas = this.createScreenImage(btnsUpColors, "black", btnOrder);
|
|
let bitmapData = com.WacomGSS.STU.Protocol.ProtocolHelper.resizeAndFlatten(canvas, 0, 0, canvas.width, canvas.height,
|
|
this.mCapability.screenWidth, this.mCapability.screenHeight, this.mEncodingMode,
|
|
com.WacomGSS.STU.Protocol.ProtocolHelper.Scale.Strech, "white", false, 0);
|
|
await this.checkSigModeImage(false, bitmapData);
|
|
|
|
canvas = this.createScreenImage(btnsDownColors, "white", btnOrder);
|
|
bitmapData = com.WacomGSS.STU.Protocol.ProtocolHelper.resizeAndFlatten(canvas, 0, 0, canvas.width, canvas.height,
|
|
this.mCapability.screenWidth, this.mCapability.screenHeight, this.mEncodingMode,
|
|
com.WacomGSS.STU.Protocol.ProtocolHelper.Scale.Strech, "white", false, 0);
|
|
await this.checkSigModeImage(true, bitmapData);
|
|
|
|
let sigMode = new com.WacomGSS.STU.Protocol.OperationMode_Signature(2, btnOrder, 0, 0);
|
|
await this.mTablet.setOperationMode(new com.WacomGSS.STU.Protocol.OperationMode(sigMode));
|
|
|
|
canvas = this.createScreenImage(btnsUpColors, "black", null);
|
|
this.mCanvasBackgroundImage = canvas.toDataURL("image/jpeg");
|
|
|
|
this.clearScreen();
|
|
return true;
|
|
}
|
|
|
|
|
|
createScreenImage(btnColors, txtColor, btnOrder) {
|
|
let canvas = document.createElement("canvas");
|
|
canvas.width = this.canvas.width;
|
|
canvas.height = this.canvas.height;
|
|
|
|
let ctx = canvas.getContext("2d");
|
|
ctx.lineWidth = 1;
|
|
ctx.strokeStyle = 'black';
|
|
ctx.font = "30px Arial";
|
|
|
|
ctx.fillStyle = "white";
|
|
ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
|
|
// Draw the buttons
|
|
for (let i = 0; i < this.mBtns.length; ++i) {
|
|
// Button objects are created in the order, left-to-right, Clear / Cancel / OK
|
|
// If reordering for Signature Mode (btnOrder != null), use bounds of another button when drawing
|
|
// for image to be sent to tablet.
|
|
let btn = this.mBtns[i];
|
|
let bounds = btnOrder != null ? this.mBtns[btnOrder[i]].Bounds : this.mBtns[i].Bounds;
|
|
|
|
if (this.mEncodingMode != com.WacomGSS.STU.Protocol.EncodingMode.EncodingMode_1bit) {
|
|
ctx.fillStyle = btnColors[i];
|
|
ctx.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
|
|
}
|
|
|
|
ctx.fillStyle = txtColor;
|
|
ctx.rect(bounds.x, bounds.y, bounds.width, bounds.height);
|
|
|
|
let xPos = bounds.x + ((bounds.width / 2) - (ctx.measureText(btn.Text).width / 2));
|
|
|
|
let metrics = ctx.measureText(btn.Text);
|
|
let fontHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent;
|
|
let actualHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
|
|
|
|
let yOffset = bounds.height - ((bounds.height / 2) - (actualHeight / 2));
|
|
/*if (m_information.idProduct == enumProductId.STU_300)
|
|
yOffset = 28;
|
|
else if (m_information.idProduct == enumProductId.STU_430)
|
|
yOffset = 26;
|
|
else
|
|
yOffset = 40;*/
|
|
ctx.fillText(btn.Text, xPos, bounds.y + yOffset);
|
|
}
|
|
|
|
ctx.stroke();
|
|
|
|
/*if ((this.mTablet.isSupported(com.WacomGSS.STU.Protocol.ReportId.EncryptionStatus)) ||
|
|
(await com.WacomGSS.STU.Protocol.ProtocolHelper.supportsEncryption(this.mTablet.getProtocol()))) {
|
|
ctx.fillStyle = "black";
|
|
ctx.fillText("\uD83D\uDD12", 20, 50);
|
|
}*/
|
|
return canvas;
|
|
}
|
|
|
|
// Check if a Signature Mode screen image is already stored on the tablet. Download it if not.
|
|
async checkSigModeImage(pushed, imageData) {
|
|
let sigScreenImageNum = 2;
|
|
let romStartImageData = com.WacomGSS.STU.Protocol.RomStartImageData.initializeSignature(this.mEncodingMode, pushed, sigScreenImageNum, [true, true, true]);
|
|
|
|
await this.mTablet.setRomImageHash(com.WacomGSS.STU.Protocol.OperationModeType.Signature_$LI$(), pushed, sigScreenImageNum);
|
|
let romImgHash = await this.mTablet.getRomImageHash();
|
|
|
|
let writeImage = true;
|
|
if (romImgHash.getResult() == 0) {
|
|
// There is already an image stored on the tablet corresponding to this image number and pushed state:
|
|
// compare image hashes to determine if we need to overwrite it.
|
|
if (arrayEquals(md5.array(imageData), romImgHash.getHash())) {
|
|
// Image hashes match: no need to write image again
|
|
writeImage = false;
|
|
}
|
|
}
|
|
// else - no image on pad, writeImage = true;
|
|
|
|
if (writeImage) {
|
|
// no image on pad
|
|
await this.mTablet.writeRomImage(romStartImageData, imageData);
|
|
}
|
|
}
|
|
|
|
async clearScreen() {
|
|
if (window.WILL) {
|
|
window.WILL.clear();
|
|
} else {
|
|
// repaint the background image on the screen.
|
|
const outer = this;
|
|
const image = new Image();
|
|
image.onload = function () {
|
|
outer.ctx.drawImage(image, 0, 0);
|
|
}
|
|
image.src = this.mCanvasBackgroundImage;
|
|
}
|
|
|
|
this.showLoadingScreen(true);
|
|
if (!this.mSignatureMode) {
|
|
// note: There is no need to clear the tablet screen prior to writing an image.
|
|
await this.mTablet.writeImage(this.mEncodingMode, this.mDeviceBackgroundImage);
|
|
}
|
|
|
|
this.mPenData = new Array();
|
|
this.mIsDown = 0;
|
|
|
|
this.showLoadingScreen(false);
|
|
}
|
|
|
|
closeModalWindow() {
|
|
this.deleteInkCanvas();
|
|
document.getElementsByTagName('body')[0].removeChild(this.mSignatureWindow);
|
|
const modalBackground = document.getElementById("modal-background");
|
|
if (modalBackground) {
|
|
document.getElementsByTagName('body')[0].removeChild(modalBackground);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
tabletToScreen(penData) {
|
|
// Screen means LCD screen of the tablet.
|
|
return new Point(penData.x * this.mScaleX, penData.y * this.mScaleY);
|
|
}
|
|
|
|
async onSignatureEvent(keyValue) {
|
|
switch (keyValue) {
|
|
case 0:
|
|
await this.btnCancelClick()
|
|
break;
|
|
case 1:
|
|
await this.btnOkClick();
|
|
break;
|
|
case 2:
|
|
await this.btnClearClick();
|
|
break;
|
|
}
|
|
}
|
|
|
|
onCanvasClick(event) {
|
|
// Enable the mouse to click on the simulated buttons that we have displayed.
|
|
|
|
// Note that this can add some tricky logic into processing pen data
|
|
// if the pen was down at the time of this click, especially if the pen was logically
|
|
// also 'pressing' a button! This demo however ignores any that.
|
|
|
|
const posX = event.pageX - $("#willCanvas").offset().left;
|
|
const posY = event.pageY - $("#willCanvas").offset().top;
|
|
|
|
for (let i = 0; i < this.mBtns.length; i++) {
|
|
if (this.mBtns[i].Bounds.Contains(new Point(posX, posY))) {
|
|
this.mBtns[i].Click();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
async btnOkClick() {
|
|
if (this.mPenData.length > 0) {
|
|
await this.getCaptureData();
|
|
await this.btnCancelClick();
|
|
this.renderSignature = true;
|
|
}
|
|
this.onClick = false;
|
|
|
|
}
|
|
|
|
async btnClearClick() {
|
|
if (this.mPenData.length > 0) {
|
|
await this.clearScreen();
|
|
}
|
|
this.onClick = false;
|
|
}
|
|
|
|
async btnCancelClick() {
|
|
await this.disconnect();
|
|
this.onClick = false;
|
|
}
|
|
|
|
// Generate the signature image
|
|
async getCaptureData() {
|
|
//Create Stroke Data
|
|
var strokeVector = new Module.StrokeVector();
|
|
var currentStroke = new Module.PointVector();
|
|
|
|
var currentStrokeID = 0;
|
|
var isDown = true;
|
|
var hasDown = false;
|
|
|
|
for (let index = 0; index < this.mPenData.length; index++) {
|
|
if (this.mPenData[index].sw == 0 && !hasDown) {
|
|
continue;
|
|
}
|
|
|
|
hasDown = true;
|
|
|
|
if (isDown && this.mPenData[index].sw == 0 || !isDown && this.mPenData[index].sw == 1) {
|
|
isDown = (this.mPenData[index].sw == 1);
|
|
//Move the current stroke data into the strokes array
|
|
strokeVector.push_back({'points': currentStroke});
|
|
currentStroke.delete();
|
|
currentStroke = new Module.PointVector();
|
|
currentStrokeID++;
|
|
}
|
|
|
|
var point = {
|
|
'x': this.mPenData[index].x,
|
|
'y': this.mPenData[index].y,
|
|
'p': this.mPenData[index].pressure,
|
|
't': this.mPenData[index].timeCount,
|
|
'tilt': 0,
|
|
'twist': 0,
|
|
'is_down': this.mPenData[index].sw,
|
|
'stroke_id': currentStrokeID
|
|
};
|
|
|
|
currentStroke.push_back(point);
|
|
}
|
|
|
|
//Create capture area character
|
|
var device = {
|
|
'device_max_X': this.mCapability.tabletMaxX,
|
|
'device_max_Y': this.mCapability.tabletMaxY,
|
|
'device_max_P': this.mCapability.tabletMaxPressure,
|
|
'device_pixels_per_m_x': 100000,
|
|
'device_pixels_per_m_y': 100000,
|
|
'device_origin_X': 0,
|
|
'device_origin_Y': 1,
|
|
'has_tilt': false,
|
|
'has_twist': false
|
|
}
|
|
|
|
var uid2;
|
|
try {
|
|
// getUid2 will throw if pad doesn't support Uid2
|
|
uid2 = mTablet.getUid2();
|
|
}
|
|
catch (e) {
|
|
}
|
|
|
|
if (!uid2) {
|
|
uid2 = 0;
|
|
}
|
|
|
|
var digitizerInfo = "STU;'"+this.mInformation.modelName+"';"+this.mInformation.firmwareMajorVersion+"."+((parseInt(this.mInformation.firmwareMinorVersion) >> 4) & 0x0f)+"."+(parseInt(this.mInformation.firmwareMinorVersion) & 0x0f)+";"+uid2;
|
|
var nicInfo = "";
|
|
var timeResolution = 1000;
|
|
var who = "Test user";
|
|
var why = "test signature";
|
|
var where = "";
|
|
|
|
await this.sigObj.generateSignature(who, why, where, this.integrityType, this.hash, strokeVector, device, digitizerInfo, nicInfo, timeResolution, new Date());
|
|
|
|
// put the extra data
|
|
for (const [key, value] of this.extraData) {
|
|
this.sigObj.setExtraData(key, value);
|
|
}
|
|
|
|
//this.hash.delete();
|
|
strokeVector.delete();
|
|
currentStroke.delete();
|
|
}
|
|
|
|
onPenDataOption(penData, time) {
|
|
this.onPenData(penData, time);
|
|
}
|
|
|
|
onPenDataTimeCountSequence(penData, time) {
|
|
this.onPenData(penData, time);
|
|
}
|
|
|
|
onPenDataTimeCountSequenceEncrypted(penData, time) {
|
|
this.onPenDataTimeCountSequence(penData, time);
|
|
}
|
|
|
|
onPenDataEncryptedOption(penData, time) { // Process incoming pen data
|
|
this.onPenData(penData.penData1, time);
|
|
this.onPenData(penData.penData2, time);
|
|
}
|
|
|
|
onPenDataEncrypted(penData, time) { // Process incoming pen data
|
|
this.onPenData(penData.penData1, time);
|
|
this.onPenData(penData.penData2, time);
|
|
}
|
|
|
|
onPenData(penData, time) { // Process incoming pen data
|
|
|
|
// console.log(JSON.stringify(penData));
|
|
|
|
|
|
if (this.onClick) {
|
|
return;
|
|
}
|
|
|
|
if (!penData.timeCount) {
|
|
penData.timeCount = Math.trunc(time)%1000000;
|
|
}
|
|
|
|
// when the pen goes behind borders there is a bug that onalsy return 0
|
|
let pt = this.tabletToScreen(penData);
|
|
|
|
let btn = 0; // will be +ve if the pen is over a button.
|
|
for (var i = 0; i < this.mBtns.length; ++i) {
|
|
if (this.mBtns[i].Bounds.Contains(pt)) {
|
|
btn = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (this.mIsDown == 0)
|
|
{
|
|
const isDown = (penData.pressure > this.mInkThreshold.onPressureMark);
|
|
|
|
if (isDown)
|
|
{
|
|
// transition to down
|
|
if (btn > 0)
|
|
{
|
|
// We have put the pen down on a button.
|
|
// Track the pen without inking on the client.
|
|
|
|
this.mIsDown = btn;
|
|
}
|
|
else
|
|
{
|
|
// We have put the pen down somewhere else.
|
|
// Treat it as part of the signature.
|
|
|
|
this.mIsDown = -1;
|
|
this.mPenData.push(penData);
|
|
this.mPainting = true;
|
|
|
|
var downEvent = new PointerEvent("pointerdown", {
|
|
pointerId: 1,
|
|
bubbles: true,
|
|
cancelable: true,
|
|
pointerType: "pen",
|
|
pressure: penData.pressure/this.mCapability.tabletMaxPressure,
|
|
isPrimary: true,
|
|
clientX: pt.x,
|
|
clientY: pt.y,
|
|
time: penData.timeCount
|
|
});
|
|
|
|
window.WILL.begin(InkBuilder.createPoint(downEvent));
|
|
}
|
|
} else {
|
|
// hover point
|
|
this.mPenData.push(penData);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const isDown = !(penData.pressure <= this.mInkThreshold.offPressureMark);
|
|
if (!isDown)
|
|
{
|
|
// transition to up
|
|
if (btn > 0) {
|
|
// The pen is over a button
|
|
|
|
if (btn == this.mIsDown) {
|
|
// The pen was pressed down over the same button as is was lifted now.
|
|
// Consider that as a clicki!
|
|
this.onClick = true; //
|
|
this.mBtns[btn-1].Click();
|
|
}
|
|
}
|
|
this.mIsDown = 0;
|
|
|
|
if (this.mPainting) {
|
|
this.mPainting = false;
|
|
this.mPenData.push(penData);
|
|
|
|
if ((penData.x == 0) && (penData.y == 0) && (penData.pressure == 0)) {
|
|
penData.x = this.lastPenData.x;
|
|
penData.y = this.lastPenData.y;
|
|
pt = this.tabletToScreen(penData);
|
|
}
|
|
|
|
var upEvent = new PointerEvent("pointerup", {
|
|
pointerId: 1,
|
|
bubbles: true,
|
|
cancelable: true,
|
|
pointerType: "pen",
|
|
pressure: penData.pressure/this.mCapability.tabletMaxPressure,
|
|
isPrimary: true,
|
|
clientX: pt.x,
|
|
clientY: pt.y,
|
|
time: penData.timeCount
|
|
});
|
|
|
|
window.WILL.end(InkBuilder.createPoint(upEvent));
|
|
}
|
|
} else {
|
|
if (this.mPainting) {
|
|
this.mPenData.push(penData);
|
|
|
|
var moveEvent = new PointerEvent("pointermove", {
|
|
pointerId: 1,
|
|
bubbles: true,
|
|
cancelable: true,
|
|
pointerType: "pen",
|
|
width: 10,
|
|
height: 10,
|
|
pressure: penData.pressure/this.mCapability.tabletMaxPressure,
|
|
isPrimary: true,
|
|
clientX: pt.x,
|
|
clientY: pt.y,
|
|
time: penData.timeCount
|
|
});
|
|
|
|
window.WILL.move(InkBuilder.createPoint(moveEvent));
|
|
}
|
|
|
|
}
|
|
}
|
|
this.lastPenData = penData;
|
|
}
|
|
|
|
onEventDataSignature(eventData) {
|
|
this.onSignatureEvent(eventData.getKeyValue());
|
|
}
|
|
|
|
onEventDataSignatureEncrypted(eventData) {
|
|
this.onSignatureEvent(eventData.getKeyValue());
|
|
}
|
|
|
|
// Capture any report exception.
|
|
onGetReportException(exception) {
|
|
try {
|
|
exception.getException();
|
|
} catch (e) {
|
|
alert(e);
|
|
}
|
|
}
|
|
|
|
async initInkController(canvas) {
|
|
const inkColor = "#0000ff";
|
|
let inkCanvas = await new InkCanvasRaster(canvas, canvas.width, canvas.height);
|
|
await BrushPalette.configure(inkCanvas.canvas.ctx);
|
|
|
|
window.WILL = inkCanvas;
|
|
WILL.setColor(Color.fromHex(inkColor));
|
|
WILL.type = "raster";
|
|
await WILL.setTool("pen");
|
|
}
|
|
|
|
async deleteInkCanvas() {
|
|
await BrushPalette.delete();
|
|
await window.WILL.delete();
|
|
window.WILL = null;
|
|
|
|
if (this.renderSignature) {
|
|
this.renderSignature = false;
|
|
renderSignature(true);
|
|
}
|
|
}
|
|
|
|
onDisconnect(device) {
|
|
if (device == this.currentDevice) {
|
|
if (document.getElementById("modal-background")) {
|
|
this.closeModalWindow();
|
|
}
|
|
alert(device.productName+" has been disconnected, please connect it again.");
|
|
this.currentDevice = null;
|
|
signatureForm = null;
|
|
}
|
|
}
|
|
}
|