|
|
@@ -228,7 +228,12 @@ async function pdflibLaden(adresse) {
|
|
|
|
* ``abschliessen`` rechnet danach alle Felder flach in die Seite: heraus kommt
|
|
|
|
* ``abschliessen`` rechnet danach alle Felder flach in die Seite: heraus kommt
|
|
|
|
* ein PDF ohne Formular -- nichts mehr zu verstellen.
|
|
|
|
* ein PDF ohne Formular -- nichts mehr zu verstellen.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
async function nachbearbeiten(bytes, bilder, adresse, { abschliessen = false } = {}) {
|
|
|
|
async function nachbearbeiten(
|
|
|
|
|
|
|
|
bytes,
|
|
|
|
|
|
|
|
bilder,
|
|
|
|
|
|
|
|
adresse,
|
|
|
|
|
|
|
|
{ abschliessen = false, unterschriften = [], zeuge = "" } = {}
|
|
|
|
|
|
|
|
) {
|
|
|
|
const PDFLib = await pdflibLaden(adresse);
|
|
|
|
const PDFLib = await pdflibLaden(adresse);
|
|
|
|
const dokument = await PDFLib.PDFDocument.load(bytes);
|
|
|
|
const dokument = await PDFLib.PDFDocument.load(bytes);
|
|
|
|
const formular = dokument.getForm();
|
|
|
|
const formular = dokument.getForm();
|
|
|
@@ -263,6 +268,40 @@ async function nachbearbeiten(bytes, bilder, adresse, { abschliessen = false } =
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Unterschriften: Bild und darunter der angegebene Name mit Zeitpunkt. Das
|
|
|
|
|
|
|
|
// Bild allein waere eine Zeichnung ohne Aussage -- auf Papier steht neben
|
|
|
|
|
|
|
|
// der Unterschrift auch der Name in Druckbuchstaben.
|
|
|
|
|
|
|
|
if (unterschriften.length) {
|
|
|
|
|
|
|
|
const schrift = await dokument.embedFont(PDFLib.StandardFonts.Helvetica);
|
|
|
|
|
|
|
|
for (const eintrag of unterschriften) {
|
|
|
|
|
|
|
|
const seite = dokument.getPage(eintrag.seite - 1);
|
|
|
|
|
|
|
|
const png = await dokument.embedPng(eintrag.bytes);
|
|
|
|
|
|
|
|
const breite = eintrag.breite;
|
|
|
|
|
|
|
|
const hoehe = (png.height / png.width) * breite;
|
|
|
|
|
|
|
|
seite.drawImage(png, { x: eintrag.x, y: eintrag.y, width: breite, height: hoehe });
|
|
|
|
|
|
|
|
const zeilen = [`${eintrag.name} -- ${eintrag.zeit}`];
|
|
|
|
|
|
|
|
if (zeuge) zeilen.push(`In TESM abgelegt durch ${zeuge}`);
|
|
|
|
|
|
|
|
let unten = eintrag.y - 2;
|
|
|
|
|
|
|
|
for (const zeile of zeilen) {
|
|
|
|
|
|
|
|
unten -= 9;
|
|
|
|
|
|
|
|
seite.drawText(zeile, {
|
|
|
|
|
|
|
|
x: eintrag.x,
|
|
|
|
|
|
|
|
y: unten,
|
|
|
|
|
|
|
|
size: 8,
|
|
|
|
|
|
|
|
font: schrift,
|
|
|
|
|
|
|
|
color: PDFLib.rgb(0.1, 0.1, 0.1),
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ein Strich wie auf dem Formular: er trennt Zeichnung und Klartext.
|
|
|
|
|
|
|
|
seite.drawLine({
|
|
|
|
|
|
|
|
start: { x: eintrag.x, y: eintrag.y - 1 },
|
|
|
|
|
|
|
|
end: { x: eintrag.x + breite, y: eintrag.y - 1 },
|
|
|
|
|
|
|
|
thickness: 0.5,
|
|
|
|
|
|
|
|
color: PDFLib.rgb(0.4, 0.4, 0.4),
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (abschliessen) {
|
|
|
|
if (abschliessen) {
|
|
|
|
// Alle Felder werden Teil der Seite. Danach gibt es kein Formular mehr --
|
|
|
|
// Alle Felder werden Teil der Seite. Danach gibt es kein Formular mehr --
|
|
|
|
// gewollt: so kann niemand mehr etwas verstellen.
|
|
|
|
// gewollt: so kann niemand mehr etwas verstellen.
|
|
|
@@ -271,6 +310,101 @@ async function nachbearbeiten(bytes, bilder, adresse, { abschliessen = false } =
|
|
|
|
return dokument.save();
|
|
|
|
return dokument.save();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Die gezeichnete Unterschrift als PNG -- auf das Gezeichnete beschnitten.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* Ungeschnitten waere jede Unterschrift so gross wie die Zeichenflaeche, mit
|
|
|
|
|
|
|
|
* viel Luft ringsum. Im Dokument sieht das aus wie ein verrutschter Stempel.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
function unterschriftAlsPng(leinwand, kasten) {
|
|
|
|
|
|
|
|
const rand = 6;
|
|
|
|
|
|
|
|
const links = Math.max(0, Math.floor(kasten.x1 - rand));
|
|
|
|
|
|
|
|
const oben = Math.max(0, Math.floor(kasten.y1 - rand));
|
|
|
|
|
|
|
|
const breite = Math.min(leinwand.width - links, Math.ceil(kasten.x2 - kasten.x1 + rand * 2));
|
|
|
|
|
|
|
|
const hoehe = Math.min(leinwand.height - oben, Math.ceil(kasten.y2 - kasten.y1 + rand * 2));
|
|
|
|
|
|
|
|
const ziel = document.createElement("canvas");
|
|
|
|
|
|
|
|
ziel.width = Math.max(1, breite);
|
|
|
|
|
|
|
|
ziel.height = Math.max(1, hoehe);
|
|
|
|
|
|
|
|
ziel
|
|
|
|
|
|
|
|
.getContext("2d")
|
|
|
|
|
|
|
|
.drawImage(leinwand, links, oben, ziel.width, ziel.height, 0, 0, ziel.width, ziel.height);
|
|
|
|
|
|
|
|
// Ueber eine data:-Adresse und nicht ueber blob: -- die Sicherheitsregeln
|
|
|
|
|
|
|
|
// der Anwendung erlauben blob: nicht, und ein stilles "img-src" waere ein
|
|
|
|
|
|
|
|
// Loch, das man fuer eine Unterschrift nicht aufmacht.
|
|
|
|
|
|
|
|
const adresse = ziel.toDataURL("image/png");
|
|
|
|
|
|
|
|
const roh = atob(adresse.slice(adresse.indexOf(",") + 1));
|
|
|
|
|
|
|
|
const bytes = new Uint8Array(roh.length);
|
|
|
|
|
|
|
|
for (let i = 0; i < roh.length; i++) bytes[i] = roh.charCodeAt(i);
|
|
|
|
|
|
|
|
return { bytes, vorschau: adresse, breite: ziel.width, hoehe: ziel.height };
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Zeichenflaeche fuer die Unterschrift -- Finger, Stift oder Maus.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* ``touch-action: none`` steht im Stylesheet: ohne das scrollt ein iPad die
|
|
|
|
|
|
|
|
* Seite, statt einen Strich zu zeichnen.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
function unterschriftPad(leinwand) {
|
|
|
|
|
|
|
|
const stift = leinwand.getContext("2d");
|
|
|
|
|
|
|
|
let kasten = null;
|
|
|
|
|
|
|
|
let zeichnet = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const punkt = (event) => {
|
|
|
|
|
|
|
|
const rechteck = leinwand.getBoundingClientRect();
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
x: ((event.clientX - rechteck.left) / rechteck.width) * leinwand.width,
|
|
|
|
|
|
|
|
y: ((event.clientY - rechteck.top) / rechteck.height) * leinwand.height,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const merken = ({ x, y }) => {
|
|
|
|
|
|
|
|
kasten = kasten
|
|
|
|
|
|
|
|
? {
|
|
|
|
|
|
|
|
x1: Math.min(kasten.x1, x),
|
|
|
|
|
|
|
|
y1: Math.min(kasten.y1, y),
|
|
|
|
|
|
|
|
x2: Math.max(kasten.x2, x),
|
|
|
|
|
|
|
|
y2: Math.max(kasten.y2, y),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
: { x1: x, y1: y, x2: x, y2: y };
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
stift.lineWidth = 2.5;
|
|
|
|
|
|
|
|
stift.lineCap = "round";
|
|
|
|
|
|
|
|
stift.lineJoin = "round";
|
|
|
|
|
|
|
|
stift.strokeStyle = "#111";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
leinwand.addEventListener("pointerdown", (event) => {
|
|
|
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
|
|
|
leinwand.setPointerCapture(event.pointerId);
|
|
|
|
|
|
|
|
zeichnet = true;
|
|
|
|
|
|
|
|
const p = punkt(event);
|
|
|
|
|
|
|
|
merken(p);
|
|
|
|
|
|
|
|
stift.beginPath();
|
|
|
|
|
|
|
|
stift.moveTo(p.x, p.y);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
leinwand.addEventListener("pointermove", (event) => {
|
|
|
|
|
|
|
|
if (!zeichnet) return;
|
|
|
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
|
|
|
const p = punkt(event);
|
|
|
|
|
|
|
|
merken(p);
|
|
|
|
|
|
|
|
stift.lineTo(p.x, p.y);
|
|
|
|
|
|
|
|
stift.stroke();
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
const beenden = () => {
|
|
|
|
|
|
|
|
zeichnet = false;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
leinwand.addEventListener("pointerup", beenden);
|
|
|
|
|
|
|
|
leinwand.addEventListener("pointercancel", beenden);
|
|
|
|
|
|
|
|
leinwand.addEventListener("pointerleave", beenden);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
leeren: () => {
|
|
|
|
|
|
|
|
stift.clearRect(0, 0, leinwand.width, leinwand.height);
|
|
|
|
|
|
|
|
kasten = null;
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
leer: () => kasten === null,
|
|
|
|
|
|
|
|
alsPng: () => (kasten ? unterschriftAlsPng(leinwand, kasten) : null),
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* -- PDF ------------------------------------------------------------------- */
|
|
|
|
/* -- PDF ------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
|
|
let pdfjsGeladen = null;
|
|
|
|
let pdfjsGeladen = null;
|
|
|
@@ -448,6 +582,56 @@ async function pdfBetrachter(behaelter, seiten, url, basis, { formulare = true }
|
|
|
|
});
|
|
|
|
});
|
|
|
|
await bildknoepfeAufsetzen();
|
|
|
|
await bildknoepfeAufsetzen();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Unterschriften: gezeichnet wird ausserhalb, gesetzt wird hier. Die Stelle
|
|
|
|
|
|
|
|
bestimmt ein Tipp ins Dokument -- ein festes Feld gibt es in den wenigsten
|
|
|
|
|
|
|
|
Vorlagen, und "unten rechts" ist bei einem Formular mit drei
|
|
|
|
|
|
|
|
Unterschriftszeilen die falsche Annahme. */
|
|
|
|
|
|
|
|
const unterschriften = [];
|
|
|
|
|
|
|
|
let wartendeUnterschrift = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const platzieren = async (event) => {
|
|
|
|
|
|
|
|
if (!wartendeUnterschrift) return;
|
|
|
|
|
|
|
|
const seitenEl = event.target.closest(".page");
|
|
|
|
|
|
|
|
if (!seitenEl) return;
|
|
|
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
|
|
|
const nummer = Number(seitenEl.dataset.pageNumber || 0);
|
|
|
|
|
|
|
|
if (!nummer) return;
|
|
|
|
|
|
|
|
const rechteck = seitenEl.getBoundingClientRect();
|
|
|
|
|
|
|
|
const anteilX = (event.clientX - rechteck.left) / rechteck.width;
|
|
|
|
|
|
|
|
const anteilY = (event.clientY - rechteck.top) / rechteck.height;
|
|
|
|
|
|
|
|
const seite = await dokument.getPage(nummer);
|
|
|
|
|
|
|
|
const blick = seite.getViewport({ scale: 1 });
|
|
|
|
|
|
|
|
const breite = Math.min(170, blick.width * 0.35);
|
|
|
|
|
|
|
|
const hoehe =
|
|
|
|
|
|
|
|
(wartendeUnterschrift.hoehe / wartendeUnterschrift.breite) * breite;
|
|
|
|
|
|
|
|
const eintrag = {
|
|
|
|
|
|
|
|
...wartendeUnterschrift,
|
|
|
|
|
|
|
|
seite: nummer,
|
|
|
|
|
|
|
|
breite,
|
|
|
|
|
|
|
|
// Der Tipp meint die Mitte der Unterschrift, nicht ihre Ecke.
|
|
|
|
|
|
|
|
x: Math.max(4, anteilX * blick.width - breite / 2),
|
|
|
|
|
|
|
|
y: Math.max(24, (1 - anteilY) * blick.height - hoehe / 2),
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
unterschriften.push(eintrag);
|
|
|
|
|
|
|
|
behaelter.classList.remove("viewer__pdfscroll--setzen");
|
|
|
|
|
|
|
|
wartendeUnterschrift = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Sichtbar machen, wo sie gelandet ist -- sonst tippt man ins Blaue.
|
|
|
|
|
|
|
|
const marke = document.createElement("img");
|
|
|
|
|
|
|
|
marke.className = "viewer__unterschrift";
|
|
|
|
|
|
|
|
marke.src = eintrag.vorschau;
|
|
|
|
|
|
|
|
marke.alt = "";
|
|
|
|
|
|
|
|
marke.style.left = `${(eintrag.x / blick.width) * 100}%`;
|
|
|
|
|
|
|
|
marke.style.bottom = `${(eintrag.y / blick.height) * 100}%`;
|
|
|
|
|
|
|
|
marke.style.width = `${(breite / blick.width) * 100}%`;
|
|
|
|
|
|
|
|
seitenEl.append(marke);
|
|
|
|
|
|
|
|
behaelter.dispatchEvent(new CustomEvent("unterschrieben", { detail: eintrag }));
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
behaelter.addEventListener("click", (event) => {
|
|
|
|
|
|
|
|
platzieren(event).catch((fehler) => console.error("Unterschrift", fehler));
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
hinein: () => {
|
|
|
|
hinein: () => {
|
|
|
|
viewer.currentScale = Math.min(MAX_ZOOM, viewer.currentScale * 1.2);
|
|
|
|
viewer.currentScale = Math.min(MAX_ZOOM, viewer.currentScale * 1.2);
|
|
|
@@ -463,12 +647,22 @@ async function pdfBetrachter(behaelter, seiten, url, basis, { formulare = true }
|
|
|
|
* pdf.js schreibt die Feldwerte; gewaehlte Fotos kommen danach mit
|
|
|
|
* pdf.js schreibt die Feldwerte; gewaehlte Fotos kommen danach mit
|
|
|
|
* pdf-lib hinein -- pdf.js kann nichts in ein PDF zeichnen.
|
|
|
|
* pdf-lib hinein -- pdf.js kann nichts in ein PDF zeichnen.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
speichern: async (pdflibAdresse, { abschliessen = false } = {}) => {
|
|
|
|
speichern: async (pdflibAdresse, { abschliessen = false, zeuge = "" } = {}) => {
|
|
|
|
const bytes = await dokument.saveDocument();
|
|
|
|
const bytes = await dokument.saveDocument();
|
|
|
|
// Ohne Bild und ohne Abschluss braucht es pdf-lib gar nicht.
|
|
|
|
// Ohne Bild, ohne Unterschrift und ohne Abschluss braucht es pdf-lib nicht.
|
|
|
|
if (!bilder.size && !abschliessen) return bytes;
|
|
|
|
if (!bilder.size && !abschliessen && !unterschriften.length) return bytes;
|
|
|
|
return nachbearbeiten(bytes, [...bilder.values()], pdflibAdresse, { abschliessen });
|
|
|
|
return nachbearbeiten(bytes, [...bilder.values()], pdflibAdresse, {
|
|
|
|
|
|
|
|
abschliessen,
|
|
|
|
|
|
|
|
unterschriften,
|
|
|
|
|
|
|
|
zeuge,
|
|
|
|
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
/** Wartet der Betrachter auf den Tipp, wohin die Unterschrift soll? */
|
|
|
|
|
|
|
|
unterschriftAnnehmen: (daten) => {
|
|
|
|
|
|
|
|
wartendeUnterschrift = daten;
|
|
|
|
|
|
|
|
behaelter.classList.add("viewer__pdfscroll--setzen");
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
unterschriften: () => unterschriften.map(({ name, zeit }) => ({ name, zeit })),
|
|
|
|
/** Wurde ueberhaupt etwas eingetragen? */
|
|
|
|
/** Wurde ueberhaupt etwas eingetragen? */
|
|
|
|
veraendert: () => {
|
|
|
|
veraendert: () => {
|
|
|
|
const s = dokument.annotationStorage;
|
|
|
|
const s = dokument.annotationStorage;
|
|
|
@@ -505,10 +699,37 @@ export function initFilePreview(wurzel = document) {
|
|
|
|
const ordnerFeld = stueck("ordner");
|
|
|
|
const ordnerFeld = stueck("ordner");
|
|
|
|
const nameFeld = stueck("name");
|
|
|
|
const nameFeld = stueck("name");
|
|
|
|
const ersetzenFeld = stueck("ersetzen");
|
|
|
|
const ersetzenFeld = stueck("ersetzen");
|
|
|
|
|
|
|
|
const ersetzenKasten = stueck("ersetzenfeld");
|
|
|
|
const jetzt = stueck("jetzt");
|
|
|
|
const jetzt = stueck("jetzt");
|
|
|
|
const abbrechen = stueck("abbrechen");
|
|
|
|
const abbrechen = stueck("abbrechen");
|
|
|
|
const modusFeld = stueck("modus");
|
|
|
|
const modusFeld = stueck("modus");
|
|
|
|
|
|
|
|
const zielFeld = stueck("ziel");
|
|
|
|
|
|
|
|
const unterschreibenKnopf = stueck("unterschreiben");
|
|
|
|
|
|
|
|
const unterschriftPanel = stueck("unterschriftpanel");
|
|
|
|
|
|
|
|
const vornameFeld = stueck("vorname");
|
|
|
|
|
|
|
|
const nachnameFeld = stueck("nachname");
|
|
|
|
|
|
|
|
const padLeinwand = stueck("signpad");
|
|
|
|
|
|
|
|
const padLeeren = stueck("signleeren");
|
|
|
|
|
|
|
|
const padSetzen = stueck("signsetzen");
|
|
|
|
|
|
|
|
const padAbbrechen = stueck("signabbrechen");
|
|
|
|
|
|
|
|
const padHinweis = stueck("signhinweis");
|
|
|
|
const basis = rumpf.dataset.viewerPdfjs || "";
|
|
|
|
const basis = rumpf.dataset.viewerPdfjs || "";
|
|
|
|
|
|
|
|
const konto = rumpf.dataset.viewerKonto || "";
|
|
|
|
|
|
|
|
const bezeugen = rumpf.dataset.viewerZeuge === "1";
|
|
|
|
|
|
|
|
// Die Uhr des Servers, nicht die des Tablets: eine verstellte Uhr am Geraet
|
|
|
|
|
|
|
|
// soll nicht das Datum unter einer Unterschrift bestimmen.
|
|
|
|
|
|
|
|
const serverStart = Date.parse(rumpf.dataset.viewerZeit || "") || Date.now();
|
|
|
|
|
|
|
|
const geladenSeit = Date.now();
|
|
|
|
|
|
|
|
const jetztText = () => {
|
|
|
|
|
|
|
|
const zeitpunkt = new Date(serverStart + (Date.now() - geladenSeit));
|
|
|
|
|
|
|
|
return zeitpunkt.toLocaleString("de-DE", {
|
|
|
|
|
|
|
|
day: "2-digit",
|
|
|
|
|
|
|
|
month: "2-digit",
|
|
|
|
|
|
|
|
year: "numeric",
|
|
|
|
|
|
|
|
hour: "2-digit",
|
|
|
|
|
|
|
|
minute: "2-digit",
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Ein Format, das dieser Browser nicht kann (HEIC ausserhalb von Safari),
|
|
|
|
// Ein Format, das dieser Browser nicht kann (HEIC ausserhalb von Safari),
|
|
|
|
// soll nicht als leere Flaeche enden.
|
|
|
|
// soll nicht als leere Flaeche enden.
|
|
|
@@ -535,6 +756,8 @@ export function initFilePreview(wurzel = document) {
|
|
|
|
rahmen.hidden = art !== "text";
|
|
|
|
rahmen.hidden = art !== "text";
|
|
|
|
leiste.hidden = art === "text";
|
|
|
|
leiste.hidden = art === "text";
|
|
|
|
if (speichern) speichern.hidden = art !== "pdf";
|
|
|
|
if (speichern) speichern.hidden = art !== "pdf";
|
|
|
|
|
|
|
|
if (unterschreibenKnopf) unterschreibenKnopf.hidden = art !== "pdf";
|
|
|
|
|
|
|
|
if (unterschriftPanel) unterschriftPanel.hidden = true;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const aufraeumen = () => {
|
|
|
|
const aufraeumen = () => {
|
|
|
@@ -624,14 +847,68 @@ export function initFilePreview(wurzel = document) {
|
|
|
|
// Ohne Endung: die haengt der Server an. Eine Datei, die auf ".pdf.txt"
|
|
|
|
// Ohne Endung: die haengt der Server an. Eine Datei, die auf ".pdf.txt"
|
|
|
|
// oder auf gar nichts endet, oeffnet auf einer Freigabe nichts mehr.
|
|
|
|
// oder auf gar nichts endet, oeffnet auf einer Freigabe nichts mehr.
|
|
|
|
// Der Zusatz haelt die Vorlage heil, auch wenn jemand blind speichert.
|
|
|
|
// Der Zusatz haelt die Vorlage heil, auch wenn jemand blind speichert.
|
|
|
|
nameFeld.value = datei.replace(/\.pdf$/i, "") + "-ausgefuellt";
|
|
|
|
// Einmal "-ausgefuellt" reicht: wer eine ausgefuellte Fassung noch einmal
|
|
|
|
|
|
|
|
// oeffnet und speichert, bekam sonst "...-ausgefuellt-ausgefuellt".
|
|
|
|
|
|
|
|
const rumpfname = datei.replace(/\.pdf$/i, "");
|
|
|
|
|
|
|
|
nameFeld.value = /-ausgefuellt$/i.test(rumpfname) ? rumpfname : `${rumpfname}-ausgefuellt`;
|
|
|
|
ersetzenFeld.checked = false;
|
|
|
|
ersetzenFeld.checked = false;
|
|
|
|
|
|
|
|
ersetzenKasten?.classList.remove("is-gefragt");
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Unterschreiben: Name tippen, zeichnen, ins Dokument setzen. */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const pad = padLeinwand ? unterschriftPad(padLeinwand) : null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const panelZeigen = (offen) => {
|
|
|
|
|
|
|
|
if (!unterschriftPanel) return;
|
|
|
|
|
|
|
|
unterschriftPanel.hidden = !offen;
|
|
|
|
|
|
|
|
if (offen) {
|
|
|
|
|
|
|
|
pad?.leeren();
|
|
|
|
|
|
|
|
if (padHinweis) padHinweis.textContent = "";
|
|
|
|
|
|
|
|
vornameFeld?.focus();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
unterschreibenKnopf?.addEventListener("click", () => {
|
|
|
|
|
|
|
|
if (!pdf) return;
|
|
|
|
|
|
|
|
panelZeigen(unterschriftPanel?.hidden !== false);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
padLeeren?.addEventListener("click", () => pad?.leeren());
|
|
|
|
|
|
|
|
padAbbrechen?.addEventListener("click", () => panelZeigen(false));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
padSetzen?.addEventListener("click", () => {
|
|
|
|
|
|
|
|
if (!pdf || !pad) return;
|
|
|
|
|
|
|
|
const name = [vornameFeld?.value, nachnameFeld?.value]
|
|
|
|
|
|
|
|
.map((wert) => (wert || "").trim())
|
|
|
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
|
|
|
.join(" ");
|
|
|
|
|
|
|
|
// Beides ist noetig, und beides wird hier gesagt statt stumm abgelehnt.
|
|
|
|
|
|
|
|
if (!name) {
|
|
|
|
|
|
|
|
if (padHinweis) padHinweis.textContent = "Bitte Vor- und Nachnamen eintragen.";
|
|
|
|
|
|
|
|
vornameFeld?.focus();
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
const zeichnung = pad.alsPng();
|
|
|
|
|
|
|
|
if (!zeichnung) {
|
|
|
|
|
|
|
|
if (padHinweis) padHinweis.textContent = "Bitte im Feld unterschreiben.";
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
pdf.unterschriftAnnehmen({ ...zeichnung, name, zeit: jetztText() });
|
|
|
|
|
|
|
|
panelZeigen(false);
|
|
|
|
|
|
|
|
window.tesm.showToast("Jetzt auf die Stelle im Dokument tippen.", "info");
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
speichern?.addEventListener("click", () => {
|
|
|
|
speichern?.addEventListener("click", () => {
|
|
|
|
if (!pdf || !aktuell) return;
|
|
|
|
if (!pdf || !aktuell) return;
|
|
|
|
vorschlagen();
|
|
|
|
vorschlagen();
|
|
|
|
zeile.hidden = false;
|
|
|
|
zeile.hidden = false;
|
|
|
|
|
|
|
|
if (modusFeld && pdf.unterschriften().length) {
|
|
|
|
|
|
|
|
// Mit Unterschrift gibt es nur einen Weg -- und das Feld soll nicht
|
|
|
|
|
|
|
|
// etwas anderes behaupten.
|
|
|
|
|
|
|
|
modusFeld.value = "fertig";
|
|
|
|
|
|
|
|
modusFeld.disabled = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
nameFeld.focus();
|
|
|
|
nameFeld.focus();
|
|
|
|
nameFeld.select();
|
|
|
|
nameFeld.select();
|
|
|
|
});
|
|
|
|
});
|
|
|
@@ -640,6 +917,31 @@ export function initFilePreview(wurzel = document) {
|
|
|
|
zeile.hidden = true;
|
|
|
|
zeile.hidden = true;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Zielfreigabe: die Ordnerliste gehoert zur gewaehlten Freigabe, nicht zur
|
|
|
|
|
|
|
|
geoeffneten. Geholt wird sie erst bei Bedarf -- alle Listen im
|
|
|
|
|
|
|
|
Seitenaufbau mitzuschicken waere Ballast fuer einen seltenen Fall. */
|
|
|
|
|
|
|
|
const ordnerListe = ordnerFeld ? document.getElementById(ordnerFeld.getAttribute("list")) : null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
zielFeld?.addEventListener("change", async () => {
|
|
|
|
|
|
|
|
if (!ordnerListe) return;
|
|
|
|
|
|
|
|
const vorlage = zielFeld.dataset.urlVorlage || "";
|
|
|
|
|
|
|
|
if (!vorlage) return;
|
|
|
|
|
|
|
|
ordnerFeld.value = "";
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
const antwort = await window.tesm.api(vorlage.replace("__token__", zielFeld.value));
|
|
|
|
|
|
|
|
ordnerListe.replaceChildren(
|
|
|
|
|
|
|
|
...(antwort.folders || []).map((eintrag) => {
|
|
|
|
|
|
|
|
const option = document.createElement("option");
|
|
|
|
|
|
|
|
option.value = eintrag;
|
|
|
|
|
|
|
|
return option;
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
} catch (fehler) {
|
|
|
|
|
|
|
|
// Kein Beinbruch: das Feld nimmt auch getippte Pfade.
|
|
|
|
|
|
|
|
console.error("Ordnerliste", fehler);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
jetzt?.addEventListener("click", async () => {
|
|
|
|
jetzt?.addEventListener("click", async () => {
|
|
|
|
if (!pdf || !aktuell) return;
|
|
|
|
if (!pdf || !aktuell) return;
|
|
|
|
const ziel = speichern.dataset.url;
|
|
|
|
const ziel = speichern.dataset.url;
|
|
|
@@ -647,13 +949,20 @@ export function initFilePreview(wurzel = document) {
|
|
|
|
const vorher = jetzt.textContent;
|
|
|
|
const vorher = jetzt.textContent;
|
|
|
|
jetzt.textContent = "Speichern ...";
|
|
|
|
jetzt.textContent = "Speichern ...";
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
|
|
|
|
const gesetzt = pdf.unterschriften();
|
|
|
|
const bytes = await pdf.speichern(rumpf.dataset.viewerPdflib || "", {
|
|
|
|
const bytes = await pdf.speichern(rumpf.dataset.viewerPdflib || "", {
|
|
|
|
abschliessen: modusFeld?.value === "fertig",
|
|
|
|
// Unterschrieben heisst abgeschlossen: ein Dokument, in dem nach der
|
|
|
|
|
|
|
|
// Unterschrift noch Felder aenderbar waeren, ist keine Unterschrift
|
|
|
|
|
|
|
|
// wert.
|
|
|
|
|
|
|
|
abschliessen: modusFeld?.value === "fertig" || gesetzt.length > 0,
|
|
|
|
|
|
|
|
zeuge: bezeugen ? konto : "",
|
|
|
|
});
|
|
|
|
});
|
|
|
|
const daten = new FormData();
|
|
|
|
const daten = new FormData();
|
|
|
|
daten.append("path", aktuell.pfad);
|
|
|
|
daten.append("path", aktuell.pfad);
|
|
|
|
daten.append("ordner", ordnerFeld.value.trim());
|
|
|
|
daten.append("ordner", ordnerFeld.value.trim());
|
|
|
|
daten.append("name", nameFeld.value.trim());
|
|
|
|
daten.append("name", nameFeld.value.trim());
|
|
|
|
|
|
|
|
if (zielFeld?.value) daten.append("ziel", zielFeld.value);
|
|
|
|
|
|
|
|
if (gesetzt.length) daten.append("unterschriften", JSON.stringify(gesetzt));
|
|
|
|
if (ersetzenFeld.checked) daten.append("ersetzen", "1");
|
|
|
|
if (ersetzenFeld.checked) daten.append("ersetzen", "1");
|
|
|
|
daten.append(
|
|
|
|
daten.append(
|
|
|
|
"datei",
|
|
|
|
"datei",
|
|
|
@@ -669,6 +978,12 @@ export function initFilePreview(wurzel = document) {
|
|
|
|
// Zeile bleibt offen: meist ist es ein Name, der schon vergeben ist.
|
|
|
|
// Zeile bleibt offen: meist ist es ein Name, der schon vergeben ist.
|
|
|
|
console.error("Formular speichern", fehler);
|
|
|
|
console.error("Formular speichern", fehler);
|
|
|
|
window.tesm.showToast(fehler.message || "Speichern fehlgeschlagen.", "danger");
|
|
|
|
window.tesm.showToast(fehler.message || "Speichern fehlgeschlagen.", "danger");
|
|
|
|
|
|
|
|
// Genau dieser Fall hat eine Antwort auf der Zeile -- dann zeigen wir
|
|
|
|
|
|
|
|
// auch darauf, statt es bei einer Meldung zu belassen.
|
|
|
|
|
|
|
|
if (fehler.status === 409) {
|
|
|
|
|
|
|
|
ersetzenKasten?.classList.add("is-gefragt");
|
|
|
|
|
|
|
|
ersetzenFeld?.focus();
|
|
|
|
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
} finally {
|
|
|
|
jetzt.disabled = false;
|
|
|
|
jetzt.disabled = false;
|
|
|
|
jetzt.textContent = vorher;
|
|
|
|
jetzt.textContent = vorher;
|
|
|
|