1628 lines
59 KiB
JavaScript
1628 lines
59 KiB
JavaScript
(function (window, document) {
|
|
"use strict";
|
|
|
|
function qs(root, selector) {
|
|
return root ? root.querySelector(selector) : null;
|
|
}
|
|
|
|
function qsa(root, selector) {
|
|
return root ? Array.prototype.slice.call(root.querySelectorAll(selector)) : [];
|
|
}
|
|
|
|
function clamp(value, min, max) {
|
|
return Math.max(min, Math.min(max, value));
|
|
}
|
|
|
|
function uid() {
|
|
return "sig_" + Date.now().toString(36) + Math.random().toString(36).slice(2);
|
|
}
|
|
|
|
function parseJson(value, fallback) {
|
|
try {
|
|
return JSON.parse(value || "");
|
|
} catch (ignore) {
|
|
return fallback;
|
|
}
|
|
}
|
|
|
|
function parseState(value) {
|
|
var state = parseJson(value, {});
|
|
state.CurrentPage = state.CurrentPage || 1;
|
|
state.Scale = state.Scale || 1;
|
|
state.Placements = state.Placements || [];
|
|
state.SignMode = !!state.SignMode;
|
|
state.SelectedPlacementId = state.SelectedPlacementId || "";
|
|
state.ActiveTab = state.ActiveTab || "draw";
|
|
state.IsFullscreen = !!state.IsFullscreen;
|
|
|
|
var hasPropertiesPanelState =
|
|
typeof state.PropertiesPanelOpen ===
|
|
"boolean";
|
|
|
|
state.PropertiesPanelOpen =
|
|
hasPropertiesPanelState
|
|
? state.PropertiesPanelOpen
|
|
: !!state.SelectedPlacementId;
|
|
|
|
return state;
|
|
}
|
|
|
|
function executePostback(code) {
|
|
if (code) {
|
|
window.eval(code);
|
|
}
|
|
}
|
|
|
|
function formatSize(size) {
|
|
var value = Number(size || 0);
|
|
if (value < 1024) { return value + " B"; }
|
|
if (value < 1024 * 1024) { return (value / 1024).toFixed(1) + " KB"; }
|
|
return (value / (1024 * 1024)).toFixed(1) + " MB";
|
|
}
|
|
|
|
function postFile(url, kind, file, callback) {
|
|
var form = new FormData();
|
|
form.append("kind", kind);
|
|
form.append("file", file, file.name);
|
|
|
|
var request = new XMLHttpRequest();
|
|
request.open("POST", url, true);
|
|
request.onreadystatechange = function () {
|
|
if (request.readyState !== 4) { return; }
|
|
|
|
var response = parseJson(request.responseText, null);
|
|
|
|
if (request.status >= 200 && request.status < 300 && response && response.success) {
|
|
callback(null, response);
|
|
} else {
|
|
callback(new Error(response && response.message ? response.message : "No fue posible cargar el archivo."));
|
|
}
|
|
};
|
|
request.send(form);
|
|
}
|
|
|
|
function postCommand(url, kind, id, callback) {
|
|
var form = new FormData();
|
|
form.append("kind", kind);
|
|
form.append("id", id || "");
|
|
|
|
var request = new XMLHttpRequest();
|
|
request.open("POST", url, true);
|
|
request.onreadystatechange = function () {
|
|
if (request.readyState !== 4) { return; }
|
|
callback(request.status >= 200 && request.status < 300);
|
|
};
|
|
request.send(form);
|
|
}
|
|
|
|
function PdfControl(root) {
|
|
this.root = root;
|
|
this.uploadUrl = root.getAttribute("data-upload-url");
|
|
this.actionField = document.getElementById(root.getAttribute("data-action-id"));
|
|
this.stateField = document.getElementById(root.getAttribute("data-state-id"));
|
|
this.uploads = [];
|
|
this.viewer = null;
|
|
this.bindUploadScreen();
|
|
this.bindViewer();
|
|
}
|
|
|
|
PdfControl.prototype.setAction = function (action) {
|
|
if (this.actionField) {
|
|
this.actionField.value = JSON.stringify(action || {});
|
|
}
|
|
};
|
|
|
|
PdfControl.prototype.bindUploadScreen = function () {
|
|
var self = this;
|
|
var zone = qs(this.root, ".sig-pdf-upload-dropzone");
|
|
var input = qs(this.root, ".sig-pdf-upload-input");
|
|
|
|
if (!zone || !input) { return; }
|
|
|
|
function addFiles(files) {
|
|
var max = Number(self.root.getAttribute("data-max-pdf-files") || 20);
|
|
var allowMultiple = self.root.getAttribute("data-allow-multiple") === "true";
|
|
var list = Array.prototype.slice.call(files || []);
|
|
|
|
if (!allowMultiple && list.length > 1) {
|
|
list = [list[0]];
|
|
}
|
|
|
|
list.forEach(function (file) {
|
|
if (self.uploads.length >= max) { return; }
|
|
if (!/\.pdf$/i.test(file.name) && file.type !== "application/pdf") {
|
|
window.alert("Solo se permiten archivos PDF.");
|
|
return;
|
|
}
|
|
|
|
postFile(self.uploadUrl, "pdf", file, function (error, response) {
|
|
if (error) {
|
|
window.alert(error.message);
|
|
return;
|
|
}
|
|
|
|
self.uploads.push({
|
|
id: response.id,
|
|
name: response.name || file.name,
|
|
size: response.size || file.size
|
|
});
|
|
self.renderUploads();
|
|
});
|
|
});
|
|
}
|
|
|
|
var usingDropzone = false;
|
|
|
|
if (window.Dropzone) {
|
|
usingDropzone = true;
|
|
window.Dropzone.autoDiscover = false;
|
|
input.style.display = "none";
|
|
|
|
var pdfDropzone = new window.Dropzone(zone, {
|
|
url: self.uploadUrl,
|
|
paramName: "file",
|
|
params: { kind: "pdf" },
|
|
acceptedFiles: "application/pdf,.pdf",
|
|
maxFilesize: 50,
|
|
maxFiles: self.root.getAttribute("data-allow-multiple") === "true"
|
|
? Number(self.root.getAttribute("data-max-pdf-files") || 20)
|
|
: 1,
|
|
uploadMultiple: false,
|
|
parallelUploads: 1,
|
|
previewsContainer: false,
|
|
createImageThumbnails: false,
|
|
clickable: true,
|
|
accept: function (file, done) {
|
|
var max = Number(self.root.getAttribute("data-max-pdf-files") || 20);
|
|
if (self.uploads.length >= max) {
|
|
done("Se alcanzó el límite de archivos.");
|
|
} else {
|
|
done();
|
|
}
|
|
}
|
|
});
|
|
|
|
pdfDropzone.on("success", function (file, response) {
|
|
var data = typeof response === "string" ? parseJson(response, null) : response;
|
|
if (!data || !data.success) {
|
|
window.alert(data && data.message ? data.message : "No fue posible cargar el PDF.");
|
|
return;
|
|
}
|
|
self.uploads.push({
|
|
id: data.id,
|
|
name: data.name || file.name,
|
|
size: data.size || file.size
|
|
});
|
|
self.renderUploads();
|
|
pdfDropzone.removeFile(file);
|
|
});
|
|
|
|
pdfDropzone.on("error", function (file, message) {
|
|
var data = typeof message === "string" ? parseJson(message, null) : message;
|
|
window.alert(data && data.message ? data.message : (typeof message === "string" ? message : "No fue posible cargar el PDF."));
|
|
pdfDropzone.removeFile(file);
|
|
});
|
|
}
|
|
|
|
if (!usingDropzone) {
|
|
zone.addEventListener("click", function (event) {
|
|
if (event.target !== input) {
|
|
input.click();
|
|
}
|
|
});
|
|
|
|
input.addEventListener("change", function () {
|
|
addFiles(this.files);
|
|
this.value = "";
|
|
});
|
|
|
|
zone.addEventListener("dragover", function (event) {
|
|
event.preventDefault();
|
|
zone.classList.add("is-dragover");
|
|
});
|
|
|
|
zone.addEventListener("dragleave", function () {
|
|
zone.classList.remove("is-dragover");
|
|
});
|
|
|
|
zone.addEventListener("drop", function (event) {
|
|
event.preventDefault();
|
|
zone.classList.remove("is-dragover");
|
|
addFiles(event.dataTransfer.files);
|
|
});
|
|
}
|
|
|
|
var clearButton = qs(this.root, ".sig-pdf-upload-clear");
|
|
var combineButton = qs(this.root, ".sig-pdf-upload-combine");
|
|
|
|
if (clearButton) {
|
|
clearButton.addEventListener("click", function () {
|
|
self.uploads.forEach(function (item) {
|
|
postCommand(self.uploadUrl, "remove-pdf", item.id, function () {});
|
|
});
|
|
self.uploads = [];
|
|
self.renderUploads();
|
|
});
|
|
}
|
|
|
|
if (combineButton) {
|
|
combineButton.addEventListener("click", function () {
|
|
if (!self.uploads.length) { return; }
|
|
self.setAction({
|
|
Action: "combine-pdfs",
|
|
UploadIds: self.uploads.map(function (item) { return item.id; })
|
|
});
|
|
executePostback(self.root.getAttribute("data-postback-combine"));
|
|
});
|
|
}
|
|
|
|
self.renderUploads();
|
|
};
|
|
|
|
PdfControl.prototype.renderUploads = function () {
|
|
var self = this;
|
|
var list = qs(this.root, ".sig-pdf-upload-list");
|
|
var combineButton = qs(this.root, ".sig-pdf-upload-combine");
|
|
|
|
if (!list) { return; }
|
|
|
|
list.innerHTML = "";
|
|
|
|
this.uploads.forEach(function (item, index) {
|
|
var row = document.createElement("div");
|
|
row.className = "sig-pdf-upload-item";
|
|
row.innerHTML =
|
|
"<span class=\"sig-pdf-upload-order\">" + (index + 1) + "</span>" +
|
|
"<span class=\"sig-pdf-upload-file\"><strong></strong><small></small></span>" +
|
|
"<span class=\"sig-pdf-upload-item-actions\">" +
|
|
"<button type=\"button\" class=\"sig-upload-up\" title=\"Subir\">↑</button>" +
|
|
"<button type=\"button\" class=\"sig-upload-down\" title=\"Bajar\">↓</button>" +
|
|
"<button type=\"button\" class=\"sig-upload-remove\" title=\"Quitar\">×</button>" +
|
|
"</span>";
|
|
|
|
qs(row, "strong").textContent = item.name;
|
|
qs(row, "small").textContent = formatSize(item.size);
|
|
|
|
qs(row, ".sig-upload-up").disabled = index === 0;
|
|
qs(row, ".sig-upload-down").disabled = index === self.uploads.length - 1;
|
|
|
|
qs(row, ".sig-upload-up").addEventListener("click", function () {
|
|
if (index <= 0) { return; }
|
|
var temp = self.uploads[index - 1];
|
|
self.uploads[index - 1] = self.uploads[index];
|
|
self.uploads[index] = temp;
|
|
self.renderUploads();
|
|
});
|
|
|
|
qs(row, ".sig-upload-down").addEventListener("click", function () {
|
|
if (index >= self.uploads.length - 1) { return; }
|
|
var temp = self.uploads[index + 1];
|
|
self.uploads[index + 1] = self.uploads[index];
|
|
self.uploads[index] = temp;
|
|
self.renderUploads();
|
|
});
|
|
|
|
qs(row, ".sig-upload-remove").addEventListener("click", function () {
|
|
postCommand(self.uploadUrl, "remove-pdf", item.id, function () {});
|
|
self.uploads.splice(index, 1);
|
|
self.renderUploads();
|
|
});
|
|
|
|
list.appendChild(row);
|
|
});
|
|
|
|
if (combineButton) {
|
|
combineButton.disabled = this.uploads.length === 0;
|
|
}
|
|
};
|
|
|
|
PdfControl.prototype.bindViewer = function () {
|
|
var viewerRoot = qs(this.root, ".sig-pdf-viewer");
|
|
if (!viewerRoot) { return; }
|
|
this.viewer = new Viewer(this, viewerRoot);
|
|
};
|
|
|
|
function Viewer(control, root) {
|
|
this.control = control;
|
|
this.root = root;
|
|
this.pdfUrl = root.getAttribute("data-pdf-url");
|
|
this.bestUrl = root.getAttribute("data-best-url");
|
|
this.downloadUrl = root.getAttribute("data-download-url");
|
|
this.workerUrl = root.getAttribute("data-worker-url");
|
|
this.state = parseState(control.stateField ? control.stateField.value : "");
|
|
this.currentPage = Number(this.state.CurrentPage || 1);
|
|
this.scale = Number(this.state.Scale || 1);
|
|
this.pdf = null;
|
|
this.renderTask = null;
|
|
this.pageWidth = 0;
|
|
this.pageHeight = 0;
|
|
this.selectedId = this.state.SelectedPlacementId || null;
|
|
this.signMode = !!this.state.SignMode;
|
|
this.propertiesPanelOpen = !!this.state.PropertiesPanelOpen;
|
|
this.currentSignature = null;
|
|
this.uploadedSignature = null;
|
|
this.strokes = [];
|
|
this.redoStrokes = [];
|
|
this.currentStroke = null;
|
|
this.drawColor = "#111111";
|
|
this.pendingDrawColor = this.drawColor;
|
|
this.bind();
|
|
this.load();
|
|
}
|
|
|
|
Viewer.prototype.bind = function () {
|
|
var self = this;
|
|
this.canvas = qs(this.root, ".sig-pdf-canvas");
|
|
this.layer = qs(this.root, ".sig-signature-layer");
|
|
this.shell = qs(this.root, ".sig-page-shell");
|
|
this.loading = qs(this.root, ".sig-loading");
|
|
this.leftPanel = qs(this.root, ".sig-left-panel");
|
|
this.rightPanel = qs(this.root, ".sig-right-panel");
|
|
this.startButton = qs(this.root, ".sig-start-signing");
|
|
this.finishButton = qs(this.root, ".sig-finish-signing");
|
|
this.propertiesToggle = qs(this.root, ".sig-toggle-properties");
|
|
this.modeLabel = qs(this.root, ".sig-mode-label");
|
|
this.pageInput = qs(this.root, ".sig-page-number");
|
|
this.pageCount = qs(this.root, ".sig-page-count");
|
|
this.zoomSelect = qs(this.root, ".sig-zoom-select");
|
|
this.drawCanvas = qs(this.root, ".sig-draw-canvas");
|
|
|
|
this.bindClick(".sig-prev-page", function () { self.goToPage(self.currentPage - 1); });
|
|
this.bindClick(".sig-next-page", function () { self.goToPage(self.currentPage + 1); });
|
|
this.bindClick(".sig-zoom-in", function () { self.setScale(self.scale + 0.25); });
|
|
this.bindClick(".sig-zoom-out", function () { self.setScale(self.scale - 0.25); });
|
|
this.bindClick(".sig-print", function () { window.open(self.bestUrl, "_blank", "noopener"); });
|
|
this.bindClick(".sig-download", function () { window.location.href = self.downloadUrl; });
|
|
this.bindClick(".sig-start-signing", function () { self.enterSignMode(); });
|
|
this.bindClick(".sig-finish-signing", function () { self.postback("data-postback-finalize"); });
|
|
this.bindClick(".sig-toggle-properties", function () { self.togglePropertiesPanel(); });
|
|
this.bindClick(".sig-refresh-document", function () { self.reloadDocument(); });
|
|
this.bindClick(".sig-change-documents", function () { self.postback("data-postback-reset-upload"); });
|
|
this.bindClick(".sig-expand-viewer", function () { self.setFullscreen(true); });
|
|
this.bindClick(".sig-collapse-viewer", function () { self.setFullscreen(false); });
|
|
this.bindClick(".sig-delete-signature", function () { self.deleteSelected(); });
|
|
this.bindClick(".sig-clear-all-signatures", function () { self.clearAll(); });
|
|
this.bindClick(".sig-download-signed", function () { self.postback("data-postback-download-signed"); });
|
|
this.bindClick(".sig-save-signed", function () { self.postback("data-postback-save-signed"); });
|
|
this.bindClick(".sig-save-exit", function () { self.postback("data-postback-save-exit"); });
|
|
|
|
if (this.pageInput) {
|
|
this.pageInput.addEventListener("change", function () {
|
|
self.goToPage(parseInt(this.value, 10));
|
|
});
|
|
}
|
|
|
|
if (this.zoomSelect) {
|
|
this.zoomSelect.addEventListener("change", function () {
|
|
self.setScale(parseFloat(this.value));
|
|
});
|
|
}
|
|
|
|
if (this.shell) {
|
|
this.shell.addEventListener("click", function () { self.select(null); });
|
|
}
|
|
|
|
this.bindTabs();
|
|
this.bindDrawPad();
|
|
this.bindDrawColorPicker();
|
|
this.bindSignatureUpload();
|
|
this.bindSavedSignatures();
|
|
this.bindPropertyEditors();
|
|
this.restoreUiState();
|
|
|
|
document.addEventListener("keydown", function (event) {
|
|
if (event.keyCode === 27 && self.root.classList.contains("is-fullscreen")) {
|
|
self.setFullscreen(false);
|
|
}
|
|
});
|
|
};
|
|
|
|
Viewer.prototype.bindClick = function (selector, handler) {
|
|
var element = qs(this.root, selector);
|
|
if (element) { element.addEventListener("click", handler); }
|
|
};
|
|
|
|
Viewer.prototype.postback = function (attributeName) {
|
|
this.syncState();
|
|
executePostback(this.control.root.getAttribute(attributeName));
|
|
};
|
|
|
|
|
|
Viewer.prototype.updatePropertiesToggle = function () {
|
|
if (!this.propertiesToggle) {
|
|
return;
|
|
}
|
|
|
|
var isOpen =
|
|
this.signMode &&
|
|
this.propertiesPanelOpen;
|
|
|
|
this.propertiesToggle.hidden =
|
|
!this.signMode;
|
|
|
|
this.propertiesToggle.classList.toggle(
|
|
"is-active",
|
|
isOpen
|
|
);
|
|
|
|
this.propertiesToggle.setAttribute(
|
|
"aria-expanded",
|
|
isOpen ? "true" : "false"
|
|
);
|
|
|
|
this.propertiesToggle.setAttribute(
|
|
"title",
|
|
isOpen
|
|
? "Ocultar propiedades de la firma"
|
|
: "Mostrar propiedades de la firma"
|
|
);
|
|
|
|
this.propertiesToggle.setAttribute(
|
|
"aria-label",
|
|
isOpen
|
|
? "Ocultar propiedades de la firma"
|
|
: "Mostrar propiedades de la firma"
|
|
);
|
|
};
|
|
|
|
Viewer.prototype.applyPropertiesPanelState = function () {
|
|
var visible =
|
|
this.signMode &&
|
|
this.propertiesPanelOpen;
|
|
|
|
if (this.rightPanel) {
|
|
this.rightPanel.classList.toggle(
|
|
"is-open",
|
|
visible
|
|
);
|
|
}
|
|
|
|
this.root.classList.toggle(
|
|
"properties-open",
|
|
visible
|
|
);
|
|
|
|
this.updatePropertiesToggle();
|
|
};
|
|
|
|
Viewer.prototype.setPropertiesPanel = function (
|
|
open,
|
|
sync
|
|
) {
|
|
this.propertiesPanelOpen = !!open;
|
|
this.state.PropertiesPanelOpen =
|
|
this.propertiesPanelOpen;
|
|
|
|
this.applyPropertiesPanelState();
|
|
|
|
if (sync !== false) {
|
|
this.syncState();
|
|
}
|
|
};
|
|
|
|
Viewer.prototype.showPropertiesForSignature = function () {
|
|
this.setPropertiesPanel(
|
|
true,
|
|
false
|
|
);
|
|
};
|
|
|
|
Viewer.prototype.togglePropertiesPanel = function () {
|
|
if (!this.signMode) {
|
|
return;
|
|
}
|
|
|
|
this.setPropertiesPanel(
|
|
!this.propertiesPanelOpen,
|
|
true
|
|
);
|
|
};
|
|
|
|
|
|
Viewer.prototype.restoreUiState = function () {
|
|
var self = this;
|
|
var activeTab = this.state.ActiveTab || "draw";
|
|
|
|
qsa(this.root, ".sig-signature-tab").forEach(function (button) {
|
|
button.classList.toggle(
|
|
"is-active",
|
|
button.getAttribute("data-tab") === activeTab
|
|
);
|
|
});
|
|
|
|
qsa(this.root, ".sig-signature-tab-panel").forEach(function (panel) {
|
|
panel.classList.toggle(
|
|
"is-active",
|
|
panel.classList.contains("sig-tab-" + activeTab)
|
|
);
|
|
});
|
|
|
|
if (this.signMode) {
|
|
this.root.classList.add("is-signing");
|
|
|
|
if (this.leftPanel) {
|
|
this.leftPanel.classList.add("is-open");
|
|
}
|
|
|
|
if (this.startButton) {
|
|
this.startButton.hidden = true;
|
|
}
|
|
|
|
if (this.finishButton) {
|
|
this.finishButton.hidden = false;
|
|
}
|
|
|
|
if (this.modeLabel) {
|
|
this.modeLabel.textContent =
|
|
"Modo de firma activo";
|
|
}
|
|
}
|
|
|
|
if (this.state.IsFullscreen) {
|
|
this.root.classList.add("is-fullscreen");
|
|
document.body.classList.add(
|
|
"sig-viewer-fullscreen-open"
|
|
);
|
|
|
|
var expand =
|
|
qs(this.root, ".sig-expand-viewer");
|
|
|
|
var collapse =
|
|
qs(this.root, ".sig-collapse-viewer");
|
|
|
|
if (expand) {
|
|
expand.hidden = true;
|
|
}
|
|
|
|
if (collapse) {
|
|
collapse.hidden = false;
|
|
}
|
|
}
|
|
|
|
if (
|
|
!this.selectedId ||
|
|
!this.state.Placements.some(
|
|
function (placement) {
|
|
return placement.Id ===
|
|
self.selectedId;
|
|
}
|
|
)
|
|
) {
|
|
this.selectedId = null;
|
|
this.state.SelectedPlacementId = "";
|
|
}
|
|
|
|
/*
|
|
* Respeta la decisión manual del usuario.
|
|
* Un postback no vuelve a abrir el panel
|
|
* si el usuario lo había cerrado.
|
|
*/
|
|
this.applyPropertiesPanelState();
|
|
};
|
|
|
|
Viewer.prototype.finishSigningClient = function () {
|
|
this.signMode = false;
|
|
this.selectedId = null;
|
|
this.state.SignMode = false;
|
|
this.state.SelectedPlacementId = "";
|
|
|
|
this.root.classList.remove("is-signing");
|
|
this.root.classList.remove("properties-open");
|
|
|
|
if (this.leftPanel) {
|
|
this.leftPanel.classList.remove("is-open");
|
|
}
|
|
|
|
if (this.rightPanel) {
|
|
this.rightPanel.classList.remove("is-open");
|
|
}
|
|
|
|
if (this.startButton) {
|
|
this.startButton.hidden = false;
|
|
}
|
|
|
|
if (this.finishButton) {
|
|
this.finishButton.hidden = true;
|
|
}
|
|
|
|
if (this.modeLabel) {
|
|
this.modeLabel.textContent = "Modo lectura";
|
|
}
|
|
|
|
this.updatePropertiesToggle();
|
|
this.renderPlacements();
|
|
this.syncState();
|
|
};
|
|
|
|
Viewer.prototype.reloadDocument = function () {
|
|
var cleanUrl = this.pdfUrl
|
|
.replace(/([?&])_sigRefresh=\d+/g, "$1")
|
|
.replace(/[?&]$/, "");
|
|
var separator = cleanUrl.indexOf("?") >= 0 ? "&" : "?";
|
|
this.pdfUrl = cleanUrl + separator + "_sigRefresh=" + new Date().getTime();
|
|
|
|
if (this.pdf && typeof this.pdf.destroy === "function") {
|
|
try { this.pdf.destroy(); } catch (ignore) {}
|
|
}
|
|
|
|
this.pdf = null;
|
|
this.load();
|
|
};
|
|
|
|
Viewer.prototype.addStoredSignatureClient = function (item) {
|
|
if (!item || !item.Id) { return; }
|
|
|
|
var list = qs(this.root, ".sig-saved-signature-list");
|
|
if (!list) { return; }
|
|
|
|
var existing = qs(
|
|
list,
|
|
'.sig-saved-signature-item[data-asset-id="' + item.Id + '"]'
|
|
);
|
|
|
|
if (existing) { return; }
|
|
|
|
var button = document.createElement("button");
|
|
button.type = "button";
|
|
button.className = "sig-saved-signature-item";
|
|
button.setAttribute("data-asset-id", item.Id);
|
|
button.setAttribute("data-image-url", item.ImageUrl || "");
|
|
button.setAttribute("data-name", item.Name || "Firma");
|
|
button.innerHTML =
|
|
'<span class="sig-saved-signature-icon"><i class="fa fa-pencil-square-o"></i></span>' +
|
|
'<span class="sig-saved-signature-name"></span>';
|
|
|
|
qs(button, ".sig-saved-signature-name").textContent = item.Name || "Firma";
|
|
|
|
var self = this;
|
|
button.addEventListener("click", function () {
|
|
var signature = {
|
|
assetId: button.getAttribute("data-asset-id"),
|
|
name: button.getAttribute("data-name"),
|
|
imageUrl: button.getAttribute("data-image-url"),
|
|
dataUrl: ""
|
|
};
|
|
self.currentSignature = signature;
|
|
self.previewSavedSignature(signature);
|
|
self.placeCurrentSignature();
|
|
});
|
|
|
|
list.appendChild(button);
|
|
};
|
|
|
|
Viewer.prototype.bindTabs = function () {
|
|
var self = this;
|
|
qsa(this.root, ".sig-signature-tab").forEach(function (button) {
|
|
button.addEventListener("click", function () {
|
|
var tab = button.getAttribute("data-tab");
|
|
self.state.ActiveTab = tab || "draw";
|
|
self.syncState();
|
|
qsa(self.root, ".sig-signature-tab").forEach(function (item) {
|
|
item.classList.toggle("is-active", item === button);
|
|
});
|
|
qsa(self.root, ".sig-signature-tab-panel").forEach(function (panel) {
|
|
panel.classList.toggle("is-active", panel.classList.contains("sig-tab-" + tab));
|
|
});
|
|
});
|
|
});
|
|
};
|
|
|
|
Viewer.prototype.bindDrawPad = function () {
|
|
var self = this;
|
|
var canvas = this.drawCanvas;
|
|
if (!canvas) { return; }
|
|
var context = canvas.getContext("2d");
|
|
var drawing = false;
|
|
|
|
function position(event) {
|
|
var rect = canvas.getBoundingClientRect();
|
|
var source = event.touches && event.touches.length ? event.touches[0] : event;
|
|
return {
|
|
x: (source.clientX - rect.left) * canvas.width / rect.width,
|
|
y: (source.clientY - rect.top) * canvas.height / rect.height
|
|
};
|
|
}
|
|
|
|
function begin(event) {
|
|
event.preventDefault();
|
|
drawing = true;
|
|
self.redoStrokes = [];
|
|
self.currentStroke = {
|
|
color: self.drawColor,
|
|
width: Number(qs(self.root, ".sig-draw-width").value || 3),
|
|
points: [position(event)]
|
|
};
|
|
}
|
|
|
|
function move(event) {
|
|
if (!drawing || !self.currentStroke) { return; }
|
|
event.preventDefault();
|
|
self.currentStroke.points.push(position(event));
|
|
self.redrawPad();
|
|
}
|
|
|
|
function end(event) {
|
|
if (!drawing) { return; }
|
|
if (event) { event.preventDefault(); }
|
|
drawing = false;
|
|
if (self.currentStroke && self.currentStroke.points.length > 1) {
|
|
self.strokes.push(self.currentStroke);
|
|
}
|
|
self.currentStroke = null;
|
|
self.redrawPad();
|
|
}
|
|
|
|
canvas.addEventListener("mousedown", begin);
|
|
canvas.addEventListener("mousemove", move);
|
|
document.addEventListener("mouseup", end);
|
|
canvas.addEventListener("touchstart", begin, { passive: false });
|
|
canvas.addEventListener("touchmove", move, { passive: false });
|
|
canvas.addEventListener("touchend", end, { passive: false });
|
|
|
|
var widthInput = qs(this.root, ".sig-draw-width");
|
|
var widthOutput = qs(this.root, ".sig-draw-width-output");
|
|
if (widthInput && widthOutput) {
|
|
widthInput.addEventListener("input", function () { widthOutput.textContent = this.value + "px"; });
|
|
}
|
|
|
|
this.bindClick(".sig-draw-undo", function () {
|
|
if (self.strokes.length) {
|
|
self.redoStrokes.push(self.strokes.pop());
|
|
self.redrawPad();
|
|
}
|
|
});
|
|
this.bindClick(".sig-draw-redo", function () {
|
|
if (self.redoStrokes.length) {
|
|
self.strokes.push(self.redoStrokes.pop());
|
|
self.redrawPad();
|
|
}
|
|
});
|
|
this.bindClick(".sig-clear-signature-pad", function () {
|
|
self.strokes = [];
|
|
self.redoStrokes = [];
|
|
self.currentStroke = null;
|
|
self.currentSignature = null;
|
|
self.redrawPad();
|
|
});
|
|
this.bindClick(".sig-use-signature-pad", function () {
|
|
var dataUrl = self.getDrawnSignatureDataUrl();
|
|
if (!dataUrl) { window.alert("Dibuja una firma antes de continuar."); return; }
|
|
self.currentSignature = { name: "Firma", dataUrl: dataUrl, assetId: "", imageUrl: dataUrl };
|
|
self.placeCurrentSignature();
|
|
});
|
|
this.bindClick(".sig-save-current-signature", function () {
|
|
var dataUrl = self.getDrawnSignatureDataUrl();
|
|
if (!dataUrl) { window.alert("Dibuja una firma antes de guardarla."); return; }
|
|
var nameInput = qs(self.root, ".sig-signature-name");
|
|
self.control.setAction({
|
|
Action: "save-signature",
|
|
Name: nameInput ? nameInput.value : "Firma",
|
|
DataUrl: dataUrl,
|
|
AssetId: ""
|
|
});
|
|
self.syncState();
|
|
executePostback(self.control.root.getAttribute("data-postback-save-signature"));
|
|
});
|
|
};
|
|
|
|
Viewer.prototype.normalizeHexColor = function (value) {
|
|
var color = String(value || "").replace(/\s+/g, "").toUpperCase();
|
|
|
|
if (/^[0-9A-F]{6}$/.test(color)) {
|
|
color = "#" + color;
|
|
}
|
|
|
|
if (!/^#[0-9A-F]{6}$/.test(color)) {
|
|
return "";
|
|
}
|
|
|
|
return color;
|
|
};
|
|
|
|
Viewer.prototype.setPendingDrawColor = function (value) {
|
|
var color = this.normalizeHexColor(value);
|
|
var input = qs(this.root, ".sig-draw-color-hex");
|
|
var preview = qs(this.root, ".sig-draw-color-pending-preview");
|
|
var error = qs(this.root, ".sig-draw-color-error");
|
|
|
|
if (input && input.value !== String(value || "").toUpperCase()) {
|
|
input.value = String(value || "").toUpperCase();
|
|
}
|
|
|
|
if (!color) {
|
|
if (error) { error.hidden = false; }
|
|
return false;
|
|
}
|
|
|
|
this.pendingDrawColor = color;
|
|
|
|
if (input) { input.value = color; }
|
|
if (preview) { preview.style.backgroundColor = color; }
|
|
if (error) { error.hidden = true; }
|
|
|
|
qsa(this.root, ".sig-draw-color-swatch").forEach(function (button) {
|
|
button.classList.toggle(
|
|
"is-selected",
|
|
String(button.getAttribute("data-color") || "").toUpperCase() === color
|
|
);
|
|
});
|
|
|
|
return true;
|
|
};
|
|
|
|
Viewer.prototype.openDrawColorPicker = function () {
|
|
var popover = qs(this.root, ".sig-draw-color-popover");
|
|
var trigger = qs(this.root, ".sig-draw-color-trigger");
|
|
|
|
if (!popover || !trigger) { return; }
|
|
|
|
this.pendingDrawColor = this.drawColor;
|
|
this.setPendingDrawColor(this.drawColor);
|
|
popover.hidden = false;
|
|
trigger.setAttribute("aria-expanded", "true");
|
|
|
|
var input = qs(this.root, ".sig-draw-color-hex");
|
|
if (input) {
|
|
window.setTimeout(function () {
|
|
input.focus();
|
|
input.select();
|
|
}, 0);
|
|
}
|
|
};
|
|
|
|
Viewer.prototype.closeDrawColorPicker = function (restore) {
|
|
var popover = qs(this.root, ".sig-draw-color-popover");
|
|
var trigger = qs(this.root, ".sig-draw-color-trigger");
|
|
|
|
if (!popover || !trigger) { return; }
|
|
|
|
if (restore) {
|
|
this.pendingDrawColor = this.drawColor;
|
|
this.setPendingDrawColor(this.drawColor);
|
|
}
|
|
|
|
popover.hidden = true;
|
|
trigger.setAttribute("aria-expanded", "false");
|
|
};
|
|
|
|
Viewer.prototype.applyDrawColor = function () {
|
|
var input = qs(this.root, ".sig-draw-color-hex");
|
|
var value = input ? input.value : this.pendingDrawColor;
|
|
|
|
if (!this.setPendingDrawColor(value)) {
|
|
return;
|
|
}
|
|
|
|
this.drawColor = this.pendingDrawColor;
|
|
|
|
var preview = qs(this.root, ".sig-draw-color-preview");
|
|
var label = qs(this.root, ".sig-draw-color-value");
|
|
|
|
if (preview) { preview.style.backgroundColor = this.drawColor; }
|
|
if (label) { label.textContent = this.drawColor; }
|
|
|
|
this.closeDrawColorPicker(false);
|
|
};
|
|
|
|
Viewer.prototype.bindDrawColorPicker = function () {
|
|
var self = this;
|
|
var picker = qs(this.root, ".sig-draw-color-picker");
|
|
var trigger = qs(this.root, ".sig-draw-color-trigger");
|
|
var input = qs(this.root, ".sig-draw-color-hex");
|
|
|
|
if (!picker || !trigger || !input) { return; }
|
|
|
|
trigger.addEventListener("click", function (event) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
var popover = qs(self.root, ".sig-draw-color-popover");
|
|
if (popover && !popover.hidden) {
|
|
self.closeDrawColorPicker(true);
|
|
} else {
|
|
self.openDrawColorPicker();
|
|
}
|
|
});
|
|
|
|
qsa(this.root, ".sig-draw-color-swatch").forEach(function (button) {
|
|
button.addEventListener("click", function (event) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
self.setPendingDrawColor(button.getAttribute("data-color"));
|
|
});
|
|
});
|
|
|
|
input.addEventListener("input", function () {
|
|
self.setPendingDrawColor(this.value);
|
|
});
|
|
|
|
input.addEventListener("keydown", function (event) {
|
|
if (event.keyCode === 13) {
|
|
event.preventDefault();
|
|
self.applyDrawColor();
|
|
} else if (event.keyCode === 27) {
|
|
event.preventDefault();
|
|
self.closeDrawColorPicker(true);
|
|
}
|
|
});
|
|
|
|
this.bindClick(".sig-draw-color-accept", function () {
|
|
self.applyDrawColor();
|
|
});
|
|
|
|
this.bindClick(".sig-draw-color-cancel", function () {
|
|
self.closeDrawColorPicker(true);
|
|
});
|
|
|
|
picker.addEventListener("click", function (event) {
|
|
event.stopPropagation();
|
|
});
|
|
|
|
this.root.addEventListener("click", function (event) {
|
|
if (!picker.contains(event.target)) {
|
|
self.closeDrawColorPicker(true);
|
|
}
|
|
});
|
|
};
|
|
|
|
Viewer.prototype.redrawPad = function () {
|
|
var canvas = this.drawCanvas;
|
|
if (!canvas) { return; }
|
|
var context = canvas.getContext("2d");
|
|
context.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
var strokes = this.strokes.slice(0);
|
|
if (this.currentStroke) { strokes.push(this.currentStroke); }
|
|
|
|
strokes.forEach(function (stroke) {
|
|
if (!stroke.points || stroke.points.length < 2) { return; }
|
|
context.beginPath();
|
|
context.strokeStyle = stroke.color;
|
|
context.lineWidth = stroke.width;
|
|
context.lineCap = "round";
|
|
context.lineJoin = "round";
|
|
context.moveTo(stroke.points[0].x, stroke.points[0].y);
|
|
for (var index = 1; index < stroke.points.length; index += 1) {
|
|
context.lineTo(stroke.points[index].x, stroke.points[index].y);
|
|
}
|
|
context.stroke();
|
|
context.closePath();
|
|
});
|
|
};
|
|
|
|
Viewer.prototype.getDrawnSignatureDataUrl = function () {
|
|
if (!this.drawCanvas || !this.strokes.length) { return ""; }
|
|
return this.drawCanvas.toDataURL("image/png");
|
|
};
|
|
|
|
Viewer.prototype.bindSignatureUpload = function () {
|
|
var self = this;
|
|
var zone = qs(this.root, ".sig-signature-upload-dropzone");
|
|
var input = qs(this.root, ".sig-signature-upload-input");
|
|
if (!zone || !input) { return; }
|
|
|
|
function upload(file) {
|
|
if (!file) { return; }
|
|
if (!/\.png$/i.test(file.name) && file.type !== "image/png") {
|
|
window.alert("La firma debe ser un archivo PNG.");
|
|
return;
|
|
}
|
|
|
|
postFile(self.control.uploadUrl, "signature", file, function (error, response) {
|
|
if (error) { window.alert(error.message); return; }
|
|
self.uploadedSignature = {
|
|
assetId: response.id,
|
|
name: response.name || file.name,
|
|
imageUrl: response.url,
|
|
dataUrl: ""
|
|
};
|
|
self.renderUploadedSignature();
|
|
});
|
|
}
|
|
|
|
var usingDropzone = false;
|
|
|
|
if (window.Dropzone) {
|
|
usingDropzone = true;
|
|
window.Dropzone.autoDiscover = false;
|
|
input.style.display = "none";
|
|
|
|
var signatureDropzone = new window.Dropzone(zone, {
|
|
url: self.control.uploadUrl,
|
|
paramName: "file",
|
|
params: { kind: "signature" },
|
|
acceptedFiles: "image/png,.png",
|
|
maxFilesize: 5,
|
|
maxFiles: 1,
|
|
uploadMultiple: false,
|
|
previewsContainer: false,
|
|
createImageThumbnails: false,
|
|
clickable: true
|
|
});
|
|
|
|
signatureDropzone.on("success", function (file, response) {
|
|
var data = typeof response === "string" ? parseJson(response, null) : response;
|
|
if (!data || !data.success) {
|
|
window.alert(data && data.message ? data.message : "No fue posible cargar la firma.");
|
|
return;
|
|
}
|
|
self.uploadedSignature = {
|
|
assetId: data.id,
|
|
name: data.name || file.name,
|
|
imageUrl: data.url,
|
|
dataUrl: ""
|
|
};
|
|
self.renderUploadedSignature();
|
|
signatureDropzone.removeAllFiles(true);
|
|
});
|
|
|
|
signatureDropzone.on("error", function (file, message) {
|
|
var data = typeof message === "string" ? parseJson(message, null) : message;
|
|
window.alert(data && data.message ? data.message : (typeof message === "string" ? message : "No fue posible cargar la firma."));
|
|
signatureDropzone.removeAllFiles(true);
|
|
});
|
|
}
|
|
|
|
if (!usingDropzone) {
|
|
zone.addEventListener("click", function (event) {
|
|
if (event.target !== input) { input.click(); }
|
|
});
|
|
input.addEventListener("change", function () {
|
|
upload(this.files && this.files[0]);
|
|
this.value = "";
|
|
});
|
|
zone.addEventListener("dragover", function (event) { event.preventDefault(); zone.classList.add("is-dragover"); });
|
|
zone.addEventListener("dragleave", function () { zone.classList.remove("is-dragover"); });
|
|
zone.addEventListener("drop", function (event) {
|
|
event.preventDefault();
|
|
zone.classList.remove("is-dragover");
|
|
upload(event.dataTransfer.files && event.dataTransfer.files[0]);
|
|
});
|
|
}
|
|
|
|
this.bindClick(".sig-use-uploaded-signature", function () {
|
|
if (!self.uploadedSignature) { return; }
|
|
self.currentSignature = self.uploadedSignature;
|
|
self.placeCurrentSignature();
|
|
});
|
|
this.bindClick(".sig-save-uploaded-signature", function () {
|
|
if (!self.uploadedSignature) { return; }
|
|
var inputName = qs(self.root, ".sig-upload-signature-name");
|
|
self.control.setAction({
|
|
Action: "save-signature",
|
|
Name: inputName && inputName.value ? inputName.value : self.uploadedSignature.name,
|
|
DataUrl: "",
|
|
AssetId: self.uploadedSignature.assetId
|
|
});
|
|
self.syncState();
|
|
executePostback(self.control.root.getAttribute("data-postback-save-signature"));
|
|
});
|
|
};
|
|
|
|
Viewer.prototype.renderUploadedSignature = function () {
|
|
var preview = qs(this.root, ".sig-uploaded-signature-preview");
|
|
var useButton = qs(this.root, ".sig-use-uploaded-signature");
|
|
var saveButton = qs(this.root, ".sig-save-uploaded-signature");
|
|
if (!preview) { return; }
|
|
|
|
preview.innerHTML = "";
|
|
if (this.uploadedSignature) {
|
|
var image = document.createElement("img");
|
|
image.src = this.uploadedSignature.imageUrl;
|
|
image.alt = this.uploadedSignature.name;
|
|
preview.appendChild(image);
|
|
}
|
|
if (useButton) { useButton.disabled = !this.uploadedSignature; }
|
|
if (saveButton) { saveButton.disabled = !this.uploadedSignature; }
|
|
};
|
|
|
|
Viewer.prototype.bindSavedSignatures = function () {
|
|
var self = this;
|
|
qsa(this.root, ".sig-saved-signature-item").forEach(function (button) {
|
|
button.addEventListener("click", function () {
|
|
var signature = {
|
|
assetId: button.getAttribute("data-asset-id"),
|
|
name: button.getAttribute("data-name"),
|
|
imageUrl: button.getAttribute("data-image-url"),
|
|
dataUrl: ""
|
|
};
|
|
self.currentSignature = signature;
|
|
self.previewSavedSignature(signature);
|
|
self.placeCurrentSignature();
|
|
});
|
|
});
|
|
|
|
var filter = qs(this.root, ".sig-saved-signature-filter");
|
|
if (filter) {
|
|
filter.addEventListener("input", function () {
|
|
var value = this.value.toLowerCase();
|
|
qsa(self.root, ".sig-saved-signature-item").forEach(function (item) {
|
|
var name = (item.getAttribute("data-name") || "").toLowerCase();
|
|
item.style.display = name.indexOf(value) >= 0 ? "" : "none";
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
Viewer.prototype.previewSavedSignature = function (signature) {
|
|
var preview = qs(this.root, ".sig-saved-signature-preview");
|
|
if (!preview) { return; }
|
|
preview.innerHTML = "";
|
|
var image = document.createElement("img");
|
|
image.src = signature.imageUrl;
|
|
image.alt = signature.name;
|
|
preview.appendChild(image);
|
|
};
|
|
|
|
Viewer.prototype.bindPropertyEditors = function () {
|
|
var self = this;
|
|
["width", "height", "x", "y"].forEach(function (name) {
|
|
var input = qs(self.root, ".sig-prop-" + name);
|
|
if (input) {
|
|
input.addEventListener("input", function () { self.updateSelectedFromInputs(name); });
|
|
}
|
|
});
|
|
|
|
var rotation = qs(this.root, ".sig-prop-rotation");
|
|
var opacity = qs(this.root, ".sig-prop-opacity");
|
|
if (rotation) {
|
|
rotation.addEventListener("input", function () {
|
|
qs(self.root, ".sig-rotation-output").textContent = this.value + "°";
|
|
self.updateSelectedFromInputs("rotation");
|
|
});
|
|
}
|
|
if (opacity) {
|
|
opacity.addEventListener("input", function () {
|
|
qs(self.root, ".sig-opacity-output").textContent = this.value + "%";
|
|
self.updateSelectedFromInputs("opacity");
|
|
});
|
|
}
|
|
|
|
qsa(this.root, ".sig-pages-fieldset input[type=radio]").forEach(function (radio) {
|
|
radio.addEventListener("change", function () {
|
|
var specific = qs(self.root, ".sig-specific-pages");
|
|
if (specific) { specific.disabled = this.value !== "specific"; }
|
|
self.updateSelectedFromInputs("pages");
|
|
});
|
|
});
|
|
|
|
var pages = qs(this.root, ".sig-specific-pages");
|
|
if (pages) { pages.addEventListener("input", function () { self.updateSelectedFromInputs("pages"); }); }
|
|
};
|
|
|
|
Viewer.prototype.load = function () {
|
|
var self = this;
|
|
if (!window.pdfjsLib) {
|
|
this.showError("No se cargó PDF.js.");
|
|
return;
|
|
}
|
|
|
|
window.pdfjsLib.GlobalWorkerOptions.workerSrc = this.workerUrl;
|
|
window.pdfjsLib.getDocument(this.pdfUrl).promise.then(function (pdf) {
|
|
self.pdf = pdf;
|
|
self.pageCount.textContent = "/ " + pdf.numPages;
|
|
self.pageInput.max = pdf.numPages;
|
|
self.goToPage(clamp(self.currentPage, 1, pdf.numPages));
|
|
}).catch(function (error) {
|
|
self.showError("No fue posible abrir el PDF: " + error.message);
|
|
});
|
|
};
|
|
|
|
Viewer.prototype.goToPage = function (pageNumber) {
|
|
if (!this.pdf) { return; }
|
|
this.currentPage = clamp(Number(pageNumber) || 1, 1, this.pdf.numPages);
|
|
this.pageInput.value = this.currentPage;
|
|
this.state.CurrentPage = this.currentPage;
|
|
this.renderPage();
|
|
};
|
|
|
|
Viewer.prototype.setScale = function (value) {
|
|
this.scale = clamp(Number(value) || 1, 0.5, 3);
|
|
this.state.Scale = this.scale;
|
|
this.zoomSelect.value = String(this.scale);
|
|
this.renderPage();
|
|
};
|
|
|
|
Viewer.prototype.renderPage = function () {
|
|
var self = this;
|
|
this.loading.hidden = false;
|
|
|
|
this.pdf.getPage(this.currentPage).then(function (page) {
|
|
var viewport = page.getViewport({ scale: self.scale });
|
|
self.pageWidth = viewport.width / self.scale;
|
|
self.pageHeight = viewport.height / self.scale;
|
|
self.canvas.width = Math.floor(viewport.width);
|
|
self.canvas.height = Math.floor(viewport.height);
|
|
self.canvas.style.width = viewport.width + "px";
|
|
self.canvas.style.height = viewport.height + "px";
|
|
self.shell.style.width = viewport.width + "px";
|
|
self.shell.style.height = viewport.height + "px";
|
|
self.layer.style.width = viewport.width + "px";
|
|
self.layer.style.height = viewport.height + "px";
|
|
|
|
if (self.renderTask) {
|
|
try { self.renderTask.cancel(); } catch (ignore) {}
|
|
}
|
|
|
|
self.renderTask = page.render({ canvasContext: self.canvas.getContext("2d"), viewport: viewport });
|
|
return self.renderTask.promise;
|
|
}).then(function () {
|
|
self.loading.hidden = true;
|
|
self.renderPlacements();
|
|
if (self.selectedId) {
|
|
self.loadInputsFromSelected();
|
|
}
|
|
self.syncState();
|
|
}).catch(function (error) {
|
|
if (error && error.name === "RenderingCancelledException") { return; }
|
|
self.showError(error.message || "No fue posible mostrar la página.");
|
|
});
|
|
};
|
|
|
|
Viewer.prototype.enterSignMode = function () {
|
|
this.signMode = true;
|
|
this.root.classList.add("is-signing");
|
|
this.leftPanel.classList.add("is-open");
|
|
this.startButton.hidden = true;
|
|
this.finishButton.hidden = false;
|
|
this.modeLabel.textContent =
|
|
"Modo de firma activo";
|
|
this.state.SignMode = true;
|
|
|
|
this.applyPropertiesPanelState();
|
|
this.renderPlacements();
|
|
this.syncState();
|
|
};
|
|
|
|
Viewer.prototype.setFullscreen = function (expanded) {
|
|
this.root.classList.toggle("is-fullscreen", !!expanded);
|
|
var expand = qs(this.root, ".sig-expand-viewer");
|
|
var collapse = qs(this.root, ".sig-collapse-viewer");
|
|
if (expand) { expand.hidden = !!expanded; }
|
|
if (collapse) { collapse.hidden = !expanded; }
|
|
document.body.classList.toggle("sig-viewer-fullscreen-open", !!expanded);
|
|
this.state.IsFullscreen = !!expanded;
|
|
this.syncState();
|
|
if (this.pdf) {
|
|
window.setTimeout(this.renderPage.bind(this), 40);
|
|
}
|
|
};
|
|
|
|
Viewer.prototype.placeCurrentSignature = function () {
|
|
if (!this.currentSignature) { return; }
|
|
if (!this.signMode) { this.enterSignMode(); }
|
|
|
|
var width = 180;
|
|
var height = 70;
|
|
var x = Math.max(0, (this.pageWidth - width) / 2);
|
|
var y = Math.max(0, this.pageHeight - height - 45);
|
|
|
|
var placement = {
|
|
Id: uid(),
|
|
AssetId: this.currentSignature.assetId || "",
|
|
Name: this.currentSignature.name || "Firma",
|
|
PageNumber: this.currentPage,
|
|
X: x,
|
|
Y: y,
|
|
Width: width,
|
|
Height: height,
|
|
PageWidth: this.pageWidth,
|
|
PageHeight: this.pageHeight,
|
|
Rotation: 0,
|
|
Opacity: 1,
|
|
ApplyToAllPages: false,
|
|
SpecificPages: "",
|
|
ImageDataUrl: this.currentSignature.dataUrl || "",
|
|
ImageUrl: this.currentSignature.imageUrl || ""
|
|
};
|
|
|
|
this.state.Placements.push(placement);
|
|
this.select(placement.Id);
|
|
this.syncState();
|
|
};
|
|
|
|
Viewer.prototype.renderPlacements = function () {
|
|
var self = this;
|
|
this.layer.innerHTML = "";
|
|
|
|
this.state.Placements.filter(function (placement) {
|
|
return Number(placement.PageNumber) === self.currentPage;
|
|
}).forEach(function (placement) {
|
|
var element = document.createElement("div");
|
|
element.className = "sig-placement" + (placement.Id === self.selectedId ? " is-selected" : "");
|
|
element.style.left = Number(placement.X) * self.scale + "px";
|
|
element.style.top = Number(placement.Y) * self.scale + "px";
|
|
element.style.width = Number(placement.Width) * self.scale + "px";
|
|
element.style.height = Number(placement.Height) * self.scale + "px";
|
|
element.style.transform = "rotate(" + Number(placement.Rotation || 0) + "deg)";
|
|
element.style.opacity = Number(placement.Opacity || 1);
|
|
element.setAttribute("data-id", placement.Id);
|
|
|
|
var image = document.createElement("img");
|
|
image.draggable = false;
|
|
image.src = placement.ImageDataUrl || placement.ImageUrl || "";
|
|
element.appendChild(image);
|
|
|
|
var handle = document.createElement("span");
|
|
handle.className = "sig-resize-handle";
|
|
element.appendChild(handle);
|
|
|
|
element.addEventListener("mousedown", function (event) {
|
|
self.startPointer(event, placement, event.target === handle ? "resize" : "move");
|
|
});
|
|
element.addEventListener("click", function (event) {
|
|
event.stopPropagation();
|
|
self.select(placement.Id);
|
|
});
|
|
self.layer.appendChild(element);
|
|
});
|
|
|
|
this.renderPlacementList();
|
|
};
|
|
|
|
Viewer.prototype.renderPlacementList = function () {
|
|
var self = this;
|
|
var list = qs(this.root, ".sig-placement-list");
|
|
if (!list) { return; }
|
|
list.innerHTML = "";
|
|
|
|
this.state.Placements.forEach(function (placement, index) {
|
|
var item = document.createElement("div");
|
|
item.className = "sig-placement-list-item" + (placement.Id === self.selectedId ? " is-selected" : "");
|
|
item.innerHTML = "<button type=\"button\" class=\"sig-placement-select\"><span></span><small></small></button><button type=\"button\" class=\"sig-placement-remove\">×</button>";
|
|
qs(item, "span").textContent = placement.Name || ("Firma " + (index + 1));
|
|
qs(item, "small").textContent = "Página " + placement.PageNumber;
|
|
qs(item, ".sig-placement-select").addEventListener("click", function () {
|
|
self.goToPage(placement.PageNumber);
|
|
self.select(placement.Id);
|
|
});
|
|
qs(item, ".sig-placement-remove").addEventListener("click", function () {
|
|
self.removePlacement(placement.Id);
|
|
});
|
|
list.appendChild(item);
|
|
});
|
|
|
|
if (!this.state.Placements.length) {
|
|
list.innerHTML = "<span class=\"sig-placement-list-empty\">No hay firmas colocadas.</span>";
|
|
}
|
|
};
|
|
|
|
Viewer.prototype.startPointer = function (event, placement, mode) {
|
|
if (!this.signMode) { return; }
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
this.select(placement.Id);
|
|
|
|
var self = this;
|
|
var start = {
|
|
x: event.clientX,
|
|
y: event.clientY,
|
|
px: Number(placement.X),
|
|
py: Number(placement.Y),
|
|
w: Number(placement.Width),
|
|
h: Number(placement.Height)
|
|
};
|
|
var keepRatio = qs(this.root, ".sig-keep-ratio").checked;
|
|
|
|
function move(currentEvent) {
|
|
var dx = (currentEvent.clientX - start.x) / self.scale;
|
|
var dy = (currentEvent.clientY - start.y) / self.scale;
|
|
|
|
if (mode === "move") {
|
|
placement.X = clamp(start.px + dx, 0, Math.max(0, self.pageWidth - placement.Width));
|
|
placement.Y = clamp(start.py + dy, 0, Math.max(0, self.pageHeight - placement.Height));
|
|
} else {
|
|
var ratio = start.w / Math.max(1, start.h);
|
|
placement.Width = clamp(start.w + dx, 20, Math.max(20, self.pageWidth - placement.X));
|
|
placement.Height = keepRatio ? placement.Width / ratio : clamp(start.h + dy, 20, Math.max(20, self.pageHeight - placement.Y));
|
|
}
|
|
|
|
self.renderPlacements();
|
|
self.loadInputsFromSelected();
|
|
}
|
|
|
|
function up() {
|
|
document.removeEventListener("mousemove", move);
|
|
document.removeEventListener("mouseup", up);
|
|
self.syncState();
|
|
}
|
|
|
|
document.addEventListener("mousemove", move);
|
|
document.addEventListener("mouseup", up);
|
|
};
|
|
|
|
Viewer.prototype.select = function (id) {
|
|
this.selectedId = id;
|
|
this.state.SelectedPlacementId = id || "";
|
|
|
|
/*
|
|
* Seleccionar o colocar una firma abre el panel.
|
|
* Deseleccionar no lo cierra.
|
|
*/
|
|
if (id) {
|
|
this.showPropertiesForSignature();
|
|
}
|
|
|
|
this.renderPlacements();
|
|
this.loadInputsFromSelected();
|
|
this.syncState();
|
|
};
|
|
|
|
Viewer.prototype.selected = function () {
|
|
var id = this.selectedId;
|
|
return this.state.Placements.filter(function (placement) { return placement.Id === id; })[0] || null;
|
|
};
|
|
|
|
Viewer.prototype.loadInputsFromSelected = function () {
|
|
var placement = this.selected();
|
|
if (!placement) { return; }
|
|
qs(this.root, ".sig-prop-width").value = Math.round(Number(placement.Width));
|
|
qs(this.root, ".sig-prop-height").value = Math.round(Number(placement.Height));
|
|
qs(this.root, ".sig-prop-x").value = Math.round(Number(placement.X));
|
|
qs(this.root, ".sig-prop-y").value = Math.round(Number(placement.Y));
|
|
qs(this.root, ".sig-prop-rotation").value = Number(placement.Rotation || 0);
|
|
qs(this.root, ".sig-prop-opacity").value = Math.round(Number(placement.Opacity || 1) * 100);
|
|
qs(this.root, ".sig-rotation-output").textContent = Number(placement.Rotation || 0) + "°";
|
|
qs(this.root, ".sig-opacity-output").textContent = Math.round(Number(placement.Opacity || 1) * 100) + "%";
|
|
|
|
var mode = placement.ApplyToAllPages ? "all" : (placement.SpecificPages ? "specific" : "current");
|
|
var radio = qs(this.root, ".sig-pages-fieldset input[value=\"" + mode + "\"]");
|
|
if (radio) { radio.checked = true; }
|
|
var specific = qs(this.root, ".sig-specific-pages");
|
|
if (specific) {
|
|
specific.value = placement.SpecificPages || "";
|
|
specific.disabled = mode !== "specific";
|
|
}
|
|
};
|
|
|
|
Viewer.prototype.updateSelectedFromInputs = function (changed) {
|
|
var placement = this.selected();
|
|
if (!placement) { return; }
|
|
var keep = qs(this.root, ".sig-keep-ratio").checked;
|
|
var ratio = Number(placement.Width) / Math.max(1, Number(placement.Height));
|
|
var width = Number(qs(this.root, ".sig-prop-width").value) || 1;
|
|
var height = Number(qs(this.root, ".sig-prop-height").value) || 1;
|
|
|
|
if (changed === "width" && keep) {
|
|
height = width / ratio;
|
|
qs(this.root, ".sig-prop-height").value = Math.round(height);
|
|
}
|
|
if (changed === "height" && keep) {
|
|
width = height * ratio;
|
|
qs(this.root, ".sig-prop-width").value = Math.round(width);
|
|
}
|
|
|
|
placement.Width = clamp(width, 1, this.pageWidth);
|
|
placement.Height = clamp(height, 1, this.pageHeight);
|
|
placement.X = clamp(Number(qs(this.root, ".sig-prop-x").value) || 0, 0, Math.max(0, this.pageWidth - placement.Width));
|
|
placement.Y = clamp(Number(qs(this.root, ".sig-prop-y").value) || 0, 0, Math.max(0, this.pageHeight - placement.Height));
|
|
placement.Rotation = Number(qs(this.root, ".sig-prop-rotation").value) || 0;
|
|
placement.Opacity = (Number(qs(this.root, ".sig-prop-opacity").value) || 100) / 100;
|
|
|
|
var checked = qs(this.root, ".sig-pages-fieldset input[type=radio]:checked");
|
|
var mode = checked ? checked.value : "current";
|
|
placement.ApplyToAllPages = mode === "all";
|
|
placement.SpecificPages = mode === "specific" ? qs(this.root, ".sig-specific-pages").value : "";
|
|
this.renderPlacements();
|
|
this.syncState();
|
|
};
|
|
|
|
Viewer.prototype.deleteSelected = function () {
|
|
if (!this.selectedId) { return; }
|
|
this.removePlacement(this.selectedId);
|
|
};
|
|
|
|
Viewer.prototype.removePlacement = function (id) {
|
|
this.state.Placements =
|
|
this.state.Placements.filter(
|
|
function (placement) {
|
|
return placement.Id !== id;
|
|
}
|
|
);
|
|
|
|
if (this.selectedId === id) {
|
|
this.selectedId = null;
|
|
this.state.SelectedPlacementId = "";
|
|
}
|
|
|
|
/*
|
|
* El panel permanece en el estado elegido
|
|
* por el usuario.
|
|
*/
|
|
this.renderPlacements();
|
|
this.syncState();
|
|
};
|
|
|
|
Viewer.prototype.clearAll = function () {
|
|
if (!this.state.Placements.length) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
!window.confirm(
|
|
"¿Deseas eliminar todas las firmas colocadas?"
|
|
)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
this.state.Placements = [];
|
|
this.selectedId = null;
|
|
this.state.SelectedPlacementId = "";
|
|
|
|
/*
|
|
* Limpiar las firmas no cambia la visibilidad
|
|
* del panel de propiedades.
|
|
*/
|
|
this.renderPlacements();
|
|
this.syncState();
|
|
};
|
|
|
|
Viewer.prototype.syncState = function () {
|
|
this.state.CurrentPage = this.currentPage;
|
|
this.state.Scale = this.scale;
|
|
this.state.SignMode = !!this.signMode;
|
|
this.state.SelectedPlacementId = this.selectedId || "";
|
|
this.state.IsFullscreen = this.root.classList.contains("is-fullscreen");
|
|
this.state.PropertiesPanelOpen = !!this.propertiesPanelOpen;
|
|
this.state.Placements.forEach(function (placement) {
|
|
delete placement.ImageUrl;
|
|
});
|
|
if (this.control.stateField) {
|
|
this.control.stateField.value = JSON.stringify(this.state);
|
|
}
|
|
};
|
|
|
|
Viewer.prototype.showError = function (message) {
|
|
if (!this.loading) { return; }
|
|
this.loading.hidden = false;
|
|
this.loading.textContent = message;
|
|
this.loading.classList.add("is-error");
|
|
};
|
|
|
|
function initialize(root) {
|
|
if (!root || root.__sigPdfControl) { return; }
|
|
root.__sigPdfControl = new PdfControl(root);
|
|
}
|
|
|
|
function initializeAll() {
|
|
qsa(document, ".sig-pdf-control").forEach(initialize);
|
|
}
|
|
|
|
window.SigPdfViewer = {
|
|
init: function (id) {
|
|
initialize(document.getElementById(id));
|
|
},
|
|
initAll: initializeAll,
|
|
command: function (controlId, commandName, payload) {
|
|
var controlRoot = document.getElementById(controlId);
|
|
if (!controlRoot) { return; }
|
|
|
|
initialize(controlRoot);
|
|
|
|
var control = controlRoot.__sigPdfControl;
|
|
var viewer = control ? control.viewer : null;
|
|
if (!viewer) { return; }
|
|
|
|
if (commandName === "finish-signing") {
|
|
viewer.finishSigningClient();
|
|
} else if (commandName === "add-stored-signature") {
|
|
viewer.addStoredSignatureClient(payload || {});
|
|
} else if (commandName === "close-drawer") {
|
|
var drawerId = controlRoot.getAttribute("data-host-drawer-id");
|
|
if (drawerId && window.SIGPdfDrawer) {
|
|
window.SIGPdfDrawer.close(drawerId);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
if (window.Sys && window.Sys.Application && typeof window.Sys.Application.add_load === "function") {
|
|
window.Sys.Application.add_load(initializeAll);
|
|
} else if (document.readyState === "loading") {
|
|
document.addEventListener("DOMContentLoaded", initializeAll);
|
|
} else {
|
|
initializeAll();
|
|
}
|
|
}(window, document));
|