- Gestaltung „Bauplan“: ruhigere Farben, Schriftskala, Abstände im 4-px-Raster, Kalenderwochen und Nummern in Mono-Ziffern, Glas beruhigt - Dashboard „Meine Aufgaben“: Kopf mit Kalenderwoche, Band „Diese Woche“ (Mo–Fr), je Projekt Meine Rolle / Team / Notizen; offene Termine blau hervorgehoben; Monatskalender unverändert - Neuer Bereich „Notizen“ in der Seitenleiste: Notizen aller eigenen Projekte und eigene Notizen ohne Projekt, später einem Projekt zuordenbar - Seitenleiste neu gegliedert (Projekte, Team, Notizen, Studenten, Einstellungen/Admin unten), Admin-Punkte in Personen/Vorgaben/System - Reiter im Projektkopf wie die Seitenleiste hervorgehoben - Behoben: „&amp;…“ in Kommentaren wuchs bei jedem Speichern; bestehende Kommentare werden beim Start repariert - Behoben: Lösch-Rückfrage bei Notizen unlesbar bzw. blieb stehen, Cursor im Notiztitel verdeckt, Dropdown im Dunkelmodus gekachelt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
418 lines
19 KiB
JavaScript
418 lines
19 KiB
JavaScript
// -----------------------------
|
||
// Projektbereich — Notizen (Eriks Wahl „A – Liste + Editor“)
|
||
// -----------------------------
|
||
// Gemeinsame Notizen je Projekt: links die Liste (angeheftete oben, danach
|
||
// zuletzt geänderte zuerst, Suche über Titel und Text), rechts die gewählte
|
||
// Notiz direkt bearbeitbar — Titel, dieselbe Werkzeugleiste wie beim
|
||
// Sitzungskommentar (richtextToolbarHtml(), js/utils.js) und der Text.
|
||
// Gespeichert wird automatisch kurz nach dem Tippen und beim Verlassen des
|
||
// Feldes; „Gespeichert ✓“ zeigt den Stand. Mail per Drag & Drop (Text oder
|
||
// .eml) bzw. Strg+V außerhalb von Eingabefeldern legt eine neue Notiz an,
|
||
// ohne KI. Live: andere Browser erfahren Änderungen über das Ereignis
|
||
// {typ:'notizen'} (notizenLiveAenderung(), aus js/store.js).
|
||
//
|
||
// Daten am Server: Tabelle projekt_notizen, Routen /projekt-notizen*
|
||
// (server.js). Die Notizen hängen am Projekt (projectUid), nicht an der
|
||
// Sitzung. Schreiben: Recht proj.plan; Löschen: Verfasser oder Admin.
|
||
(function(){
|
||
const app = window.PlanApp;
|
||
const SPEICHER_PAUSE_MS = 1200;
|
||
|
||
let liste = []; // Notizen des geladenen Projekts
|
||
let uidGeladen = null; // projectUid, zu dem `liste` gehört
|
||
let aktivId = null; // gewählte Notiz
|
||
let suche = '';
|
||
let basis = ''; // geaendertAm, auf dem die Bearbeitung beruht
|
||
let ungespeichert = false;
|
||
let speicherTimer = null;
|
||
let speichertGerade = false;
|
||
let beobachter = null; // MutationObserver auf dem Textfeld
|
||
let loeschenFragt = null; // Timer der zweistufigen Löschen-Abfrage
|
||
let ladeFehler = '';
|
||
|
||
const kannSchreiben = () => typeof hatRecht === 'function' && hatRecht('proj.plan');
|
||
const projektUid = () => app.project ? String(app.project.projectUid || app.project.id || '') : '';
|
||
const wrap = () => document.getElementById('planNotizenWrap');
|
||
const aktiv = () => liste.find(n => n.id === aktivId) || null;
|
||
|
||
function zeit(iso){
|
||
if(!iso) return '';
|
||
const d = new Date(iso);
|
||
if(isNaN(d)) return '';
|
||
const heute = new Date();
|
||
const gleicherTag = d.toDateString() === heute.toDateString();
|
||
const gestern = new Date(heute); gestern.setDate(heute.getDate() - 1);
|
||
const uhr = d.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' });
|
||
if(gleicherTag) return 'heute ' + uhr;
|
||
if(d.toDateString() === gestern.toDateString()) return 'gestern ' + uhr;
|
||
return d.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: d.getFullYear() === heute.getFullYear() ? undefined : 'numeric' });
|
||
}
|
||
function kurzname(name){
|
||
const teile = String(name || '').trim().split(/\s+/);
|
||
return teile.length > 1 ? teile[0][0] + '. ' + teile.slice(1).join(' ') : (name || '');
|
||
}
|
||
function klartext(html){
|
||
return typeof statusPlainText === 'function' ? statusPlainText(html) : String(html || '').replace(/<[^>]+>/g, ' ');
|
||
}
|
||
function sortieren(){
|
||
liste.sort((a, b) => (b.angeheftet - a.angeheftet) || String(b.geaendertAm).localeCompare(String(a.geaendertAm)));
|
||
}
|
||
|
||
// ---------- Laden ----------
|
||
function laden(uid){
|
||
uidGeladen = uid;
|
||
ladeFehler = '';
|
||
return fetch(baseUrl() + '/projekt-notizen?uid=' + encodeURIComponent(uid))
|
||
.then(r => r.json().then(d => ({ ok: r.ok, d })))
|
||
.then(({ ok, d }) => {
|
||
if(uid !== uidGeladen) return;
|
||
if(!ok || !d.ok) throw new Error(d.error || 'Laden fehlgeschlagen');
|
||
liste = d.notizen || [];
|
||
sortieren();
|
||
})
|
||
.catch(e => { ladeFehler = e.message || 'Die Notizen konnten nicht geladen werden.'; liste = []; });
|
||
}
|
||
|
||
// ---------- Darstellung ----------
|
||
window.renderPlanNotizen = function(){
|
||
const el = wrap();
|
||
if(!el || !app.project) return;
|
||
const uid = projektUid();
|
||
if(uid !== uidGeladen){
|
||
jetztSpeichern();
|
||
aktivId = null; liste = []; suche = '';
|
||
el.innerHTML = '<div class="notizen-laedt">Notizen werden geladen…</div>';
|
||
laden(uid).then(() => { if(projektUid() === uid) aufbauen(); });
|
||
return;
|
||
}
|
||
aufbauen();
|
||
};
|
||
|
||
function aufbauen(){
|
||
const el = wrap();
|
||
if(!el) return;
|
||
if(!aktiv() && liste.length) aktivId = liste[0].id;
|
||
el.innerHTML = `
|
||
<div class="notizen" id="notizenBereich">
|
||
<aside class="notizen-liste">
|
||
<div class="notizen-suchzeile">
|
||
<input type="search" id="notizenSuche" class="notizen-suche" placeholder="Notizen durchsuchen…"
|
||
aria-label="Notizen durchsuchen" value="${esc(suche)}" oninput="notizenSuchen(this.value)">
|
||
${kannSchreiben() ? '<button type="button" class="notizen-neu" onclick="notizNeu()">+ Neu</button>' : ''}
|
||
</div>
|
||
<div id="notizenEintraege" class="notizen-eintraege"></div>
|
||
${kannSchreiben() ? '<div class="notizen-ablage">Mail hierher ziehen = neue Notiz</div>' : ''}
|
||
</aside>
|
||
<div class="notizen-editor" id="notizenEditor"></div>
|
||
</div>`;
|
||
listeZeichnen();
|
||
editorZeichnen();
|
||
}
|
||
|
||
function listeZeichnen(){
|
||
const box = document.getElementById('notizenEintraege');
|
||
if(!box) return;
|
||
if(ladeFehler){ box.innerHTML = '<div class="notizen-leer ist-fehler">' + esc(ladeFehler) + '</div>'; return; }
|
||
const s = suche.trim().toLowerCase();
|
||
const treffer = s ? liste.filter(n => (n.titel + ' ' + klartext(n.html)).toLowerCase().includes(s)) : liste;
|
||
if(!liste.length){ box.innerHTML = '<div class="notizen-leer">Noch keine Notizen.</div>'; return; }
|
||
if(!treffer.length){ box.innerHTML = '<div class="notizen-leer">Keine Notiz enthält „' + esc(suche) + '“.</div>'; return; }
|
||
const zeile = n => {
|
||
const vorschau = klartext(n.html).replace(/\s+/g, ' ').trim().slice(0, 90);
|
||
return `<button type="button" class="notizen-eintrag${n.id === aktivId ? ' ist-aktiv' : ''}" onclick="notizWaehlen('${esc(n.id)}')">
|
||
<span class="notizen-eintrag-titel">${esc(n.titel || 'Ohne Titel')}${n.quelle === 'mail' ? ' <span class="notizen-chip">Mail</span>' : ''}</span>
|
||
<span class="notizen-eintrag-text">${esc(vorschau || 'Kein Text')}</span>
|
||
<span class="notizen-eintrag-meta">${esc(kurzname(n.geaendertVon))} · ${esc(zeit(n.geaendertAm))}</span>
|
||
</button>`;
|
||
};
|
||
const oben = treffer.filter(n => n.angeheftet), rest = treffer.filter(n => !n.angeheftet);
|
||
box.innerHTML = (oben.length ? '<div class="notizen-gruppe"><span class="notizen-pin-punkt"></span>Angeheftet</div>' + oben.map(zeile).join('') : '')
|
||
+ (rest.length ? (oben.length ? '<div class="notizen-gruppe">Weitere</div>' : '') + rest.map(zeile).join('') : '');
|
||
}
|
||
|
||
function editorZeichnen(){
|
||
const el = document.getElementById('notizenEditor');
|
||
if(!el) return;
|
||
if(beobachter){ beobachter.disconnect(); beobachter = null; }
|
||
const n = aktiv();
|
||
if(!n){
|
||
el.innerHTML = `<div class="notizen-editor-leer">
|
||
<b>${liste.length ? 'Notiz links auswählen' : 'Noch keine Notizen in diesem Projekt'}</b>
|
||
<span>${kannSchreiben() ? 'Mit „+ Neu“ anlegen oder eine Mail auf diesen Bereich ziehen.' : 'Zum Anlegen fehlt das Recht, den Terminplan zu bearbeiten.'}</span>
|
||
</div>`;
|
||
return;
|
||
}
|
||
const schreiben = kannSchreiben();
|
||
basis = n.geaendertAm;
|
||
ungespeichert = false;
|
||
el.innerHTML = `
|
||
<div class="notizen-editor-kopf">
|
||
<input type="text" id="notizTitel" class="notizen-titel" value="${esc(n.titel)}" placeholder="Titel der Notiz"
|
||
aria-label="Titel der Notiz" ${schreiben ? '' : 'readonly'} oninput="notizGeaendert()" onblur="notizJetztSpeichern()">
|
||
${schreiben ? `<button type="button" class="notizen-symbol${n.angeheftet ? ' ist-an' : ''}" onclick="notizAnheften()"
|
||
title="${n.angeheftet ? 'Nicht mehr anheften' : 'Oben anheften'}" aria-pressed="${n.angeheftet}">${n.angeheftet ? 'Angeheftet' : 'Anheften'}</button>
|
||
<button type="button" class="notizen-symbol ist-gefahr" id="notizLoeschen" onclick="notizLoeschen()">Löschen</button>` : ''}
|
||
</div>
|
||
<div class="richtext-wrap notizen-richtext">
|
||
${schreiben ? richtextToolbarHtml('.notizen-text') : ''}
|
||
${richtextFeldHtml('notizen-text', sanitizeStatusHtml(n.html || ''), 'Notiz schreiben…', 'id="notizText" onblur="notizJetztSpeichern()"', schreiben)}
|
||
</div>
|
||
<div class="notizen-fuss">
|
||
<span>Angelegt von ${esc(n.erstelltVon)} ${esc(zeit(n.erstelltAm))}${n.geaendertAm !== n.erstelltAm ? ' · zuletzt geändert von ' + esc(n.geaendertVon) + ' ' + esc(zeit(n.geaendertAm)) : ''}</span>
|
||
<span id="notizStatus" class="notizen-status"></span>
|
||
</div>`;
|
||
const text = document.getElementById('notizText');
|
||
// Auch Fett/Farbe aus der Werkzeugleiste ändern den Inhalt, ohne ein
|
||
// input-Ereignis auszulösen — daher ein MutationObserver.
|
||
if(text && schreiben){
|
||
beobachter = new MutationObserver(() => notizGeaendert());
|
||
beobachter.observe(text, { childList: true, subtree: true, characterData: true, attributes: true });
|
||
}
|
||
}
|
||
|
||
function status(text, art){
|
||
const el = document.getElementById('notizStatus');
|
||
if(!el) return;
|
||
el.className = 'notizen-status' + (art ? ' ist-' + art : '');
|
||
el.innerHTML = text;
|
||
}
|
||
|
||
// ---------- Bearbeiten und Speichern ----------
|
||
window.notizenSuchen = function(wert){ suche = wert || ''; listeZeichnen(); };
|
||
|
||
window.notizWaehlen = function(id){
|
||
if(id === aktivId) return;
|
||
jetztSpeichern();
|
||
aktivId = id;
|
||
listeZeichnen();
|
||
editorZeichnen();
|
||
};
|
||
|
||
window.notizGeaendert = function(){
|
||
if(!kannSchreiben() || !aktiv()) return;
|
||
ungespeichert = true;
|
||
status('Ungespeichert');
|
||
clearTimeout(speicherTimer);
|
||
speicherTimer = setTimeout(jetztSpeichern, SPEICHER_PAUSE_MS);
|
||
};
|
||
window.notizJetztSpeichern = function(){ jetztSpeichern(); };
|
||
|
||
// jetztSpeichern: schickt die gewählte Notiz, falls geändert. ohneBasis =
|
||
// bewusst überschreiben (nach einem Konflikt „Meine Fassung behalten“).
|
||
function jetztSpeichern(ohneBasis){
|
||
clearTimeout(speicherTimer);
|
||
const n = aktiv();
|
||
if(!n || !ungespeichert || !kannSchreiben()) return Promise.resolve();
|
||
if(speichertGerade){ speicherTimer = setTimeout(jetztSpeichern, 400); return Promise.resolve(); }
|
||
const titelEl = document.getElementById('notizTitel');
|
||
const textEl = document.getElementById('notizText');
|
||
const titel = titelEl ? titelEl.value : n.titel;
|
||
const html = textEl ? textEl.innerHTML : n.html;
|
||
ungespeichert = false;
|
||
speichertGerade = true;
|
||
status('Speichert…');
|
||
return fetch(baseUrl() + '/projekt-notizen/' + encodeURIComponent(n.id), {
|
||
method: 'PUT', headers: { 'Content-Type': 'application/json; charset=utf-8' },
|
||
body: JSON.stringify({ titel, html, basis: ohneBasis ? '' : basis })
|
||
}).then(r => r.json().then(d => ({ r, d }))).then(({ r, d }) => {
|
||
if(r.status === 409 && d.konflikt){
|
||
ungespeichert = true;
|
||
status(esc(d.error) + ' <button type="button" class="notizen-link" onclick="notizKonflikt(true)">Meine Fassung speichern</button>'
|
||
+ ' <button type="button" class="notizen-link" onclick="notizKonflikt(false)">Neuen Stand laden</button>', 'warnung');
|
||
n._fremd = d.notiz;
|
||
return;
|
||
}
|
||
if(!r.ok || !d.ok) throw new Error(d.error || 'Speichern fehlgeschlagen');
|
||
Object.assign(n, d.notiz, { titel, html });
|
||
if(n.id === aktivId) basis = d.notiz.geaendertAm;
|
||
sortieren();
|
||
listeZeichnen();
|
||
if(!ungespeichert) status('Gespeichert ✓', 'ok');
|
||
}).catch(e => {
|
||
ungespeichert = true;
|
||
status('Nicht gespeichert: ' + esc(e.message) + ' — wird erneut versucht.', 'fehler');
|
||
speicherTimer = setTimeout(jetztSpeichern, 5000);
|
||
}).finally(() => { speichertGerade = false; });
|
||
}
|
||
|
||
window.notizKonflikt = function(meine){
|
||
const n = aktiv();
|
||
if(!n) return;
|
||
if(meine){ ungespeichert = true; jetztSpeichern(true); return; }
|
||
if(n._fremd) Object.assign(n, n._fremd);
|
||
delete n._fremd;
|
||
ungespeichert = false;
|
||
listeZeichnen();
|
||
editorZeichnen();
|
||
};
|
||
|
||
window.notizNeu = function(){
|
||
const uid = projektUid();
|
||
if(!uid || !kannSchreiben()) return;
|
||
jetztSpeichern();
|
||
fetch(baseUrl() + '/projekt-notizen', {
|
||
method: 'POST', headers: { 'Content-Type': 'application/json; charset=utf-8' },
|
||
body: JSON.stringify({ uid, titel: '', html: '' })
|
||
}).then(r => r.json()).then(d => {
|
||
if(!d.ok) throw new Error(d.error || 'Anlegen fehlgeschlagen');
|
||
neueNotizZeigen(d.notiz);
|
||
const t = document.getElementById('notizTitel');
|
||
if(t) t.focus();
|
||
}).catch(e => app.showToast('Notiz konnte nicht angelegt werden: ' + e.message));
|
||
};
|
||
|
||
function neueNotizZeigen(notiz){
|
||
if(!notiz || notiz.uid !== uidGeladen) return;
|
||
liste = liste.filter(x => x.id !== notiz.id);
|
||
liste.push(notiz);
|
||
sortieren();
|
||
aktivId = notiz.id;
|
||
suche = '';
|
||
const s = document.getElementById('notizenSuche');
|
||
if(s) s.value = '';
|
||
listeZeichnen();
|
||
editorZeichnen();
|
||
}
|
||
|
||
window.notizAnheften = function(){
|
||
const n = aktiv();
|
||
if(!n) return;
|
||
jetztSpeichern().then(() => fetch(baseUrl() + '/projekt-notizen/' + encodeURIComponent(n.id), {
|
||
method: 'PUT', headers: { 'Content-Type': 'application/json; charset=utf-8' },
|
||
body: JSON.stringify({ angeheftet: !n.angeheftet })
|
||
})).then(r => r.json()).then(d => {
|
||
if(!d.ok) throw new Error(d.error || 'Fehler');
|
||
n.angeheftet = d.notiz.angeheftet;
|
||
sortieren();
|
||
listeZeichnen();
|
||
const knopf = document.querySelector('.notizen-editor-kopf .notizen-symbol:not(.ist-gefahr)');
|
||
if(knopf){
|
||
knopf.classList.toggle('ist-an', n.angeheftet);
|
||
knopf.textContent = n.angeheftet ? 'Angeheftet' : 'Anheften';
|
||
knopf.title = n.angeheftet ? 'Nicht mehr anheften' : 'Oben anheften';
|
||
knopf.setAttribute('aria-pressed', String(n.angeheftet));
|
||
}
|
||
}).catch(e => app.showToast('Anheften fehlgeschlagen: ' + e.message));
|
||
};
|
||
|
||
// notizLoeschen: zweistufig im Knopf selbst (kein confirm()).
|
||
window.notizLoeschen = function(){
|
||
const n = aktiv();
|
||
const knopf = document.getElementById('notizLoeschen');
|
||
if(!n || !knopf) return;
|
||
if(!loeschenFragt){
|
||
knopf.textContent = 'Wirklich löschen?';
|
||
knopf.classList.add('ist-fragend');
|
||
loeschenFragt = setTimeout(() => {
|
||
loeschenFragt = null;
|
||
const k = document.getElementById('notizLoeschen');
|
||
if(k){ k.textContent = 'Löschen'; k.classList.remove('ist-fragend'); }
|
||
}, 4000);
|
||
return;
|
||
}
|
||
clearTimeout(loeschenFragt); loeschenFragt = null;
|
||
clearTimeout(speicherTimer); ungespeichert = false;
|
||
fetch(baseUrl() + '/projekt-notizen/' + encodeURIComponent(n.id), { method: 'DELETE' })
|
||
.then(r => r.json().then(d => ({ r, d }))).then(({ r, d }) => {
|
||
if(!r.ok || !d.ok) throw new Error(d.error || 'Löschen fehlgeschlagen');
|
||
liste = liste.filter(x => x.id !== n.id);
|
||
aktivId = liste.length ? liste[0].id : null;
|
||
listeZeichnen();
|
||
editorZeichnen();
|
||
}).catch(e => { status(esc(e.message), 'fehler'); const k = document.getElementById('notizLoeschen'); if(k){ k.textContent = 'Löschen'; k.classList.remove('ist-fragend'); } });
|
||
};
|
||
|
||
// ---------- Live: Änderungen aus anderen Browsern ----------
|
||
// Liste neu holen; die gewählte Notiz nur ersetzen, wenn hier gerade
|
||
// nichts ungespeichert ist und niemand in ihr tippt.
|
||
window.notizenLiveAenderung = function(uid){
|
||
if(!uid || uid !== uidGeladen) return;
|
||
const n = aktiv();
|
||
laden(uid).then(() => {
|
||
if(!wrap()) return;
|
||
if(aktivId && !liste.some(x => x.id === aktivId)){ aktivId = liste.length ? liste[0].id : null; editorZeichnen(); }
|
||
listeZeichnen();
|
||
const frisch = aktiv();
|
||
const tippt = document.activeElement && document.activeElement.closest && document.activeElement.closest('#notizenEditor');
|
||
if(frisch && n && frisch.id === n.id && !ungespeichert && !tippt && frisch.geaendertAm !== basis) editorZeichnen();
|
||
});
|
||
};
|
||
|
||
// ---------- Mail ablegen (Drag & Drop, Strg+V) ----------
|
||
function imNotizenBereich(){
|
||
const s = document.querySelector('.plan-section.is-active');
|
||
return typeof currentTab !== 'undefined' && currentTab === 'plan' && s && s.dataset.section === 'notizen' && !!app.project;
|
||
}
|
||
function ausMail(nutzlast){
|
||
const uid = projektUid();
|
||
if(!uid) return;
|
||
jetztSpeichern();
|
||
fetch(baseUrl() + '/projekt-notizen/aus-mail', {
|
||
method: 'POST', headers: { 'Content-Type': 'application/json; charset=utf-8' },
|
||
body: JSON.stringify(Object.assign({ uid }, nutzlast))
|
||
}).then(r => r.json()).then(d => {
|
||
if(!d.ok) throw new Error(d.error || 'Fehlgeschlagen');
|
||
neueNotizZeigen(d.notiz);
|
||
app.showToast('Notiz aus der Mail angelegt.');
|
||
}).catch(e => app.showToast('Mail konnte nicht übernommen werden: ' + e.message));
|
||
}
|
||
function ausDaten(dt){
|
||
const dateien = Array.from(dt.files || []);
|
||
const eml = dateien.find(f => /\.eml$/i.test(f.name || '') || f.type === 'message/rfc822');
|
||
if(eml){
|
||
if(eml.size > 5 * 1024 * 1024){ app.showToast('Die Mail ist zu groß (mehr als 5 MB).'); return; }
|
||
eml.text().then(t => ausMail({ eml: t }));
|
||
return;
|
||
}
|
||
if(dateien.some(f => /\.msg$/i.test(f.name || ''))){
|
||
app.showToast('Outlook-Dateien (.msg) lassen sich nicht lesen. Bitte den Mailtext markieren und hineinziehen.');
|
||
return;
|
||
}
|
||
let text = (dt.getData('text/plain') || '').trim();
|
||
if(!text){
|
||
const div = document.createElement('div');
|
||
div.innerHTML = (dt.getData('text/html') || '').replace(/<(script|style)[\s\S]*?<\/\1>/gi, '');
|
||
text = (div.innerText || div.textContent || '').trim();
|
||
}
|
||
if(text) ausMail({ text });
|
||
}
|
||
const bereich = () => document.getElementById('planSection-notizen');
|
||
document.addEventListener('dragover', e => {
|
||
if(!imNotizenBereich() || !kannSchreiben() || !bereich().contains(e.target)) return;
|
||
if(e.target.closest && e.target.closest('#notizenEditor [contenteditable="true"], #notizTitel')) return;
|
||
e.preventDefault();
|
||
e.dataTransfer.dropEffect = 'copy';
|
||
bereich().classList.add('notizen-zieht');
|
||
});
|
||
document.addEventListener('dragleave', e => {
|
||
const b = bereich();
|
||
if(b && (!e.relatedTarget || !b.contains(e.relatedTarget))) b.classList.remove('notizen-zieht');
|
||
});
|
||
document.addEventListener('drop', e => {
|
||
const b = bereich();
|
||
if(b) b.classList.remove('notizen-zieht');
|
||
if(!imNotizenBereich() || !kannSchreiben() || !b.contains(e.target)) return;
|
||
// In das Textfeld gezogen: normal einfügen lassen, keine neue Notiz.
|
||
if(e.target.closest && e.target.closest('#notizenEditor [contenteditable="true"], #notizTitel')) return;
|
||
e.preventDefault();
|
||
ausDaten(e.dataTransfer);
|
||
});
|
||
document.addEventListener('paste', e => {
|
||
if(!imNotizenBereich() || !kannSchreiben()) return;
|
||
const ziel = e.target;
|
||
if(ziel && ziel.closest && ziel.closest('input, textarea, select, [contenteditable="true"], .modal-bg')) return;
|
||
if(!e.clipboardData) return;
|
||
e.preventDefault();
|
||
ausDaten(e.clipboardData);
|
||
});
|
||
// Beim Verlassen der Seite noch speichern, was offen ist.
|
||
// Klick irgendwo anders: die Rückfrage „Wirklich löschen?“ zurücknehmen.
|
||
document.addEventListener('pointerdown', e => {
|
||
if(!loeschenFragt || (e.target.closest && e.target.closest('#notizLoeschen'))) return;
|
||
clearTimeout(loeschenFragt); loeschenFragt = null;
|
||
const k = document.getElementById('notizLoeschen');
|
||
if(k){ k.textContent = 'Löschen'; k.classList.remove('ist-fragend'); }
|
||
}, true);
|
||
window.addEventListener('pagehide', () => { if(ungespeichert) jetztSpeichern(); });
|
||
})();
|