Compare commits
No files matched your search
@@ -29,6 +29,11 @@ server {
|
||||
}
|
||||
|
||||
location / {
|
||||
# nginx' Standard (1m) reicht für Fileshare-Uploads nicht -- etwas
|
||||
# großzügiger als Flasks eigenes MAX_CONTENT_LENGTH (siehe app.py),
|
||||
# damit bei einer knapp 15MB großen Datei nginx nicht schon vor
|
||||
# Flask mit seiner eigenen, unschöneren 413-Seite abbricht.
|
||||
client_max_body_size 16m;
|
||||
proxy_pass http://127.0.0.1:5000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
|
||||
@@ -6,6 +6,16 @@ After=network.target
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/srv/tesm
|
||||
# Markiert genau DIESEN Prozess als den echten Web-App-Dienst -- app.py
|
||||
# wird nämlich NICHT nur hierüber gestartet, sondern auch von
|
||||
# generate_ips.py (via "from app import ...") als reines Hilfsmodul
|
||||
# importiert, z.B. aus poe.sh/tesm-check.service heraus, alle paar
|
||||
# Sekunden. Ein einfacher Import führt JEDEN Modul-Level-Code in app.py
|
||||
# erneut aus -- ohne dieses Flag würde jeder generate_ips.py-Aufruf
|
||||
# _fileshare_cleanup_all_on_startup() erneut auslösen und damit gerade
|
||||
# aktive Fileshare-Mounts anderer, echter Sitzungen sofort wieder
|
||||
# aushängen. Siehe die Prüfung auf TESM_WEB_PROCESS in app.py.
|
||||
Environment=TESM_WEB_PROCESS=1
|
||||
# Produktiver WSGI-Server (gunicorn) statt Flasks eigenem app.run()-
|
||||
# Entwicklungsserver -- siehe requirements.txt für die ausführliche
|
||||
# Begründung von "--workers 1" (In-Memory-Zustand) und "--worker-class
|
||||
|
||||
+2
-1
@@ -114,7 +114,7 @@ fi
|
||||
# ---- Pakete ----
|
||||
step "Installing system packages"
|
||||
sudo apt-get update >>/var/log/tesm-install.log 2>&1 && print_status "apt update"
|
||||
sudo apt-get install -y python3 python3-venv python3-pip nginx sqlite3 expect openssh-client git rsync iputils-ping logrotate certbot >>/var/log/tesm-install.log 2>&1 && print_status "Packages installed"
|
||||
sudo apt-get install -y python3 python3-venv python3-pip nginx sqlite3 expect openssh-client git rsync iputils-ping logrotate certbot cifs-utils >>/var/log/tesm-install.log 2>&1 && print_status "Packages installed"
|
||||
|
||||
# ---- Log-Verzeichnis ----
|
||||
# NICHT weltweit beschreibbar (0755 reicht) -- sowohl tesm.service als
|
||||
@@ -134,6 +134,7 @@ sudo chmod 755 /var/log/tesm
|
||||
step "Deploying application to /srv/tesm"
|
||||
sudo mkdir -p /srv/tesm
|
||||
sudo rsync -a --delete --exclude 'venv' --exclude 'sqlite.db' --exclude 'fernet.key' --exclude 'secret.key' \
|
||||
--exclude 'known_hosts' --exclude 'license.json' \
|
||||
"$REPO_DIR/srv/tesm/" /srv/tesm/ >>/var/log/tesm-install.log 2>&1
|
||||
print_status "Application files copied"
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
1
|
||||
2
|
||||
+1
-1
@@ -1 +1 @@
|
||||
1.0.9
|
||||
1.2.8
|
||||
+2011
-70
File diff suppressed because it is too large.
Load diff
@@ -176,6 +176,17 @@ CREATE TABLE IF NOT EXISTS ldap_group_mappings (
|
||||
);
|
||||
""")
|
||||
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS ldap_fileshare_mappings (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ad_group_dn TEXT NOT NULL,
|
||||
ad_group_name TEXT NOT NULL,
|
||||
share_label TEXT NOT NULL,
|
||||
share_unc TEXT NOT NULL,
|
||||
UNIQUE(ad_group_dn, share_unc)
|
||||
);
|
||||
""")
|
||||
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS audit_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
@@ -0,0 +1,363 @@
|
||||
"""
|
||||
TESM Lizenzsystem — gemeinsame Kryptographie- und Protokoll-Bausteine für
|
||||
Kundeninstanz (Client) UND Lizenzserver (Master). Beide Seiten importieren
|
||||
exakt dieselbe Datei (auf dem Master 1:1 mitkopiert) — Signieren und
|
||||
Verifizieren müssen bitidentisch funktionieren, jede Abweichung würde
|
||||
Lizenzen der jeweils anderen Seite unlesbar machen.
|
||||
|
||||
Vollständiges Design: siehe C:\\Users\\tim\\.claude\\plans\\toasty-twirling-hickey.md
|
||||
(Phase 0). Kurzfassung der Schlüsselhierarchie:
|
||||
|
||||
- Master-Signaturschlüssel (Ed25519): signiert Lizenzdateien selbst UND
|
||||
jede Aktivierungs-/Deaktivierungs-/Heartbeat-Antwort. Privater Teil bleibt
|
||||
ausschließlich auf dem Master, öffentlicher Teil steckt in jeder
|
||||
ausgestellten Lizenzdatei (ermöglicht rein OFFLINE verifizierbare
|
||||
Master-Antworten beim Kunden).
|
||||
- Pro-Lizenz-Schlüsselpaar (Ed25519, einmalig je Lizenz erzeugt): der
|
||||
private Teil reist in der Lizenzdatei zum Kunden und signiert dessen
|
||||
Aktivierungs-/Deaktivierungs-/Heartbeat-*Anfragen*; der öffentliche Teil
|
||||
bleibt beim Master in dessen Datenbank (ermöglicht dem Master, jede
|
||||
Anfrage einer bestimmten Lizenz zweifelsfrei zuzuordnen, ohne die
|
||||
ursprüngliche Aktivierung "live" gesehen haben zu müssen).
|
||||
|
||||
Ed25519 statt RSA: kleine Schlüssel/Signaturen (wichtig, da Aktivierungs-
|
||||
Anfragen/-Antworten für den Offline-Fall als von Hand kopierbarer Code
|
||||
dargestellt werden), fest vorgegebene, sichere Parameter (keine
|
||||
Padding-/Hash-Wahl wie bei RSA-PSS nötig).
|
||||
"""
|
||||
import base64
|
||||
import datetime
|
||||
import hashlib
|
||||
import json
|
||||
import socket
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from cryptography.exceptions import InvalidSignature
|
||||
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey, Ed25519PublicKey
|
||||
|
||||
LICENSE_TYPES = ("trial", "standard", "custom", "enterprise")
|
||||
ALL_MODULES = ("dhcp", "fileshare", "maintenance")
|
||||
|
||||
# Rolle der lizenzierten Instanz, eingebettet in JEDE ausgestellte
|
||||
# Lizenzdatei (signiert, siehe issue_license) -- NICHT nur DB-seitige
|
||||
# Buchfuehrung wie das gleichnamige, bislang ungenutzte licenses.role-Feld
|
||||
# in der Master-DB vorher. Ermoeglicht einer lizenzierten Instanz, anhand
|
||||
# ihrer EIGENEN, geladenen Lizenz strukturell zu erkennen, was sie darf:
|
||||
# "master" -- die Wurzel-Instanz. Wird AUSSCHLIESSLICH lokal per
|
||||
# create_master_license.py erzeugt, NIE ueber die
|
||||
# normale Ticket-/Ausstellungs-UI -- das waere sonst
|
||||
# eine Moeglichkeit, sich selbst zur Wurzel zu
|
||||
# erklaeren. Einzige Rolle, die weitere
|
||||
# "license_server"-Lizenzen ausstellen darf.
|
||||
# "license_server" -- ein vom Master (oder einer anderen "master"-Instanz)
|
||||
# ausgestellter Lizenzserver ("Sub-Lizenzserver").
|
||||
# Darf alles, was ein Lizenzserver koennen soll
|
||||
# (Kunden/Tickets/Lizenzen fuer eigene TESM-Kunden
|
||||
# ausstellen, Benutzer/Gruppen/Einstellungen
|
||||
# verwalten) -- AUSSER selbst weitere
|
||||
# "license_server"-Lizenzen ausstellen. Diese Sperre
|
||||
# ist strukturell (haengt an der eigenen Rolle, nicht
|
||||
# an einem vergebbaren Recht) und daher auch von einem
|
||||
# lokalen Admin dieser Instanz nicht aufhebbar.
|
||||
# "customer" -- eine gewoehnliche TESM-Kundeninstanz (Default,
|
||||
# unveraendertes Verhalten).
|
||||
LICENSE_ROLES = ("master", "license_server", "customer")
|
||||
|
||||
GRACE_PERIOD_DAYS = 30 # nach Ablauf, bevor lizenzpflichtige Funktionen tatsächlich abgeschaltet werden
|
||||
EXPIRY_WARNING_DAYS = 30 # Vorlauf für die orange "läuft bald ab"-Anzeige
|
||||
HEARTBEAT_WARNING_DAYS = 14 # Nichterreichbarkeits-Hinweis (rein informativ, schaltet nie etwas ab)
|
||||
|
||||
|
||||
# ============================================================== Schlüssel ==
|
||||
|
||||
def generate_keypair():
|
||||
"""Erzeugt ein neues Ed25519-Schlüsselpaar. Rückgabe: (private_b64, public_b64)."""
|
||||
priv = Ed25519PrivateKey.generate()
|
||||
return _encode_private_key(priv), _encode_public_key(priv.public_key())
|
||||
|
||||
|
||||
def _encode_private_key(priv: Ed25519PrivateKey) -> str:
|
||||
return base64.urlsafe_b64encode(priv.private_bytes_raw()).decode("ascii")
|
||||
|
||||
|
||||
def _encode_public_key(pub: Ed25519PublicKey) -> str:
|
||||
return base64.urlsafe_b64encode(pub.public_bytes_raw()).decode("ascii")
|
||||
|
||||
|
||||
def _decode_private_key(s: str) -> Ed25519PrivateKey:
|
||||
return Ed25519PrivateKey.from_private_bytes(base64.urlsafe_b64decode(s.encode("ascii")))
|
||||
|
||||
|
||||
def _decode_public_key(s: str) -> Ed25519PublicKey:
|
||||
return Ed25519PublicKey.from_public_bytes(base64.urlsafe_b64decode(s.encode("ascii")))
|
||||
|
||||
|
||||
# ===================================================== Signieren/Prüfen ==
|
||||
|
||||
def _canonical_bytes(payload: dict) -> bytes:
|
||||
"""Deterministische JSON-Kodierung (sortierte Keys, kompakte Trenner) --
|
||||
Voraussetzung dafür, dass Signieren und Verifizieren exakt dieselben
|
||||
Bytes sehen, unabhängig von der Dict-Einfügereihenfolge."""
|
||||
return json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
||||
|
||||
|
||||
def sign_payload(payload: dict, private_key_b64: str) -> str:
|
||||
priv = _decode_private_key(private_key_b64)
|
||||
return base64.urlsafe_b64encode(priv.sign(_canonical_bytes(payload))).decode("ascii")
|
||||
|
||||
|
||||
def verify_payload(payload: dict, signature_b64: str, public_key_b64: str) -> bool:
|
||||
"""Gibt bewusst nur True/False zurück (nie eine Exception nach außen) --
|
||||
ein Aufrufer soll "ungültig" nie mit einem Absturz verwechseln können."""
|
||||
try:
|
||||
pub = _decode_public_key(public_key_b64)
|
||||
pub.verify(base64.urlsafe_b64decode(signature_b64.encode("ascii")), _canonical_bytes(payload))
|
||||
return True
|
||||
except (InvalidSignature, ValueError, TypeError, KeyError):
|
||||
return False
|
||||
|
||||
|
||||
# ============================================================ Zeitformat ==
|
||||
|
||||
def _iso(ts: float) -> str:
|
||||
return datetime.datetime.fromtimestamp(ts, tz=datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
|
||||
def _parse_iso(s: str) -> float:
|
||||
return datetime.datetime.strptime(s, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=datetime.timezone.utc).timestamp()
|
||||
|
||||
|
||||
# Sentinel-Ablaufdatum fuer "Lifetime"-Lizenzen. Bewusst ein fixes, weit in
|
||||
# der Zukunft liegendes Datum statt eines Sonderwerts wie None/0 fuer
|
||||
# expires_at -- dadurch bleibt JEDE bestehende Datumsvergleichs-/
|
||||
# Differenzrechnung (hier, im Master-Dashboard, UND im TESM-Client) ohne
|
||||
# jede Sonderbehandlung korrekt: "in > 90 Tagen faellig" o.ae. ist fuer ein
|
||||
# Datum im Jahr 2099 schlicht immer wahr. Ob eine Lizenz als "Lifetime"
|
||||
# geflaggt ist, wird zusaetzlich explizit in der Master-DB gespeichert
|
||||
# (licenses.lifetime) -- dieses Sentinel-Datum ist nur die tatsaechlich in
|
||||
# die signierte Lizenzdatei eingebettete, fuer den Client sichtbare Reprae-
|
||||
# sentation davon.
|
||||
LIFETIME_EXPIRES_AT = "2099-12-31T00:00:00Z"
|
||||
|
||||
|
||||
# ================================================================ Lizenz ==
|
||||
|
||||
def issue_license(*, customer, license_type, modules, valid_days,
|
||||
master_private_key_b64, master_public_key_b64,
|
||||
master_endpoint, vendor, license_id=None, now=None, lifetime=False,
|
||||
role="customer"):
|
||||
"""Vom MASTER aufgerufen: erzeugt eine neue, signierte Lizenz samt
|
||||
frischem Pro-Lizenz-Schlüsselpaar.
|
||||
|
||||
lifetime=True ignoriert valid_days komplett und setzt stattdessen das
|
||||
feste Sentinel-Datum LIFETIME_EXPIRES_AT -- siehe dortigen Kommentar.
|
||||
|
||||
role (siehe LICENSE_ROLES): "customer" (Default, unverändertes
|
||||
Verhalten) für gewöhnliche TESM-Kundeninstanzen, "license_server" für
|
||||
einen vom Aufrufer ausgestellten Sub-Lizenzserver. role="master" darf
|
||||
NUR von create_master_license.py verwendet werden (lokal, ohne diese
|
||||
Funktion über die normale Ticket-UI aufzurufen) -- diese Funktion prüft
|
||||
das nicht selbst, das Verbot ist stattdessen dadurch durchgesetzt, dass
|
||||
die App-seitige Ausstellungsfunktion role="master" gar nicht erst als
|
||||
Option anbietet (siehe app.py can_issue_license_server_licenses()).
|
||||
|
||||
Rückgabe: (license_file, license_pubkey) -- license_file ist die
|
||||
komplette, an den Kunden auszuhändigende Datei (inkl. dem PRIVATEN
|
||||
Lizenzschlüssel); license_pubkey ist NUR für die Master-Datenbank
|
||||
bestimmt (Verifikation künftiger Aktivierungs-/Heartbeat-Anfragen
|
||||
dieser Lizenz) und wird nicht an den Kunden weitergegeben."""
|
||||
if license_type not in LICENSE_TYPES:
|
||||
raise ValueError(f"Unbekannter Lizenztyp: {license_type!r} (erlaubt: {LICENSE_TYPES})")
|
||||
if role not in LICENSE_ROLES:
|
||||
raise ValueError(f"Unbekannte Rolle: {role!r} (erlaubt: {LICENSE_ROLES})")
|
||||
unknown = set(modules) - set(ALL_MODULES)
|
||||
if unknown:
|
||||
raise ValueError(f"Unbekannte Module: {sorted(unknown)} (erlaubt: {ALL_MODULES})")
|
||||
|
||||
license_priv, license_pub = generate_keypair()
|
||||
now = now if now is not None else time.time()
|
||||
payload = {
|
||||
"license_id": license_id or str(uuid.uuid4()),
|
||||
"customer": customer,
|
||||
"type": license_type,
|
||||
"modules": sorted(modules),
|
||||
"issued_at": _iso(now),
|
||||
"expires_at": LIFETIME_EXPIRES_AT if lifetime else _iso(now + valid_days * 86400),
|
||||
"license_pubkey": license_pub,
|
||||
"master_pubkey": master_public_key_b64,
|
||||
"master_endpoint": master_endpoint,
|
||||
"vendor": vendor,
|
||||
"role": role,
|
||||
}
|
||||
signature = sign_payload(payload, master_private_key_b64)
|
||||
license_file = {**payload, "license_privkey": license_priv, "signature": signature}
|
||||
return license_file, license_pub
|
||||
|
||||
|
||||
def verify_license_file(license_file: dict) -> bool:
|
||||
"""Prüft die Master-Signatur über die Lizenz-Nutzdaten. license_privkey
|
||||
und signature selbst sind nicht Teil der signierten Nutzlast (die
|
||||
Signatur wurde ja gerade über den Rest gebildet, siehe issue_license)."""
|
||||
payload = {k: v for k, v in license_file.items() if k not in ("license_privkey", "signature")}
|
||||
return verify_payload(payload, license_file.get("signature", ""), license_file.get("master_pubkey", ""))
|
||||
|
||||
|
||||
def license_status(license_file: dict, now=None) -> dict:
|
||||
"""Berechnet den aktuellen Anzeige-/Gate-Status einer (bereits als
|
||||
signaturgültig geprüften!) Lizenz -- ruft NICHT selbst verify_license_file
|
||||
auf, das bleibt bewusst Sache des Aufrufers, damit hier niemand versehentlich
|
||||
den Status einer manipulierten Datei berechnet, ohne die Prüfung
|
||||
überhaupt gemacht zu haben."""
|
||||
now = now if now is not None else time.time()
|
||||
expires_at = _parse_iso(license_file["expires_at"])
|
||||
days_left = (expires_at - now) / 86400
|
||||
expired = days_left < 0
|
||||
days_since_expiry = -days_left if expired else 0.0
|
||||
grace_active = expired and days_since_expiry <= GRACE_PERIOD_DAYS
|
||||
modules_active = (not expired) or grace_active
|
||||
|
||||
return {
|
||||
"expired": expired,
|
||||
"days_left": days_left,
|
||||
"days_since_expiry": days_since_expiry,
|
||||
"expiring_soon": (not expired) and days_left <= EXPIRY_WARNING_DAYS,
|
||||
"grace_active": grace_active,
|
||||
"modules_active": modules_active,
|
||||
"modules": set(license_file.get("modules", [])) if modules_active else set(),
|
||||
"type": license_file.get("type"),
|
||||
"customer": license_file.get("customer"),
|
||||
"expires_at": license_file.get("expires_at"),
|
||||
# .get(..., "customer"): aeltere, vor Einfuehrung von role signierte
|
||||
# Lizenzdateien haben dieses Feld noch nicht -- fehlend bedeutet
|
||||
# unveraendert "gewoehnliche Kundeninstanz", nie "master"/
|
||||
# "license_server" (sonst koennte eine alte Datei rueckwirkend
|
||||
# unbeabsichtigt privilegiert erscheinen).
|
||||
"role": license_file.get("role", "customer"),
|
||||
}
|
||||
|
||||
|
||||
def heartbeat_stale_warning(last_heartbeat_ts, now=None) -> bool:
|
||||
"""True, wenn der letzte erfolgreiche Heartbeat HEARTBEAT_WARNING_DAYS
|
||||
oder länger zurückliegt. Rein informativ (siehe Moduldocstring) -- ein
|
||||
fehlender/alter Heartbeat schaltet nie ein Modul ab, nur das
|
||||
eingebettete Ablaufdatum selbst zählt dafür (siehe license_status)."""
|
||||
if last_heartbeat_ts is None:
|
||||
return False # noch nie verbunden gewesen ist der normale Ausgangszustand, kein Ausfall
|
||||
now = now if now is not None else time.time()
|
||||
return (now - last_heartbeat_ts) / 86400 >= HEARTBEAT_WARNING_DAYS
|
||||
|
||||
|
||||
# ========================================== Fingerprint (System-Bindung) ==
|
||||
|
||||
def system_fingerprint():
|
||||
"""Stabiler Identifikator dieses Hosts: /etc/machine-id (von systemd bei
|
||||
der Erstinstallation einmalig erzeugt, übersteht Reboots und normale
|
||||
Updates) kombiniert mit dem Hostnamen, gehasht damit die rohe
|
||||
machine-id nie im Klartext übertragen/gespeichert wird. Unter
|
||||
Windows/ohne /etc/machine-id fällt dies auf den Hostnamen allein
|
||||
zurück (nur für lokale Tests relevant, Produktivsysteme sind Linux)."""
|
||||
machine_id = ""
|
||||
try:
|
||||
with open("/etc/machine-id", "r", encoding="utf-8") as f:
|
||||
machine_id = f.read().strip()
|
||||
except OSError:
|
||||
pass
|
||||
raw = f"{machine_id}:{socket.gethostname()}".encode("utf-8")
|
||||
return hashlib.sha256(raw).hexdigest()
|
||||
|
||||
|
||||
# ============================ Aktivierung / Deaktivierung / Heartbeat =====
|
||||
# Ein Protokoll, zwei Transportwege: online automatisch per HTTPS-API,
|
||||
# offline exakt dasselbe Anfrage/Antwort-Paar als von Hand kopierbarer Code
|
||||
# (encode_code/decode_code) -- der Master ist dadurch in BEIDEN Fällen die
|
||||
# einzige Quelle der Wahrheit dafür, an welches System eine Lizenz gerade
|
||||
# gebunden ist, siehe Plan-Abschnitt "Warum das die Doppelnutzung wirksam
|
||||
# verhindert".
|
||||
|
||||
def build_client_request(action, license_file, nonce=None, report=None) -> dict:
|
||||
"""Vom CLIENT aufgerufen: erzeugt eine signierte Anfrage (action:
|
||||
"activate"/"deactivate"/"heartbeat"). Das Ergebnis ist unverändert für
|
||||
einen Online-API-Aufruf nutzbar; für den Offline-Fall wird es
|
||||
zusätzlich mit encode_code() in einen kopierbaren Code umgewandelt.
|
||||
|
||||
report: NUR für Heartbeats einer role="license_server"-Lizenz gedacht
|
||||
(siehe Lizenzserver-app.py _build_own_issuance_report()) -- ein
|
||||
optionaler Schnappschuss der eigenen Kunden/Tickets/Lizenzen dieses
|
||||
Sub-Lizenzservers, den der Master beim Empfang in seine
|
||||
reported_sub_*-Tabellen übernimmt, um einen Gesamtüberblick zu haben.
|
||||
Bewusst Teil DIESER generischen, gemeinsam genutzten Funktion (nicht
|
||||
ein eigenes Protokoll-Feature) -- ein normaler TESM-Client/eine
|
||||
"customer"-Lizenz übergibt hier einfach nie einen Wert, das Feld fehlt
|
||||
dann in der Payload wie bisher (keine Verhaltensänderung für alle
|
||||
bestehenden Aufrufer). Wie jedes andere Feld wird auch report
|
||||
mitsigniert -- ein Angreifer ohne den privaten Lizenzschlüssel kann
|
||||
keine gefälschten Berichte unterschieben."""
|
||||
if action not in ("activate", "deactivate", "heartbeat"):
|
||||
raise ValueError(f"Unbekannte Aktion: {action!r}")
|
||||
payload = {
|
||||
"license_id": license_file["license_id"],
|
||||
"fingerprint": system_fingerprint(),
|
||||
# Rein informativ fuer die Dashboard-Kacheln des Masters (siehe
|
||||
# dortiges index()) -- fliesst NICHT in irgendeine Sicherheits-
|
||||
# entscheidung ein (die bleibt allein Sache des Fingerprints), wird
|
||||
# aber wie alle anderen Felder mitsigniert, damit ein Angreifer
|
||||
# ohne den privaten Lizenzschluessel keinen falschen Hostnamen
|
||||
# unterschieben kann.
|
||||
"hostname": socket.gethostname(),
|
||||
"action": action,
|
||||
"nonce": nonce or uuid.uuid4().hex,
|
||||
"timestamp": _iso(time.time()),
|
||||
}
|
||||
if report is not None:
|
||||
payload["report"] = report
|
||||
signature = sign_payload(payload, license_file["license_privkey"])
|
||||
return {**payload, "signature": signature}
|
||||
|
||||
|
||||
def verify_client_request(request: dict, license_public_key_b64: str) -> bool:
|
||||
"""Vom MASTER aufgerufen: prüft eine Client-Anfrage gegen den bei
|
||||
Ausstellung dieser Lizenz in der Master-DB gespeicherten Public-Key."""
|
||||
payload = {k: v for k, v in request.items() if k != "signature"}
|
||||
return verify_payload(payload, request.get("signature", ""), license_public_key_b64)
|
||||
|
||||
|
||||
def build_master_response(action, license_id, fingerprint, master_private_key_b64,
|
||||
status="ok", license_update=None, now=None) -> dict:
|
||||
"""Vom MASTER aufgerufen: erzeugt eine signierte Antwort/Bestätigung
|
||||
(z.B. "activated", "deactivated", oder das Ergebnis eines Heartbeats).
|
||||
license_update: optional eine komplette, neu signierte Lizenzdatei
|
||||
(z.B. nach nachträglicher Modul-Änderung durch den Admin) -- der Client
|
||||
übernimmt sie nur nach eigener, erfolgreicher Signaturprüfung."""
|
||||
payload = {
|
||||
"license_id": license_id,
|
||||
"fingerprint": fingerprint,
|
||||
"action": action,
|
||||
"status": status,
|
||||
"timestamp": _iso(now if now is not None else time.time()),
|
||||
}
|
||||
if license_update is not None:
|
||||
payload["license_update"] = license_update
|
||||
signature = sign_payload(payload, master_private_key_b64)
|
||||
return {**payload, "signature": signature}
|
||||
|
||||
|
||||
def verify_master_response(response: dict, master_public_key_b64: str) -> bool:
|
||||
"""Vom CLIENT aufgerufen: prüft eine Master-Antwort komplett OFFLINE
|
||||
gegen den in der eigenen Lizenzdatei eingebetteten master_pubkey."""
|
||||
payload = {k: v for k, v in response.items() if k != "signature"}
|
||||
return verify_payload(payload, response.get("signature", ""), master_public_key_b64)
|
||||
|
||||
|
||||
# ==================================================== Codes für Offline ==
|
||||
|
||||
def encode_code(data: dict) -> str:
|
||||
"""Kodiert ein Anfrage-/Antwort-dict als kompakten, per Copy-Paste
|
||||
übertragbaren Code (Base64 einer kanonischen JSON-Darstellung, keine
|
||||
Zeilenumbrüche/Sonderzeichen, damit Kopieren aus/in ein Textfeld nichts
|
||||
kaputt macht)."""
|
||||
return base64.urlsafe_b64encode(_canonical_bytes(data)).decode("ascii")
|
||||
|
||||
|
||||
def decode_code(code: str) -> dict:
|
||||
return json.loads(base64.urlsafe_b64decode(code.strip().encode("ascii")).decode("utf-8"))
|
||||
@@ -446,6 +446,18 @@ button { font-family: inherit; }
|
||||
|
||||
.card-pad { padding: 20px 22px; }
|
||||
|
||||
.notice-banner {
|
||||
padding: 12px 16px;
|
||||
border-radius: var(--radius);
|
||||
font-size: 12.5px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.notice-banner--warning {
|
||||
background: var(--warning-dim);
|
||||
color: var(--warning);
|
||||
border: 1px solid var(--warning);
|
||||
}
|
||||
|
||||
.section-head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
@@ -498,6 +510,7 @@ button { font-family: inherit; }
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 650;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.pill svg { width: 12px; height: 12px; }
|
||||
.pill::before {
|
||||
@@ -516,6 +529,11 @@ button { font-family: inherit; }
|
||||
.pill.action-pill { background: var(--muted-dim); color: var(--text-dim); }
|
||||
.pill.action-pill::before { display: none; }
|
||||
.pill.action-pill svg { width: 13px; height: 13px; }
|
||||
/* Auditlog: Hinzufuegen gruen, Loeschen rot, alles andere (Bearbeiten/
|
||||
Aktivieren/Zuweisen/...) orange -- siehe activity_log.html */
|
||||
.pill.action-pill--create { background: var(--success-dim); color: var(--success); }
|
||||
.pill.action-pill--delete { background: var(--danger-dim); color: var(--danger); }
|
||||
.pill.action-pill--edit { background: var(--accent-dim); color: var(--accent-strong); }
|
||||
|
||||
.avatar-sm {
|
||||
display: inline-flex;
|
||||
@@ -556,6 +574,22 @@ button { font-family: inherit; }
|
||||
70% { box-shadow: 0 0 0 6px rgba(47,208,122,0); }
|
||||
100% { box-shadow: 0 0 0 0 rgba(47,208,122,0); }
|
||||
}
|
||||
/* Lizenz-Topbar-Pill: pulse-dot ist fest auf Grün (Erfolg) verdrahtet --
|
||||
eigene Varianten für die rot/orange blinkenden Lizenz-Zustände, sonst
|
||||
würde die Halo-Animation weiter grün pulsieren, egal welche
|
||||
Hintergrundfarbe der Punkt selbst per Inline-Style bekommt. */
|
||||
.timer-pill .dot.dot--blink-danger { animation: pulse-dot-danger 1.4s infinite; }
|
||||
.timer-pill .dot.dot--blink-warning { animation: pulse-dot-warning 1.4s infinite; }
|
||||
@keyframes pulse-dot-danger {
|
||||
0% { box-shadow: 0 0 0 0 rgba(240,71,92,0.5); }
|
||||
70% { box-shadow: 0 0 0 6px rgba(240,71,92,0); }
|
||||
100% { box-shadow: 0 0 0 0 rgba(240,71,92,0); }
|
||||
}
|
||||
@keyframes pulse-dot-warning {
|
||||
0% { box-shadow: 0 0 0 0 rgba(245,166,35,0.5); }
|
||||
70% { box-shadow: 0 0 0 6px rgba(245,166,35,0); }
|
||||
100% { box-shadow: 0 0 0 0 rgba(245,166,35,0); }
|
||||
}
|
||||
|
||||
.timer-pill-refresh {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
@@ -693,14 +727,17 @@ button { font-family: inherit; }
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-soft);
|
||||
border-radius: var(--radius);
|
||||
/* overflow-x:auto statt hidden -- Tabellen mit vielen Spalten (Geräte,
|
||||
Switche, Papierkorb, ...) würden auf Handy/Tablet sonst entweder die
|
||||
ganze Seite waagerecht aufreißen oder (mit overflow:hidden) Spalten
|
||||
einfach lautlos abschneiden. Scrollt jetzt NUR die Tabelle selbst,
|
||||
der Rest der Seite bleibt an Ort und Stelle -- dasselbe Muster wie
|
||||
Bootstraps .table-responsive. */
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
/* Zurück auf overflow:hidden (nur für die abgerundeten Ecken) --
|
||||
JEDES Template, das .table-wrap verwendet, hat bereits einen eigenen
|
||||
<div style="overflow-x:auto;"> exakt um die <table> herum (nicht um
|
||||
die ganze Karte). overflow-x:auto zusätzlich HIER hätte einen zweiten,
|
||||
verschachtelten Scroll-Container um denselben Inhalt erzeugt -- live
|
||||
auf einem iPhone-Viewport nachgewiesen: bei der aufklappbaren
|
||||
Rechte-Tabelle (Gruppen-Seite) landete der Inhalt dadurch dauerhaft
|
||||
unsichtbar bei negativem x. Die Tabellen scrollen also weiterhin
|
||||
horizontal, nur über den bereits vorhandenen inneren Wrapper, nicht
|
||||
zusätzlich über die Karte selbst. */
|
||||
overflow: hidden;
|
||||
}
|
||||
.table-wrap table.data-table { min-width: 560px; }
|
||||
|
||||
@@ -810,6 +847,12 @@ table.data-table {
|
||||
========================================================================== */
|
||||
|
||||
.field { margin-bottom: 15px; }
|
||||
/* Zwei .field nebeneinander in einer .flex-Zeile (z.B. HTTP-/HTTPS-Port
|
||||
bei NGINX) -- als Klasse statt inline style="flex:1", damit die
|
||||
Mobile-Stapel-Regel (siehe @media max-width:640px weiter unten) sie
|
||||
per externem Stylesheet überschreiben kann; ein inline style gewinnt
|
||||
sonst grundsätzlich gegen jede @media-Regel, ganz gleich wie spezifisch. */
|
||||
.field--half { flex: 1; min-width: 0; }
|
||||
.field label {
|
||||
display: block;
|
||||
font-size: 12.5px;
|
||||
@@ -941,6 +984,48 @@ select {
|
||||
flex-direction: column;
|
||||
animation: modal-in 0.16s ease;
|
||||
}
|
||||
|
||||
/* ---- Hinweis-Icons (Hover-Tooltip + Klick-Modal), siehe _hint_icon.html
|
||||
und initHintIcons() in app.js ---- */
|
||||
.hint-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
flex: 0 0 auto;
|
||||
border-radius: 50%;
|
||||
border: 1.5px solid var(--text-faint);
|
||||
background: transparent;
|
||||
color: var(--text-faint);
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
font-style: italic;
|
||||
font-family: Georgia, "Times New Roman", serif;
|
||||
line-height: 1;
|
||||
cursor: help;
|
||||
vertical-align: middle;
|
||||
margin-left: 6px;
|
||||
padding: 0;
|
||||
transition: border-color 0.15s, color 0.15s;
|
||||
}
|
||||
.hint-icon:hover, .hint-icon:focus-visible { border-color: var(--accent); color: var(--accent); }
|
||||
|
||||
#hint-tooltip {
|
||||
position: fixed;
|
||||
z-index: 10000;
|
||||
max-width: 280px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 8px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
background: var(--bg-elevated);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: var(--shadow);
|
||||
pointer-events: none;
|
||||
display: none;
|
||||
}
|
||||
@keyframes modal-in {
|
||||
from { opacity: 0; transform: translateY(8px) scale(0.98); }
|
||||
to { opacity: 1; transform: translateY(0) scale(1); }
|
||||
@@ -1137,6 +1222,31 @@ select {
|
||||
}
|
||||
[data-theme="light"] .raw-log-content { background: #0e1116; }
|
||||
|
||||
/* Zeilennummern im RAW-Log-Popup (aktuell nur "Verlauf") -- Nummer kommt
|
||||
rein aus CSS-Countern (kein Extra-Markup pro Zeile mit fest eingebrannter
|
||||
Zahl), damit Filtern/Kopieren des reinen Logtexts unverändert bleibt. */
|
||||
.raw-log-content.with-line-numbers {
|
||||
counter-reset: raw-line;
|
||||
white-space: normal;
|
||||
}
|
||||
.raw-log-content.with-line-numbers .raw-log-line {
|
||||
display: flex;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
.raw-log-content.with-line-numbers .raw-log-line::before {
|
||||
counter-increment: raw-line;
|
||||
content: counter(raw-line);
|
||||
flex: 0 0 auto;
|
||||
min-width: 3.5em;
|
||||
margin-right: 14px;
|
||||
padding-right: 10px;
|
||||
border-right: 1px solid var(--border);
|
||||
text-align: right;
|
||||
color: var(--text-faint);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Permission checklists (Gruppen)
|
||||
========================================================================== */
|
||||
@@ -1283,6 +1393,92 @@ select {
|
||||
}
|
||||
.xterm-container .xterm { height: 100%; }
|
||||
|
||||
/* ==========================================================================
|
||||
Fileshare (Baum-Navigation + Vorschau)
|
||||
========================================================================== */
|
||||
|
||||
/* align-items:stretch (statt flex-start) + die main-Seite selbst als
|
||||
Flex-Spalte mit table-wrap{flex:1}, damit beide Kacheln (Baum links,
|
||||
Tabelle rechts) immer gleich hoch sind, unabhängig davon welche Seite
|
||||
gerade mehr Inhalt hat. */
|
||||
.fileshare-layout { display: flex; align-items: stretch; gap: 16px; }
|
||||
.fileshare-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
|
||||
.fileshare-main .table-wrap { flex: 1; }
|
||||
|
||||
.fileshare-tree {
|
||||
flex: 0 0 260px;
|
||||
max-width: 260px;
|
||||
padding: 14px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.fileshare-tree-title {
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--text-faint);
|
||||
font-weight: 650;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tree-root, .tree-children { list-style: none; margin: 0; padding: 0; }
|
||||
.tree-children { padding-left: 16px; }
|
||||
|
||||
.tree-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
padding: 4px 6px;
|
||||
border-radius: 7px;
|
||||
}
|
||||
.tree-row:hover { background: var(--bg-card-hover); }
|
||||
.tree-row.active { background: var(--bg-card-hover); color: var(--accent); font-weight: 600; }
|
||||
|
||||
.tree-toggle {
|
||||
width: 18px; height: 18px;
|
||||
flex-shrink: 0;
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
border: none; background: transparent; color: var(--text-faint);
|
||||
font-size: 10px;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
}
|
||||
.tree-toggle:hover { color: var(--text); }
|
||||
.tree-toggle:disabled { visibility: hidden; }
|
||||
|
||||
.tree-label {
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* Vorschau: von mammoth.js/SheetJS erzeugter bzw. selbst gebauter Inhalt */
|
||||
.docx-preview { font-size: 14px; line-height: 1.65; }
|
||||
.docx-preview table { border-collapse: collapse; margin: 10px 0; }
|
||||
.docx-preview table td, .docx-preview table th { border: 1px solid var(--border); padding: 6px 10px; }
|
||||
.docx-preview img { max-width: 100%; }
|
||||
.xlsx-preview-sheet-title { margin: 20px 0 8px; font-size: 13px; font-weight: 650; }
|
||||
.xlsx-preview-sheet-title:first-child { margin-top: 0; }
|
||||
|
||||
/* Angesammelte Dateien im Upload-Modal (Mehrfachauswahl, siehe fileshare.html) */
|
||||
.upload-file-list { margin-top: 8px; display: flex; flex-direction: column; gap: 4px; }
|
||||
.upload-file-row {
|
||||
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
||||
padding: 5px 10px;
|
||||
background: var(--bg-elevated);
|
||||
border: 1px solid var(--border-soft);
|
||||
border-radius: 7px;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
.upload-file-row span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.upload-file-remove {
|
||||
flex-shrink: 0;
|
||||
border: none; background: transparent; color: var(--text-faint);
|
||||
font-size: 16px; line-height: 1; cursor: pointer; padding: 0 2px;
|
||||
}
|
||||
.upload-file-remove:hover { color: var(--danger); }
|
||||
|
||||
/* ==========================================================================
|
||||
Utilities
|
||||
========================================================================== */
|
||||
@@ -1327,6 +1523,12 @@ select {
|
||||
Buttons/Suchfeld ineinanderzuschieben. */
|
||||
.modal-footer { flex-wrap: wrap; }
|
||||
.table-toolbar .search-input { min-width: 0; flex: 1 1 160px; }
|
||||
|
||||
/* Fileshare-Baum + Tabelle nebeneinander sprengt auf Tablet-/Handy-
|
||||
Breite die Seite (Baum-Spalte ist fest 260px breit) -- Baum stapelt
|
||||
stattdessen oben, Tabelle darunter in voller Breite. */
|
||||
.fileshare-layout { flex-direction: column; }
|
||||
.fileshare-tree { flex: 1 1 auto; max-width: 100%; max-height: 240px; }
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
@@ -1351,4 +1553,16 @@ select {
|
||||
Breite ein statt als schmaler Block rechts zu kleben. */
|
||||
.section-head > .btn,
|
||||
.section-head > .btn-sm { flex: 1 1 100%; }
|
||||
|
||||
/* Auf einem iPhone quetschen die beiden rechten Topbar-Pillen (Prüf-
|
||||
Countdown + DHCP) .topbar-left (flex:1 1 0%, min-width:0) so weit
|
||||
zusammen, dass der Seitentitel über seine eigene Spaltenbreite
|
||||
hinausläuft und sichtbar mit der Pille kollidiert (per Playwright-
|
||||
Screenshot auf 393px Breite nachgewiesen: .topbar-title ragte 24.5px
|
||||
über .topbar-lefts berechnete Breite hinaus). Titel und Pillen
|
||||
bekommen ab hier je eine eigene Zeile statt sich einen Platz zu
|
||||
teilen, der für beide zusammen nicht reicht. */
|
||||
.topbar { flex-wrap: wrap; row-gap: 6px; }
|
||||
.topbar-left { flex: 1 1 100%; }
|
||||
.topbar-right { flex: 1 1 100%; justify-content: flex-start; }
|
||||
}
|
||||
+120
-2
@@ -245,12 +245,30 @@
|
||||
});
|
||||
}
|
||||
|
||||
window.openRawLogModal = function (url, title) {
|
||||
function renderRawLogLines(container, text) {
|
||||
// Eine Zeile = ein <div>, Nummer kommt per CSS-Counter (::before) --
|
||||
// so bleibt der eigentliche Zeileninhalt reiner textContent (kein XSS-
|
||||
// Risiko) und die Nummerierung muss nirgends von Hand mitgezählt werden.
|
||||
const lines = text.split("\n");
|
||||
if (lines.length && lines[lines.length - 1] === "") lines.pop(); // trailing \n erzeugt sonst eine Phantomzeile
|
||||
container.textContent = "";
|
||||
const frag = document.createDocumentFragment();
|
||||
lines.forEach((line) => {
|
||||
const row = document.createElement("div");
|
||||
row.className = "raw-log-line";
|
||||
row.textContent = line;
|
||||
frag.appendChild(row);
|
||||
});
|
||||
container.appendChild(frag);
|
||||
}
|
||||
|
||||
window.openRawLogModal = function (url, title, withLineNumbers) {
|
||||
if (!url) return;
|
||||
ensureRawLogModal();
|
||||
document.getElementById("raw-log-modal-title").innerText = title || "Komplettes Log (RAW)";
|
||||
const content = document.getElementById("raw-log-modal-content");
|
||||
const meta = document.getElementById("raw-log-modal-meta");
|
||||
content.classList.toggle("with-line-numbers", !!withLineNumbers);
|
||||
content.textContent = "Lade …";
|
||||
meta.textContent = "";
|
||||
openModal("raw-log-modal");
|
||||
@@ -260,10 +278,109 @@
|
||||
if (logName) meta.textContent = logName;
|
||||
return r.text();
|
||||
})
|
||||
.then((text) => { content.textContent = text; })
|
||||
.then((text) => {
|
||||
if (withLineNumbers) renderRawLogLines(content, text);
|
||||
else content.textContent = text;
|
||||
})
|
||||
.catch(() => { content.textContent = "Fehler beim Laden des Logs."; });
|
||||
};
|
||||
|
||||
/* ---------------- Hinweis-Icons (Hover-Tooltip + Klick-Modal) ---------------- */
|
||||
/* Zentraler Mechanismus für ALLE "i"-Icons (siehe _hint_icon.html) app-weit
|
||||
-- ein einziges delegiertes Set von Listenern statt pro Icon eigener
|
||||
Handler, damit auch nachträglich per JS eingefügte Icons (z.B. in
|
||||
dynamisch nachgeladenen Tabellenzeilen) ohne weiteres Zutun funktionieren.
|
||||
Hover zeigt den Text als Tooltip neben dem Mauszeiger -- aber NUR auf
|
||||
Geräten, die tatsächlich sinnvoll hovern können (matchMedia-Check bei
|
||||
JEDEM mouseenter neu ausgewertet, nicht einmalig beim Laden gecacht,
|
||||
damit z.B. ein Convertible/Tablet beim Umschalten Maus/Touch korrekt
|
||||
reagiert). Auf reinen Touch-Geräten bleibt so nur Tap -> Modal übrig,
|
||||
da dort ohnehin kein echtes mouseenter/mousemove-Hover stattfindet. */
|
||||
function ensureHintTooltip() {
|
||||
if (document.getElementById("hint-tooltip")) return;
|
||||
const el = document.createElement("div");
|
||||
el.id = "hint-tooltip";
|
||||
document.body.appendChild(el);
|
||||
}
|
||||
|
||||
function ensureHintModal() {
|
||||
if (document.getElementById("hint-modal")) return;
|
||||
const html = `
|
||||
<div class="modal-overlay" id="hint-modal">
|
||||
<div class="modal" style="max-width:440px;">
|
||||
<div class="modal-header">
|
||||
<h3 id="hint-modal-title">Hinweis</h3>
|
||||
<button type="button" class="modal-close" data-close-modal>×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p id="hint-modal-body" class="text-dim" style="margin:0; line-height:1.6;"></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-close-modal>Schließen</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
document.body.insertAdjacentHTML("beforeend", html);
|
||||
document.querySelectorAll("#hint-modal [data-close-modal]").forEach((btn) => {
|
||||
btn.addEventListener("click", () => closeModal(btn));
|
||||
});
|
||||
document.getElementById("hint-modal").addEventListener("click", (e) => {
|
||||
if (e.target.id === "hint-modal") closeModal(e.target);
|
||||
});
|
||||
}
|
||||
|
||||
function canHover() {
|
||||
return window.matchMedia && window.matchMedia("(hover: hover) and (pointer: fine)").matches;
|
||||
}
|
||||
|
||||
function initHintIcons() {
|
||||
if (!document.querySelector(".hint-icon")) return;
|
||||
ensureHintTooltip();
|
||||
const tooltip = document.getElementById("hint-tooltip");
|
||||
|
||||
function positionTooltip(e) {
|
||||
const offset = 14;
|
||||
const rect = tooltip.getBoundingClientRect();
|
||||
let x = e.clientX + offset;
|
||||
let y = e.clientY + offset;
|
||||
if (x + rect.width > window.innerWidth - 8) x = e.clientX - rect.width - offset;
|
||||
if (y + rect.height > window.innerHeight - 8) y = e.clientY - rect.height - offset;
|
||||
tooltip.style.left = Math.max(8, x) + "px";
|
||||
tooltip.style.top = Math.max(8, y) + "px";
|
||||
}
|
||||
|
||||
// mouseenter/mouseleave bubbeln nicht -- Delegation via Capture-Phase
|
||||
// auf document funktioniert dafür trotzdem zuverlässig.
|
||||
document.addEventListener("mouseenter", function (e) {
|
||||
const icon = e.target.closest && e.target.closest(".hint-icon");
|
||||
if (!icon || !canHover()) return;
|
||||
tooltip.textContent = icon.dataset.hint || "";
|
||||
tooltip.style.display = "block";
|
||||
positionTooltip(e);
|
||||
}, true);
|
||||
|
||||
document.addEventListener("mouseleave", function (e) {
|
||||
const icon = e.target.closest && e.target.closest(".hint-icon");
|
||||
if (!icon) return;
|
||||
tooltip.style.display = "none";
|
||||
}, true);
|
||||
|
||||
document.addEventListener("mousemove", function (e) {
|
||||
if (tooltip.style.display === "block") positionTooltip(e);
|
||||
});
|
||||
|
||||
document.addEventListener("click", function (e) {
|
||||
const icon = e.target.closest && e.target.closest(".hint-icon");
|
||||
if (!icon) return;
|
||||
e.preventDefault();
|
||||
tooltip.style.display = "none";
|
||||
ensureHintModal();
|
||||
document.getElementById("hint-modal-title").textContent = icon.dataset.hintTitle || "Hinweis";
|
||||
document.getElementById("hint-modal-body").textContent = icon.dataset.hint || "";
|
||||
openModal("hint-modal");
|
||||
});
|
||||
}
|
||||
|
||||
/* ---------------- Warnung bei ungespeicherten Änderungen ---------------- */
|
||||
/* Erkennt generisch auf JEDER Seite, ob ein Formular mit echten
|
||||
Eingabefeldern (nicht nur versteckten Aktions-Feldern wie bei Löschen/
|
||||
@@ -607,6 +724,7 @@
|
||||
initLeaseCountdowns();
|
||||
initUnsavedChangesGuard();
|
||||
initSortableTables();
|
||||
initHintIcons();
|
||||
document.querySelectorAll("[data-theme-toggle]").forEach((btn) => btn.addEventListener("click", toggleTheme));
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
!function(f){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=f();else if("function"==typeof define&&define.amd)define([],f);else{var g;g="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,g.mammoth=f()}}(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof require&&require,o=0;o<r.length;o++)s(r[o]);return s}({1:[function(require,module,exports){function Files(){function read(uri){return promises.reject(new Error("could not open external image: '"+uri+"'\ncannot open linked files from a web browser"))}return{read:read}}var promises=require("../../lib/promises");exports.Files=Files},{"../../lib/promises":23}],2:[function(require,module,exports){function openZip(options){return options.arrayBuffer?promises.resolve(zipfile.openArrayBuffer(options.arrayBuffer)):promises.reject(new Error("Could not find file in options"))}var promises=require("../lib/promises"),zipfile=require("../lib/zipfile");exports.openZip=openZip},{"../lib/promises":23,"../lib/zipfile":40}],3:[function(require,module,exports){function DocumentConverter(options){return{convertToHtml:function(element){var comments=_.indexBy(element.type===documents.types.document?element.comments:[],"commentId"),conversion=new DocumentConversion(options,comments);return conversion.convertToHtml(element)}}}function DocumentConversion(options,comments){function convertToHtml(document){var messages=[],html=elementToHtml(document,messages,{}),deferredNodes=[];walkHtml(html,function(node){"deferred"===node.type&&deferredNodes.push(node)});var deferredValues={};return promises.mapSeries(deferredNodes,function(deferred){return deferred.value().then(function(value){deferredValues[deferred.id]=value})}).then(function(){function replaceDeferred(nodes){return flatMap(nodes,function(node){return"deferred"===node.type?deferredValues[node.id]:node.children?[_.extend({},node,{children:replaceDeferred(node.children)})]:[node]})}var writer=writers.writer({prettyPrint:options.prettyPrint,outputFormat:options.outputFormat});return Html.write(writer,Html.simplify(replaceDeferred(html))),new results.Result(writer.asString(),messages)})}function convertElements(elements,messages,options){return flatMap(elements,function(element){return elementToHtml(element,messages,options)})}function elementToHtml(element,messages,options){if(!options)throw new Error("options not set");var handler=elementConverters[element.type];return handler?handler(element,messages,options):[]}function convertParagraph(element,messages,options){return htmlPathForParagraph(element,messages).wrap(function(){var content=convertElements(element.children,messages,options);return ignoreEmptyParagraphs?content:[Html.forceWrite].concat(content)})}function htmlPathForParagraph(element,messages){var style=findStyle(element);return style?style.to:(element.styleId&&messages.push(unrecognisedStyleWarning("paragraph",element)),defaultParagraphStyle)}function convertRun(run,messages,options){var nodes=function(){return convertElements(run.children,messages,options)},paths=[];if(null!==run.highlight){var path=findHtmlPath({type:"highlight",color:run.highlight});path&&paths.push(path)}run.isSmallCaps&&paths.push(findHtmlPathForRunProperty("smallCaps")),run.isAllCaps&&paths.push(findHtmlPathForRunProperty("allCaps")),run.isStrikethrough&&paths.push(findHtmlPathForRunProperty("strikethrough","s")),run.isUnderline&&paths.push(findHtmlPathForRunProperty("underline")),run.verticalAlignment===documents.verticalAlignment.subscript&&paths.push(htmlPaths.element("sub",{},{fresh:!1})),run.verticalAlignment===documents.verticalAlignment.superscript&&paths.push(htmlPaths.element("sup",{},{fresh:!1})),run.isItalic&&paths.push(findHtmlPathForRunProperty("italic","em")),run.isBold&&paths.push(findHtmlPathForRunProperty("bold","strong"));var stylePath=htmlPaths.empty,style=findStyle(run);return style?stylePath=style.to:run.styleId&&messages.push(unrecognisedStyleWarning("run",run)),paths.push(stylePath),paths.forEach(function(path){nodes=path.wrap.bind(path,nodes)}),nodes()}function findHtmlPathForRunProperty(elementType,defaultTagName){var path=findHtmlPath({type:elementType});return path?path:defaultTagName?htmlPaths.element(defaultTagName,{},{fresh:!1}):htmlPaths.empty}function findHtmlPath(element,defaultPath){var style=findStyle(element);return style?style.to:defaultPath}function findStyle(element){for(var i=0;i<styleMap.length;i++)if(styleMap[i].from.matches(element))return styleMap[i]}function recoveringConvertImage(convertImage){return function(image,messages){return promises.attempt(functionLine truncated
|
||||
return new ReadResult(func(first.value,second.value),joinElements(first.extra,second.extra),first.messages.concat(second.messages))}},{"../documents":4,"../results":25,"./uris":16,"dingbat-to-unicode":85,underscore:102}],6:[function(require,module,exports){function createCommentsReader(bodyReader){function readCommentsXml(element){return Result.combine(element.getElementsByTagName("w:comment").map(readCommentElement))}function readCommentElement(element){function readOptionalAttribute(name){return(element.attributes[name]||"").trim()||null}var id=element.attributes["w:id"];return bodyReader.readXmlElements(element.children).map(function(body){return documents.comment({commentId:id,body:body,authorName:readOptionalAttribute("w:author"),authorInitials:readOptionalAttribute("w:initials")})})}return readCommentsXml}var documents=require("../documents"),Result=require("../results").Result;exports.createCommentsReader=createCommentsReader},{"../documents":4,"../results":25}],7:[function(require,module,exports){function readContentTypesFromXml(element){var extensionDefaults={},overrides={};return element.children.forEach(function(child){if("content-types:Default"===child.name&&(extensionDefaults[child.attributes.Extension]=child.attributes.ContentType),"content-types:Override"===child.name){var name=child.attributes.PartName;"/"===name.charAt(0)&&(name=name.substring(1)),overrides[name]=child.attributes.ContentType}}),contentTypes(overrides,extensionDefaults)}function contentTypes(overrides,extensionDefaults){return{findContentType:function(path){var overrideContentType=overrides[path];if(overrideContentType)return overrideContentType;var pathParts=path.split("."),extension=pathParts[pathParts.length-1];if(extensionDefaults.hasOwnProperty(extension))return extensionDefaults[extension];var fallback=fallbackContentTypes[extension.toLowerCase()];return fallback?"image/"+fallback:null}}}exports.readContentTypesFromXml=readContentTypesFromXml;var fallbackContentTypes={png:"png",gif:"gif",jpeg:"jpeg",jpg:"jpeg",tif:"tiff",tiff:"tiff",bmp:"bmp"};exports.defaultContentTypes=contentTypes({},{})},{}],8:[function(require,module,exports){function DocumentXmlReader(options){function convertXmlToDocument(element){var body=element.first("w:body");if(null==body)throw new Error("Could not find the body element: are you sure this is a docx file?");var result=bodyReader.readXmlElements(body.children).map(function(children){return new documents.Document(children,{notes:options.notes,comments:options.comments})});return new Result(result.value,result.messages)}var bodyReader=options.bodyReader;return{convertXmlToDocument:convertXmlToDocument}}exports.DocumentXmlReader=DocumentXmlReader;var documents=require("../documents"),Result=require("../results").Result},{"../documents":4,"../results":25}],9:[function(require,module,exports){function read(docxFile,input){return input=input||{},promises.props({contentTypes:readContentTypesFromZipFile(docxFile),partPaths:findPartPaths(docxFile),docxFile:docxFile,files:input.path?Files.relativeToFile(input.path):new Files(null)}).also(function(result){return{styles:readStylesFromZipFile(docxFile,result.partPaths.styles)}}).also(function(result){return{numbering:readNumberingFromZipFile(docxFile,result.partPaths.numbering,result.styles)}}).also(function(result){return{footnotes:readXmlFileWithBody(result.partPaths.footnotes,result,function(bodyReader,xml){return xml?notesReader.createFootnotesReader(bodyReader)(xml):new Result([])}),endnotes:readXmlFileWithBody(result.partPaths.endnotes,result,function(bodyReader,xml){return xml?notesReader.createEndnotesReader(bodyReader)(xml):new Result([])}),comments:readXmlFileWithBody(result.partPaths.comments,result,function(bodyReader,xml){return xml?commentsReader.createCommentsReader(bodyReader)(xml):new Result([])})}}).also(function(result){return{notes:result.footnotes.flatMap(function(footnotes){return result.endnotes.map(function(endnotes){return new documents.Notes(footnotes.concat(endnotes))})})}}).then(function(result){return readXmlFileWithBody(result.partPaths.mainDocument,result,function(bodyReader,xml){return result.notes.flatMap(function(notes){return result.comments.flatMap(function(comments){var reader=new DocumentXmlReader({bodyReader:bodyReader,notes:notes,comments:comments});return reader.convertXmlToDocument(xml)})})})})}function findPartPaths(docxFile){return readPackageRelationships(docxFile).then(function(packageRelationships){var mainDocumentPath=findPartPath({docxFile:docxFile,relationships:packageRelationships,relationshipType:"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",basePath:"",fallbackPath:"word/document.xml"});if(!docxFile.exists(mainDocumentPath))throw new Error("Could not find main document part. Are you sure this is a valid .docx file?");return xmlFileReader({filename:relationshipsFilename(mainDocumentPath),readElement:relationshipsReader.readRelationships,defaultValue:relationshipsReLine truncated
|
||||
return{from:documentMatcher,to:htmlPath.valueOrElse(htmlPaths.empty)}})}function readDocumentMatcher(string){return parseString(documentMatcherRule(),string)}function documentMatcherRule(){function createMatcherSuffixesRule(rules){var matcherSuffix=lop.rules.firstOf.apply(lop.rules.firstOf,["matcher suffix"].concat(rules)),matcherSuffixes=lop.rules.zeroOrMore(matcherSuffix);return lop.rules.then(matcherSuffixes,function(suffixes){var matcherOptions={};return suffixes.forEach(function(suffix){_.extend(matcherOptions,suffix)}),matcherOptions})}var sequence=lop.rules.sequence,identifierToConstant=function(identifier,constant){return lop.rules.then(lop.rules.token("identifier",identifier),function(){return constant})},paragraphRule=identifierToConstant("p",documentMatchers.paragraph),runRule=identifierToConstant("r",documentMatchers.run),elementTypeRule=lop.rules.firstOf("p or r or table",paragraphRule,runRule),styleIdRule=lop.rules.sequence(lop.rules.tokenOfType("dot"),lop.rules.sequence.cut(),lop.rules.sequence.capture(identifierRule)).map(function(styleId){return{styleId:styleId}}),styleNameMatcherRule=lop.rules.firstOf("style name matcher",lop.rules.then(lop.rules.sequence(lop.rules.tokenOfType("equals"),lop.rules.sequence.cut(),lop.rules.sequence.capture(stringRule)).head(),function(styleName){return{styleName:documentMatchers.equalTo(styleName)}}),lop.rules.then(lop.rules.sequence(lop.rules.tokenOfType("startsWith"),lop.rules.sequence.cut(),lop.rules.sequence.capture(stringRule)).head(),function(styleName){return{styleName:documentMatchers.startsWith(styleName)}})),styleNameRule=lop.rules.sequence(lop.rules.tokenOfType("open-square-bracket"),lop.rules.sequence.cut(),lop.rules.token("identifier","style-name"),lop.rules.sequence.capture(styleNameMatcherRule),lop.rules.tokenOfType("close-square-bracket")).head(),listTypeRule=lop.rules.firstOf("list type",identifierToConstant("ordered-list",{isOrdered:!0}),identifierToConstant("unordered-list",{isOrdered:!1})),listRule=sequence(lop.rules.tokenOfType("colon"),sequence.capture(listTypeRule),sequence.cut(),lop.rules.tokenOfType("open-paren"),sequence.capture(integerRule),lop.rules.tokenOfType("close-paren")).map(function(listType,levelNumber){return{list:{isOrdered:listType.isOrdered,levelIndex:levelNumber-1}}}),paragraphOrRun=sequence(sequence.capture(elementTypeRule),sequence.capture(createMatcherSuffixesRule([styleIdRule,styleNameRule,listRule]))).map(function(createMatcher,matcherOptions){return createMatcher(matcherOptions)}),table=sequence(lop.rules.token("identifier","table"),sequence.capture(createMatcherSuffixesRule([styleIdRule,styleNameRule]))).map(function(options){return documentMatchers.table(options)}),bold=identifierToConstant("b",documentMatchers.bold),italic=identifierToConstant("i",documentMatchers.italic),underline=identifierToConstant("u",documentMatchers.underline),strikethrough=identifierToConstant("strike",documentMatchers.strikethrough),allCaps=identifierToConstant("all-caps",documentMatchers.allCaps),smallCaps=identifierToConstant("small-caps",documentMatchers.smallCaps),highlight=sequence(lop.rules.token("identifier","highlight"),lop.rules.sequence.capture(lop.rules.optional(lop.rules.sequence(lop.rules.tokenOfType("open-square-bracket"),lop.rules.sequence.cut(),lop.rules.token("identifier","color"),lop.rules.tokenOfType("equals"),lop.rules.sequence.capture(stringRule),lop.rules.tokenOfType("close-square-bracket")).head()))).map(function(color){return documentMatchers.highlight({color:color.valueOrElse(void 0)})}),commentReference=identifierToConstant("comment-reference",documentMatchers.commentReference),breakMatcher=sequence(lop.rules.token("identifier","br"),sequence.cut(),lop.rules.tokenOfType("open-square-bracket"),lop.rules.token("identifier","type"),lop.rules.tokenOfType("equals"),sequence.capture(stringRule),lop.rules.tokenOfType("close-square-bracket")).map(function(breakType){switch(breakType){case"line":return documentMatchers.lineBreak;case"page":return documentMatchers.pageBreak;case"column":return documentMatchers.columnBreak}});return lop.rules.firstOf("element type",paragraphOrRun,table,bold,italic,underline,strikethrough,allCaps,smallCaps,highlight,commentReference,breakMatcher)}function readHtmlPath(string){return parseString(htmlPathRule(),string)}function htmlPathRule(){var capture=lop.rules.sequence.capture,whitespaceRule=lop.rules.tokenOfType("whitespace"),freshRule=lop.rules.then(lop.rules.optional(lop.rules.sequence(lop.rules.tokenOfType("colon"),lop.rules.token("identifier","fresh"))),function(option){return option.map(function(){return!0}).valueOrElse(!1)}),separatorRule=lop.rules.then(lop.rules.optional(lop.rules.sequence(lop.rules.tokenOfType("colon"),lop.rules.token("identifier","separator"),lop.rules.tokenOfType("open-paren"),capture(stringRule),lop.rules.tokenOfType("close-paren")).head()),function(option){return option.valueOrElse("")}),tagNamesRule=lop.rules.oneOrMoreWithSeparator(identifierRule,lop.ruLine truncated
|
||||
},{"./conventions":41,"./dom":43,"./entities":44,"./sax":46}],43:[function(require,module,exports){function notEmptyString(input){return""!==input}function splitOnASCIIWhitespace(input){return input?input.split(/[\t\n\f\r ]+/).filter(notEmptyString):[]}function orderedSetReducer(current,element){return current.hasOwnProperty(element)||(current[element]=!0),current}function toOrderedSet(input){if(!input)return[];var list=splitOnASCIIWhitespace(input);return Object.keys(list.reduce(orderedSetReducer,{}))}function arrayIncludes(list){return function(element){return list&&-1!==list.indexOf(element)}}function copy(src,dest){for(var p in src)Object.prototype.hasOwnProperty.call(src,p)&&(dest[p]=src[p])}function _extends(Class,Super){function t(){}var pt=Class.prototype;pt instanceof Super||(t.prototype=Super.prototype,t=new t,copy(pt,t),Class.prototype=pt=t),pt.constructor!=Class&&("function"!=typeof Class&&console.error("unknown Class:"+Class),pt.constructor=Class)}function DOMException(code,message){if(message instanceof Error)var error=message;else error=this,Error.call(this,ExceptionMessage[code]),this.message=ExceptionMessage[code],Error.captureStackTrace&&Error.captureStackTrace(this,DOMException);return error.code=code,message&&(this.message=this.message+": "+message),error}function NodeList(){}function LiveNodeList(node,refresh){this._node=node,this._refresh=refresh,_updateLiveList(this)}function _updateLiveList(list){var inc=list._node._inc||list._node.ownerDocument._inc;if(list._inc!=inc){var ls=list._refresh(list._node);__set__(list,"length",ls.length),copy(ls,list),list._inc=inc}}function NamedNodeMap(){}function _findNodeIndex(list,node){for(var i=list.length;i--;)if(list[i]===node)return i}function _addNamedNode(el,list,newAttr,oldAttr){if(oldAttr?list[_findNodeIndex(list,oldAttr)]=newAttr:list[list.length++]=newAttr,el){newAttr.ownerElement=el;var doc=el.ownerDocument;doc&&(oldAttr&&_onRemoveAttribute(doc,el,oldAttr),_onAddAttribute(doc,el,newAttr))}}function _removeNamedNode(el,list,attr){var i=_findNodeIndex(list,attr);if(!(i>=0))throw new DOMException(NOT_FOUND_ERR,new Error(el.tagName+"@"+attr));for(var lastIndex=list.length-1;lastIndex>i;)list[i]=list[++i];if(list.length=lastIndex,el){var doc=el.ownerDocument;doc&&(_onRemoveAttribute(doc,el,attr),attr.ownerElement=null)}}function DOMImplementation(){}function Node(){}function _xmlEncoder(c){return"<"==c&&"<"||">"==c&&">"||"&"==c&&"&"||'"'==c&&"""||"&#"+c.charCodeAt()+";"}function _visitNode(node,callback){if(callback(node))return!0;if(node=node.firstChild)do if(_visitNode(node,callback))return!0;while(node=node.nextSibling)}function Document(){this.ownerDocument=this}function _onAddAttribute(doc,el,newAttr){doc&&doc._inc++;var ns=newAttr.namespaceURI;ns===NAMESPACE.XMLNS&&(el._nsMap[newAttr.prefix?newAttr.localName:""]=newAttr.value)}function _onRemoveAttribute(doc,el,newAttr,remove){doc&&doc._inc++;var ns=newAttr.namespaceURI;ns===NAMESPACE.XMLNS&&delete el._nsMap[newAttr.prefix?newAttr.localName:""]}function _onUpdateChild(doc,el,newChild){if(doc&&doc._inc){doc._inc++;var cs=el.childNodes;if(newChild)cs[cs.length++]=newChild;else{for(var child=el.firstChild,i=0;child;)cs[i++]=child,child=child.nextSibling;cs.length=i,delete cs[cs.length]}}}function _removeChild(parentNode,child){var previous=child.previousSibling,next=child.nextSibling;return previous?previous.nextSibling=next:parentNode.firstChild=next,next?next.previousSibling=previous:parentNode.lastChild=previous,child.parentNode=null,child.previousSibling=null,child.nextSibling=null,_onUpdateChild(parentNode.ownerDocument,parentNode),child}function hasValidParentNodeType(node){return node&&(node.nodeType===Node.DOCUMENT_NODE||node.nodeType===Node.DOCUMENT_FRAGMENT_NODE||node.nodeType===Node.ELEMENT_NODE)}function hasInsertableNodeType(node){return node&&(isElementNode(node)||isTextNode(node)||isDocTypeNode(node)||node.nodeType===Node.DOCUMENT_FRAGMENT_NODE||node.nodeType===Node.COMMENT_NODE||node.nodeType===Node.PROCESSING_INSTRUCTION_NODE)}function isDocTypeNode(node){return node&&node.nodeType===Node.DOCUMENT_TYPE_NODE}function isElementNode(node){return node&&node.nodeType===Node.ELEMENT_NODE}function isTextNode(node){return node&&node.nodeType===Node.TEXT_NODE}function isElementInsertionPossible(doc,child){var parentChildNodes=doc.childNodes||[];if(find(parentChildNodes,isElementNode)||isDocTypeNode(child))return!1;var docTypeNode=find(parentChildNodes,isDocTypeNode);return!(child&&docTypeNode&&parentChildNodes.indexOf(docTypeNode)>parentChildNodes.indexOf(child))}function isElementReplacementPossible(doc,child){function hasElementChildThatIsNotChild(node){return isElementNode(node)&&node!==child}var parentChildNodes=doc.childNodes||[];if(find(parentChildNodes,hasElementChildThatIsNotChild))return!1;var docTypeNode=find(parentChildNodes,isDocTypeNode);return!(child&&docTypeNode&&parentChildNodes.indexOf(docTypeNode)>parentChildNodes.indexOf(child))}function assertPreLine truncated
|
||||
circ:"ˆ",tilde:"˜",ensp:" ",emsp:" ",thinsp:" ",zwnj:"",zwj:"",lrm:"",rlm:"",ndash:"–",mdash:"—",lsquo:"‘",rsquo:"’",sbquo:"‚",ldquo:"“",rdquo:"”",bdquo:"„",dagger:"†",Dagger:"‡",bull:"•",hellip:"…",permil:"‰",prime:"′",Prime:"″",lsaquo:"‹",rsaquo:"›",oline:"‾",euro:"€",trade:"™",larr:"←",uarr:"↑",rarr:"→",darr:"↓",harr:"↔",crarr:"↵",lceil:"⌈",rceil:"⌉",lfloor:"⌊",rfloor:"⌋",loz:"◊",spades:"♠",clubs:"♣",hearts:"♥",diams:"♦"}),exports.entityMap=exports.HTML_ENTITIES},{"./conventions":41}],45:[function(require,module,exports){var dom=require("./dom");exports.DOMImplementation=dom.DOMImplementation,exports.XMLSerializer=dom.XMLSerializer,exports.DOMParser=require("./dom-parser").DOMParser},{"./dom":43,"./dom-parser":42}],46:[function(require,module,exports){function ParseError(message,locator){this.message=message,this.locator=locator,Error.captureStackTrace&&Error.captureStackTrace(this,ParseError)}function XMLReader(){}function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){function fixedFromCharCode(code){if(code>65535){code-=65536;var surrogate1=55296+(code>>10),surrogate2=56320+(1023&code);return String.fromCharCode(surrogate1,surrogate2)}return String.fromCharCode(code)}function entityReplacer(a){var k=a.slice(1,-1);return Object.hasOwnProperty.call(entityMap,k)?entityMap[k]:"#"===k.charAt(0)?fixedFromCharCode(parseInt(k.substr(1).replace("x","0x"))):(errorHandler.error("entity not found:"+a),a)}function appendText(end){if(end>start){var xt=source.substring(start,end).replace(/&#?\w+;/g,entityReplacer);locator&&position(start),domBuilder.characters(xt,0,end-start),start=end}}function position(p,m){for(;p>=lineEnd&&(m=linePattern.exec(source));)lineStart=m.index,lineEnd=lineStart+m[0].length,locator.lineNumber++;locator.columnNumber=p-lineStart+1}for(var lineStart=0,lineEnd=0,linePattern=/.*(?:\r\n?|\n)|.*$/g,locator=domBuilder.locator,parseStack=[{currentNSMap:defaultNSMapCopy}],closeMap={},start=0;;){try{var tagStart=source.indexOf("<",start);if(0>tagStart){if(!source.substr(start).match(/^\s*$/)){var doc=domBuilder.doc,text=doc.createTextNode(source.substr(start));doc.appendChild(text),domBuilder.currentElement=text}return}switch(tagStart>start&&appendText(tagStart),source.charAt(tagStart+1)){case"/":var end=source.indexOf(">",tagStart+3),tagName=source.substring(tagStart+2,end).replace(/[ \t\n\r]+$/g,""),config=parseStack.pop();0>end?(tagName=source.substring(tagStart+2).replace(/[\s<].*/,""),errorHandler.error("end tag name: "+tagName+" is not complete:"+config.tagName),end=tagStart+1+tagName.length):tagName.match(/\s</)&&(tagName=tagName.replace(/[\s<].*/,""),errorHandler.error("end tag name: "+tagName+" maybe not complete"),end=tagStart+1+tagName.length);var localNSMap=config.localNSMap,endMatch=config.tagName==tagName,endIgnoreCaseMach=endMatch||config.tagName&&config.tagName.toLowerCase()==tagName.toLowerCase();if(endIgnoreCaseMach){if(domBuilder.endElement(config.uri,config.localName,tagName),localNSMap)for(var prefix in localNSMap)Object.prototype.hasOwnProperty.call(localNSMap,prefix)&&domBuilder.endPrefixMapping(prefix);endMatch||errorHandler.fatalError("end tag name: "+tagName+" is not match the current start tagName:"+config.tagName)}else parseStack.push(config);end++;break;case"?":locator&&position(tagStart),end=parseInstruction(source,tagStart,domBuilder);break;case"!":locator&&position(tagStart),end=parseDCC(source,tagStart,domBuilder,errorHandler);break;default:locator&&position(tagStart);var el=new ElementAttributes,currentNSMap=parseStack[parseStack.length-1].currentNSMap,end=parseElementStartPart(source,tagStart,el,currentNSMap,entityReplacer,errorHandler),len=el.length;if(!el.closed&&fixSelfClosed(source,end,el.tagName,closeMap)&&(el.closed=!0,entityMap.nbsp||errorHandler.warning("unclosed xml attribute")),locator&&len){for(var locator2=copyLocator(locator,{}),i=0;len>i;i++){var a=el[i];position(a.offset),a.locator=copyLocator(locator,{})}domBuilder.locator=locator2,appendElement(el,domBuilder,currentNSMap)&&parseStack.push(el),domBuilder.locator=locator}else appendElement(el,domBuilder,currentNSMap)&&parseStack.push(el);NAMESPACE.isHTML(el.uri)&&!el.closed?end=parseHtmlSpecialContent(source,end,el.tagName,entityReplacer,domBuilder):end++}}catch(e){if(e instanceof ParseError)throw e;errorHandler.error("element parse error: "+e),end=-1}end>start?start=end:appendText(Math.max(tagStart,start)+1)}}function copyLocator(f,t){return t.lineNumber=f.lineNumber,t.columnNumber=f.columnNumber,t}function parseElementStartPart(source,start,el,currentNSMap,entityReplacer,errorHandler){function addAttribute(qname,value,startIndex){el.attributeNames.hasOwnProperty(qname)&&errorHandler.fatalError("Attribute "+qname+" redefined"),el.addValue(qname,value.replace(/[\t\n\r]/g," ").replace(/&#?\w+;/g,entityReplacer),startIndex)}for(var attrName,value,p=++start,s=S_TAG;;){var c=source.charAt(p);switLine truncated
|
||||
i>0&&(creatorLine="\n"+traceLines[i-1]);break}}var msg="a promise was created in a "+name+"handler "+handlerLine+"but was not returned from it, see http://goo.gl/rRqMUw"+creatorLine;promise._warn(msg,!0,promiseCreated)}}function deprecated(name,replacement){var message=name+" is deprecated and will be removed in a future version.";return replacement&&(message+=" Use "+replacement+" instead."),warn(message)}function warn(message,shouldUseOwnTrace,promise){if(config.warnings){var ctx,warning=new Warning(message);if(shouldUseOwnTrace)promise._attachExtraTrace(warning);else if(config.longStackTraces&&(ctx=Promise._peekContext()))ctx.attachExtraTrace(warning);else{var parsed=parseStackAndMessage(warning);warning.stack=parsed.message+"\n"+parsed.stack.join("\n")}activeFireEvent("warning",warning)||formatAndLogError(warning,"",!0)}}function reconstructStack(message,stacks){for(var i=0;i<stacks.length-1;++i)stacks[i].push("From previous event:"),stacks[i]=stacks[i].join("\n");return i<stacks.length&&(stacks[i]=stacks[i].join("\n")),message+"\n"+stacks.join("\n")}function removeDuplicateOrEmptyJumps(stacks){for(var i=0;i<stacks.length;++i)(0===stacks[i].length||i+1<stacks.length&&stacks[i][0]===stacks[i+1][0])&&(stacks.splice(i,1),i--)}function removeCommonRoots(stacks){for(var current=stacks[0],i=1;i<stacks.length;++i){for(var prev=stacks[i],currentLastIndex=current.length-1,currentLastLine=current[currentLastIndex],commonRootMeetPoint=-1,j=prev.length-1;j>=0;--j)if(prev[j]===currentLastLine){commonRootMeetPoint=j;break}for(var j=commonRootMeetPoint;j>=0;--j){var line=prev[j];if(current[currentLastIndex]!==line)break;current.pop(),currentLastIndex--}current=prev}}function cleanStack(stack){for(var ret=[],i=0;i<stack.length;++i){var line=stack[i],isTraceLine=" (No stack trace)"===line||stackFramePattern.test(line),isInternalFrame=isTraceLine&&shouldIgnore(line);isTraceLine&&!isInternalFrame&&(indentStackFrames&&" "!==line.charAt(0)&&(line=" "+line),ret.push(line))}return ret}function stackFramesAsArray(error){for(var stack=error.stack.replace(/\s+$/g,"").split("\n"),i=0;i<stack.length;++i){var line=stack[i];if(" (No stack trace)"===line||stackFramePattern.test(line))break}return i>0&&"SyntaxError"!=error.name&&(stack=stack.slice(i)),stack}function parseStackAndMessage(error){var stack=error.stack,message=error.toString();return stack="string"==typeof stack&&stack.length>0?stackFramesAsArray(error):[" (No stack trace)"],{message:message,stack:"SyntaxError"==error.name?stack:cleanStack(stack)}}function formatAndLogError(error,title,isSoft){if("undefined"!=typeof console){var message;if(util.isObject(error)){var stack=error.stack;message=title+formatStack(stack,error)}else message=title+String(error);"function"==typeof printWarning?printWarning(message,isSoft):("function"==typeof console.log||"object"==typeof console.log)&&console.log(message)}}function fireRejectionEvent(name,localHandler,reason,promise){var localEventFired=!1;try{"function"==typeof localHandler&&(localEventFired=!0,"rejectionHandled"===name?localHandler(promise):localHandler(reason,promise))}catch(e){async.throwLater(e)}"unhandledRejection"===name?activeFireEvent(name,reason,promise)||localEventFired||formatAndLogError(reason,"Unhandled rejection "):activeFireEvent(name,promise)}function formatNonError(obj){var str;if("function"==typeof obj)str="[function "+(obj.name||"anonymous")+"]";else{str=obj&&"function"==typeof obj.toString?obj.toString():util.toString(obj);var ruselessToString=/\[object [a-zA-Z0-9$_]+\]/;if(ruselessToString.test(str))try{var newStr=JSON.stringify(obj);str=newStr}catch(e){}0===str.length&&(str="(empty array)")}return"(<"+snip(str)+">, no stack trace)"}function snip(str){var maxChars=41;return str.length<maxChars?str:str.substr(0,maxChars-3)+"..."}function longStackTracesIsSupported(){return"function"==typeof captureStackTrace}function parseLineInfo(line){var matches=line.match(parseLineInfoRegex);return matches?{fileName:matches[1],line:parseInt(matches[2],10)}:void 0}function setBounds(firstLineError,lastLineError){if(longStackTracesIsSupported()){for(var firstFileName,lastFileName,firstStackLines=firstLineError.stack.split("\n"),lastStackLines=lastLineError.stack.split("\n"),firstIndex=-1,lastIndex=-1,i=0;i<firstStackLines.length;++i){var result=parseLineInfo(firstStackLines[i]);if(result){firstFileName=result.fileName,firstIndex=result.line;break}}for(var i=0;i<lastStackLines.length;++i){var result=parseLineInfo(lastStackLines[i]);if(result){lastFileName=result.fileName,lastIndex=result.line;break}}0>firstIndex||0>lastIndex||!firstFileName||!lastFileName||firstFileName!==lastFileName||firstIndex>=lastIndex||(shouldIgnore=function(line){if(bluebirdFramePattern.test(line))return!0;var info=parseLineInfo(line);return info&&info.fileName===firstFileName&&firstIndex<=info.line&&info.line<=lastIndex?!0:!1})}}function CapturedTrace(parent){this._parent=parent,this._promisesCreated=0;var length=this._length=1+(void 0===Line truncated
|
||||
},promiseSetter=function(i){return new Function("promise","holder"," \n 'use strict'; \n holder.pIndex = promise; \n ".replace(/Index/g,i))},generateHolderClass=function(total){for(var props=new Array(total),i=0;i<props.length;++i)props[i]="this.p"+(i+1);var assignment=props.join(" = ")+" = null;",cancellationCode="var promise;\n"+props.map(function(prop){return" \n promise = "+prop+"; \n if (promise instanceof Promise) { \n promise.cancel(); \n } \n "}).join("\n"),passedArguments=props.join(", "),name="Holder$"+total,code="return function(tryCatch, errorObj, Promise, async) { \n 'use strict'; \n function [TheName](fn) { \n [TheProperties] \n this.fn = fn; \n this.asyncNeeded = true; \n this.now = 0; \n } \n \n [TheName].prototype._callFunction = function(promise) { \n promise._pushContext(); \n var ret = tryCatch(this.fn)([ThePassedArguments]); \n promise._popContext(); \n if (ret === errorObj) { \n promise._rejectCallback(ret.e, false); \n } else { \n promise._resolveCallback(ret); \n } \n }; \n \n [TheName].prototype.checkFulfillment = function(promise) { \n var now = ++this.now; \n if (now === [TheTotal]) { \n if (this.asyncNeeded) { \n async.invoke(this._callFunction, this, promise); \n } else { \n this._callFunction(promise); \n } \n \n } \n }; \n \n [TheName].prototype._resultCancelled = function() { \n [CancellationCode] \n }; \n \n return [TheName]; \n }(tryCatch, errorObj, Promise, async); \n ";return code=code.replace(/\[TheName\]/g,name).replace(/\[TheTotal\]/g,total).replace(/\[ThePassedArguments\]/g,passedArguments).replace(/\[TheProperties\]/g,assignment).replace(/\[CancellationCode\]/g,cancellationCode),new Function("tryCatch","errorObj","Promise","async",code)(tryCatch,errorObj,Promise,async)},holderClasses=[],thenCallbacks=[],promiseSetters=[],i=0;8>i;++i)holderClasses.push(generateHolderClass(i+1)),thenCallbacks.push(thenCallback(i+1)),promiseSetters.push(promiseSetter(i+1));reject=function(reason){this._reject(reason)}}Promise.join=function(){var fn,last=arguments.length-1;if(last>0&&"function"==typeof arguments[last]&&(fn=arguments[last],8>=last&&canEvaluate)){var ret=new Promise(INTERNAL);ret._captureStackTrace();for(var HolderClass=holderClasses[last-1],holder=new HolderClass(fn),callbacks=thenCallbacks,i=0;last>i;++i){var maybePromise=tryConvertToPromise(arguments[i],ret);if(maybePromise instanceof Promise){maybePromise=maybePromise._target();var bitField=maybePromise._bitField;0===(50397184&bitLine truncated
|
||||
var promise=this._promise=new Promise(INTERNAL);values instanceof Promise&&promise._propagateFrom(values,3),promise._setOnCancel(this),this._values=values,this._length=0,this._totalResolved=0,this._init(void 0,-2)}var util=require("./util");util.isArray;return util.inherits(PromiseArray,Proxyable),PromiseArray.prototype.length=function(){return this._length},PromiseArray.prototype.promise=function(){return this._promise},PromiseArray.prototype._init=function init(_,resolveValueIfEmpty){var values=tryConvertToPromise(this._values,this._promise);if(values instanceof Promise){values=values._target();var bitField=values._bitField;if(this._values=values,0===(50397184&bitField))return this._promise._setAsyncGuaranteed(),values._then(init,this._reject,void 0,this,resolveValueIfEmpty);if(0===(33554432&bitField))return 0!==(16777216&bitField)?this._reject(values._reason()):this._cancel();values=values._value()}if(values=util.asArray(values),null===values){var err=apiRejection("expecting an array or an iterable object but got "+util.classString(values)).reason();return void this._promise._rejectCallback(err,!1)}return 0===values.length?void(-5===resolveValueIfEmpty?this._resolveEmptyArray():this._resolve(toResolutionValue(resolveValueIfEmpty))):void this._iterate(values)},PromiseArray.prototype._iterate=function(values){var len=this.getActualLength(values.length);this._length=len,this._values=this.shouldCopyValues()?new Array(len):this._values;for(var result=this._promise,isResolved=!1,bitField=null,i=0;len>i;++i){var maybePromise=tryConvertToPromise(values[i],result);maybePromise instanceof Promise?(maybePromise=maybePromise._target(),bitField=maybePromise._bitField):bitField=null,isResolved?null!==bitField&&maybePromise.suppressUnhandledRejections():null!==bitField?0===(50397184&bitField)?(maybePromise._proxy(this,i),this._values[i]=maybePromise):isResolved=0!==(33554432&bitField)?this._promiseFulfilled(maybePromise._value(),i):0!==(16777216&bitField)?this._promiseRejected(maybePromise._reason(),i):this._promiseCancelled(i):isResolved=this._promiseFulfilled(maybePromise,i)}isResolved||result._setAsyncGuaranteed()},PromiseArray.prototype._isResolved=function(){return null===this._values},PromiseArray.prototype._resolve=function(value){this._values=null,this._promise._fulfill(value)},PromiseArray.prototype._cancel=function(){!this._isResolved()&&this._promise._isCancellable()&&(this._values=null,this._promise._cancel())},PromiseArray.prototype._reject=function(reason){this._values=null,this._promise._rejectCallback(reason,!1)},PromiseArray.prototype._promiseFulfilled=function(value,index){this._values[index]=value;var totalResolved=++this._totalResolved;return totalResolved>=this._length?(this._resolve(this._values),!0):!1},PromiseArray.prototype._promiseCancelled=function(){return this._cancel(),!0},PromiseArray.prototype._promiseRejected=function(reason){return this._totalResolved++,this._reject(reason),!0},PromiseArray.prototype._resultCancelled=function(){if(!this._isResolved()){var values=this._values;if(this._cancel(),values instanceof Promise)values.cancel();else for(var i=0;i<values.length;++i)values[i]instanceof Promise&&values[i].cancel()}},PromiseArray.prototype.shouldCopyValues=function(){return!0},PromiseArray.prototype.getActualLength=function(len){return len},PromiseArray}},{"./util":82}],70:[function(require,module,exports){"use strict";module.exports=function(Promise,INTERNAL){function propsFilter(key){return!noCopyPropsPattern.test(key)}function isPromisified(fn){try{return fn.__isPromisified__===!0}catch(e){return!1}}function hasPromisified(obj,key,suffix){var val=util.getDataPropertyOrDefault(obj,key+suffix,defaultPromisified);return val?isPromisified(val):!1}function checkValid(ret,suffix,suffixRegexp){for(var i=0;i<ret.length;i+=2){var key=ret[i];if(suffixRegexp.test(key))for(var keyWithoutAsyncSuffix=key.replace(suffixRegexp,""),j=0;j<ret.length;j+=2)if(ret[j]===keyWithoutAsyncSuffix)throw new TypeError("Cannot promisify an API that has normal methods with '%s'-suffix\n\n See http://goo.gl/MqrFmX\n".replace("%s",suffix))}}function promisifiableMethods(obj,suffix,suffixRegexp,filter){for(var keys=util.inheritedDataKeys(obj),ret=[],i=0;i<keys.length;++i){var key=keys[i],value=obj[key],passesDefaultFilter=filter===defaultFilter?!0:defaultFilter(key,value,obj);"function"!=typeof value||isPromisified(value)||hasPromisified(obj,key,suffix)||!filter(key,value,obj,passesDefaultFilter)||ret.push(key,value)}return checkValid(ret,suffix,suffixRegexp),ret}function makeNodePromisifiedClosure(callback,receiver,_,fn,__,multiArgs){function promisified(){var _receiver=receiver;receiver===THIS&&(_receiver=this);var promise=new Promise(INTERNAL);promise._captureStackTrace();var cb="string"==typeof method&&this!==defaultThis?this[method]:callback,fn=nodebackForPromise(promise,multiArgs);try{cb.apply(_receiver,withAppended(arguments,fn))}catch(e){promise._rejectCallback(maybeWrapAsError(e),!0,!0)}return promiseLine truncated
|
||||
ret}}},{"./util":82}],81:[function(require,module,exports){"use strict";module.exports=function(Promise,apiRejection,tryConvertToPromise,createContext,INTERNAL,debug){function thrower(e){setTimeout(function(){throw e},0)}function castPreservingDisposable(thenable){var maybePromise=tryConvertToPromise(thenable);return maybePromise!==thenable&&"function"==typeof thenable._isDisposable&&"function"==typeof thenable._getDisposer&&thenable._isDisposable()&&maybePromise._setDisposable(thenable._getDisposer()),maybePromise}function dispose(resources,inspection){function iterator(){if(i>=len)return ret._fulfill();var maybePromise=castPreservingDisposable(resources[i++]);if(maybePromise instanceof Promise&&maybePromise._isDisposable()){try{maybePromise=tryConvertToPromise(maybePromise._getDisposer().tryDispose(inspection),resources.promise)}catch(e){return thrower(e)}if(maybePromise instanceof Promise)return maybePromise._then(iterator,thrower,null,null,null)}iterator()}var i=0,len=resources.length,ret=new Promise(INTERNAL);return iterator(),ret}function Disposer(data,promise,context){this._data=data,this._promise=promise,this._context=context}function FunctionDisposer(fn,promise,context){this.constructor$(fn,promise,context)}function maybeUnwrapDisposer(value){return Disposer.isDisposer(value)?(this.resources[this.index]._setDisposable(value),value.promise()):value}function ResourceList(length){this.length=length,this.promise=null,this[length-1]=null}var util=require("./util"),TypeError=require("./errors").TypeError,inherits=require("./util").inherits,errorObj=util.errorObj,tryCatch=util.tryCatch,NULL={};Disposer.prototype.data=function(){return this._data},Disposer.prototype.promise=function(){return this._promise},Disposer.prototype.resource=function(){return this.promise().isFulfilled()?this.promise().value():NULL},Disposer.prototype.tryDispose=function(inspection){var resource=this.resource(),context=this._context;void 0!==context&&context._pushContext();var ret=resource!==NULL?this.doDispose(resource,inspection):null;return void 0!==context&&context._popContext(),this._promise._unsetDisposable(),this._data=null,ret},Disposer.isDisposer=function(d){return null!=d&&"function"==typeof d.resource&&"function"==typeof d.tryDispose},inherits(FunctionDisposer,Disposer),FunctionDisposer.prototype.doDispose=function(resource,inspection){var fn=this.data();return fn.call(resource,resource,inspection)},ResourceList.prototype._resultCancelled=function(){for(var len=this.length,i=0;len>i;++i){var item=this[i];item instanceof Promise&&item.cancel()}},Promise.using=function(){var len=arguments.length;if(2>len)return apiRejection("you must pass at least 2 arguments to Promise.using");var fn=arguments[len-1];if("function"!=typeof fn)return apiRejection("expecting a function but got "+util.classString(fn));var input,spreadArgs=!0;2===len&&Array.isArray(arguments[0])?(input=arguments[0],len=input.length,spreadArgs=!1):(input=arguments,len--);for(var resources=new ResourceList(len),i=0;len>i;++i){var resource=input[i];if(Disposer.isDisposer(resource)){var disposer=resource;resource=resource.promise(),resource._setDisposable(disposer)}else{var maybePromise=tryConvertToPromise(resource);maybePromise instanceof Promise&&(resource=maybePromise._then(maybeUnwrapDisposer,null,null,{resources:resources,index:i},void 0))}resources[i]=resource}for(var reflectedResources=new Array(resources.length),i=0;i<reflectedResources.length;++i)reflectedResources[i]=Promise.resolve(resources[i]).reflect();var resultPromise=Promise.all(reflectedResources).then(function(inspections){for(var i=0;i<inspections.length;++i){var inspection=inspections[i];if(inspection.isRejected())return errorObj.e=inspection.error(),errorObj;if(!inspection.isFulfilled())return void resultPromise.cancel();inspections[i]=inspection.value()}promise._pushContext(),fn=tryCatch(fn);var ret=spreadArgs?fn.apply(void 0,inspections):fn(inspections),promiseCreated=promise._popContext();return debug.checkForgottenReturns(ret,promiseCreated,"Promise.using",promise),ret}),promise=resultPromise.lastly(function(){var inspection=new Promise.PromiseInspection(resultPromise);return dispose(resources,inspection)});return resources.promise=promise,promise._setOnCancel(resources),promise},Promise.prototype._setDisposable=function(disposer){this._bitField=131072|this._bitField,this._disposer=disposer},Promise.prototype._isDisposable=function(){return(131072&this._bitField)>0},Promise.prototype._getDisposer=function(){return this._disposer},Promise.prototype._unsetDisposable=function(){this._bitField=-131073&this._bitField,this._disposer=void 0},Promise.prototype.disposer=function(fn){if("function"==typeof fn)return new FunctionDisposer(fn,this,createContext());throw new TypeError}}},{"./errors":58,"./util":82}],82:[function(require,module,exports){(function(process,global){"use strict";function tryCatcher(){try{var target=tryCatchTarget;return tryCatchTarget=null,target.apply(this,arguments)}catcLine truncated
|
||||
}return newBuf},Buffer.prototype.readUIntLE=function(offset,byteLength,noAssert){offset=0|offset,byteLength=0|byteLength,noAssert||checkOffset(offset,byteLength,this.length);for(var val=this[offset],mul=1,i=0;++i<byteLength&&(mul*=256);)val+=this[offset+i]*mul;return val},Buffer.prototype.readUIntBE=function(offset,byteLength,noAssert){offset=0|offset,byteLength=0|byteLength,noAssert||checkOffset(offset,byteLength,this.length);for(var val=this[offset+--byteLength],mul=1;byteLength>0&&(mul*=256);)val+=this[offset+--byteLength]*mul;return val},Buffer.prototype.readUInt8=function(offset,noAssert){return noAssert||checkOffset(offset,1,this.length),this[offset]},Buffer.prototype.readUInt16LE=function(offset,noAssert){return noAssert||checkOffset(offset,2,this.length),this[offset]|this[offset+1]<<8},Buffer.prototype.readUInt16BE=function(offset,noAssert){return noAssert||checkOffset(offset,2,this.length),this[offset]<<8|this[offset+1]},Buffer.prototype.readUInt32LE=function(offset,noAssert){return noAssert||checkOffset(offset,4,this.length),(this[offset]|this[offset+1]<<8|this[offset+2]<<16)+16777216*this[offset+3]},Buffer.prototype.readUInt32BE=function(offset,noAssert){return noAssert||checkOffset(offset,4,this.length),16777216*this[offset]+(this[offset+1]<<16|this[offset+2]<<8|this[offset+3])},Buffer.prototype.readIntLE=function(offset,byteLength,noAssert){offset=0|offset,byteLength=0|byteLength,noAssert||checkOffset(offset,byteLength,this.length);for(var val=this[offset],mul=1,i=0;++i<byteLength&&(mul*=256);)val+=this[offset+i]*mul;return mul*=128,val>=mul&&(val-=Math.pow(2,8*byteLength)),val},Buffer.prototype.readIntBE=function(offset,byteLength,noAssert){offset=0|offset,byteLength=0|byteLength,noAssert||checkOffset(offset,byteLength,this.length);for(var i=byteLength,mul=1,val=this[offset+--i];i>0&&(mul*=256);)val+=this[offset+--i]*mul;return mul*=128,val>=mul&&(val-=Math.pow(2,8*byteLength)),val},Buffer.prototype.readInt8=function(offset,noAssert){return noAssert||checkOffset(offset,1,this.length),128&this[offset]?-1*(255-this[offset]+1):this[offset]},Buffer.prototype.readInt16LE=function(offset,noAssert){noAssert||checkOffset(offset,2,this.length);var val=this[offset]|this[offset+1]<<8;return 32768&val?4294901760|val:val},Buffer.prototype.readInt16BE=function(offset,noAssert){noAssert||checkOffset(offset,2,this.length);var val=this[offset+1]|this[offset]<<8;return 32768&val?4294901760|val:val},Buffer.prototype.readInt32LE=function(offset,noAssert){return noAssert||checkOffset(offset,4,this.length),this[offset]|this[offset+1]<<8|this[offset+2]<<16|this[offset+3]<<24},Buffer.prototype.readInt32BE=function(offset,noAssert){return noAssert||checkOffset(offset,4,this.length),this[offset]<<24|this[offset+1]<<16|this[offset+2]<<8|this[offset+3]},Buffer.prototype.readFloatLE=function(offset,noAssert){return noAssert||checkOffset(offset,4,this.length),ieee754.read(this,offset,!0,23,4)},Buffer.prototype.readFloatBE=function(offset,noAssert){return noAssert||checkOffset(offset,4,this.length),ieee754.read(this,offset,!1,23,4)},Buffer.prototype.readDoubleLE=function(offset,noAssert){return noAssert||checkOffset(offset,8,this.length),ieee754.read(this,offset,!0,52,8)},Buffer.prototype.readDoubleBE=function(offset,noAssert){return noAssert||checkOffset(offset,8,this.length),ieee754.read(this,offset,!1,52,8)},Buffer.prototype.writeUIntLE=function(value,offset,byteLength,noAssert){if(value=+value,offset=0|offset,byteLength=0|byteLength,!noAssert){var maxBytes=Math.pow(2,8*byteLength)-1;checkInt(this,value,offset,byteLength,maxBytes,0)}var mul=1,i=0;for(this[offset]=255&value;++i<byteLength&&(mul*=256);)this[offset+i]=value/mul&255;return offset+byteLength},Buffer.prototype.writeUIntBE=function(value,offset,byteLength,noAssert){if(value=+value,offset=0|offset,byteLength=0|byteLength,!noAssert){var maxBytes=Math.pow(2,8*byteLength)-1;checkInt(this,value,offset,byteLength,maxBytes,0)}var i=byteLength-1,mul=1;for(this[offset+i]=255&value;--i>=0&&(mul*=256);)this[offset+i]=value/mul&255;return offset+byteLength},Buffer.prototype.writeUInt8=function(value,offset,noAssert){return value=+value,offset=0|offset,noAssert||checkInt(this,value,offset,1,255,0),Buffer.TYPED_ARRAY_SUPPORT||(value=Math.floor(value)),this[offset]=255&value,offset+1},Buffer.prototype.writeUInt16LE=function(value,offset,noAssert){return value=+value,offset=0|offset,noAssert||checkInt(this,value,offset,2,65535,0),Buffer.TYPED_ARRAY_SUPPORT?(this[offset]=255&value,this[offset+1]=value>>>8):objectWriteUInt16(this,value,offset,!0),offset+2},Buffer.prototype.writeUInt16BE=function(value,offset,noAssert){return value=+value,offset=0|offset,noAssert||checkInt(this,value,offset,2,65535,0),Buffer.TYPED_ARRAY_SUPPORT?(this[offset]=value>>>8,this[offset+1]=255&value):objectWriteUInt16(this,value,offset,!1),offset+2},Buffer.prototype.writeUInt32LE=function(value,offset,noAssert){return value=+value,offset=0|offset,noAssert||checkInt(this,value,offset,4,4294967295,0),BufLine truncated
|
||||
"Dingbat dec":"44","Dingbat hex":"2C","Unicode dec":"127798","Unicode hex":"1F336"},{"Typeface name":"Webdings","Dingbat dec":"45","Dingbat hex":"2D","Unicode dec":"127895","Unicode hex":"1F397"},{"Typeface name":"Webdings","Dingbat dec":"46","Dingbat hex":"2E","Unicode dec":"128638","Unicode hex":"1F67E"},{"Typeface name":"Webdings","Dingbat dec":"47","Dingbat hex":"2F","Unicode dec":"128636","Unicode hex":"1F67C"},{"Typeface name":"Webdings","Dingbat dec":"48","Dingbat hex":"30","Unicode dec":"128469","Unicode hex":"1F5D5"},{"Typeface name":"Webdings","Dingbat dec":"49","Dingbat hex":"31","Unicode dec":"128470","Unicode hex":"1F5D6"},{"Typeface name":"Webdings","Dingbat dec":"50","Dingbat hex":"32","Unicode dec":"128471","Unicode hex":"1F5D7"},{"Typeface name":"Webdings","Dingbat dec":"51","Dingbat hex":"33","Unicode dec":"9204","Unicode hex":"23F4"},{"Typeface name":"Webdings","Dingbat dec":"52","Dingbat hex":"34","Unicode dec":"9205","Unicode hex":"23F5"},{"Typeface name":"Webdings","Dingbat dec":"53","Dingbat hex":"35","Unicode dec":"9206","Unicode hex":"23F6"},{"Typeface name":"Webdings","Dingbat dec":"54","Dingbat hex":"36","Unicode dec":"9207","Unicode hex":"23F7"},{"Typeface name":"Webdings","Dingbat dec":"55","Dingbat hex":"37","Unicode dec":"9194","Unicode hex":"23EA"},{"Typeface name":"Webdings","Dingbat dec":"56","Dingbat hex":"38","Unicode dec":"9193","Unicode hex":"23E9"},{"Typeface name":"Webdings","Dingbat dec":"57","Dingbat hex":"39","Unicode dec":"9198","Unicode hex":"23EE"},{"Typeface name":"Webdings","Dingbat dec":"58","Dingbat hex":"3A","Unicode dec":"9197","Unicode hex":"23ED"},{"Typeface name":"Webdings","Dingbat dec":"59","Dingbat hex":"3B","Unicode dec":"9208","Unicode hex":"23F8"},{"Typeface name":"Webdings","Dingbat dec":"60","Dingbat hex":"3C","Unicode dec":"9209","Unicode hex":"23F9"},{"Typeface name":"Webdings","Dingbat dec":"61","Dingbat hex":"3D","Unicode dec":"9210","Unicode hex":"23FA"},{"Typeface name":"Webdings","Dingbat dec":"62","Dingbat hex":"3E","Unicode dec":"128474","Unicode hex":"1F5DA"},{"Typeface name":"Webdings","Dingbat dec":"63","Dingbat hex":"3F","Unicode dec":"128499","Unicode hex":"1F5F3"},{"Typeface name":"Webdings","Dingbat dec":"64","Dingbat hex":"40","Unicode dec":"128736","Unicode hex":"1F6E0"},{"Typeface name":"Webdings","Dingbat dec":"65","Dingbat hex":"41","Unicode dec":"127959","Unicode hex":"1F3D7"},{"Typeface name":"Webdings","Dingbat dec":"66","Dingbat hex":"42","Unicode dec":"127960","Unicode hex":"1F3D8"},{"Typeface name":"Webdings","Dingbat dec":"67","Dingbat hex":"43","Unicode dec":"127961","Unicode hex":"1F3D9"},{"Typeface name":"Webdings","Dingbat dec":"68","Dingbat hex":"44","Unicode dec":"127962","Unicode hex":"1F3DA"},{"Typeface name":"Webdings","Dingbat dec":"69","Dingbat hex":"45","Unicode dec":"127964","Unicode hex":"1F3DC"},{"Typeface name":"Webdings","Dingbat dec":"70","Dingbat hex":"46","Unicode dec":"127981","Unicode hex":"1F3ED"},{"Typeface name":"Webdings","Dingbat dec":"71","Dingbat hex":"47","Unicode dec":"127963","Unicode hex":"1F3DB"},{"Typeface name":"Webdings","Dingbat dec":"72","Dingbat hex":"48","Unicode dec":"127968","Unicode hex":"1F3E0"},{"Typeface name":"Webdings","Dingbat dec":"73","Dingbat hex":"49","Unicode dec":"127958","Unicode hex":"1F3D6"},{"Typeface name":"Webdings","Dingbat dec":"74","Dingbat hex":"4A","Unicode dec":"127965","Unicode hex":"1F3DD"},{"Typeface name":"Webdings","Dingbat dec":"75","Dingbat hex":"4B","Unicode dec":"128739","Unicode hex":"1F6E3"},{"Typeface name":"Webdings","Dingbat dec":"76","Dingbat hex":"4C","Unicode dec":"128269","Unicode hex":"1F50D"},{"Typeface name":"Webdings","Dingbat dec":"77","Dingbat hex":"4D","Unicode dec":"127956","Unicode hex":"1F3D4"},{"Typeface name":"Webdings","Dingbat dec":"78","Dingbat hex":"4E","Unicode dec":"128065","Unicode hex":"1F441"},{"Typeface name":"Webdings","Dingbat dec":"79","Dingbat hex":"4F","Unicode dec":"128066","Unicode hex":"1F442"},{"Typeface name":"Webdings","Dingbat dec":"80","Dingbat hex":"50","Unicode dec":"127966","Unicode hex":"1F3DE"},{"Typeface name":"Webdings","Dingbat dec":"81","Dingbat hex":"51","Unicode dec":"127957","Unicode hex":"1F3D5"},{"Typeface name":"Webdings","Dingbat dec":"82","Dingbat hex":"52","Unicode dec":"128740","Unicode hex":"1F6E4"},{"Typeface name":"Webdings","Dingbat dec":"83","Dingbat hex":"53","Unicode dec":"127967","Unicode hex":"1F3DF"},{"Typeface name":"Webdings","Dingbat dec":"84","Dingbat hex":"54","Unicode dec":"128755","Unicode hex":"1F6F3"},{"Typeface name":"Webdings","Dingbat dec":"85","Dingbat hex":"55","Unicode dec":"128364","Unicode hex":"1F56C"},{"Typeface name":"Webdings","Dingbat dec":"86","Dingbat hex":"56","Unicode dec":"128363","Unicode hex":"1F56B"},{"Typeface name":"Webdings","Dingbat dec":"87","Dingbat hex":"57","Unicode dec":"128360","Unicode hex":"1F568"},{"Typeface name":"Webdings","Dingbat dec":"88","Dingbat hex":"58","Unicode dec":"128264","Unicode hex":"1F508"},{"Typeface name":"WeLine truncated
|
||||
"Dingbat hex":"6A","Unicode dec":"128624","Unicode hex":"1F670"},{"Typeface name":"Wingdings","Dingbat dec":"107","Dingbat hex":"6B","Unicode dec":"128629","Unicode hex":"1F675"},{"Typeface name":"Wingdings","Dingbat dec":"108","Dingbat hex":"6C","Unicode dec":"9899","Unicode hex":"26AB"},{"Typeface name":"Wingdings","Dingbat dec":"109","Dingbat hex":"6D","Unicode dec":"128318","Unicode hex":"1F53E"},{"Typeface name":"Wingdings","Dingbat dec":"110","Dingbat hex":"6E","Unicode dec":"9724","Unicode hex":"25FC"},{"Typeface name":"Wingdings","Dingbat dec":"111","Dingbat hex":"6F","Unicode dec":"128911","Unicode hex":"1F78F"},{"Typeface name":"Wingdings","Dingbat dec":"112","Dingbat hex":"70","Unicode dec":"128912","Unicode hex":"1F790"},{"Typeface name":"Wingdings","Dingbat dec":"113","Dingbat hex":"71","Unicode dec":"10065","Unicode hex":"2751"},{"Typeface name":"Wingdings","Dingbat dec":"114","Dingbat hex":"72","Unicode dec":"10066","Unicode hex":"2752"},{"Typeface name":"Wingdings","Dingbat dec":"115","Dingbat hex":"73","Unicode dec":"128927","Unicode hex":"1F79F"},{"Typeface name":"Wingdings","Dingbat dec":"116","Dingbat hex":"74","Unicode dec":"10731","Unicode hex":"29EB"},{"Typeface name":"Wingdings","Dingbat dec":"117","Dingbat hex":"75","Unicode dec":"9670","Unicode hex":"25C6"},{"Typeface name":"Wingdings","Dingbat dec":"118","Dingbat hex":"76","Unicode dec":"10070","Unicode hex":"2756"},{"Typeface name":"Wingdings","Dingbat dec":"119","Dingbat hex":"77","Unicode dec":"11049","Unicode hex":"2B29"},{"Typeface name":"Wingdings","Dingbat dec":"120","Dingbat hex":"78","Unicode dec":"8999","Unicode hex":"2327"},{"Typeface name":"Wingdings","Dingbat dec":"121","Dingbat hex":"79","Unicode dec":"11193","Unicode hex":"2BB9"},{"Typeface name":"Wingdings","Dingbat dec":"122","Dingbat hex":"7A","Unicode dec":"8984","Unicode hex":"2318"},{"Typeface name":"Wingdings","Dingbat dec":"123","Dingbat hex":"7B","Unicode dec":"127989","Unicode hex":"1F3F5"},{"Typeface name":"Wingdings","Dingbat dec":"124","Dingbat hex":"7C","Unicode dec":"127990","Unicode hex":"1F3F6"},{"Typeface name":"Wingdings","Dingbat dec":"125","Dingbat hex":"7D","Unicode dec":"128630","Unicode hex":"1F676"},{"Typeface name":"Wingdings","Dingbat dec":"126","Dingbat hex":"7E","Unicode dec":"128631","Unicode hex":"1F677"},{"Typeface name":"Wingdings","Dingbat dec":"127","Dingbat hex":"7F","Unicode dec":"9647","Unicode hex":"25AF"},{"Typeface name":"Wingdings","Dingbat dec":"128","Dingbat hex":"80","Unicode dec":"127243","Unicode hex":"1F10B"},{"Typeface name":"Wingdings","Dingbat dec":"129","Dingbat hex":"81","Unicode dec":"10112","Unicode hex":"2780"},{"Typeface name":"Wingdings","Dingbat dec":"130","Dingbat hex":"82","Unicode dec":"10113","Unicode hex":"2781"},{"Typeface name":"Wingdings","Dingbat dec":"131","Dingbat hex":"83","Unicode dec":"10114","Unicode hex":"2782"},{"Typeface name":"Wingdings","Dingbat dec":"132","Dingbat hex":"84","Unicode dec":"10115","Unicode hex":"2783"},{"Typeface name":"Wingdings","Dingbat dec":"133","Dingbat hex":"85","Unicode dec":"10116","Unicode hex":"2784"},{"Typeface name":"Wingdings","Dingbat dec":"134","Dingbat hex":"86","Unicode dec":"10117","Unicode hex":"2785"},{"Typeface name":"Wingdings","Dingbat dec":"135","Dingbat hex":"87","Unicode dec":"10118","Unicode hex":"2786"},{"Typeface name":"Wingdings","Dingbat dec":"136","Dingbat hex":"88","Unicode dec":"10119","Unicode hex":"2787"},{"Typeface name":"Wingdings","Dingbat dec":"137","Dingbat hex":"89","Unicode dec":"10120","Unicode hex":"2788"},{"Typeface name":"Wingdings","Dingbat dec":"138","Dingbat hex":"8A","Unicode dec":"10121","Unicode hex":"2789"},{"Typeface name":"Wingdings","Dingbat dec":"139","Dingbat hex":"8B","Unicode dec":"127244","Unicode hex":"1F10C"},{"Typeface name":"Wingdings","Dingbat dec":"140","Dingbat hex":"8C","Unicode dec":"10122","Unicode hex":"278A"},{"Typeface name":"Wingdings","Dingbat dec":"141","Dingbat hex":"8D","Unicode dec":"10123","Unicode hex":"278B"},{"Typeface name":"Wingdings","Dingbat dec":"142","Dingbat hex":"8E","Unicode dec":"10124","Unicode hex":"278C"},{"Typeface name":"Wingdings","Dingbat dec":"143","Dingbat hex":"8F","Unicode dec":"10125","Unicode hex":"278D"},{"Typeface name":"Wingdings","Dingbat dec":"144","Dingbat hex":"90","Unicode dec":"10126","Unicode hex":"278E"},{"Typeface name":"Wingdings","Dingbat dec":"145","Dingbat hex":"91","Unicode dec":"10127","Unicode hex":"278F"},{"Typeface name":"Wingdings","Dingbat dec":"146","Dingbat hex":"92","Unicode dec":"10128","Unicode hex":"2790"},{"Typeface name":"Wingdings","Dingbat dec":"147","Dingbat hex":"93","Unicode dec":"10129","Unicode hex":"2791"},{"Typeface name":"Wingdings","Dingbat dec":"148","Dingbat hex":"94","Unicode dec":"10130","Unicode hex":"2792"},{"Typeface name":"Wingdings","Dingbat dec":"149","Dingbat hex":"95","Unicode dec":"10131","Unicode hex":"2793"},{"Typeface name":"Wingdings","Dingbat dec":"150","Dingbat hex":"96","Unicode dec":"128610","Unicode heLine truncated
|
||||
},{"Typeface name":"Wingdings 2","Dingbat dec":"166","Dingbat hex":"A6","Unicode dec":"128915","Unicode hex":"1F793"},{"Typeface name":"Wingdings 2","Dingbat dec":"167","Dingbat hex":"A7","Unicode dec":"128916","Unicode hex":"1F794"},{"Typeface name":"Wingdings 2","Dingbat dec":"168","Dingbat hex":"A8","Unicode dec":"9635","Unicode hex":"25A3"},{"Typeface name":"Wingdings 2","Dingbat dec":"169","Dingbat hex":"A9","Unicode dec":"128917","Unicode hex":"1F795"},{"Typeface name":"Wingdings 2","Dingbat dec":"170","Dingbat hex":"AA","Unicode dec":"128918","Unicode hex":"1F796"},{"Typeface name":"Wingdings 2","Dingbat dec":"171","Dingbat hex":"AB","Unicode dec":"128919","Unicode hex":"1F797"},{"Typeface name":"Wingdings 2","Dingbat dec":"172","Dingbat hex":"AC","Unicode dec":"128920","Unicode hex":"1F798"},{"Typeface name":"Wingdings 2","Dingbat dec":"173","Dingbat hex":"AD","Unicode dec":"11049","Unicode hex":"2B29"},{"Typeface name":"Wingdings 2","Dingbat dec":"174","Dingbat hex":"AE","Unicode dec":"11045","Unicode hex":"2B25"},{"Typeface name":"Wingdings 2","Dingbat dec":"175","Dingbat hex":"AF","Unicode dec":"9671","Unicode hex":"25C7"},{"Typeface name":"Wingdings 2","Dingbat dec":"176","Dingbat hex":"B0","Unicode dec":"128922","Unicode hex":"1F79A"},{"Typeface name":"Wingdings 2","Dingbat dec":"177","Dingbat hex":"B1","Unicode dec":"9672","Unicode hex":"25C8"},{"Typeface name":"Wingdings 2","Dingbat dec":"178","Dingbat hex":"B2","Unicode dec":"128923","Unicode hex":"1F79B"},{"Typeface name":"Wingdings 2","Dingbat dec":"179","Dingbat hex":"B3","Unicode dec":"128924","Unicode hex":"1F79C"},{"Typeface name":"Wingdings 2","Dingbat dec":"180","Dingbat hex":"B4","Unicode dec":"128925","Unicode hex":"1F79D"},{"Typeface name":"Wingdings 2","Dingbat dec":"181","Dingbat hex":"B5","Unicode dec":"128926","Unicode hex":"1F79E"},{"Typeface name":"Wingdings 2","Dingbat dec":"182","Dingbat hex":"B6","Unicode dec":"11050","Unicode hex":"2B2A"},{"Typeface name":"Wingdings 2","Dingbat dec":"183","Dingbat hex":"B7","Unicode dec":"11047","Unicode hex":"2B27"},{"Typeface name":"Wingdings 2","Dingbat dec":"184","Dingbat hex":"B8","Unicode dec":"9674","Unicode hex":"25CA"},{"Typeface name":"Wingdings 2","Dingbat dec":"185","Dingbat hex":"B9","Unicode dec":"128928","Unicode hex":"1F7A0"},{"Typeface name":"Wingdings 2","Dingbat dec":"186","Dingbat hex":"BA","Unicode dec":"9686","Unicode hex":"25D6"},{"Typeface name":"Wingdings 2","Dingbat dec":"187","Dingbat hex":"BB","Unicode dec":"9687","Unicode hex":"25D7"},{"Typeface name":"Wingdings 2","Dingbat dec":"188","Dingbat hex":"BC","Unicode dec":"11210","Unicode hex":"2BCA"},{"Typeface name":"Wingdings 2","Dingbat dec":"189","Dingbat hex":"BD","Unicode dec":"11211","Unicode hex":"2BCB"},{"Typeface name":"Wingdings 2","Dingbat dec":"190","Dingbat hex":"BE","Unicode dec":"11200","Unicode hex":"2BC0"},{"Typeface name":"Wingdings 2","Dingbat dec":"191","Dingbat hex":"BF","Unicode dec":"11201","Unicode hex":"2BC1"},{"Typeface name":"Wingdings 2","Dingbat dec":"192","Dingbat hex":"C0","Unicode dec":"11039","Unicode hex":"2B1F"},{"Typeface name":"Wingdings 2","Dingbat dec":"193","Dingbat hex":"C1","Unicode dec":"11202","Unicode hex":"2BC2"},{"Typeface name":"Wingdings 2","Dingbat dec":"194","Dingbat hex":"C2","Unicode dec":"11043","Unicode hex":"2B23"},{"Typeface name":"Wingdings 2","Dingbat dec":"195","Dingbat hex":"C3","Unicode dec":"11042","Unicode hex":"2B22"},{"Typeface name":"Wingdings 2","Dingbat dec":"196","Dingbat hex":"C4","Unicode dec":"11203","Unicode hex":"2BC3"},{"Typeface name":"Wingdings 2","Dingbat dec":"197","Dingbat hex":"C5","Unicode dec":"11204","Unicode hex":"2BC4"},{"Typeface name":"Wingdings 2","Dingbat dec":"198","Dingbat hex":"C6","Unicode dec":"128929","Unicode hex":"1F7A1"},{"Typeface name":"Wingdings 2","Dingbat dec":"199","Dingbat hex":"C7","Unicode dec":"128930","Unicode hex":"1F7A2"},{"Typeface name":"Wingdings 2","Dingbat dec":"200","Dingbat hex":"C8","Unicode dec":"128931","Unicode hex":"1F7A3"},{"Typeface name":"Wingdings 2","Dingbat dec":"201","Dingbat hex":"C9","Unicode dec":"128932","Unicode hex":"1F7A4"},{"Typeface name":"Wingdings 2","Dingbat dec":"202","Dingbat hex":"CA","Unicode dec":"128933","Unicode hex":"1F7A5"},{"Typeface name":"Wingdings 2","Dingbat dec":"203","Dingbat hex":"CB","Unicode dec":"128934","Unicode hex":"1F7A6"},{"Typeface name":"Wingdings 2","Dingbat dec":"204","Dingbat hex":"CC","Unicode dec":"128935","Unicode hex":"1F7A7"},{"Typeface name":"Wingdings 2","Dingbat dec":"205","Dingbat hex":"CD","Unicode dec":"128936","Unicode hex":"1F7A8"},{"Typeface name":"Wingdings 2","Dingbat dec":"206","Dingbat hex":"CE","Unicode dec":"128937","Unicode hex":"1F7A9"},{"Typeface name":"Wingdings 2","Dingbat dec":"207","Dingbat hex":"CF","Unicode dec":"128938","Unicode hex":"1F7AA"},{"Typeface name":"Wingdings 2","Dingbat dec":"208","Dingbat hex":"D0","Unicode dec":"128939","Unicode hex":"1F7AB"},{"Typeface name":"Wingdings 2","Dingbat dec":"209","DinLine truncated
|
||||
"Unicode hex":"1F881"},{"Typeface name":"Wingdings 3","Dingbat dec":"228","Dingbat hex":"E4","Unicode dec":"129155","Unicode hex":"1F883"},{"Typeface name":"Wingdings 3","Dingbat dec":"229","Dingbat hex":"E5","Unicode dec":"129156","Unicode hex":"1F884"},{"Typeface name":"Wingdings 3","Dingbat dec":"230","Dingbat hex":"E6","Unicode dec":"129157","Unicode hex":"1F885"},{"Typeface name":"Wingdings 3","Dingbat dec":"231","Dingbat hex":"E7","Unicode dec":"129159","Unicode hex":"1F887"},{"Typeface name":"Wingdings 3","Dingbat dec":"232","Dingbat hex":"E8","Unicode dec":"129158","Unicode hex":"1F886"},{"Typeface name":"Wingdings 3","Dingbat dec":"233","Dingbat hex":"E9","Unicode dec":"129168","Unicode hex":"1F890"},{"Typeface name":"Wingdings 3","Dingbat dec":"234","Dingbat hex":"EA","Unicode dec":"129170","Unicode hex":"1F892"},{"Typeface name":"Wingdings 3","Dingbat dec":"235","Dingbat hex":"EB","Unicode dec":"129169","Unicode hex":"1F891"},{"Typeface name":"Wingdings 3","Dingbat dec":"236","Dingbat hex":"EC","Unicode dec":"129171","Unicode hex":"1F893"},{"Typeface name":"Wingdings 3","Dingbat dec":"237","Dingbat hex":"ED","Unicode dec":"129172","Unicode hex":"1F894"},{"Typeface name":"Wingdings 3","Dingbat dec":"238","Dingbat hex":"EE","Unicode dec":"129174","Unicode hex":"1F896"},{"Typeface name":"Wingdings 3","Dingbat dec":"239","Dingbat hex":"EF","Unicode dec":"129173","Unicode hex":"1F895"},{"Typeface name":"Wingdings 3","Dingbat dec":"240","Dingbat hex":"F0","Unicode dec":"129175","Unicode hex":"1F897"}];exports["default"]=dingbats},{}],85:[function(require,module,exports){"use strict";function codePoint(typeface,codePoint){return dingbatsByCodePoint[typeface.toUpperCase()+"_"+codePoint]}function dec(typeface,dec){return codePoint(typeface,parseInt(dec,10))}function hex(typeface,hex){return codePoint(typeface,parseInt(hex,16))}function fromCodePointPolyfill(codePoint){if(65535>=codePoint)return String.fromCharCode(codePoint);var highSurrogate=Math.floor((codePoint-65536)/1024)+55296,lowSurrogate=(codePoint-65536)%1024+56320;return String.fromCharCode(highSurrogate,lowSurrogate)}var __importDefault=this&&this.__importDefault||function(mod){return mod&&mod.__esModule?mod:{"default":mod}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.hex=exports.dec=exports.codePoint=void 0;for(var dingbats_1=__importDefault(require("./dingbats")),dingbatsByCodePoint={},fromCodePoint=String.fromCodePoint?String.fromCodePoint:fromCodePointPolyfill,_i=0,dingbats_2=dingbats_1["default"];_i<dingbats_2.length;_i++){var dingbat=dingbats_2[_i],codePoint_1=parseInt(dingbat["Unicode dec"],10),scalarValue={codePoint:codePoint_1,string:fromCodePoint(codePoint_1)};dingbatsByCodePoint[dingbat["Typeface name"].toUpperCase()+"_"+dingbat["Dingbat dec"]]=scalarValue}exports.codePoint=codePoint,exports.dec=dec,exports.hex=hex},{"./dingbats":84}],86:[function(require,module,exports){exports.read=function(buffer,offset,isLE,mLen,nBytes){var e,m,eLen=8*nBytes-mLen-1,eMax=(1<<eLen)-1,eBias=eMax>>1,nBits=-7,i=isLE?nBytes-1:0,d=isLE?-1:1,s=buffer[offset+i];for(i+=d,e=s&(1<<-nBits)-1,s>>=-nBits,nBits+=eLen;nBits>0;e=256*e+buffer[offset+i],i+=d,nBits-=8);for(m=e&(1<<-nBits)-1,e>>=-nBits,nBits+=mLen;nBits>0;m=256*m+buffer[offset+i],i+=d,nBits-=8);if(0===e)e=1-eBias;else{if(e===eMax)return m?NaN:(s?-1:1)*(1/0);m+=Math.pow(2,mLen),e-=eBias}return(s?-1:1)*m*Math.pow(2,e-mLen)},exports.write=function(buffer,value,offset,isLE,mLen,nBytes){var e,m,c,eLen=8*nBytes-mLen-1,eMax=(1<<eLen)-1,eBias=eMax>>1,rt=23===mLen?Math.pow(2,-24)-Math.pow(2,-77):0,i=isLE?0:nBytes-1,d=isLE?1:-1,s=0>value||0===value&&0>1/value?1:0;for(value=Math.abs(value),isNaN(value)||value===1/0?(m=isNaN(value)?1:0,e=eMax):(e=Math.floor(Math.log(value)/Math.LN2),value*(c=Math.pow(2,-e))<1&&(e--,c*=2),value+=e+eBias>=1?rt/c:rt*Math.pow(2,1-eBias),value*c>=2&&(e++,c/=2),e+eBias>=eMax?(m=0,e=eMax):e+eBias>=1?(m=(value*c-1)*Math.pow(2,mLen),e+=eBias):(m=value*Math.pow(2,eBias-1)*Math.pow(2,mLen),e=0));mLen>=8;buffer[offset+i]=255&m,i+=d,m/=256,mLen-=8);for(e=e<<mLen|m,eLen+=mLen;eLen>0;buffer[offset+i]=255&e,i+=d,e/=256,eLen-=8);buffer[offset+i-d]|=128*s}},{}],87:[function(require,module,exports){var toString={}.toString;module.exports=Array.isArray||function(arr){return"[object Array]"==toString.call(arr)}},{}],88:[function(require,module,exports){(function(global,Buffer){!function(t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).JSZip=t()}(function(){return function s(a,o,h){function u(r,t){if(!o[r]){if(!a[r]){var e="function"==typeof require&&require;if(!t&&e)return e(r,!0);if(l)return l(r,!0);var i=new Error("Cannot find module '"+r+"'");throw i.code="MODULE_NOT_FOUND",i}var n=o[r]={exports:{}};a[r][0].call(n.exports,function(t){var e=a[r][1][t];return u(e||t)},n,n.exports,s,a,o,h)}return o[r].Line truncated
|
||||
h.delay(e,arguments,r)}),this},resume:function(){return h.delay(this._worker.resume,[],this._worker),this},pause:function(){return this._worker.pause(),this},toNodejsStream:function(t){if(h.checkSupport("nodestream"),"nodebuffer"!==this._outputType)throw new Error(this._outputType+" is not supported by this method");return new o(this,{objectMode:"nodebuffer"!==this._outputType},t)}},e.exports=f},{"../base64":1,"../external":6,"../nodejs/NodejsStreamOutputAdapter":13,"../support":30,"../utils":32,"./ConvertWorker":24,"./GenericWorker":28}],30:[function(t,e,r){"use strict";if(r.base64=!0,r.array=!0,r.string=!0,r.arraybuffer="undefined"!=typeof ArrayBuffer&&"undefined"!=typeof Uint8Array,r.nodebuffer="undefined"!=typeof Buffer,r.uint8array="undefined"!=typeof Uint8Array,"undefined"==typeof ArrayBuffer)r.blob=!1;else{var i=new ArrayBuffer(0);try{r.blob=0===new Blob([i],{type:"application/zip"}).size}catch(t){try{var n=new(self.BlobBuilder||self.WebKitBlobBuilder||self.MozBlobBuilder||self.MSBlobBuilder);n.append(i),r.blob=0===n.getBlob("application/zip").size}catch(t){r.blob=!1}}}try{r.nodestream=!!t("readable-stream").Readable}catch(t){r.nodestream=!1}},{"readable-stream":16}],31:[function(t,e,s){"use strict";function a(){i.call(this,"utf-8 decode"),this.leftOver=null}function l(){i.call(this,"utf-8 encode")}for(var o=t("./utils"),h=t("./support"),r=t("./nodejsUtils"),i=t("./stream/GenericWorker"),u=new Array(256),n=0;256>n;n++)u[n]=n>=252?6:n>=248?5:n>=240?4:n>=224?3:n>=192?2:1;u[254]=u[254]=1,s.utf8encode=function(t){return h.nodebuffer?r.newBufferFrom(t,"utf-8"):function(t){var e,r,i,n,s,a=t.length,o=0;for(n=0;a>n;n++)55296==(64512&(r=t.charCodeAt(n)))&&a>n+1&&56320==(64512&(i=t.charCodeAt(n+1)))&&(r=65536+(r-55296<<10)+(i-56320),n++),o+=128>r?1:2048>r?2:65536>r?3:4;for(e=h.uint8array?new Uint8Array(o):new Array(o),n=s=0;o>s;n++)55296==(64512&(r=t.charCodeAt(n)))&&a>n+1&&56320==(64512&(i=t.charCodeAt(n+1)))&&(r=65536+(r-55296<<10)+(i-56320),n++),128>r?e[s++]=r:(2048>r?e[s++]=192|r>>>6:(65536>r?e[s++]=224|r>>>12:(e[s++]=240|r>>>18,e[s++]=128|r>>>12&63),e[s++]=128|r>>>6&63),e[s++]=128|63&r);return e}(t)},s.utf8decode=function(t){return h.nodebuffer?o.transformTo("nodebuffer",t).toString("utf-8"):function(t){var e,r,i,n,s=t.length,a=new Array(2*s);for(e=r=0;s>e;)if((i=t[e++])<128)a[r++]=i;else if(4<(n=u[i]))a[r++]=65533,e+=n-1;else{for(i&=2===n?31:3===n?15:7;n>1&&s>e;)i=i<<6|63&t[e++],n--;n>1?a[r++]=65533:65536>i?a[r++]=i:(i-=65536,a[r++]=55296|i>>10&1023,a[r++]=56320|1023&i)}return a.length!==r&&(a.subarray?a=a.subarray(0,r):a.length=r),o.applyFromCharCode(a)}(t=o.transformTo(h.uint8array?"uint8array":"array",t))},o.inherits(a,i),a.prototype.processChunk=function(t){var e=o.transformTo(h.uint8array?"uint8array":"array",t.data);if(this.leftOver&&this.leftOver.length){if(h.uint8array){var r=e;(e=new Uint8Array(r.length+this.leftOver.length)).set(this.leftOver,0),e.set(r,this.leftOver.length)}else e=this.leftOver.concat(e);this.leftOver=null}var i=function(t,e){var r;for((e=e||t.length)>t.length&&(e=t.length),r=e-1;r>=0&&128==(192&t[r]);)r--;return 0>r?e:0===r?e:r+u[t[r]]>e?r:e}(e),n=e;i!==e.length&&(h.uint8array?(n=e.subarray(0,i),this.leftOver=e.subarray(i,e.length)):(n=e.slice(0,i),this.leftOver=e.slice(i,e.length))),this.push({data:s.utf8decode(n),meta:t.meta})},a.prototype.flush=function(){this.leftOver&&this.leftOver.length&&(this.push({data:s.utf8decode(this.leftOver),meta:{}}),this.leftOver=null)},s.Utf8DecodeWorker=a,o.inherits(l,i),l.prototype.processChunk=function(t){this.push({data:s.utf8encode(t.data),meta:t.meta})},s.Utf8EncodeWorker=l},{"./nodejsUtils":14,"./stream/GenericWorker":28,"./support":30,"./utils":32}],32:[function(t,e,a){"use strict";function n(t){return t}function l(t,e){for(var r=0;r<t.length;++r)e[r]=255&t.charCodeAt(r);return e}function f(t){var e=65536,r=a.getTypeOf(t),i=!0;if("uint8array"===r?i=s.applyCanBeUsed.uint8array:"nodebuffer"===r&&(i=s.applyCanBeUsed.nodebuffer),i)for(;e>1;)try{return s.stringifyByChunk(t,r,e)}catch(t){e=Math.floor(e/2)}return s.stringifyByChar(t)}function d(t,e){for(var r=0;r<t.length;r++)e[r]=t[r];return e}var o=t("./support"),h=t("./base64"),r=t("./nodejsUtils"),i=t("set-immediate-shim"),u=t("./external");a.newBlob=function(e,r){a.checkSupport("blob");try{return new Blob([e],{type:r})}catch(t){try{var i=new(self.BlobBuilder||self.WebKitBlobBuilder||self.MozBlobBuilder||self.MSBlobBuilder);return i.append(e),i.getBlob(r)}catch(t){throw new Error("Bug : can't construct the Blob.")}}};var s={stringifyByChunk:function(t,e,r){var i=[],n=0,s=t.length;if(r>=s)return String.fromCharCode.apply(null,t);for(;s>n;)"array"===e||"nodebuffer"===e?i.push(String.fromCharCode.apply(null,t.slice(n,Math.min(n+r,s)))):i.push(String.fromCharCode.apply(null,t.subarray(n,Math.min(n+r,s)))),n+=r;return i.join("")},stringifyByChar:function(t){for(var e="",r=0;r<t.length;r++)e+=String.fromCharCode(t[r]);return e},applyCanBeUsed:{uint8array:function(){try{return o.uint8array&&1=Line truncated
|
||||
t.pending_buf[t.pending++]=e>>>8&255,t.pending_buf[t.pending++]=255&e}function L(t,e){var r,i,n=t.max_chain_length,s=t.strstart,a=t.prev_length,o=t.nice_match,h=t.strstart>t.w_size-z?t.strstart-(t.w_size-z):0,u=t.window,l=t.w_mask,f=t.prev,d=t.strstart+S,c=u[s+a-1],p=u[s+a];t.prev_length>=t.good_match&&(n>>=2),o>t.lookahead&&(o=t.lookahead);do if(u[(r=e)+a]===p&&u[r+a-1]===c&&u[r]===u[s]&&u[++r]===u[s+1]){s+=2,r++;do;while(u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&d>s);if(i=S-(d-s),s=d-S,i>a){if(t.match_start=e,o<=(a=i))break;c=u[s+a-1],p=u[s+a]}}while((e=f[e&l])>h&&0!=--n);return a<=t.lookahead?a:t.lookahead}function j(t){var e,r,i,n,s,a,o,h,u,l,f=t.w_size;do{if(n=t.window_size-t.lookahead-t.strstart,t.strstart>=f+(f-z)){for(d.arraySet(t.window,t.window,f,f,0),t.match_start-=f,t.strstart-=f,t.block_start-=f,e=r=t.hash_size;i=t.head[--e],t.head[e]=i>=f?i-f:0,--r;);for(e=r=f;i=t.prev[--e],t.prev[e]=i>=f?i-f:0,--r;);n+=f}if(0===t.strm.avail_in)break;if(a=t.strm,o=t.window,h=t.strstart+t.lookahead,u=n,l=void 0,l=a.avail_in,l>u&&(l=u),r=0===l?0:(a.avail_in-=l,d.arraySet(o,a.input,a.next_in,l,h),1===a.state.wrap?a.adler=c(a.adler,o,l,h):2===a.state.wrap&&(a.adler=p(a.adler,o,l,h)),a.next_in+=l,a.total_in+=l,l),t.lookahead+=r,t.lookahead+t.insert>=x)for(s=t.strstart-t.insert,t.ins_h=t.window[s],t.ins_h=(t.ins_h<<t.hash_shift^t.window[s+1])&t.hash_mask;t.insert&&(t.ins_h=(t.ins_h<<t.hash_shift^t.window[s+x-1])&t.hash_mask,t.prev[s&t.w_mask]=t.head[t.ins_h],t.head[t.ins_h]=s,s++,t.insert--,!(t.lookahead+t.insert<x)););}while(t.lookahead<z&&0!==t.strm.avail_in)}function Z(t,e){for(var r,i;;){if(t.lookahead<z){if(j(t),t.lookahead<z&&e===l)return A;if(0===t.lookahead)break}if(r=0,t.lookahead>=x&&(t.ins_h=(t.ins_h<<t.hash_shift^t.window[t.strstart+x-1])&t.hash_mask,r=t.prev[t.strstart&t.w_mask]=t.head[t.ins_h],t.head[t.ins_h]=t.strstart),0!==r&&t.strstart-r<=t.w_size-z&&(t.match_length=L(t,r)),t.match_length>=x)if(i=u._tr_tally(t,t.strstart-t.match_start,t.match_length-x),t.lookahead-=t.match_length,t.match_length<=t.max_lazy_match&&t.lookahead>=x){for(t.match_length--;t.strstart++,t.ins_h=(t.ins_h<<t.hash_shift^t.window[t.strstart+x-1])&t.hash_mask,r=t.prev[t.strstart&t.w_mask]=t.head[t.ins_h],t.head[t.ins_h]=t.strstart,0!=--t.match_length;);t.strstart++}else t.strstart+=t.match_length,t.match_length=0,t.ins_h=t.window[t.strstart],t.ins_h=(t.ins_h<<t.hash_shift^t.window[t.strstart+1])&t.hash_mask;else i=u._tr_tally(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++;if(i&&(N(t,!1),0===t.strm.avail_out))return A}return t.insert=t.strstart<x-1?t.strstart:x-1,e===f?(N(t,!0),0===t.strm.avail_out?O:B):t.last_lit&&(N(t,!1),0===t.strm.avail_out)?A:I}function W(t,e){for(var r,i,n;;){if(t.lookahead<z){if(j(t),t.lookahead<z&&e===l)return A;if(0===t.lookahead)break}if(r=0,t.lookahead>=x&&(t.ins_h=(t.ins_h<<t.hash_shift^t.window[t.strstart+x-1])&t.hash_mask,r=t.prev[t.strstart&t.w_mask]=t.head[t.ins_h],t.head[t.ins_h]=t.strstart),t.prev_length=t.match_length,t.prev_match=t.match_start,t.match_length=x-1,0!==r&&t.prev_length<t.max_lazy_match&&t.strstart-r<=t.w_size-z&&(t.match_length=L(t,r),t.match_length<=5&&(1===t.strategy||t.match_length===x&&4096<t.strstart-t.match_start)&&(t.match_length=x-1)),t.prev_length>=x&&t.match_length<=t.prev_length){for(n=t.strstart+t.lookahead-x,i=u._tr_tally(t,t.strstart-1-t.prev_match,t.prev_length-x),t.lookahead-=t.prev_length-1,t.prev_length-=2;++t.strstart<=n&&(t.ins_h=(t.ins_h<<t.hash_shift^t.window[t.strstart+x-1])&t.hash_mask,r=t.prev[t.strstart&t.w_mask]=t.head[t.ins_h],t.head[t.ins_h]=t.strstart),0!=--t.prev_length;);if(t.match_available=0,t.match_length=x-1,t.strstart++,i&&(N(t,!1),0===t.strm.avail_out))return A}else if(t.match_available){if((i=u._tr_tally(t,0,t.window[t.strstart-1]))&&N(t,!1),t.strstart++,t.lookahead--,0===t.strm.avail_out)return A}else t.match_available=1,t.strstart++,t.lookahead--}return t.match_available&&(i=u._tr_tally(t,0,t.window[t.strstart-1]),t.match_available=0),t.insert=t.strstart<x-1?t.strstart:x-1,e===f?(N(t,!0),0===t.strm.avail_out?O:B):t.last_lit&&(N(t,!1),0===t.strm.avail_out)?A:I}function M(t,e,r,i,n){this.good_length=t,this.max_lazy=e,this.nice_length=r,this.max_chain=i,this.func=n}function H(){this.strm=null,this.status=0,this.pending_buf=null,this.pending_buf_size=0,this.pending_out=0,this.pending=0,this.wrap=0,this.gzhead=null,this.gzindex=0,this.method=v,this.last_flush=-1,this.w_size=0,this.w_bits=0,this.w_mask=0,this.window=null,this.window_size=0,this.prev=null,this.head=null,this.ins_h=0,this.hash_size=0,this.hash_bits=0,this.hash_mask=0,this.hash_shift=0,this.block_start=0,this.match_length=0,this.prev_match=0,this.match_available=0,this.strstart=0,this.match_start=0,this.lookahead=0,this.prev_length=0,this.max_chain_length=0,this.max_lazy_match=0,this.level=0,this.strategy=0,this.good_match=0,this.nice_match=0,this.dyn_ltree=new d.Buf16(2*w),this.dynLine truncated
|
||||
s=n,u=(o=0)===a?(h=138,3):n===a?(h=6,3):(h=7,4))}function V(t,e,r){var i,n,s=-1,a=e[1],o=0,h=7,u=4;for(0===a&&(h=138,u=3),i=0;r>=i;i++)if(n=a,a=e[2*(i+1)+1],!(++o<h&&n===a)){if(u>o)for(;L(t,n,t.bl_tree),0!=--o;);else 0!==n?(n!==s&&(L(t,n,t.bl_tree),o--),L(t,b,t.bl_tree),P(t,o-3,2)):10>=o?(L(t,v,t.bl_tree),P(t,o-3,3)):(L(t,y,t.bl_tree),P(t,o-11,7));s=n,u=(o=0)===a?(h=138,3):n===a?(h=6,3):(h=7,4)}}function J(t,e,r,i){P(t,(s<<1)+(i?1:0),3),function(t,e,r,i){M(t),i&&(U(t,r),U(t,~r)),n.arraySet(t.pending_buf,t.window,e,r,t.pending),t.pending+=r}(t,e,r,!0)}var n=t("../utils/common"),o=0,h=1,s=0,a=29,u=256,l=u+1+a,f=30,d=19,_=2*l+1,g=15,c=16,p=7,m=256,b=16,v=17,y=18,w=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0],k=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],x=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],S=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],z=new Array(2*(l+2));i(z);var C=new Array(2*f);i(C);var E=new Array(512);i(E);var A=new Array(256);i(A);var I=new Array(a);i(I);var O,B,R,T=new Array(f);i(T);var q=!1;r._tr_init=function(t){q||(function(){var t,e,r,i,n,s=new Array(g+1);for(i=r=0;a-1>i;i++)for(I[i]=r,t=0;t<1<<w[i];t++)A[r++]=i;for(A[r-1]=i,i=n=0;16>i;i++)for(T[i]=n,t=0;t<1<<k[i];t++)E[n++]=i;for(n>>=7;f>i;i++)for(T[i]=n<<7,t=0;t<1<<k[i]-7;t++)E[256+n++]=i;for(e=0;g>=e;e++)s[e]=0;for(t=0;143>=t;)z[2*t+1]=8,t++,s[8]++;for(;255>=t;)z[2*t+1]=9,t++,s[9]++;for(;279>=t;)z[2*t+1]=7,t++,s[7]++;for(;287>=t;)z[2*t+1]=8,t++,s[8]++;for(Z(z,l+1,s),t=0;f>t;t++)C[2*t+1]=5,C[2*t]=j(t,5);O=new D(z,w,u+1,l,g),B=new D(C,k,0,f,g),R=new D(new Array(0),x,0,d,p)}(),q=!0),t.l_desc=new F(t.dyn_ltree,O),t.d_desc=new F(t.dyn_dtree,B),t.bl_desc=new F(t.bl_tree,R),t.bi_buf=0,t.bi_valid=0,W(t)},r._tr_stored_block=J,r._tr_flush_block=function(t,e,r,i){var n,s,a=0;0<t.level?(2===t.strm.data_type&&(t.strm.data_type=function(t){var e,r=4093624447;for(e=0;31>=e;e++,r>>>=1)if(1&r&&0!==t.dyn_ltree[2*e])return o;if(0!==t.dyn_ltree[18]||0!==t.dyn_ltree[20]||0!==t.dyn_ltree[26])return h;for(e=32;u>e;e++)if(0!==t.dyn_ltree[2*e])return h;return o}(t)),Y(t,t.l_desc),Y(t,t.d_desc),a=function(t){var e;for(X(t,t.dyn_ltree,t.l_desc.max_code),X(t,t.dyn_dtree,t.d_desc.max_code),Y(t,t.bl_desc),e=d-1;e>=3&&0===t.bl_tree[2*S[e]+1];e--);return t.opt_len+=3*(e+1)+5+5+4,e}(t),n=t.opt_len+3+7>>>3,(s=t.static_len+3+7>>>3)<=n&&(n=s)):n=s=r+5,n>=r+4&&-1!==e?J(t,e,r,i):4===t.strategy||s===n?(P(t,2+(i?1:0),3),K(t,z,C)):(P(t,4+(i?1:0),3),function(t,e,r,i){var n;for(P(t,e-257,5),P(t,r-1,5),P(t,i-4,4),n=0;i>n;n++)P(t,t.bl_tree[2*S[n]+1],3);V(t,t.dyn_ltree,e-1),V(t,t.dyn_dtree,r-1)}(t,t.l_desc.max_code+1,t.d_desc.max_code+1,a+1),K(t,t.dyn_ltree,t.dyn_dtree)),W(t),i&&M(t)},r._tr_tally=function(t,e,r){return t.pending_buf[t.d_buf+2*t.last_lit]=e>>>8&255,t.pending_buf[t.d_buf+2*t.last_lit+1]=255&e,t.pending_buf[t.l_buf+t.last_lit]=255&r,t.last_lit++,0===e?t.dyn_ltree[2*r]++:(t.matches++,e--,t.dyn_ltree[2*(A[r]+u+1)]++,t.dyn_dtree[2*N(e)]++),t.last_lit===t.lit_bufsize-1},r._tr_align=function(t){P(t,2,3),L(t,m,z),function(t){16===t.bi_valid?(U(t,t.bi_buf),t.bi_buf=0,t.bi_valid=0):8<=t.bi_valid&&(t.pending_buf[t.pending++]=255&t.bi_buf,t.bi_buf>>=8,t.bi_valid-=8)}(t)}},{"../utils/common":41}],53:[function(t,e,r){"use strict";e.exports=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}},{}],54:[function(t,e,r){"use strict";e.exports="function"==typeof setImmediate?setImmediate:function(){var t=[].slice.apply(arguments);t.splice(1,0,0),setTimeout.apply(null,t)}},{}]},{},[10])(10)})}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},require("buffer").Buffer)},{buffer:83}],89:[function(require,module,exports){exports.Parser=require("./lib/parser").Parser,exports.rules=require("./lib/rules"),exports.errors=require("./lib/errors"),exports.results=require("./lib/parsing-results"),exports.StringSource=require("./lib/StringSource"),exports.Token=require("./lib/Token"),exports.bottomUp=require("./lib/bottom-up"),exports.RegexTokeniser=require("./lib/regex-tokeniser").RegexTokeniser,exports.rule=function(ruleBuilder){var rule;return function(input){return rule||(rule=ruleBuilder()),rule(input)}}},{"./lib/StringSource":90,"./lib/Token":91,"./lib/bottom-up":93,"./lib/errors":94,"./lib/parser":96,"./lib/parsing-results":97,"./lib/regex-tokeniser":98,"./lib/rules":99}],90:[function(require,module,exports){var util=require("util"),StringSourceRange=(module.exports=function(string,description){var self={asString:function(){return string},range:function(startIndex,endIndex){return new StringSourceRange(string,description,startIndex,endIndex)}};return self},function(string,description,startIndex,endIndex){this._string=string,this._description=description,this._startIndex=startIndex,this._endIndex=endIndex});StringSourceRange.prototype.to=functioLine truncated
|
||||
for(var length=path.length,i=0;length>i;i++){if(null==obj)return void 0;obj=obj[path[i]]}return length?obj:void 0}function get(object,path,defaultValue){var value=deepGet(object,toPath(path));return isUndefined(value)?defaultValue:value}function has(obj,path){path=toPath(path);for(var length=path.length,i=0;length>i;i++){var key=path[i];if(!has$1(obj,key))return!1;obj=obj[key]}return!!length}function identity(value){return value}function matcher(attrs){return attrs=extendOwn({},attrs),function(obj){return isMatch(obj,attrs)}}function property(path){return path=toPath(path),function(obj){return deepGet(obj,path)}}function optimizeCb(func,context,argCount){if(void 0===context)return func;switch(null==argCount?3:argCount){case 1:return function(value){return func.call(context,value)};case 3:return function(value,index,collection){return func.call(context,value,index,collection)};case 4:return function(accumulator,value,index,collection){return func.call(context,accumulator,value,index,collection)}}return function(){return func.apply(context,arguments)}}function baseIteratee(value,context,argCount){return null==value?identity:isFunction$1(value)?optimizeCb(value,context,argCount):isObject(value)&&!isArray(value)?matcher(value):property(value)}function iteratee(value,context){return baseIteratee(value,context,1/0)}function cb(value,context,argCount){return _$1.iteratee!==iteratee?_$1.iteratee(value,context):baseIteratee(value,context,argCount)}function mapObject(obj,iteratee,context){iteratee=cb(iteratee,context);for(var _keys=keys(obj),length=_keys.length,results={},index=0;length>index;index++){var currentKey=_keys[index];results[currentKey]=iteratee(obj[currentKey],currentKey,obj)}return results}function noop(){}function propertyOf(obj){return null==obj?noop:function(path){return get(obj,path)}}function times(n,iteratee,context){var accum=Array(Math.max(0,n));iteratee=optimizeCb(iteratee,context,1);for(var i=0;n>i;i++)accum[i]=iteratee(i);return accum}function random(min,max){return null==max&&(max=min,min=0),min+Math.floor(Math.random()*(max-min+1))}function createEscaper(map){var escaper=function(match){return map[match]},source="(?:"+keys(map).join("|")+")",testRegexp=RegExp(source),replaceRegexp=RegExp(source,"g");return function(string){return string=null==string?"":""+string,testRegexp.test(string)?string.replace(replaceRegexp,escaper):string}}function escapeChar(match){return"\\"+escapes[match]}function template(text,settings,oldSettings){!settings&&oldSettings&&(settings=oldSettings),settings=defaults({},settings,_$1.templateSettings);var matcher=RegExp([(settings.escape||noMatch).source,(settings.interpolate||noMatch).source,(settings.evaluate||noMatch).source].join("|")+"|$","g"),index=0,source="__p+='";text.replace(matcher,function(match,escape,interpolate,evaluate,offset){return source+=text.slice(index,offset).replace(escapeRegExp,escapeChar),index=offset+match.length,escape?source+="'+\n((__t=("+escape+"))==null?'':_.escape(__t))+\n'":interpolate?source+="'+\n((__t=("+interpolate+"))==null?'':__t)+\n'":evaluate&&(source+="';\n"+evaluate+"\n__p+='"),match}),source+="';\n";var argument=settings.variable;if(argument){if(!bareIdentifier.test(argument))throw new Error("variable is not a bare identifier: "+argument)}else source="with(obj||{}){\n"+source+"}\n",argument="obj";source="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+source+"return __p;\n";var render;try{render=new Function(argument,"_",source)}catch(e){throw e.source=source,e}var template=function(data){return render.call(this,data,_$1)};return template.source="function("+argument+"){\n"+source+"}",template}function result(obj,path,fallback){path=toPath(path);var length=path.length;if(!length)return isFunction$1(fallback)?fallback.call(obj):fallback;for(var i=0;length>i;i++){var prop=null==obj?void 0:obj[path[i]];void 0===prop&&(prop=fallback,i=length),obj=isFunction$1(prop)?prop.call(obj):prop}return obj}function uniqueId(prefix){var id=++idCounter+"";return prefix?prefix+id:id}function chain(obj){var instance=_$1(obj);return instance._chain=!0,instance}function executeBound(sourceFunc,boundFunc,context,callingContext,args){if(!(callingContext instanceof boundFunc))return sourceFunc.apply(context,args);var self=baseCreate(sourceFunc.prototype),result=sourceFunc.apply(self,args);return isObject(result)?result:self}function flatten$1(input,depth,strict,output){if(output=output||[],depth||0===depth){if(0>=depth)return output.concat(input)}else depth=1/0;for(var idx=output.length,i=0,length=getLength(input);length>i;i++){var value=input[i];if(isArrayLike(value)&&(isArray(value)||isArguments$1(value)))if(depth>1)flatten$1(value,depth-1,strict,output),idx=output.length;else for(var j=0,len=value.length;len>j;)output[idx++]=value[j++];else strict||(output[idx++]=value)}return output}function memoize(func,hasher){var memoize=function(key){var cache=memoize.cache,address=""+(hasher?hasher.apply(this,argLine truncated
|
||||
if(i>=len)return x;switch(x){case"%s":return String(args[i++]);case"%d":return Number(args[i++]);case"%j":try{return JSON.stringify(args[i++])}catch(_){return"[Circular]"}default:return x}}),x=args[i];len>i;x=args[++i])str+=isNull(x)||!isObject(x)?" "+x:" "+inspect(x);return str},exports.deprecate=function(fn,msg){function deprecated(){if(!warned){if(process.throwDeprecation)throw new Error(msg);process.traceDeprecation?console.trace(msg):console.error(msg),warned=!0}return fn.apply(this,arguments)}if(isUndefined(global.process))return function(){return exports.deprecate(fn,msg).apply(this,arguments)};if(process.noDeprecation===!0)return fn;var warned=!1;return deprecated};var debugEnviron,debugs={};exports.debuglog=function(set){if(isUndefined(debugEnviron)&&(debugEnviron=process.env.NODE_DEBUG||""),set=set.toUpperCase(),!debugs[set])if(new RegExp("\\b"+set+"\\b","i").test(debugEnviron)){var pid=process.pid;debugs[set]=function(){var msg=exports.format.apply(exports,arguments);console.error("%s %d: %s",set,pid,msg)}}else debugs[set]=function(){};return debugs[set]},exports.inspect=inspect,inspect.colors={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]},inspect.styles={special:"cyan",number:"yellow","boolean":"yellow",undefined:"grey","null":"bold",string:"green",date:"magenta",regexp:"red"},exports.isArray=isArray,exports.isBoolean=isBoolean,exports.isNull=isNull,exports.isNullOrUndefined=isNullOrUndefined,exports.isNumber=isNumber,exports.isString=isString,exports.isSymbol=isSymbol,exports.isUndefined=isUndefined,exports.isRegExp=isRegExp,exports.isObject=isObject,exports.isDate=isDate,exports.isError=isError,exports.isFunction=isFunction,exports.isPrimitive=isPrimitive,exports.isBuffer=require("./support/isBuffer");var months=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];exports.log=function(){console.log("%s - %s",timestamp(),exports.format.apply(exports,arguments))},exports.inherits=require("inherits"),exports._extend=function(origin,add){if(!add||!isObject(add))return origin;for(var keys=Object.keys(add),i=keys.length;i--;)origin[keys[i]]=add[keys[i]];return origin}}).call(this,require("_process"),"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./support/isBuffer":104,_process:101,inherits:103}],106:[function(require,module,exports){(function(){var assign,getValue,isArray,isEmpty,isFunction,isObject,isPlainObject,slice=[].slice,hasProp={}.hasOwnProperty;assign=function(){var i,key,len,source,sources,target;if(target=arguments[0],sources=2<=arguments.length?slice.call(arguments,1):[],isFunction(Object.assign))Object.assign.apply(null,arguments);else for(i=0,len=sources.length;len>i;i++)if(source=sources[i],null!=source)for(key in source)hasProp.call(source,key)&&(target[key]=source[key]);return target},isFunction=function(val){return!!val&&"[object Function]"===Object.prototype.toString.call(val)},isObject=function(val){var ref;return!!val&&("function"==(ref=typeof val)||"object"===ref)},isArray=function(val){return isFunction(Array.isArray)?Array.isArray(val):"[object Array]"===Object.prototype.toString.call(val)},isEmpty=function(val){var key;if(isArray(val))return!val.length;for(key in val)if(hasProp.call(val,key))return!1;return!0},isPlainObject=function(val){var ctor,proto;return isObject(val)&&(proto=Object.getPrototypeOf(val))&&(ctor=proto.constructor)&&"function"==typeof ctor&&ctor instanceof ctor&&Function.prototype.toString.call(ctor)===Function.prototype.toString.call(Object)},getValue=function(obj){return isFunction(obj.valueOf)?obj.valueOf():obj},module.exports.assign=assign,module.exports.isFunction=isFunction,module.exports.isObject=isObject,module.exports.isArray=isArray,module.exports.isEmpty=isEmpty,module.exports.isPlainObject=isPlainObject,module.exports.getValue=getValue}).call(this)},{}],107:[function(require,module,exports){(function(){var XMLAttribute;module.exports=XMLAttribute=function(){function XMLAttribute(parent,name,value){if(this.options=parent.options,this.stringify=parent.stringify,this.parent=parent,null==name)throw new Error("Missing attribute name. "+this.debugInfo(name));if(null==value)throw new Error("Missing attribute value. "+this.debugInfo(name));this.name=this.stringify.attName(name),this.value=this.stringify.attValue(value)}return XMLAttribute.prototype.clone=function(){return Object.create(this)},XMLAttribute.prototype.toString=function(options){return this.options.writer.set(options).attribute(this)},XMLAttribute.prototype.debugInfo=function(name){var ref,ref1;return name=name||this.name,null!=name||(null!=(ref=this.parent)?ref.name:void 0)?null==name?"parent: <"+this.parent.name+">":(null!=(ref1=this.parent)?ref1.name:void 0)?"attribute: {"+name+"}, parent: <"+this.parent.name+">":"attribute: {"+name+"}":""},XMLAttribute}()}).call(this)},{}Line truncated
|
||||
len1=val.length;len1>k;k++)item=val[k],childNode={},childNode[key]=item,lastChild=this.element(childNode);else isObject(val)?(lastChild=this.element(key),lastChild.element(val)):lastChild=this.element(key,val)}else lastChild=!this.options.ignoreDecorators&&this.stringify.convertTextKey&&0===name.indexOf(this.stringify.convertTextKey)?this.text(text):!this.options.ignoreDecorators&&this.stringify.convertCDataKey&&0===name.indexOf(this.stringify.convertCDataKey)?this.cdata(text):!this.options.ignoreDecorators&&this.stringify.convertCommentKey&&0===name.indexOf(this.stringify.convertCommentKey)?this.comment(text):!this.options.ignoreDecorators&&this.stringify.convertRawKey&&0===name.indexOf(this.stringify.convertRawKey)?this.raw(text):!this.options.ignoreDecorators&&this.stringify.convertPIKey&&0===name.indexOf(this.stringify.convertPIKey)?this.instruction(name.substr(this.stringify.convertPIKey.length),text):this.node(name,attributes,text);if(null==lastChild)throw new Error("Could not create any elements with: "+name+". "+this.debugInfo());return lastChild},XMLNode.prototype.insertBefore=function(name,attributes,text){var child,i,removed;if(this.isRoot)throw new Error("Cannot insert elements at root level. "+this.debugInfo(name));return i=this.parent.children.indexOf(this),removed=this.parent.children.splice(i),child=this.parent.element(name,attributes,text),Array.prototype.push.apply(this.parent.children,removed),child},XMLNode.prototype.insertAfter=function(name,attributes,text){var child,i,removed;if(this.isRoot)throw new Error("Cannot insert elements at root level. "+this.debugInfo(name));return i=this.parent.children.indexOf(this),removed=this.parent.children.splice(i+1),child=this.parent.element(name,attributes,text),Array.prototype.push.apply(this.parent.children,removed),child},XMLNode.prototype.remove=function(){var i,ref1;if(this.isRoot)throw new Error("Cannot remove the root element. "+this.debugInfo());return i=this.parent.children.indexOf(this),[].splice.apply(this.parent.children,[i,i-i+1].concat(ref1=[])),ref1,this.parent},XMLNode.prototype.node=function(name,attributes,text){var child,ref1;return null!=name&&(name=getValue(name)),attributes||(attributes={}),attributes=getValue(attributes),isObject(attributes)||(ref1=[attributes,text],text=ref1[0],attributes=ref1[1]),child=new XMLElement(this,name,attributes),null!=text&&child.text(text),this.children.push(child),child},XMLNode.prototype.text=function(value){var child;return child=new XMLText(this,value),this.children.push(child),this},XMLNode.prototype.cdata=function(value){var child;return child=new XMLCData(this,value),this.children.push(child),this},XMLNode.prototype.comment=function(value){var child;return child=new XMLComment(this,value),this.children.push(child),this},XMLNode.prototype.commentBefore=function(value){var child,i,removed;return i=this.parent.children.indexOf(this),removed=this.parent.children.splice(i),child=this.parent.comment(value),Array.prototype.push.apply(this.parent.children,removed),this},XMLNode.prototype.commentAfter=function(value){var child,i,removed;return i=this.parent.children.indexOf(this),removed=this.parent.children.splice(i+1),child=this.parent.comment(value),Array.prototype.push.apply(this.parent.children,removed),this},XMLNode.prototype.raw=function(value){var child;return child=new XMLRaw(this,value),this.children.push(child),this},XMLNode.prototype.instruction=function(target,value){var insTarget,insValue,instruction,j,len;if(null!=target&&(target=getValue(target)),null!=value&&(value=getValue(value)),Array.isArray(target))for(j=0,len=target.length;len>j;j++)insTarget=target[j],this.instruction(insTarget);else if(isObject(target))for(insTarget in target)hasProp.call(target,insTarget)&&(insValue=target[insTarget],this.instruction(insTarget,insValue));else isFunction(value)&&(value=value.apply()),instruction=new XMLProcessingInstruction(this,target,value),this.children.push(instruction);return this},XMLNode.prototype.instructionBefore=function(target,value){var child,i,removed;return i=this.parent.children.indexOf(this),removed=this.parent.children.splice(i),child=this.parent.instruction(target,value),Array.prototype.push.apply(this.parent.children,removed),this},XMLNode.prototype.instructionAfter=function(target,value){var child,i,removed;return i=this.parent.children.indexOf(this),removed=this.parent.children.splice(i+1),child=this.parent.instruction(target,value),Array.prototype.push.apply(this.parent.children,removed),this},XMLNode.prototype.declaration=function(version,encoding,standalone){var doc,xmldec;return doc=this.document(),xmldec=new XMLDeclaration(doc,version,encoding,standalone),doc.children[0]instanceof XMLDeclaration?doc.children[0]=xmldec:doc.children.unshift(xmldec),doc.root()||doc},XMLNode.prototype.doctype=function(pubID,sysID){var child,doc,doctype,i,j,k,len,len1,ref1,ref2;for(doc=this.document(),doctype=new XMLDocType(doc,pubID,sysID),ref1=doc.children,i=j=0,len=ref1.length;len>jLine truncated
|
||||
},{}],127:[function(require,module,exports){(function(){var XMLDocument,XMLDocumentCB,XMLStreamWriter,XMLStringWriter,assign,isFunction,ref;ref=require("./Utility"),assign=ref.assign,isFunction=ref.isFunction,XMLDocument=require("./XMLDocument"),XMLDocumentCB=require("./XMLDocumentCB"),XMLStringWriter=require("./XMLStringWriter"),XMLStreamWriter=require("./XMLStreamWriter"),module.exports.create=function(name,xmldec,doctype,options){var doc,root;if(null==name)throw new Error("Root element needs a name.");return options=assign({},xmldec,doctype,options),doc=new XMLDocument(options),root=doc.element(name),options.headless||(doc.declaration(options),(null!=options.pubID||null!=options.sysID)&&doc.doctype(options)),root},module.exports.begin=function(options,onData,onEnd){var ref1;return isFunction(options)&&(ref1=[options,onData],onData=ref1[0],onEnd=ref1[1],options={}),onData?new XMLDocumentCB(options,onData,onEnd):new XMLDocument(options)},module.exports.stringWriter=function(options){return new XMLStringWriter(options)},module.exports.streamWriter=function(stream,options){return new XMLStreamWriter(stream,options)}}).call(this)},{"./Utility":106,"./XMLDocument":116,"./XMLDocumentCB":117,"./XMLStreamWriter":122,"./XMLStringWriter":123}]},{},[21])(21)});
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*! xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
var DO_NOT_EXPORT_CODEPAGE=true;var cptable={version:"1.15.0"};cptable[437]=function(){var e="\0\b\t\n\x0B\f\r !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ ",r=[],t={};for(var a=0;a!=e.length;++a){if(e.charCodeAt(a)!==65533)t[e.charAt(a)]=a;r[a]=e.charAt(a)}return{enc:t,dec:r}}();cptable[620]=function(){var e="\0\b\t\n\x0B\f\r !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ÇüéâäàąçêëèïîćÄĄĘęłôöĆûùŚÖܢ٥śƒŹŻóÓńŃźż¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ ",r=[],t={};for(var a=0;a!=e.length;++a){if(e.charCodeAt(a)!==65533)t[e.charAt(a)]=a;r[a]=e.charAt(a)}return{enc:t,dec:r}}();cptable[737]=function(){var e="\0\b\t\n\x0B\f\r !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρσςτυφχψ░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀ωάέήϊίόύϋώΆΈΉΊΌΎΏ±≥≤ΪΫ÷≈°∙·√ⁿ²■ ",r=[],t={};for(var a=0;a!=e.length;++a){if(e.charCodeAt(a)!==65533)t[e.charAt(a)]=a;r[a]=e.charAt(a)}return{enc:t,dec:r}}();cptable[850]=function(){var e="\0\b\t\n\x0B\f\r !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜø£Ø×ƒáíóúñѪº¿®¬½¼¡«»░▒▓│┤ÁÂÀ©╣║╗╝¢¥┐└┴┬├─┼ãÃ╚╔╩╦╠═╬¤ðÐÊËÈıÍÎÏ┘┌█▄¦Ì▀ÓßÔÒõÕµþÞÚÛÙýݯ´±‗¾¶§÷¸°¨·¹³²■ ",r=[],t={};for(var a=0;a!=e.length;++a){if(e.charCodeAt(a)!==65533)t[e.charAt(a)]=a;r[a]=e.charAt(a)}return{enc:t,dec:r}}();cptable[852]=function(){var e="\0\b\t\n\x0B\f\r !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ÇüéâäůćçłëŐőîŹÄĆÉĹĺôöĽľŚśÖÜŤťŁ×čáíóúĄąŽžĘ꬟Ⱥ«»░▒▓│┤ÁÂĚŞ╣║╗╝Żż┐└┴┬├─┼Ăă╚╔╩╦╠═╬¤đĐĎËďŇÍÎě┘┌█▄ŢŮ▀ÓßÔŃńňŠšŔÚŕŰýÝţ´˝˛ˇ˘§÷¸°¨˙űŘř■ ",r=[],t={};for(var a=0;a!=e.length;++a){if(e.charCodeAt(a)!==65533)t[e.charAt(a)]=a;r[a]=e.charAt(a)}return{enc:t,dec:r}}();cptable[857]=function(){var e="\0\b\t\n\x0B\f\r !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ÇüéâäàåçêëèïîıÄÅÉæÆôöòûùİÖÜø£ØŞşáíóúñÑĞ𿮬½¼¡«»░▒▓│┤ÁÂÀ©╣║╗╝¢¥┐└┴┬├─┼ãÃ╚╔╩╦╠═╬¤ºªÊËÈ�ÍÎÏ┘┌█▄¦Ì▀ÓßÔÒõÕµ�×ÚÛÙìÿ¯´±�¾¶§÷¸°¨·¹³²■ ",r=[],t={};for(var a=0;a!=e.length;++a){if(e.charCodeAt(a)!==65533)t[e.charAt(a)]=a;r[a]=e.charAt(a)}return{enc:t,dec:r}}();cptable[861]=function(){var e="\0\b\t\n\x0B\f\r !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ÇüéâäàåçêëèÐðÞÄÅÉæÆôöþûÝýÖÜø£Ø₧ƒáíóúÁÍÓÚ¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ ",r=[],t={};for(var a=0;a!=e.length;++a){if(e.charCodeAt(a)!==65533)t[e.charAt(a)]=a;r[a]=e.charAt(a)}return{enc:t,dec:r}}();cptable[865]=function(){var e="\0\b\t\n\x0B\f\r !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜø£Ø₧ƒáíóúñѪº¿⌐¬½¼¡«¤░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ ",r=[],t={};for(var a=0;a!=e.length;++a){if(e.charCodeAt(a)!==65533)t[e.charAt(a)]=a;r[a]=e.charAt(a)}return{enc:t,dec:r}}();cptable[866]=function(){var e="\0\b\t\n\x0B\f\r !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\Line truncated
|
||||
}t[153]="����������������������������������������������������������������橜橝橞機橠橢橣橤橦橧橨橩橪橫橬橭橮橯橰橲橳橴橵橶橷橸橺橻橽橾橿檁檂檃檅檆檇檈檉檊檋檌檍檏檒檓檔檕檖檘檙檚檛檜檝檞檟檡檢檣檤檥檦�檧檨檪檭檮檯檰檱檲檳檴檵檶檷檸檹檺檻檼檽檾檿櫀櫁櫂櫃櫄櫅櫆櫇櫈櫉櫊櫋櫌櫍櫎櫏櫐櫑櫒櫓櫔櫕櫖櫗櫘櫙櫚櫛櫜櫝櫞櫟櫠櫡櫢櫣櫤櫥櫦櫧櫨櫩櫪櫫櫬櫭櫮櫯櫰櫱櫲櫳櫴櫵櫶櫷櫸櫹櫺櫻櫼櫽櫾櫿欀欁欂欃欄欅欆欇欈欉權欋欌欍欎欏欐欑欒欓欔欕欖欗欘欙欚欛欜欝欞欟欥欦欨欩欪欫欬欭欮�".split("");for(a=0;a!=t[153].length;++a)if(t[153][a].charCodeAt(0)!==65533){r[t[153][a]]=39168+a;e[39168+a]=t[153][a]}t[154]="����������������������������������������������������������������欯欰欱欳欴欵欶欸欻欼欽欿歀歁歂歄歅歈歊歋歍歎歏歐歑歒歓歔歕歖歗歘歚歛歜歝歞歟歠歡歨歩歫歬歭歮歯歰歱歲歳歴歵歶歷歸歺歽歾歿殀殅殈�殌殎殏殐殑殔殕殗殘殙殜殝殞殟殠殢殣殤殥殦殧殨殩殫殬殭殮殯殰殱殲殶殸殹殺殻殼殽殾毀毃毄毆毇毈毉毊毌毎毐毑毘毚毜毝毞毟毠毢毣毤毥毦毧毨毩毬毭毮毰毱毲毴毶毷毸毺毻毼毾毿氀氁氂氃氄氈氉氊氋氌氎氒気氜氝氞氠氣氥氫氬氭氱氳氶氷氹氺氻氼氾氿汃汄汅汈汋汌汍汎汏汑汒汓汖汘�".split("");for(a=0;a!=t[154].length;++a)if(t[154][a].charCodeAt(0)!==65533){r[t[154][a]]=39424+a;e[39424+a]=t[154][a]}t[155]="����������������������������������������������������������������汙汚汢汣汥汦汧汫汬汭汮汯汱汳汵汷汸決汻汼汿沀沄沇沊沋沍沎沑沒沕沖沗沘沚沜沝沞沠沢沨沬沯沰沴沵沶沷沺泀況泂泃泆泇泈泋泍泎泏泑泒泘�泙泚泜泝泟泤泦泧泩泬泭泲泴泹泿洀洂洃洅洆洈洉洊洍洏洐洑洓洔洕洖洘洜洝洟洠洡洢洣洤洦洨洩洬洭洯洰洴洶洷洸洺洿浀浂浄浉浌浐浕浖浗浘浛浝浟浡浢浤浥浧浨浫浬浭浰浱浲浳浵浶浹浺浻浽浾浿涀涁涃涄涆涇涊涋涍涏涐涒涖涗涘涙涚涜涢涥涬涭涰涱涳涴涶涷涹涺涻涼涽涾淁淂淃淈淉淊�".split("");for(a=0;a!=t[155].length;++a)if(t[155][a].charCodeAt(0)!==65533){r[t[155][a]]=39680+a;e[39680+a]=t[155][a]}t[156]="����������������������������������������������������������������淍淎淏淐淒淓淔淕淗淚淛淜淟淢淣淥淧淨淩淪淭淯淰淲淴淵淶淸淺淽淾淿渀渁渂渃渄渆渇済渉渋渏渒渓渕渘渙減渜渞渟渢渦渧渨渪測渮渰渱渳渵�渶渷渹渻渼渽渾渿湀湁湂湅湆湇湈湉湊湋湌湏湐湑湒湕湗湙湚湜湝湞湠湡湢湣湤湥湦湧湨湩湪湬湭湯湰湱湲湳湴湵湶湷湸湹湺湻湼湽満溁溂溄溇溈溊溋溌溍溎溑溒溓溔溕準溗溙溚溛溝溞溠溡溣溤溦溨溩溫溬溭溮溰溳溵溸溹溼溾溿滀滃滄滅滆滈滉滊滌滍滎滐滒滖滘滙滛滜滝滣滧滪滫滬滭滮滯�".split("");for(a=0;a!=t[156].length;++a)if(t[156][a].charCodeAt(0)!==65533){r[t[156][a]]=39936+a;e[39936+a]=t[156][a]}t[157]="����������������������������������������������������������������滰滱滲滳滵滶滷滸滺滻滼滽滾滿漀漁漃漄漅漇漈漊漋漌漍漎漐漑漒漖漗漘漙漚漛漜漝漞漟漡漢漣漥漦漧漨漬漮漰漲漴漵漷漸漹漺漻漼漽漿潀潁潂�潃潄潅潈潉潊潌潎潏潐潑潒潓潔潕潖潗潙潚潛潝潟潠潡潣潤潥潧潨潩潪潫潬潯潰潱潳潵潶潷潹潻潽潾潿澀澁澂澃澅澆澇澊澋澏澐澑澒澓澔澕澖澗澘澙澚澛澝澞澟澠澢澣澤澥澦澨澩澪澫澬澭澮澯澰澱澲澴澵澷澸澺澻澼澽澾澿濁濃濄濅濆濇濈濊濋濌濍濎濏濐濓濔濕濖濗濘濙濚濛濜濝濟濢濣濤濥�".split("");for(a=0;a!=t[157].length;++a)if(t[157][a].charCodeAt(0)!==65533){r[t[157][a]]=40192+a;e[40192+a]=t[157][a]}t[158]="����������������������������������������������������������������濦濧濨濩濪濫濬濭濰濱濲濳濴濵濶濷濸濹濺濻濼濽濾濿瀀瀁瀂瀃瀄瀅瀆瀇瀈瀉瀊瀋瀌瀍瀎瀏瀐瀒瀓瀔瀕瀖瀗瀘瀙瀜瀝瀞瀟瀠瀡瀢瀤瀥瀦瀧瀨瀩瀪�瀫瀬瀭瀮瀯瀰瀱瀲瀳瀴瀶瀷瀸瀺瀻瀼瀽瀾瀿灀灁灂灃灄灅灆灇灈灉灊灋灍灎灐灑灒灓灔灕灖灗灘灙Line truncated
|
||||
for(a=0;a!=t[236].length;++a)if(t[236][a].charCodeAt(0)!==65533){r[t[236][a]]=60416+a;e[60416+a]=t[236][a]}t[237]="����������������������������������������������������������������鞞鞟鞡鞢鞤鞥鞦鞧鞨鞩鞪鞬鞮鞰鞱鞳鞵鞶鞷鞸鞹鞺鞻鞼鞽鞾鞿韀韁韂韃韄韅韆韇韈韉韊韋韌韍韎韏韐韑韒韓韔韕韖韗韘韙韚韛韜韝韞韟韠韡韢韣�韤韥韨韮韯韰韱韲韴韷韸韹韺韻韼韽韾響頀頁頂頃頄項順頇須頉頊頋頌頍頎怼恝恚恧恁恙恣悫愆愍慝憩憝懋懑戆肀聿沓泶淼矶矸砀砉砗砘砑斫砭砜砝砹砺砻砟砼砥砬砣砩硎硭硖硗砦硐硇硌硪碛碓碚碇碜碡碣碲碹碥磔磙磉磬磲礅磴礓礤礞礴龛黹黻黼盱眄眍盹眇眈眚眢眙眭眦眵眸睐睑睇睃睚睨�".split("");for(a=0;a!=t[237].length;++a)if(t[237][a].charCodeAt(0)!==65533){r[t[237][a]]=60672+a;e[60672+a]=t[237][a]}t[238]="����������������������������������������������������������������頏預頑頒頓頔頕頖頗領頙頚頛頜頝頞頟頠頡頢頣頤頥頦頧頨頩頪頫頬頭頮頯頰頱頲頳頴頵頶頷頸頹頺頻頼頽頾頿顀顁顂顃顄顅顆顇顈顉顊顋題額�顎顏顐顑顒顓顔顕顖顗願顙顚顛顜顝類顟顠顡顢顣顤顥顦顧顨顩顪顫顬顭顮睢睥睿瞍睽瞀瞌瞑瞟瞠瞰瞵瞽町畀畎畋畈畛畲畹疃罘罡罟詈罨罴罱罹羁罾盍盥蠲钅钆钇钋钊钌钍钏钐钔钗钕钚钛钜钣钤钫钪钭钬钯钰钲钴钶钷钸钹钺钼钽钿铄铈铉铊铋铌铍铎铐铑铒铕铖铗铙铘铛铞铟铠铢铤铥铧铨铪�".split("");for(a=0;a!=t[238].length;++a)if(t[238][a].charCodeAt(0)!==65533){r[t[238][a]]=60928+a;e[60928+a]=t[238][a]}t[239]="����������������������������������������������������������������顯顰顱顲顳顴颋颎颒颕颙颣風颩颪颫颬颭颮颯颰颱颲颳颴颵颶颷颸颹颺颻颼颽颾颿飀飁飂飃飄飅飆飇飈飉飊飋飌飍飏飐飔飖飗飛飜飝飠飡飢飣飤�飥飦飩飪飫飬飭飮飯飰飱飲飳飴飵飶飷飸飹飺飻飼飽飾飿餀餁餂餃餄餅餆餇铩铫铮铯铳铴铵铷铹铼铽铿锃锂锆锇锉锊锍锎锏锒锓锔锕锖锘锛锝锞锟锢锪锫锩锬锱锲锴锶锷锸锼锾锿镂锵镄镅镆镉镌镎镏镒镓镔镖镗镘镙镛镞镟镝镡镢镤镥镦镧镨镩镪镫镬镯镱镲镳锺矧矬雉秕秭秣秫稆嵇稃稂稞稔�".split("");for(a=0;a!=t[239].length;++a)if(t[239][a].charCodeAt(0)!==65533){r[t[239][a]]=61184+a;e[61184+a]=t[239][a]}t[240]="����������������������������������������������������������������餈餉養餋餌餎餏餑餒餓餔餕餖餗餘餙餚餛餜餝餞餟餠餡餢餣餤餥餦餧館餩餪餫餬餭餯餰餱餲餳餴餵餶餷餸餹餺餻餼餽餾餿饀饁饂饃饄饅饆饇饈饉�饊饋饌饍饎饏饐饑饒饓饖饗饘饙饚饛饜饝饞饟饠饡饢饤饦饳饸饹饻饾馂馃馉稹稷穑黏馥穰皈皎皓皙皤瓞瓠甬鸠鸢鸨鸩鸪鸫鸬鸲鸱鸶鸸鸷鸹鸺鸾鹁鹂鹄鹆鹇鹈鹉鹋鹌鹎鹑鹕鹗鹚鹛鹜鹞鹣鹦鹧鹨鹩鹪鹫鹬鹱鹭鹳疒疔疖疠疝疬疣疳疴疸痄疱疰痃痂痖痍痣痨痦痤痫痧瘃痱痼痿瘐瘀瘅瘌瘗瘊瘥瘘瘕瘙�".split("");for(a=0;a!=t[240].length;++a)if(t[240][a].charCodeAt(0)!==65533){r[t[240][a]]=61440+a;e[61440+a]=t[240][a]}t[241]="����������������������������������������������������������������馌馎馚馛馜馝馞馟馠馡馢馣馤馦馧馩馪馫馬馭馮馯馰馱馲馳馴馵馶馷馸馹馺馻馼馽馾馿駀駁駂駃駄駅駆駇駈駉駊駋駌駍駎駏駐駑駒駓駔駕駖駗駘�駙駚駛駜駝駞駟駠駡駢駣駤駥駦駧駨駩駪駫駬駭駮駯駰駱駲駳駴駵駶駷駸駹瘛瘼瘢瘠癀瘭瘰瘿瘵癃瘾瘳癍癞癔癜癖癫癯翊竦穸穹窀窆窈窕窦窠窬窨窭窳衤衩衲衽衿袂袢裆袷袼裉裢裎裣裥裱褚裼裨裾裰褡褙褓褛褊褴褫褶襁襦襻疋胥皲皴矜耒耔耖耜耠耢耥耦耧耩耨耱耋耵聃聆聍聒聩聱覃顸颀颃�".split("");for(a=0;a!=t[241].length;++a)if(t[241][a].charCodeAt(0)!==65533){r[t[241][a]]=61696+a;e[61696+a]=t[241][a]}t[242]="����������������������������������������������������������������駺駻駼駽駾駿騀騁騂騃騄騅騆騇騈騉騊騋騌騍騎騏騐騑騒験騔騕騖騗騘騙騚騛騜騝騞騟騠騡騢騣騤騥騦騧騨騩騪騫騬騭騮騯騰騱騲騳騴騵騶騷騸�騹騺騻騼騽騾騿驀Line truncated
|
||||
for(a=0;a!=t[193].length;++a)if(t[193][a].charCodeAt(0)!==65533){r[t[193][a]]=49408+a;e[49408+a]=t[193][a]}t[194]="�����������������������������������������������������������������헊헋헍헎헏헑헓헔헕헖헗헚헜헞헟헠헡헢헣헦헧헩헪헫헭헮������헯헰헱헲헳헶헸헺헻헼헽헾헿혂혃혅혆혇혉혊혋혌혍혎혏혒������혖혗혘혙혚혛혝혞혟혡혢혣혥혦혧혨혩혪혫혬혮혯혰혱혲혳혴혵혶혷혺혻징짖짙짚짜짝짠짢짤짧짬짭짯짰짱째짹짼쨀쨈쨉쨋쨌쨍쨔쨘쨩쩌쩍쩐쩔쩜쩝쩟쩠쩡쩨쩽쪄쪘쪼쪽쫀쫄쫌쫍쫏쫑쫓쫘쫙쫠쫬쫴쬈쬐쬔쬘쬠쬡쭁쭈쭉쭌쭐쭘쭙쭝쭤쭸쭹쮜쮸쯔쯤쯧쯩찌찍찐찔찜찝찡찢찧차착찬찮찰참찹찻�".split("");for(a=0;a!=t[194].length;++a)if(t[194][a].charCodeAt(0)!==65533){r[t[194][a]]=49664+a;e[49664+a]=t[194][a]}t[195]="�����������������������������������������������������������������혽혾혿홁홂홃홄홆홇홊홌홎홏홐홒홓홖홗홙홚홛홝홞홟홠홡������홢홣홤홥홦홨홪홫홬홭홮홯홲홳홵홶홷홸홹홺홻홼홽홾홿횀������횁횂횄횆횇횈횉횊횋횎횏횑횒횓횕횖횗횘횙횚횛횜횞횠횢횣횤횥횦횧횩횪찼창찾채책챈챌챔챕챗챘챙챠챤챦챨챰챵처척천철첨첩첫첬청체첵첸첼쳄쳅쳇쳉쳐쳔쳤쳬쳰촁초촉촌촐촘촙촛총촤촨촬촹최쵠쵤쵬쵭쵯쵱쵸춈추축춘출춤춥춧충춰췄췌췐취췬췰췸췹췻췽츄츈츌츔츙츠측츤츨츰츱츳층�".split("");for(a=0;a!=t[195].length;++a)if(t[195][a].charCodeAt(0)!==65533){r[t[195][a]]=49920+a;e[49920+a]=t[195][a]}t[196]="�����������������������������������������������������������������횫횭횮횯횱횲횳횴횵횶횷횸횺횼횽횾횿훀훁훂훃훆훇훉훊훋������훍훎훏훐훒훓훕훖훘훚훛훜훝훞훟훡훢훣훥훦훧훩훪훫훬훭������훮훯훱훲훳훴훶훷훸훹훺훻훾훿휁휂휃휅휆휇휈휉휊휋휌휍휎휏휐휒휓휔치칙친칟칠칡침칩칫칭카칵칸칼캄캅캇캉캐캑캔캘캠캡캣캤캥캬캭컁커컥컨컫컬컴컵컷컸컹케켁켄켈켐켑켓켕켜켠켤켬켭켯켰켱켸코콕콘콜콤콥콧콩콰콱콴콸쾀쾅쾌쾡쾨쾰쿄쿠쿡쿤쿨쿰쿱쿳쿵쿼퀀퀄퀑퀘퀭퀴퀵퀸퀼�".split("");for(a=0;a!=t[196].length;++a)if(t[196][a].charCodeAt(0)!==65533){r[t[196][a]]=50176+a;e[50176+a]=t[196][a]}t[197]="�����������������������������������������������������������������휕휖휗휚휛휝휞휟휡휢휣휤휥휦휧휪휬휮휯휰휱휲휳휶휷휹������휺휻휽휾휿흀흁흂흃흅흆흈흊흋흌흍흎흏흒흓흕흚흛흜흝흞������흟흢흤흦흧흨흪흫흭흮흯흱흲흳흵흶흷흸흹흺흻흾흿힀힂힃힄힅힆힇힊힋큄큅큇큉큐큔큘큠크큭큰클큼큽킁키킥킨킬킴킵킷킹타탁탄탈탉탐탑탓탔탕태택탠탤탬탭탯탰탱탸턍터턱턴털턺텀텁텃텄텅테텍텐텔템텝텟텡텨텬텼톄톈토톡톤톨톰톱톳통톺톼퇀퇘퇴퇸툇툉툐투툭툰툴툼툽툿퉁퉈퉜�".split("");for(a=0;a!=t[197].length;++a)if(t[197][a].charCodeAt(0)!==65533){r[t[197][a]]=50432+a;e[50432+a]=t[197][a]}t[198]="�����������������������������������������������������������������힍힎힏힑힒힓힔힕힖힗힚힜힞힟힠힡힢힣������������������������������������������������������������������������������퉤튀튁튄튈튐튑튕튜튠튤튬튱트특튼튿틀틂틈틉틋틔틘틜틤틥티틱틴틸팀팁팃팅파팍팎판팔팖팜팝팟팠팡팥패팩팬팰팸팹팻팼팽퍄퍅퍼퍽펀펄펌펍펏펐펑페펙펜펠펨펩펫펭펴편펼폄폅폈평폐폘폡폣포폭폰폴폼폽폿퐁�".split("");for(a=0;a!=t[198].length;++a)if(t[198][a].charCodeAt(0)!==65533){r[t[198][a]]=50688+a;e[50688+a]=t[198][a]}t[199]="����������������������������������������������������������������������������������������������������������������������������������������Line truncated
|
||||
for(a=0;a!=t[184].length;++a)if(t[184][a].charCodeAt(0)!==65533){r[t[184][a]]=47104+a;e[47104+a]=t[184][a]}t[185]="����������������������������������������������������������������辟農運遊道遂達逼違遐遇遏過遍遑逾遁鄒鄗酬酪酩釉鈷鉗鈸鈽鉀鈾鉛鉋鉤鉑鈴鉉鉍鉅鈹鈿鉚閘隘隔隕雍雋雉雊雷電雹零靖靴靶預頑頓頊頒頌飼飴����������������������������������飽飾馳馱馴髡鳩麂鼎鼓鼠僧僮僥僖僭僚僕像僑僱僎僩兢凳劃劂匱厭嗾嘀嘛嘗嗽嘔嘆嘉嘍嘎嗷嘖嘟嘈嘐嗶團圖塵塾境墓墊塹墅塽壽夥夢夤奪奩嫡嫦嫩嫗嫖嫘嫣孵寞寧寡寥實寨寢寤察對屢嶄嶇幛幣幕幗幔廓廖弊彆彰徹慇�".split("");for(a=0;a!=t[185].length;++a)if(t[185][a].charCodeAt(0)!==65533){r[t[185][a]]=47360+a;e[47360+a]=t[185][a]}t[186]="����������������������������������������������������������������愿態慷慢慣慟慚慘慵截撇摘摔撤摸摟摺摑摧搴摭摻敲斡旗旖暢暨暝榜榨榕槁榮槓構榛榷榻榫榴槐槍榭槌榦槃榣歉歌氳漳演滾漓滴漩漾漠漬漏漂漢����������������������������������滿滯漆漱漸漲漣漕漫漯澈漪滬漁滲滌滷熔熙煽熊熄熒爾犒犖獄獐瑤瑣瑪瑰瑭甄疑瘧瘍瘋瘉瘓盡監瞄睽睿睡磁碟碧碳碩碣禎福禍種稱窪窩竭端管箕箋筵算箝箔箏箸箇箄粹粽精綻綰綜綽綾綠緊綴網綱綺綢綿綵綸維緒緇綬�".split("");for(a=0;a!=t[186].length;++a)if(t[186][a].charCodeAt(0)!==65533){r[t[186][a]]=47616+a;e[47616+a]=t[186][a]}t[187]="����������������������������������������������������������������罰翠翡翟聞聚肇腐膀膏膈膊腿膂臧臺與舔舞艋蓉蒿蓆蓄蒙蒞蒲蒜蓋蒸蓀蓓蒐蒼蓑蓊蜿蜜蜻蜢蜥蜴蜘蝕蜷蜩裳褂裴裹裸製裨褚裯誦誌語誣認誡誓誤����������������������������������說誥誨誘誑誚誧豪貍貌賓賑賒赫趙趕跼輔輒輕輓辣遠遘遜遣遙遞遢遝遛鄙鄘鄞酵酸酷酴鉸銀銅銘銖鉻銓銜銨鉼銑閡閨閩閣閥閤隙障際雌雒需靼鞅韶頗領颯颱餃餅餌餉駁骯骰髦魁魂鳴鳶鳳麼鼻齊億儀僻僵價儂儈儉儅凜�".split("");for(a=0;a!=t[187].length;++a)if(t[187][a].charCodeAt(0)!==65533){r[t[187][a]]=47872+a;e[47872+a]=t[187][a]}t[188]="����������������������������������������������������������������劇劈劉劍劊勰厲嘮嘻嘹嘲嘿嘴嘩噓噎噗噴嘶嘯嘰墀墟增墳墜墮墩墦奭嬉嫻嬋嫵嬌嬈寮寬審寫層履嶝嶔幢幟幡廢廚廟廝廣廠彈影德徵慶慧慮慝慕憂����������������������������������慼慰慫慾憧憐憫憎憬憚憤憔憮戮摩摯摹撞撲撈撐撰撥撓撕撩撒撮播撫撚撬撙撢撳敵敷數暮暫暴暱樣樟槨樁樞標槽模樓樊槳樂樅槭樑歐歎殤毅毆漿潼澄潑潦潔澆潭潛潸潮澎潺潰潤澗潘滕潯潠潟熟熬熱熨牖犛獎獗瑩璋璃�".split("");for(a=0;a!=t[188].length;++a)if(t[188][a].charCodeAt(0)!==65533){r[t[188][a]]=48128+a;e[48128+a]=t[188][a]}t[189]="����������������������������������������������������������������瑾璀畿瘠瘩瘟瘤瘦瘡瘢皚皺盤瞎瞇瞌瞑瞋磋磅確磊碾磕碼磐稿稼穀稽稷稻窯窮箭箱範箴篆篇篁箠篌糊締練緯緻緘緬緝編緣線緞緩綞緙緲緹罵罷羯����������������������������������翩耦膛膜膝膠膚膘蔗蔽蔚蓮蔬蔭蔓蔑蔣蔡蔔蓬蔥蓿蔆螂蝴蝶蝠蝦蝸蝨蝙蝗蝌蝓衛衝褐複褒褓褕褊誼諒談諄誕請諸課諉諂調誰論諍誶誹諛豌豎豬賠賞賦賤賬賭賢賣賜質賡赭趟趣踫踐踝踢踏踩踟踡踞躺輝輛輟輩輦輪輜輞�".split("");for(a=0;a!=t[189].length;++a)if(t[189][a].charCodeAt(0)!==65533){r[t[189][a]]=48384+a;e[48384+a]=t[189][a]}t[190]="����������������������������������������������������������������輥適遮遨遭遷鄰鄭鄧鄱醇醉醋醃鋅銻銷鋪銬鋤鋁銳銼鋒鋇鋰銲閭閱霄霆震霉靠鞍鞋鞏頡頫頜颳養餓餒餘駝駐駟駛駑駕駒駙骷髮髯鬧魅魄魷魯鴆鴉���������Line truncated
|
||||
r[t[166][a]]=42496+a;e[42496+a]=t[166][a]}t[167]="�����������������������������������������������������������������������������������������������������������������������������������������������������������������АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ���������������абвгдеёжзийклмнопрстуфхцчшщъыьэюя��������������".split("");for(a=0;a!=t[167].length;++a)if(t[167][a].charCodeAt(0)!==65533){r[t[167][a]]=42752+a;e[42752+a]=t[167][a]}t[168]="�����������������������������������������������������������������������������������������������������������������������������������������������������������������āáǎàēéěèīíǐìōóǒòūúǔùǖǘǚǜüê����������ㄅㄆㄇㄈㄉㄊㄋㄌㄍㄎㄏㄐㄑㄒㄓㄔㄕㄖㄗㄘㄙㄚㄛㄜㄝㄞㄟㄠㄡㄢㄣㄤㄥㄦㄧㄨㄩ����������������������".split("");for(a=0;a!=t[168].length;++a)if(t[168][a].charCodeAt(0)!==65533){r[t[168][a]]=43008+a;e[43008+a]=t[168][a]}t[169]="��������������������������������������������������������������������������������������������������������������������������������������������������������������������─━│┃┄┅┆┇┈┉┊┋┌┍┎┏┐┑┒┓└┕┖┗┘┙┚┛├┝┞┟┠┡┢┣┤┥┦┧┨┩┪┫┬┭┮┯┰┱┲┳┴┵┶┷┸┹┺┻┼┽┾┿╀╁╂╃╄╅╆╇╈╉╊╋����������������".split("");for(a=0;a!=t[169].length;++a)if(t[169][a].charCodeAt(0)!==65533){r[t[169][a]]=43264+a;e[43264+a]=t[169][a]}t[176]="�����������������������������������������������������������������������������������������������������������������������������������������������������������������啊阿埃挨哎唉哀皑癌蔼矮艾碍爱隘鞍氨安俺按暗岸胺案肮昂盎凹敖熬翱袄傲奥懊澳芭捌扒叭吧笆八疤巴拔跋靶把耙坝霸罢爸白柏百摆佰败拜稗斑班搬扳般颁板版扮拌伴瓣半办绊邦帮梆榜膀绑棒磅蚌镑傍谤苞胞包褒剥�".split("");for(a=0;a!=t[176].length;++a)if(t[176][a].charCodeAt(0)!==65533){r[t[176][a]]=45056+a;e[45056+a]=t[176][a]}t[177]="�����������������������������������������������������������������������������������������������������������������������������������������������������������������薄雹保堡饱宝抱报暴豹鲍爆杯碑悲卑北辈背贝钡倍狈备惫焙被奔苯本笨崩绷甭泵蹦迸逼鼻比鄙笔彼碧蓖蔽毕毙毖币庇痹闭敝弊必辟壁臂避陛鞭边编贬扁便变卞辨辩辫遍标彪膘表鳖憋别瘪彬斌濒滨宾摈兵冰柄丙秉饼炳�".split("");for(a=0;a!=t[177].length;++a)if(t[177][a].charCodeAt(0)!==65533){r[t[177][a]]=45312+a;e[45312+a]=t[177][a]}t[178]="�����������������������������������������������������������������������������������������������������������������������������������������������������������������病并玻菠播拨钵波博勃搏铂箔伯帛舶脖膊渤泊驳捕卜哺补埠不Line truncated
|
||||
n=Buffer.allocUnsafe(2*a);s=0;for(i=0;i<a;++i){f=e[i];if(f<128)n[s++]=t[f];else if(f<224){n[s++]=t[((f&31)<<6)+(e[i+1]&63)];++i}else if(f<240){n[s++]=t[((f&15)<<12)+((e[i+1]&63)<<6)+(e[i+2]&63)];i+=2}else{o=((f&7)<<18)+((e[i+1]&63)<<12)+((e[i+2]&63)<<6)+(e[i+3]&63);i+=3;if(o<65536)n[s++]=t[o];else{o-=65536;n[s++]=t[55296+(o>>10&1023)];n[s++]=t[56320+(o&1023)]}}}n=n.slice(0,s)}else{n=Buffer.allocUnsafe(a);for(i=0;i<a;++i)n[i]=t[e[i].charCodeAt(0)]}if(!r||r==="buf")return n;if(r!=="arr")return n.toString("binary");return[].slice.call(n)}};var w=function U(r){var t=e[r].dec;var a=Buffer.allocUnsafe(131072),n=0,i="";for(n=0;n<t.length;++n){if(!(i=t[n]))continue;var s=i.charCodeAt(0);a[2*n]=s&255;a[2*n+1]=s>>8}return function f(e){var r=e.length,t=0,n=0;if(2*r>p){p=2*r;g=Buffer.allocUnsafe(p)}if(Buffer.isBuffer(e)){for(t=0;t<r;t++){n=2*e[t];g[2*t]=a[n];g[2*t+1]=a[n+1]}}else if(typeof e==="string"){for(t=0;t<r;t++){n=2*e.charCodeAt(t);g[2*t]=a[n];g[2*t+1]=a[n+1]}}else{for(t=0;t<r;t++){n=2*e[t];g[2*t]=a[n];g[2*t+1]=a[n+1]}}return g.slice(0,2*r).toString("ucs2")}};var k=function B(r){var t=e[r].enc;var a=Buffer.allocUnsafe(131072);for(var n=0;n<131072;++n)a[n]=0;var i=Object.keys(t);for(var s=0,f=i[s];s<i.length;++s){if(!(f=i[s]))continue;var o=f.charCodeAt(0);a[2*o]=t[f]&255;a[2*o+1]=t[f]>>8}return function l(e,r){var t=e.length,n=Buffer.allocUnsafe(2*t),i=0,s=0,f=0,o=0,l=0;if(typeof e==="string"){for(i=o=0;i<t;++i){s=e.charCodeAt(i)*2;n[o++]=a[s+1]||a[s];if(a[s+1]>0)n[o++]=a[s]}n=n.slice(0,o)}else if(Buffer.isBuffer(e)){for(i=o=0;i<t;++i){l=e[i];if(l<128)s=l;else if(l<224){s=((l&31)<<6)+(e[i+1]&63);++i}else if(l<240){s=((l&15)<<12)+((e[i+1]&63)<<6)+(e[i+2]&63);i+=2}else{s=((l&7)<<18)+((e[i+1]&63)<<12)+((e[i+2]&63)<<6)+(e[i+3]&63);i+=3}if(s<65536){s*=2;n[o++]=a[s+1]||a[s];if(a[s+1]>0)n[o++]=a[s]}else{f=s-65536;s=2*(55296+(f>>10&1023));n[o++]=a[s+1]||a[s];if(a[s+1]>0)n[o++]=a[s];s=2*(56320+(f&1023));n[o++]=a[s+1]||a[s];if(a[s+1]>0)n[o++]=a[s]}}n=n.slice(0,o)}else{for(i=o=0;i<t;i++){s=e[i].charCodeAt(0)*2;n[o++]=a[s+1]||a[s];if(a[s+1]>0)n[o++]=a[s]}}if(!r||r==="buf")return n;if(r!=="arr")return n.toString("binary");return[].slice.call(n)}};var T=function W(r){var t=e[r].dec;var a=Buffer.allocUnsafe(131072),n=0,i,s=0,f=0,o=0;for(o=0;o<65536;++o){a[2*o]=255;a[2*o+1]=253}for(n=0;n<t.length;++n){if(!(i=t[n]))continue;s=i.charCodeAt(0);f=2*n;a[f]=s&255;a[f+1]=s>>8}return function l(e){var r=e.length,t=Buffer.allocUnsafe(2*r),n=0,i=0,s=0;if(Buffer.isBuffer(e)){for(n=0;n<r;n++){i=2*e[n];if(a[i]===255&&a[i+1]===253){i=2*((e[n]<<8)+e[n+1]);++n}t[s++]=a[i];t[s++]=a[i+1]}}else if(typeof e==="string"){for(n=0;n<r;n++){i=2*e.charCodeAt(n);if(a[i]===255&&a[i+1]===253){i=2*((e.charCodeAt(n)<<8)+e.charCodeAt(n+1));++n}t[s++]=a[i];t[s++]=a[i+1]}}else{for(n=0;n<r;n++){i=2*e[n];if(a[i]===255&&a[i+1]===253){i=2*((e[n]<<8)+e[n+1]);++n}t[s++]=a[i];t[s++]=a[i+1]}}return t.slice(0,s).toString("ucs2")}};i[65001]=function H(e){if(typeof e==="string")return H(e.split("").map(c));var r=e.length,t=0,a=0;if(4*r>p){p=4*r;g=Buffer.allocUnsafe(p)}var n=0;if(r>=3&&e[0]==239)if(e[1]==187&&e[2]==191)n=3;for(var i=1,s=0,f=0;n<r;n+=i){i=1;f=e[n];if(f<128)t=f;else if(f<224){t=(f&31)*64+(e[n+1]&63);i=2}else if(f<240){t=((f&15)<<12)+(e[n+1]&63)*64+(e[n+2]&63);i=3}else{t=(f&7)*262144+((e[n+1]&63)<<12)+(e[n+2]&63)*64+(e[n+3]&63);i=4}if(t<65536){g[s++]=t&255;g[s++]=t>>8}else{t-=65536;a=55296+(t>>10&1023);t=56320+(t&1023);g[s++]=a&255;g[s++]=a>>>8;g[s++]=t&255;g[s++]=t>>>8&255}}return g.slice(0,s).toString("ucs2")};s[65001]=function z(e,r){if(h&&Buffer.isBuffer(e)){if(!r||r==="buf")return e;if(r!=="arr")return e.toString("binary");return[].slice.call(e)}var t=e.length,a=0,n=0,i=0;var s=typeof e==="string";if(4*t>p){p=4*t;g=Buffer.allocUnsafe(p)}for(var f=0;f<t;++f){a=s?e.charCodeAt(f):e[f].charCodeAt(0);if(a<=127)g[i++]=a;else if(a<=2047){g[i++]=192+(a>>6);g[i++]=128+(a&63)}else if(a>=55296&&a<=57343){a-=55296;++f;n=(s?e.charCodeAt(f):e[f].charCodeAt(0))-56320+(a<<10);g[i++]=240+(n>>>18&7);g[i++]=144+(n>>>12&63);g[i++]=128+(n>>>6&63);g[i++]=128+(n&63)}else{g[i++]=224+(a>>12);g[i++]=128+(a>>6&63);g[i++]=128+(a&63)}}if(!r||r==="buf")return g.slice(0,i);if(r!=="arr")return g.slice(0,i).toString("binary");return[].slice.call(g,0,i)}}var A=function V(){if(h){if(f[t[0]])return;var r=0,l=0;for(r=0;r<t.length;++r){l=t[r];if(e[l]){f[l]=w(l);o[l]=b(l)}}for(r=0;r<a.length;++r){l=a[r];if(e[l]){f[l]=T(l);o[l]=k(l)}}for(r=0;r<n.length;++r){l=n[r];if(i[l])f[l]=i[l];if(s[l])o[l]=s[l]}}};var E=function(e,r){void r;return""};var C=function G(e){delete f[e];delete o[e]};var y=function j(){if(h){if(!f[t[0]])return;t.forEach(C);a.forEach(C);n.forEach(C)}O=E;R=0};var S={encache:A,decache:y,sbcs:t,dbcs:a};A();var _="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var x="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'(),-./:?";var O=E,R=0;var I=function X(t,a,n){if(t===R&&O){return O(a,n)}if(o[t]){O=o[R=t];return O(a,n)}if(h&&Buffer.isBuffer(a))a=a.Line truncated
|
||||
var n=[],i="",s=0,f="",o="t",l,c,h;var u="H";while(s<e.length){switch(f=e.charAt(s)){case"G":if(!z(e,s))throw new Error("unrecognized character "+f+" in "+e);n[n.length]={t:"G",v:"General"};s+=7;break;case'"':for(i="";(h=e.charCodeAt(++s))!==34&&s<e.length;)i+=String.fromCharCode(h);n[n.length]={t:"t",v:i};++s;break;case"\\":var d=e.charAt(++s),v=d==="("||d===")"?d:"t";n[n.length]={t:v,v:d};++s;break;case"_":n[n.length]={t:"t",v:" "};s+=2;break;case"@":n[n.length]={t:"T",v:r};++s;break;case"B":;case"b":if(e.charAt(s+1)==="1"||e.charAt(s+1)==="2"){if(l==null){l=q(r,t,e.charAt(s+1)==="2");if(l==null)return""}n[n.length]={t:"X",v:e.substr(s,2)};o=f;s+=2;break};case"M":;case"D":;case"Y":;case"H":;case"S":;case"E":f=f.toLowerCase();case"m":;case"d":;case"y":;case"h":;case"s":;case"e":;case"g":if(r<0)return"";if(l==null){l=q(r,t);if(l==null)return""}i=f;while(++s<e.length&&e.charAt(s).toLowerCase()===f)i+=f;if(f==="m"&&o.toLowerCase()==="h")f="M";if(f==="h")f=u;n[n.length]={t:f,v:i};o=f;break;case"A":;case"a":;case"上":var p={t:f,v:f};if(l==null)l=q(r,t);if(e.substr(s,3).toUpperCase()==="A/P"){if(l!=null)p.v=l.H>=12?"P":"A";p.t="T";u="h";s+=3}else if(e.substr(s,5).toUpperCase()==="AM/PM"){if(l!=null)p.v=l.H>=12?"PM":"AM";p.t="T";s+=5;u="h"}else if(e.substr(s,5).toUpperCase()==="上午/下午"){if(l!=null)p.v=l.H>=12?"下午":"上午";p.t="T";s+=5;u="h"}else{p.t="t";++s}if(l==null&&p.t==="T")return"";n[n.length]=p;o=f;break;case"[":i=f;while(e.charAt(s++)!=="]"&&s<e.length)i+=e.charAt(s);if(i.slice(-1)!=="]")throw'unterminated "[" block: |'+i+"|";if(i.match(De)){if(l==null){l=q(r,t);if(l==null)return""}n[n.length]={t:"Z",v:i.toLowerCase()};o=i.charAt(1)}else if(i.indexOf("$")>-1){i=(i.match(/\$([^-\[\]]*)/)||[])[1]||"$";if(!Fe(e))n[n.length]={t:"t",v:i}}break;case".":if(l!=null){i=f;while(++s<e.length&&(f=e.charAt(s))==="0")i+=f;n[n.length]={t:"s",v:i};break};case"0":;case"#":i=f;while(++s<e.length&&"0#?.,E+-%".indexOf(f=e.charAt(s))>-1)i+=f;n[n.length]={t:"n",v:i};break;case"?":i=f;while(e.charAt(++s)===f)i+=f;n[n.length]={t:f,v:i};o=f;break;case"*":++s;if(e.charAt(s)==" "||e.charAt(s)=="*")++s;break;case"(":;case")":n[n.length]={t:a===1?"t":f,v:f};++s;break;case"1":;case"2":;case"3":;case"4":;case"5":;case"6":;case"7":;case"8":;case"9":i=f;while(s<e.length&&"0123456789".indexOf(e.charAt(++s))>-1)i+=e.charAt(s);n[n.length]={t:"D",v:i};break;case" ":n[n.length]={t:f,v:f};++s;break;case"$":n[n.length]={t:"t",v:"$"};++s;break;default:if(",$-+/():!^&'~{}<>=€acfijklopqrtuvwxzP".indexOf(f)===-1)throw new Error("unrecognized character "+f+" in "+e);n[n.length]={t:"t",v:f};++s;break;}}var g=0,m=0,b;for(s=n.length-1,o="t";s>=0;--s){switch(n[s].t){case"h":;case"H":n[s].t=u;o="h";if(g<1)g=1;break;case"s":if(b=n[s].v.match(/\.0+$/))m=Math.max(m,b[0].length-1);if(g<3)g=3;case"d":;case"y":;case"M":;case"e":o=n[s].t;break;case"m":if(o==="s"){n[s].t="M";if(g<2)g=2}break;case"X":break;case"Z":if(g<1&&n[s].v.match(/[Hh]/))g=1;if(g<2&&n[s].v.match(/[Mm]/))g=2;if(g<3&&n[s].v.match(/[Ss]/))g=3;}}switch(g){case 0:break;case 1:if(l.u>=.5){l.u=0;++l.S}if(l.S>=60){l.S=0;++l.M}if(l.M>=60){l.M=0;++l.H}break;case 2:if(l.u>=.5){l.u=0;++l.S}if(l.S>=60){l.S=0;++l.M}break;}var w="",k;for(s=0;s<n.length;++s){switch(n[s].t){case"t":;case"T":;case" ":;case"D":break;case"X":n[s].v="";n[s].t=";";break;case"d":;case"m":;case"y":;case"h":;case"H":;case"M":;case"s":;case"e":;case"b":;case"Z":n[s].v=le(n[s].t.charCodeAt(0),n[s].v,l,m);n[s].t="t";break;case"n":;case"?":k=s+1;while(n[k]!=null&&((f=n[k].t)==="?"||f==="D"||(f===" "||f==="t")&&n[k+1]!=null&&(n[k+1].t==="?"||n[k+1].t==="t"&&n[k+1].v==="/")||n[s].t==="("&&(f===" "||f==="n"||f===")")||f==="t"&&(n[k].v==="/"||n[k].v===" "&&n[k+1]!=null&&n[k+1].t=="?"))){n[s].v+=n[k].v;n[k]={v:"",t:";"};++k}w+=n[s].v;s=k-1;break;case"G":n[s].t="t";n[s].v=fe(r,t);break;}}var T="",A,E;if(w.length>0){if(w.charCodeAt(0)==40){A=r<0&&w.charCodeAt(0)===45?-r:r;E=Ie("n",w,A)}else{A=r<0&&a>1?-r:r;E=Ie("n",w,A);if(A<0&&n[0]&&n[0].t=="t"){E=E.substr(1);n[0].v="-"+n[0].v}}k=E.length-1;var C=n.length;for(s=0;s<n.length;++s)if(n[s]!=null&&n[s].t!="t"&&n[s].v.indexOf(".")>-1){C=s;break}var y=n.length;if(C===n.length&&E.indexOf("E")===-1){for(s=n.length-1;s>=0;--s){if(n[s]==null||"n?".indexOf(n[s].t)===-1)continue;if(k>=n[s].v.length-1){k-=n[s].v.length;n[s].v=E.substr(k+1,n[s].v.length)}else if(k<0)n[s].v="";else{n[s].v=E.substr(0,k+1);k=-1}n[s].t="t";y=s}if(k>=0&&y<n.length)n[y].v=E.substr(0,k+1)+n[y].v}else if(C!==n.length&&E.indexOf("E")===-1){k=E.indexOf(".")-1;for(s=C;s>=0;--s){if(n[s]==null||"n?".indexOf(n[s].t)===-1)continue;c=n[s].v.indexOf(".")>-1&&s===C?n[s].v.indexOf(".")-1:n[s].v.length-1;T=n[s].v.substr(c+1);for(;c>=0;--c){if(k>=0&&(n[s].v.charAt(c)==="0"||n[s].v.charAt(c)==="#"))T=E.charAt(k--)+T}n[s].v=T;n[s].t="t";y=s}if(k>=0&&y<n.length)n[y].v=E.substr(0,k+1)+n[y].v;k=E.indexOf(".")+1;for(s=C;s<n.length;++s){if(n[s]==null||"n?(".indexOf(n[s].t)===-1&&s!==C)continue;c=n[s].v.indexOf(".")>-1&&s===C?n[s].v.indLine truncated
|
||||
s=ua(30);s._W(4,67324752);s._W(2,20);s._W(2,o);s._W(2,f);if(g.mt)n(s,g.mt);else s._W(4,0);s._W(-4,o&8?0:m[d]);s._W(4,o&8?0:T.length);s._W(4,o&8?0:g.content.length);s._W(2,k.length);s._W(2,0);u+=s.length;a.push(s);u+=k.length;a.push(k);u+=T.length;a.push(T);if(o&8){s=ua(12);s._W(-4,m[d]);s._W(4,T.length);s._W(4,g.content.length);u+=s.l;a.push(s)}s=ua(46);s._W(4,33639248);s._W(2,0);s._W(2,20);s._W(2,o);s._W(2,f);s._W(4,0);s._W(-4,m[d]);s._W(4,T.length);s._W(4,g.content.length);s._W(2,k.length);s._W(2,0);s._W(2,0);s._W(2,0);s._W(2,0);s._W(4,0);s._W(4,w);b+=s.l;i.push(s);b+=k.length;i.push(k);++d}s=ua(22);s._W(4,101010256);s._W(2,0);s._W(2,0);s._W(2,d);s._W(2,d);s._W(4,b);s._W(4,u);s._W(2,0);return R([R(a),R(i),s])}var Fe={htm:"text/html",xml:"text/xml",gif:"image/gif",jpg:"image/jpeg",png:"image/png",mso:"application/x-mso",thmx:"application/vnd.ms-officetheme",sh33tj5:"application/octet-stream"};function Pe(e,r){if(e.ctype)return e.ctype;var t=e.name||"",a=t.match(/\.([^\.]+)$/);if(a&&Fe[a[1]])return Fe[a[1]];if(r){a=(t=r).match(/[\.\\]([^\.\\])+$/);if(a&&Fe[a[1]])return Fe[a[1]]}return"application/octet-stream"}function Le(e){var r=w(e);var t=[];for(var a=0;a<r.length;a+=76)t.push(r.slice(a,a+76));return t.join("\r\n")+"\r\n"}function Me(e){var r=e.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7E-\xFF=]/g,function(e){var r=e.charCodeAt(0).toString(16).toUpperCase();return"="+(r.length==1?"0"+r:r)});r=r.replace(/ $/gm,"=20").replace(/\t$/gm,"=09");if(r.charAt(0)=="\n")r="=0D"+r.slice(1);r=r.replace(/\r(?!\n)/gm,"=0D").replace(/\n\n/gm,"\n=0A").replace(/([^\r\n])\n/gm,"$1=0A");var t=[],a=r.split("\r\n");for(var n=0;n<a.length;++n){var i=a[n];if(i.length==0){t.push("");continue}for(var s=0;s<i.length;){var f=76;var o=i.slice(s,s+f);if(o.charAt(f-1)=="=")f--;else if(o.charAt(f-2)=="=")f-=2;else if(o.charAt(f-3)=="=")f-=3;o=i.slice(s,s+f);s+=f;if(s<i.length)o+="=";t.push(o)}}return t.join("\r\n")}function Ue(e){var r=[];for(var t=0;t<e.length;++t){var a=e[t];while(t<=e.length&&a.charAt(a.length-1)=="=")a=a.slice(0,a.length-1)+e[++t];r.push(a)}for(var n=0;n<r.length;++n)r[n]=r[n].replace(/[=][0-9A-Fa-f]{2}/g,function(e){return String.fromCharCode(parseInt(e.slice(1),16))});return y(r.join("\r\n"))}function Be(e,r,t){var a="",n="",i="",s;var f=0;for(;f<10;++f){var o=r[f];if(!o||o.match(/^\s*$/))break;var l=o.match(/^(.*?):\s*([^\s].*)$/);if(l)switch(l[1].toLowerCase()){case"content-location":a=l[2].trim();break;case"content-type":i=l[2].trim();break;case"content-transfer-encoding":n=l[2].trim();break;}}++f;switch(n.toLowerCase()){case"base64":s=y(k(r.slice(f).join("")));break;case"quoted-printable":s=Ue(r.slice(f));break;default:throw new Error("Unsupported Content-Transfer-Encoding "+n);}var c=Ve(e,a.slice(t.length),s,{unsafe:true});if(i)c.ctype=i}function We(e,r){if(G(e.slice(0,13)).toLowerCase()!="mime-version:")throw new Error("Unsupported MAD header");var t=r&&r.root||"";var a=(T&&Buffer.isBuffer(e)?e.toString("binary"):G(e)).split("\r\n");var n=0,i="";for(n=0;n<a.length;++n){i=a[n];if(!/^Content-Location:/i.test(i))continue;i=i.slice(i.indexOf("file"));if(!t)t=i.slice(0,i.lastIndexOf("/")+1);if(i.slice(0,t.length)==t)continue;while(t.length>0){t=t.slice(0,t.length-1);t=t.slice(0,t.lastIndexOf("/")+1);if(i.slice(0,t.length)==t)break}}var s=(a[1]||"").match(/boundary="(.*?)"/);if(!s)throw new Error("MAD cannot find boundary");var f="--"+(s[1]||"");var o=[],l=[];var c={FileIndex:o,FullPaths:l};O(c);var h,u=0;for(n=0;n<a.length;++n){var d=a[n];if(d!==f&&d!==f+"--")continue;if(u++)Be(c,a.slice(h,n),t);h=n}return c}function He(e,r){var t=r||{};var a=t.boundary||"SheetJS";a="------="+a;var n=["MIME-Version: 1.0",'Content-Type: multipart/related; boundary="'+a.slice(2)+'"',"","",""];var i=e.FullPaths[0],s=i,f=e.FileIndex[0];for(var o=1;o<e.FullPaths.length;++o){s=e.FullPaths[o].slice(i.length);f=e.FileIndex[o];if(!f.size||!f.content||s=="Sh33tJ5")continue;s=s.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7E-\xFF]/g,function(e){return"_x"+e.charCodeAt(0).toString(16)+"_"}).replace(/[\u0080-\uFFFF]/g,function(e){return"_u"+e.charCodeAt(0).toString(16)+"_"});var l=f.content;var c=T&&Buffer.isBuffer(l)?l.toString("binary"):G(l);var h=0,u=Math.min(1024,c.length),d=0;for(var v=0;v<=u;++v)if((d=c.charCodeAt(v))>=32&&d<128)++h;var p=h>=u*4/5;n.push(a);n.push("Content-Location: "+(t.root||"file:///C:/SheetJS/")+s);n.push("Content-Transfer-Encoding: "+(p?"quoted-printable":"base64"));n.push("Content-Type: "+Pe(f,s));n.push("");n.push(p?Me(c):Le(c))}n.push(a+"--\r\n");return n.join("\r\n")}function ze(e){var r={};O(r,e);return r}function Ve(e,r,t,n){var i=n&&n.unsafe;if(!i)O(e);var s=!i&&Ke.find(e,r);if(!s){var f=e.FullPaths[0];if(r.slice(0,f.length)==f)f=r;else{if(f.slice(-1)!="/")f+="/";f=(f+r).replace("//","/")}s={name:a(r),type:2};e.FileIndex.push(s);e.FullPaths.push(f);if(!i)Ke.utils.cfb_gc(e)}s.content=t;s.size=t?t.length:0;if(n){if(n.CLSID)s.clsid=n.CLSID;if(n.mt)s.mt=n.mt;if(n.ct)s.ct=n.ct}return s}function Ge(e,r){O(e);var t=Ke.findLine truncated
|
||||
}}if(e.s.c==0&&!e.s.cRel){if(e.e.c==(r.biff>=12?16383:255)&&!e.e.cRel){return(e.s.rRel?"":"$")+Ta(e.s.r)+":"+(e.e.rRel?"":"$")+Ta(e.e.r)}}return ba(e.s,r.biff)+":"+ba(e.e,r.biff)}function ka(e){return parseInt(Ea(e),10)-1}function Ta(e){return""+(e+1)}function Aa(e){return e.replace(/([A-Z]|^)(\d+)$/,"$1$$$2")}function Ea(e){return e.replace(/\$(\d+)$/,"$1")}function Ca(e){var r=_a(e),t=0,a=0;for(;a!==r.length;++a)t=26*t+r.charCodeAt(a)-64;return t-1}function ya(e){if(e<0)throw new Error("invalid column "+e);var r="";for(++e;e;e=Math.floor((e-1)/26))r=String.fromCharCode((e-1)%26+65)+r;return r}function Sa(e){return e.replace(/^([A-Z])/,"$$$1")}function _a(e){return e.replace(/^\$([A-Z])/,"$1")}function xa(e){return e.replace(/(\$?[A-Z]*)(\$?\d*)/,"$1,$2").split(",")}function Oa(e){var r=0,t=0;for(var a=0;a<e.length;++a){var n=e.charCodeAt(a);if(n>=48&&n<=57)r=10*r+(n-48);else if(n>=65&&n<=90)t=26*t+(n-64)}return{c:t-1,r:r-1}}function Ra(e){var r=e.c+1;var t="";for(;r;r=(r-1)/26|0)t=String.fromCharCode((r-1)%26+65)+t;return t+(e.r+1)}function Ia(e){var r=e.indexOf(":");if(r==-1)return{s:Oa(e),e:Oa(e)};return{s:Oa(e.slice(0,r)),e:Oa(e.slice(r+1))}}function Na(e,r){if(typeof r==="undefined"||typeof r==="number"){return Na(e.s,e.e)}if(typeof e!=="string")e=Ra(e);if(typeof r!=="string")r=Ra(r);return e==r?e:e+":"+r}function Da(e){var r={s:{c:0,r:0},e:{c:0,r:0}};var t=0,a=0,n=0;var i=e.length;for(t=0;a<i;++a){if((n=e.charCodeAt(a)-64)<1||n>26)break;t=26*t+n}r.s.c=--t;for(t=0;a<i;++a){if((n=e.charCodeAt(a)-48)<0||n>9)break;t=10*t+n}r.s.r=--t;if(a===i||n!=10){r.e.c=r.s.c;r.e.r=r.s.r;return r}++a;for(t=0;a!=i;++a){if((n=e.charCodeAt(a)-64)<1||n>26)break;t=26*t+n}r.e.c=--t;for(t=0;a!=i;++a){if((n=e.charCodeAt(a)-48)<0||n>9)break;t=10*t+n}r.e.r=--t;return r}function Fa(e,r){var t=e.t=="d"&&r instanceof Date;if(e.z!=null)try{return e.w=Be(e.z,t?fr(r):r)}catch(a){}try{return e.w=Be((e.XF||{}).numFmtId||(t?14:0),t?fr(r):r)}catch(a){return""+r}}function Pa(e,r,t){if(e==null||e.t==null||e.t=="z")return"";if(e.w!==undefined)return e.w;if(e.t=="d"&&!e.z&&t&&t.dateNF)e.z=t.dateNF;if(e.t=="e")return Gn[e.v]||e.v;if(r==undefined)return Fa(e,e.v);return Fa(e,r)}function La(e,r){var t=r&&r.sheet?r.sheet:"Sheet1";var a={};a[t]=e;return{SheetNames:[t],Sheets:a}}function Ma(e,r,t){var a=t||{};var n=e?Array.isArray(e):a.dense;if(g!=null&&n==null)n=g;var i=e||(n?[]:{});var s=0,f=0;if(i&&a.origin!=null){if(typeof a.origin=="number")s=a.origin;else{var o=typeof a.origin=="string"?Oa(a.origin):a.origin;s=o.r;f=o.c}if(!i["!ref"])i["!ref"]="A1:A1"}var l={s:{c:1e7,r:1e7},e:{c:0,r:0}};if(i["!ref"]){var c=Da(i["!ref"]);l.s.c=c.s.c;l.s.r=c.s.r;l.e.c=Math.max(l.e.c,c.e.c);l.e.r=Math.max(l.e.r,c.e.r);if(s==-1)l.e.r=s=c.e.r+1}for(var h=0;h!=r.length;++h){if(!r[h])continue;if(!Array.isArray(r[h]))throw new Error("aoa_to_sheet expects an array of arrays");for(var u=0;u!=r[h].length;++u){if(typeof r[h][u]==="undefined")continue;var d={v:r[h][u]};var v=s+h,p=f+u;if(l.s.r>v)l.s.r=v;if(l.s.c>p)l.s.c=p;if(l.e.r<v)l.e.r=v;if(l.e.c<p)l.e.c=p;if(r[h][u]&&typeof r[h][u]==="object"&&!Array.isArray(r[h][u])&&!(r[h][u]instanceof Date))d=r[h][u];else{if(Array.isArray(d.v)){d.f=r[h][u][1];d.v=d.v[0]}if(d.v===null){if(d.f)d.t="n";else if(a.nullError){d.t="e";d.v=0}else if(!a.sheetStubs)continue;else d.t="z"}else if(typeof d.v==="number")d.t="n";else if(typeof d.v==="boolean")d.t="b";else if(d.v instanceof Date){d.z=a.dateNF||X[14];if(a.cellDates){d.t="d";d.w=Be(d.z,fr(d.v))}else{d.t="n";d.v=fr(d.v);d.w=Be(d.z,d.v)}}else d.t="s"}if(n){if(!i[v])i[v]=[];if(i[v][p]&&i[v][p].z)d.z=i[v][p].z;i[v][p]=d}else{var m=Ra({c:p,r:v});if(i[m]&&i[m].z)d.z=i[m].z;i[m]=d}}}if(l.s.c<1e7)i["!ref"]=Na(l);return i}function Ua(e,r){return Ma(null,e,r)}function Ba(e){return e._R(4,"i")}function Wa(e,r){if(!r)r=ua(4);r._W(4,e);return r}function Ha(e){var r=e._R(4);return r===0?"":e._R(r,"dbcs")}function za(e,r){var t=false;if(r==null){t=true;r=ua(4+2*e.length)}r._W(4,e.length);if(e.length>0)r._W(0,e,"dbcs");return t?r.slice(0,r.l):r}function Va(e){return{ich:e._R(2),ifnt:e._R(2)}}function Ga(e,r){if(!r)r=ua(4);r._W(2,e.ich||0);r._W(2,e.ifnt||0);return r}function ja(e,r){var t=e.l;var a=e._R(1);var n=Ha(e);var i=[];var s={t:n,h:n};if((a&1)!==0){var f=e._R(4);for(var o=0;o!=f;++o)i.push(Va(e));s.r=i}else s.r=[{ich:0,ifnt:0}];e.l=t+r;return s}function Xa(e,r){var t=false;if(r==null){t=true;r=ua(15+4*e.t.length)}r._W(1,0);za(e.t,r);return t?r.slice(0,r.l):r}var $a=ja;function Ya(e,r){var t=false;if(r==null){t=true;r=ua(23+4*e.t.length)}r._W(1,1);za(e.t,r);r._W(4,1);Ga({ich:0,ifnt:0},r);return t?r.slice(0,r.l):r}function Ka(e){var r=e._R(4);var t=e._R(2);t+=e._R(1)<<16;e.l++;return{c:r,iStyleRef:t}}function Ja(e,r){if(r==null)r=ua(8);r._W(-4,e.c);r._W(3,e.iStyleRef||e.s);r._W(1,0);return r}function qa(e){var r=e._R(2);r+=e._R(1)<<16;e.l++;return{c:-1,iStyleRef:r}}function Za(e,r){if(r==null)r=ua(4);r._W(3,e.iStyleRef||e.s);r._W(1,0);return r}var Qa=Ha;var en=za;function rn(e){var r=e._R(4);Line truncated
|
||||
Language:"Language"};var Ei;function Ci(e,r,t){if(!Ei)Ei=ar(Ai);r=Ei[r]||r;e[r]=t}function yi(e,r){var t=[];rr(Ai).map(function(e){for(var r=0;r<li.length;++r)if(li[r][1]==e)return li[r];for(r=0;r<vi.length;++r)if(vi[r][1]==e)return vi[r];throw e}).forEach(function(a){if(e[a[1]]==null)return;var n=r&&r.Props&&r.Props[a[1]]!=null?r.Props[a[1]]:e[a[1]];switch(a[2]){case"date":n=new Date(n).toISOString().replace(/\.\d*Z/,"Z");break;}if(typeof n=="number")n=String(n);else if(n===true||n===false){n=n?"1":"0"}else if(n instanceof Date)n=new Date(n).toISOString().replace(/\.\d*Z/,"");t.push(bt(Ai[a[1]]||a[1],n))});return kt("DocumentProperties",t.join(""),{xmlns:_t.o})}function Si(e,r){var t=["Worksheets","SheetNames"];var a="CustomDocumentProperties";var n=[];if(e)rr(e).forEach(function(r){if(!Object.prototype.hasOwnProperty.call(e,r))return;for(var a=0;a<li.length;++a)if(r==li[a][1])return;for(a=0;a<vi.length;++a)if(r==vi[a][1])return;for(a=0;a<t.length;++a)if(r==t[a])return;var i=e[r];var s="string";if(typeof i=="number"){s="float";i=String(i)}else if(i===true||i===false){s="boolean";i=i?"1":"0"}else i=String(i);n.push(kt(Zr(r),i,{"dt:dt":s}))});if(r)rr(r).forEach(function(t){if(!Object.prototype.hasOwnProperty.call(r,t))return;if(e&&Object.prototype.hasOwnProperty.call(e,t))return;var a=r[t];var i="string";if(typeof a=="number"){i="float";a=String(a)}else if(a===true||a===false){i="boolean";a=a?"1":"0"}else if(a instanceof Date){i="dateTime.tz";a=a.toISOString()}else a=String(a);n.push(kt(Zr(t),a,{"dt:dt":i}))});return"<"+a+' xmlns="'+_t.o+'">'+n.join("")+"</"+a+">"}function _i(e){var r=e._R(4),t=e._R(4);return new Date((t/1e7*Math.pow(2,32)+r/1e7-11644473600)*1e3).toISOString().replace(/\.000/,"")}function xi(e){var r=typeof e=="string"?new Date(Date.parse(e)):e;var t=r.getTime()/1e3+11644473600;var a=t%Math.pow(2,32),n=(t-a)/Math.pow(2,32);a*=1e7;n*=1e7;var i=a/Math.pow(2,32)|0;if(i>0){a=a%Math.pow(2,32);n+=i}var s=ua(8);s._W(4,a);s._W(4,n);return s}function Oi(e,r,t){var a=e.l;var n=e._R(0,"lpstr-cp");if(t)while(e.l-a&3)++e.l;return n}function Ri(e,r,t){var a=e._R(0,"lpwstr");if(t)e.l+=4-(a.length+1&3)&3;return a}function Ii(e,r,t){if(r===31)return Ri(e);return Oi(e,r,t)}function Ni(e,r,t){return Ii(e,r,t===false?0:4)}function Di(e,r){if(!r)throw new Error("VtUnalignedString must have positive length");return Ii(e,r,0)}function Fi(e){var r=e._R(4);var t=[];for(var a=0;a!=r;++a){var n=e.l;t[a]=e._R(0,"lpwstr").replace(N,"");if(e.l-n&2)e.l+=2}return t}function Pi(e){var r=e._R(4);var t=[];for(var a=0;a!=r;++a)t[a]=e._R(0,"lpstr-cp").replace(N,"");return t}function Li(e){var r=e.l;var t=Hi(e,Pn);if(e[e.l]==0&&e[e.l+1]==0&&e.l-r&2)e.l+=2;var a=Hi(e,En);return[t,a]}function Mi(e){var r=e._R(4);var t=[];for(var a=0;a<r/2;++a)t.push(Li(e));return t}function Ui(e,r){var t=e._R(4);var a={};for(var n=0;n!=t;++n){var i=e._R(4);var s=e._R(4);a[i]=e._R(s,r===1200?"utf16le":"utf8").replace(N,"").replace(D,"!");if(r===1200&&s%2)e.l+=2}if(e.l&3)e.l=e.l>>2+1<<2;return a}function Bi(e){var r=e._R(4);var t=e.slice(e.l,e.l+r);e.l+=r;if((r&3)>0)e.l+=4-(r&3)&3;return t}function Wi(e){var r={};r.Size=e._R(4);e.l+=r.Size+3-(r.Size-1)%4;return r}function Hi(e,r,t){var a=e._R(2),n,i=t||{};e.l+=2;if(r!==yn)if(a!==r&&Ln.indexOf(r)===-1&&!((r&65534)==4126&&(a&65534)==4126))throw new Error("Expected type "+r+" saw "+a);switch(r===yn?a:r){case 2:n=e._R(2,"i");if(!i.raw)e.l+=2;return n;case 3:n=e._R(4,"i");return n;case 11:return e._R(4)!==0;case 19:n=e._R(4);return n;case 30:return Oi(e,a,4).replace(N,"");case 31:return Ri(e);case 64:return _i(e);case 65:return Bi(e);case 71:return Wi(e);case 80:return Ni(e,a,!i.raw).replace(N,"");case 81:return Di(e,a).replace(N,"");case 4108:return Mi(e);case 4126:;case 4127:return a==4127?Fi(e):Pi(e);default:throw new Error("TypedPropertyValue unrecognized type "+r+" "+a);}}function zi(e,r){var t=ua(4),a=ua(4);t._W(4,e==80?31:e);switch(e){case 3:a._W(-4,r);break;case 5:a=ua(8);a._W(8,r,"f");break;case 11:a._W(4,r?1:0);break;case 64:a=xi(r);break;case 31:;case 80:a=ua(4+2*(r.length+1)+(r.length%2?0:2));a._W(4,r.length+1);a._W(0,r,"dbcs");while(a.l!=a.length)a._W(1,0);break;default:throw new Error("TypedPropertyValue unrecognized type "+e+" "+r);}return R([t,a])}function Vi(e,r){var t=e.l;var a=e._R(4);var n=e._R(4);var i=[],s=0;var f=0;var l=-1,c={};for(s=0;s!=n;++s){var h=e._R(4);var u=e._R(4);i[s]=[h,u+t]}i.sort(function(e,r){return e[1]-r[1]});var d={};for(s=0;s!=n;++s){if(e.l!==i[s][1]){var v=true;if(s>0&&r)switch(r[i[s-1][0]].t){case 2:if(e.l+2===i[s][1]){e.l+=2;v=false}break;case 80:if(e.l<=i[s][1]){e.l=i[s][1];v=false}break;case 4108:if(e.l<=i[s][1]){e.l=i[s][1];v=false}break;}if((!r||s==0)&&e.l<=i[s][1]){v=false;e.l=i[s][1]}if(v)throw new Error("Read Error: Expected address "+i[s][1]+" at "+e.l+" :"+s)}if(r){var p=r[i[s][0]];d[p.n]=Hi(e,p.t,{raw:true});if(p.p==="version")d[p.n]=String(d[p.n]>>16)+"."+("0000"+String(d[p.n]&65535)).slice(-4);if(p.n=="CodePage")switch(d[p.n]){case 0:d[p.n]=1252;cLine truncated
|
||||
break;case"D":if(A.length===8)n[b][w]=new Date(+A.slice(0,4),+A.slice(4,6)-1,+A.slice(6,8));else n[b][w]=A;break;case"F":n[b][w]=parseFloat(A.trim());break;case"+":;case"I":n[b][w]=l?T._R(-4,"i")^2147483648:T._R(4,"i");break;case"L":switch(A.trim().toUpperCase()){case"Y":;case"T":n[b][w]=true;break;case"N":;case"F":n[b][w]=false;break;case"":;case"?":break;default:throw new Error("DBF Unrecognized L:|"+A+"|");}break;case"M":if(!f)throw new Error("DBF Unexpected MEMO for type "+s.toString(16));n[b][w]="##MEMO##"+(l?parseInt(A.trim(),10):T._R(4));break;case"N":A=A.replace(/\u0000/g,"").trim();if(A&&A!=".")n[b][w]=+A||0;break;case"@":n[b][w]=new Date(T._R(-8,"f")-621356832e5);break;case"T":n[b][w]=new Date((T._R(4)-2440588)*864e5+T._R(4));break;case"Y":n[b][w]=T._R(4,"i")/1e4+T._R(4,"i")/1e4*Math.pow(2,32);break;case"O":n[b][w]=-T._R(-8,"f");break;case"B":if(o&&v[w].len==8){n[b][w]=T._R(8,"f");break};case"G":;case"P":T.l+=v[w].len;break;case"0":if(v[w].name==="_NullFlags")break;default:throw new Error("DBF Unsupported data type "+v[w].type);}}}if(s!=2)if(i.l<i.length&&i[i.l++]!=26)throw new Error("DBF EOF Marker missing "+(i.l-1)+" of "+i.length+" "+i[i.l-1].toString(16));if(t&&t.sheetRows)n=n.slice(0,t.sheetRows);t.DBF=v;return n}function i(e,r){var t=r||{};if(!t.dateNF)t.dateNF="yyyymmdd";var a=Ua(n(e,t),t);a["!cols"]=t.DBF.map(function(e){return{wch:e.len,DBF:e}});delete t.DBF;return a}function s(e,r){try{return La(i(e,r),r)}catch(t){if(r&&r.WTF)throw t}return{SheetNames:[],Sheets:{}}}var f={B:8,C:250,L:1,D:8,"?":0,"":0};function l(e,a){var n=a||{};if(+n.codepage>=0)o(+n.codepage);if(n.type=="string")throw new Error("Cannot write DBF to JS string");var i=va();var s=Aw(e,{header:1,raw:true,cellDates:true});var l=s[0],c=s.slice(1),h=e["!cols"]||[];var u=0,d=0,v=0,p=1;for(u=0;u<l.length;++u){if(((h[u]||{}).DBF||{}).name){l[u]=h[u].DBF.name;++v;continue}if(l[u]==null)continue;++v;if(typeof l[u]==="number")l[u]=l[u].toString(10);if(typeof l[u]!=="string")throw new Error("DBF Invalid column name "+l[u]+" |"+typeof l[u]+"|");if(l.indexOf(l[u])!==u)for(d=0;d<1024;++d)if(l.indexOf(l[u]+"_"+d)==-1){l[u]+="_"+d;break}}var g=Da(e["!ref"]);var m=[];var b=[];var w=[];for(u=0;u<=g.e.c-g.s.c;++u){var k="",T="",A=0;var E=[];for(d=0;d<c.length;++d){if(c[d][u]!=null)E.push(c[d][u])}if(E.length==0||l[u]==null){m[u]="?";continue}for(d=0;d<E.length;++d){switch(typeof E[d]){case"number":T="B";break;case"string":T="C";break;case"boolean":T="L";break;case"object":T=E[d]instanceof Date?"D":"C";break;default:T="C";}A=Math.max(A,String(E[d]).length);k=k&&k!=T?"C":T}if(A>250)A=250;T=((h[u]||{}).DBF||{}).type;if(T=="C"){if(h[u].DBF.len>A)A=h[u].DBF.len}if(k=="B"&&T=="N"){k="N";w[u]=h[u].DBF.dec;A=h[u].DBF.len}b[u]=k=="C"||T=="N"?A:f[k]||0;p+=b[u];m[u]=k}var C=i.next(32);C._W(4,318902576);C._W(4,c.length);C._W(2,296+32*v);C._W(2,p);for(u=0;u<4;++u)C._W(4,0);C._W(4,0|(+r[t]||3)<<8);for(u=0,d=0;u<l.length;++u){if(l[u]==null)continue;var y=i.next(32);var S=(l[u].slice(-10)+"\0\0\0\0\0\0\0\0\0\0\0").slice(0,11);y._W(1,S,"sbcs");y._W(1,m[u]=="?"?"C":m[u],"sbcs");y._W(4,d);y._W(1,b[u]||f[m[u]]||0);y._W(1,w[u]||0);y._W(1,2);y._W(4,0);y._W(1,0);y._W(4,0);y._W(4,0);d+=b[u]||f[m[u]]||0}var _=i.next(264);_._W(4,13);for(u=0;u<65;++u)_._W(4,0);for(u=0;u<c.length;++u){var x=i.next(p);x._W(1,0);for(d=0;d<l.length;++d){if(l[d]==null)continue;switch(m[d]){case"L":x._W(1,c[u][d]==null?63:c[u][d]?84:70);break;case"B":x._W(8,c[u][d]||0,"f");break;case"N":var O="0";if(typeof c[u][d]=="number")O=c[u][d].toFixed(w[d]||0);for(v=0;v<b[d]-O.length;++v)x._W(1,32);x._W(1,O,"sbcs");break;case"D":if(!c[u][d])x._W(8,"00000000","sbcs");else{x._W(4,("0000"+c[u][d].getFullYear()).slice(-4),"sbcs");x._W(2,("00"+(c[u][d].getMonth()+1)).slice(-2),"sbcs");x._W(2,("00"+c[u][d].getDate()).slice(-2),"sbcs")}break;case"C":var R=String(c[u][d]!=null?c[u][d]:"").slice(0,b[d]);x._W(1,R,"sbcs");for(v=0;v<b[d]-R.length;++v)x._W(1,32);break;}}}i.next(1)._W(1,26);return i.end()}return{to_workbook:s,to_sheet:i,from_sheet:l}}();var Eo=function(){var e={AA:"À",BA:"Á",CA:"Â",DA:195,HA:"Ä",JA:197,AE:"È",BE:"É",CE:"Ê",HE:"Ë",AI:"Ì",BI:"Í",CI:"Î",HI:"Ï",AO:"Ò",BO:"Ó",CO:"Ô",DO:213,HO:"Ö",AU:"Ù",BU:"Ú",CU:"Û",HU:"Ü",Aa:"à",Ba:"á",Ca:"â",Da:227,Ha:"ä",Ja:229,Ae:"è",Be:"é",Ce:"ê",He:"ë",Ai:"ì",Bi:"í",Ci:"î",Hi:"ï",Ao:"ò",Bo:"ó",Co:"ô",Do:245,Ho:"ö",Au:"ù",Bu:"ú",Cu:"û",Hu:"ü",KC:"Ç",Kc:"ç",q:"æ",z:"œ",a:"Æ",j:"Œ",DN:209,Dn:241,Hy:255,S:169,c:170,R:174,"B ":180,0:176,1:177,2:178,3:179,5:181,6:182,7:183,Q:185,k:186,b:208,i:216,l:222,s:240,y:248,"!":161,'"':162,"#":163,"(":164,"%":165,"'":167,"H ":168,"+":171,";":187,"<":188,"=":189,">":190,"?":191,"{":223};var r=new RegExp("N("+rr(e).join("|").replace(/\|\|\|/,"|\\||").replace(/([?()+])/g,"\\$1")+"|\\|)","gm");var t=function(r,t){var a=e[t];return typeof a=="number"?p(a):a};var n=function(e,r,t){var a=r.charCodeAt(0)-32<<4|t.charCodeAt(0)-48;return a==59?e:p(a)};e["|"]=254;function i(e,r){switch(r.tyLine truncated
|
||||
switch(s.val){case"double":r.uval="double";break;case"singleAccounting":r.uval="single-accounting";break;case"doubleAccounting":r.uval="double-accounting";break;};case"<u>":;case"<u/>":r.u=1;break;case"</u>":break;case"<b":if(s.val=="0")break;case"<b>":;case"<b/>":r.b=1;break;case"</b>":break;case"<i":if(s.val=="0")break;case"<i>":;case"<i/>":r.i=1;break;case"</i>":break;case"<color":if(s.rgb)r.color=s.rgb.slice(2,8);break;case"<color>":;case"<color/>":;case"</color>":break;case"<family":r.family=s.val;break;case"<family>":;case"<family/>":;case"</family>":break;case"<vertAlign":r.valign=s.val;break;case"<vertAlign>":;case"<vertAlign/>":;case"</vertAlign>":break;case"<scheme":break;case"<scheme>":;case"<scheme/>":;case"</scheme>":break;case"<extLst":;case"<extLst>":;case"</extLst>":break;case"<ext":n=true;break;case"</ext>":n=false;break;default:if(s[0].charCodeAt(1)!==47&&!n)throw new Error("Unrecognized rich format "+s[0]);}}return r}var Ro=function(){var e=ht("t"),r=ht("rPr");function t(t){var a=t.match(e);if(!a)return{t:"s",v:""};var n={t:"s",v:Yr(a[1])};var i=t.match(r);if(i)n.s=Oo(i[1]);return n}var a=/<(?:\w+:)?r>/g,n=/<\/(?:\w+:)?r>/;return function i(e){return e.replace(a,"").split(n).map(t).filter(function(e){return e.v})}}();var Io=function hk(){var e=/(\r\n|\n)/g;function r(e,r,t){var a=[];if(e.u)a.push("text-decoration: underline;");if(e.uval)a.push("text-underline-style:"+e.uval+";");if(e.sz)a.push("font-size:"+e.sz+"pt;");if(e.outline)a.push("text-effect: outline;");if(e.shadow)a.push("text-shadow: auto;");r.push('<span style="'+a.join("")+'">');if(e.b){r.push("<b>");t.push("</b>")}if(e.i){r.push("<i>");t.push("</i>")}if(e.strike){r.push("<s>");t.push("</s>")}var n=e.valign||"";if(n=="superscript"||n=="super")n="sup";else if(n=="subscript")n="sub";if(n!=""){r.push("<"+n+">");t.push("</"+n+">")}t.push("</span>");return e}function t(t){var a=[[],t.v,[]];if(!t.v)return"";if(t.s)r(t.s,a[0],a[2]);return a[0].join("")+a[1].replace(e,"<br/>")+a[2].join("")}return function a(e){return e.map(t).join("")}}();var No=/<(?:\w+:)?t[^>]*>([^<]*)<\/(?:\w+:)?t>/g,Do=/<(?:\w+:)?r>/;var Fo=/<(?:\w+:)?rPh.*?>([\s\S]*?)<\/(?:\w+:)?rPh>/g;function Po(e,r){var t=r?r.cellHTML:true;var a={};if(!e)return{t:""};if(e.match(/^\s*<(?:\w+:)?t[^>]*>/)){a.t=Yr(lt(e.slice(e.indexOf(">")+1).split(/<\/(?:\w+:)?t>/)[0]||""));a.r=lt(e);if(t)a.h=et(a.t)}else if(e.match(Do)){a.r=lt(e);a.t=Yr(lt((e.replace(Fo,"").match(No)||[]).join("").replace(Hr,"")));if(t)a.h=Io(Ro(a.r))}return a}var Lo=/<(?:\w+:)?sst([^>]*)>([\s\S]*)<\/(?:\w+:)?sst>/;var Mo=/<(?:\w+:)?(?:si|sstItem)>/g;var Uo=/<\/(?:\w+:)?(?:si|sstItem)>/;function Bo(e,r){var t=[],a="";if(!e)return t;var n=e.match(Lo);if(n){a=n[2].replace(Mo,"").split(Uo);for(var i=0;i!=a.length;++i){var s=Po(a[i].trim(),r);if(s!=null)t[t.length]=s}n=Gr(n[1]);t.Count=n.count;t.Unique=n.uniqueCount}return t}var Wo=/^\s|\s$|[\t\n\r]/;function Ho(e,r){if(!r.bookSST)return"";var t=[Mr];t[t.length]=kt("sst",null,{xmlns:St[0],count:e.Count,uniqueCount:e.Unique});for(var a=0;a!=e.length;++a){if(e[a]==null)continue;var n=e[a];var i="<si>";if(n.r)i+=n.r;else{i+="<t";if(!n.t)n.t="";if(n.t.match(Wo))i+=' xml:space="preserve"';i+=">"+qr(n.t)+"</t>"}i+="</si>";t[t.length]=i}if(t.length>2){t[t.length]="</sst>";t[1]=t[1].replace("/>",">")}return t.join("")}function zo(e){return[e._R(4),e._R(4)]}function Vo(e,r){var t=[];var a=false;da(e,function n(e,i,s){switch(s){case 159:t.Count=e[0];t.Unique=e[1];break;case 19:t.push(e);break;case 160:return true;case 35:a=true;break;case 36:a=false;break;default:if(i.T){}if(!a||r.WTF)throw new Error("Unexpected record 0x"+s.toString(16));}});return t}function Go(e,r){if(!r)r=ua(8);r._W(4,e.Count);r._W(4,e.Unique);return r}var jo=Xa;function Xo(e){var r=va();pa(r,159,Go(e));for(var t=0;t<e.length;++t)pa(r,19,jo(e[t]));pa(r,160);return r.end()}function $o(e){if(typeof a!=="undefined")return a.utils.encode(t,e);var r=[],n=e.split("");for(var i=0;i<n.length;++i)r[i]=n[i].charCodeAt(0);return r}function Yo(e,r){var t={};t.Major=e._R(2);t.Minor=e._R(2);if(r>=4)e.l+=r-4;return t}function Ko(e){var r={};r.id=e._R(0,"lpp4");r.R=Yo(e,4);r.U=Yo(e,4);r.W=Yo(e,4);return r}function Jo(e){var r=e._R(4);var t=e.l+r-4;var a={};var n=e._R(4);var i=[];while(n-- >0)i.push({t:e._R(4),v:e._R(0,"lpp4")});a.name=e._R(0,"lpp4");a.comps=i;if(e.l!=t)throw new Error("Bad DataSpaceMapEntry: "+e.l+" != "+t);return a}function qo(e){var r=[];e.l+=4;var t=e._R(4);while(t-- >0)r.push(Jo(e));return r}function Zo(e){var r=[];e.l+=4;var t=e._R(4);while(t-- >0)r.push(e._R(0,"lpp4"));return r}function Qo(e){var r={};e._R(4);e.l+=4;r.id=e._R(0,"lpp4");r.name=e._R(0,"lpp4");r.R=Yo(e,4);r.U=Yo(e,4);r.W=Yo(e,4);return r}function el(e){var r=Qo(e);r.ename=e._R(0,"8lpp4");r.blksz=e._R(4);r.cmode=e._R(4);if(e._R(4)!=4)throw new Error("Bad !Primary record");return r}function rl(e,r){var t=e.l+r;var a={};a.Flags=e._R(4)&63;e.l+=4;a.AlgID=e._R(4);var n=false;switch(a.AlgID){case 26126:;case 26127:;case 26128:n=a.Flags==Line truncated
|
||||
t[t.length]='<a:font script="Hang" typeface="맑은 고딕"/>';t[t.length]='<a:font script="Hans" typeface="宋体"/>';t[t.length]='<a:font script="Hant" typeface="新細明體"/>';t[t.length]='<a:font script="Arab" typeface="Arial"/>';t[t.length]='<a:font script="Hebr" typeface="Arial"/>';t[t.length]='<a:font script="Thai" typeface="Tahoma"/>';t[t.length]='<a:font script="Ethi" typeface="Nyala"/>';t[t.length]='<a:font script="Beng" typeface="Vrinda"/>';t[t.length]='<a:font script="Gujr" typeface="Shruti"/>';t[t.length]='<a:font script="Khmr" typeface="DaunPenh"/>';t[t.length]='<a:font script="Knda" typeface="Tunga"/>';t[t.length]='<a:font script="Guru" typeface="Raavi"/>';t[t.length]='<a:font script="Cans" typeface="Euphemia"/>';t[t.length]='<a:font script="Cher" typeface="Plantagenet Cherokee"/>';t[t.length]='<a:font script="Yiii" typeface="Microsoft Yi Baiti"/>';t[t.length]='<a:font script="Tibt" typeface="Microsoft Himalaya"/>';t[t.length]='<a:font script="Thaa" typeface="MV Boli"/>';t[t.length]='<a:font script="Deva" typeface="Mangal"/>';t[t.length]='<a:font script="Telu" typeface="Gautami"/>';t[t.length]='<a:font script="Taml" typeface="Latha"/>';t[t.length]='<a:font script="Syrc" typeface="Estrangelo Edessa"/>';t[t.length]='<a:font script="Orya" typeface="Kalinga"/>';t[t.length]='<a:font script="Mlym" typeface="Kartika"/>';t[t.length]='<a:font script="Laoo" typeface="DokChampa"/>';t[t.length]='<a:font script="Sinh" typeface="Iskoola Pota"/>';t[t.length]='<a:font script="Mong" typeface="Mongolian Baiti"/>';t[t.length]='<a:font script="Viet" typeface="Arial"/>';t[t.length]='<a:font script="Uigh" typeface="Microsoft Uighur"/>';t[t.length]='<a:font script="Geor" typeface="Sylfaen"/>';t[t.length]="</a:minorFont>";t[t.length]="</a:fontScheme>";t[t.length]='<a:fmtScheme name="Office">';t[t.length]="<a:fillStyleLst>";t[t.length]='<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>';t[t.length]='<a:gradFill rotWithShape="1">';t[t.length]="<a:gsLst>";t[t.length]='<a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="50000"/><a:satMod val="300000"/></a:schemeClr></a:gs>';t[t.length]='<a:gs pos="35000"><a:schemeClr val="phClr"><a:tint val="37000"/><a:satMod val="300000"/></a:schemeClr></a:gs>';t[t.length]='<a:gs pos="100000"><a:schemeClr val="phClr"><a:tint val="15000"/><a:satMod val="350000"/></a:schemeClr></a:gs>';t[t.length]="</a:gsLst>";t[t.length]='<a:lin ang="16200000" scaled="1"/>';t[t.length]="</a:gradFill>";t[t.length]='<a:gradFill rotWithShape="1">';t[t.length]="<a:gsLst>";t[t.length]='<a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="100000"/><a:shade val="100000"/><a:satMod val="130000"/></a:schemeClr></a:gs>';t[t.length]='<a:gs pos="100000"><a:schemeClr val="phClr"><a:tint val="50000"/><a:shade val="100000"/><a:satMod val="350000"/></a:schemeClr></a:gs>';t[t.length]="</a:gsLst>";t[t.length]='<a:lin ang="16200000" scaled="0"/>';t[t.length]="</a:gradFill>";t[t.length]="</a:fillStyleLst>";t[t.length]="<a:lnStyleLst>";t[t.length]='<a:ln w="9525" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"><a:shade val="95000"/><a:satMod val="105000"/></a:schemeClr></a:solidFill><a:prstDash val="solid"/></a:ln>';t[t.length]='<a:ln w="25400" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln>';t[t.length]='<a:ln w="38100" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln>';t[t.length]="</a:lnStyleLst>";t[t.length]="<a:effectStyleLst>";t[t.length]="<a:effectStyle>";t[t.length]="<a:effectLst>";t[t.length]='<a:outerShdw blurRad="40000" dist="20000" dir="5400000" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="38000"/></a:srgbClr></a:outerShdw>';t[t.length]="</a:effectLst>";t[t.length]="</a:effectStyle>";t[t.length]="<a:effectStyle>";t[t.length]="<a:effectLst>";t[t.length]='<a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="35000"/></a:srgbClr></a:outerShdw>';t[t.length]="</a:effectLst>";t[t.length]="</a:effectStyle>";t[t.length]="<a:effectStyle>";t[t.length]="<a:effectLst>";t[t.length]='<a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="35000"/></a:srgbClr></a:outerShdw>';t[t.length]="</a:effectLst>";t[t.length]='<a:scene3d><a:camera prst="orthographicFront"><a:rot lat="0" lon="0" rev="0"/></a:camera><a:lightRig rig="threePt" dir="t"><a:rot lat="0" lon="0" rev="1200000"/></a:lightRig></a:scene3d>';t[t.length]='<a:sp3d><a:bevelT w="63500" h="25400"/></a:sp3d>';t[t.length]="</a:effectStyle>";t[t.length]="</a:effectStyleLst>";t[t.length]="<a:bgFillStyleLst>";t[t.length]='<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>';t[t.length]='<a:gradFill rotWithShape="1">';t[t.length]="<a:gsLst>";t[t.length]='<a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="40000"/><a:satMod val="350000"/></a:schemeClr></a:gs>';t[t.length]='<a:gs pos="40000"><Line truncated
|
||||
var a=e.XTI[r];if(t.biff<8){if(r>1e4)r-=65536;if(r<0)r=-r;return r==0?"":e.XTI[r-1]}if(!a)return"SH33TJSERR1";var n="";if(t.biff>8)switch(e[a[0]][0]){case 357:n=a[1]==-1?"#REF":e.SheetNames[a[1]];return a[1]==a[2]?n:n+":"+e.SheetNames[a[2]];case 358:if(t.SID!=null)return e.SheetNames[t.SID];return"SH33TJSSAME"+e[a[0]][0];case 355:;default:return"SH33TJSSRC"+e[a[0]][0];}switch(e[a[0]][0][0]){case 1025:n=a[1]==-1?"#REF":e.SheetNames[a[1]]||"SH33TJSERR3";return a[1]==a[2]?n:n+":"+e.SheetNames[a[2]];case 14849:return e[a[0]].slice(1).map(function(e){return e.Name}).join(";;");default:if(!e[a[0]][0][3])return"SH33TJSERR2";n=a[1]==-1?"#REF":e[a[0]][0][3][a[1]]||"SH33TJSERR4";return a[1]==a[2]?n:n+":"+e[a[0]][0][3][a[2]];}}function sd(e,r,t){var a=id(e,r,t);return a=="#REF"?a:nd(a,t)}function fd(e,r,t,a,n){var i=n&&n.biff||8;var s={s:{c:0,r:0},e:{c:0,r:0}};var f=[],o,l,c,h=0,u=0,d,v="";if(!e[0]||!e[0][0])return"";var p=-1,g="";for(var m=0,b=e[0].length;m<b;++m){var w=e[0][m];switch(w[0]){case"PtgUminus":f.push("-"+f.pop());break;case"PtgUplus":f.push("+"+f.pop());break;case"PtgPercent":f.push(f.pop()+"%");break;case"PtgAdd":;case"PtgConcat":;case"PtgDiv":;case"PtgEq":;case"PtgGe":;case"PtgGt":;case"PtgLe":;case"PtgLt":;case"PtgMul":;case"PtgNe":;case"PtgPower":;case"PtgSub":o=f.pop();l=f.pop();if(p>=0){switch(e[0][p][1][0]){case 0:g=wr(" ",e[0][p][1][1]);break;case 1:g=wr("\r",e[0][p][1][1]);break;default:g="";if(n.WTF)throw new Error("Unexpected PtgAttrSpaceType "+e[0][p][1][0]);}l=l+g;p=-1}f.push(l+ad[w[0]]+o);break;case"PtgIsect":o=f.pop();l=f.pop();f.push(l+" "+o);break;case"PtgUnion":o=f.pop();l=f.pop();f.push(l+","+o);break;case"PtgRange":o=f.pop();l=f.pop();f.push(l+":"+o);break;case"PtgAttrChoose":break;case"PtgAttrGoto":break;case"PtgAttrIf":break;case"PtgAttrIfError":break;case"PtgRef":c=ga(w[1][1],s,n);f.push(ba(c,i));break;case"PtgRefN":c=t?ga(w[1][1],t,n):w[1][1];f.push(ba(c,i));break;case"PtgRef3d":h=w[1][1];c=ga(w[1][2],s,n);v=sd(a,h,n);var k=v;f.push(v+"!"+ba(c,i));break;case"PtgFunc":;case"PtgFuncVar":var T=w[1][0],A=w[1][1];if(!T)T=0;T&=127;var E=T==0?[]:f.slice(-T);f.length-=T;if(A==="User")A=E.shift();f.push(A+"("+E.join(",")+")");break;case"PtgBool":f.push(w[1]?"TRUE":"FALSE");break;case"PtgInt":f.push(w[1]);break;case"PtgNum":f.push(String(w[1]));break;case"PtgStr":f.push('"'+w[1].replace(/"/g,'""')+'"');break;case"PtgErr":f.push(w[1]);break;case"PtgAreaN":d=ma(w[1][1],t?{s:t}:s,n);f.push(wa(d,n));break;case"PtgArea":d=ma(w[1][1],s,n);f.push(wa(d,n));break;case"PtgArea3d":h=w[1][1];d=w[1][2];v=sd(a,h,n);f.push(v+"!"+wa(d,n));break;case"PtgAttrSum":f.push("SUM("+f.pop()+")");break;case"PtgAttrBaxcel":;case"PtgAttrSemi":break;case"PtgName":u=w[1][2];var C=(a.names||[])[u-1]||(a[0]||[])[u];var y=C?C.Name:"SH33TJSNAME"+String(u);if(y&&y.slice(0,6)=="_xlfn."&&!n.xlfn)y=y.slice(6);f.push(y);break;case"PtgNameX":var S=w[1][1];u=w[1][2];var _;if(n.biff<=5){if(S<0)S=-S;if(a[S])_=a[S][u]}else{var x="";if(((a[S]||[])[0]||[])[0]==14849){}else if(((a[S]||[])[0]||[])[0]==1025){if(a[S][u]&&a[S][u].itab>0){x=a.SheetNames[a[S][u].itab-1]+"!"}}else x=a.SheetNames[u-1]+"!";if(a[S]&&a[S][u])x+=a[S][u].Name;else if(a[0]&&a[0][u])x+=a[0][u].Name;else{var O=(id(a,S,n)||"").split(";;");if(O[u-1])x=O[u-1];else x+="SH33TJSERRX"}f.push(x);break}if(!_)_={Name:"SH33TJSERRY"};f.push(_.Name);break;case"PtgParen":var R="(",I=")";if(p>=0){g="";switch(e[0][p][1][0]){case 2:R=wr(" ",e[0][p][1][1])+R;break;case 3:R=wr("\r",e[0][p][1][1])+R;break;case 4:I=wr(" ",e[0][p][1][1])+I;break;case 5:I=wr("\r",e[0][p][1][1])+I;break;default:if(n.WTF)throw new Error("Unexpected PtgAttrSpaceType "+e[0][p][1][0]);}p=-1}f.push(R+f.pop()+I);break;case"PtgRefErr":f.push("#REF!");break;case"PtgRefErr3d":f.push("#REF!");break;case"PtgExp":c={c:w[1][1],r:w[1][0]};var N={c:t.c,r:t.r};if(a.sharedf[Ra(c)]){var D=a.sharedf[Ra(c)];f.push(fd(D,s,N,a,n))}else{var F=false;for(o=0;o!=a.arrayf.length;++o){l=a.arrayf[o];if(c.c<l[0].s.c||c.c>l[0].e.c)continue;if(c.r<l[0].s.r||c.r>l[0].e.r)continue;f.push(fd(l[1],s,N,a,n));F=true;break}if(!F)f.push(w[1])}break;case"PtgArray":f.push("{"+td(w[1])+"}");break;case"PtgMemArea":break;case"PtgAttrSpace":;case"PtgAttrSpaceSemi":p=m;break;case"PtgTbl":break;case"PtgMemErr":break;case"PtgMissArg":f.push("");break;case"PtgAreaErr":f.push("#REF!");break;case"PtgAreaErr3d":f.push("#REF!");break;case"PtgList":f.push("Table"+w[1].idx+"[#"+w[1].rt+"]");break;case"PtgMemAreaN":;case"PtgMemNoMemN":;case"PtgAttrNoop":;case"PtgSheet":;case"PtgEndSheet":break;case"PtgMemFunc":break;case"PtgMemNoMem":break;case"PtgElfCol":;case"PtgElfColS":;case"PtgElfColSV":;case"PtgElfColV":;case"PtgElfLel":;case"PtgElfRadical":;case"PtgElfRadicalLel":;case"PtgElfRadicalS":;case"PtgElfRw":;case"PtgElfRwV":throw new Error("Unsupported ELFs");case"PtgSxName":throw new Error("Unrecognized Formula Token: "+String(w));default:throw new Error("Unrecognized Formula Token: "+String(w));}var P=["PtgAttrSpace","PtgAttrSpaceSemi","PtgAttrGoto"];if(n.biff!Line truncated
|
||||
var n=/ref=["']([^"']*)["']/;var i=ht("v"),s=ht("f");return function f(o,l,c,h,u,d){var v=0,p="",g=[],m=[],b=0,w=0,k=0,T="",A;var E,C=0,y=0;var S,_;var x=0,O=0;var R=Array.isArray(d.CellXf),I;var N=[];var D=[];var F=Array.isArray(l);var P=[],L={},M=false;var U=!!c.sheetStubs;for(var B=o.split(r),W=0,H=B.length;W!=H;++W){p=B[W].trim();var z=p.length;if(z===0)continue;var V=0;e:for(v=0;v<z;++v)switch(p[v]){case">":if(p[v-1]!="/"){++v;break e}if(c&&c.cellStyles){E=Gr(p.slice(V,v),true);C=E.r!=null?parseInt(E.r,10):C+1;y=-1;if(c.sheetRows&&c.sheetRows<C)continue;L={};M=false;if(E.ht){M=true;L.hpt=parseFloat(E.ht);L.hpx=Pl(L.hpt)}if(E.hidden=="1"){M=true;L.hidden=true}if(E.outlineLevel!=null){M=true;L.level=+E.outlineLevel}if(M)P[C-1]=L}break;case"<":V=v;break;}if(V>=v)break;E=Gr(p.slice(V,v),true);C=E.r!=null?parseInt(E.r,10):C+1;y=-1;if(c.sheetRows&&c.sheetRows<C)continue;if(h.s.r>C-1)h.s.r=C-1;if(h.e.r<C-1)h.e.r=C-1;if(c&&c.cellStyles){L={};M=false;if(E.ht){M=true;L.hpt=parseFloat(E.ht);L.hpx=Pl(L.hpt)}if(E.hidden=="1"){M=true;L.hidden=true}if(E.outlineLevel!=null){M=true;L.level=+E.outlineLevel}if(M)P[C-1]=L}g=p.slice(v).split(e);for(var G=0;G!=g.length;++G)if(g[G].trim().charAt(0)!="<")break;g=g.slice(G);for(v=0;v!=g.length;++v){p=g[v].trim();if(p.length===0)continue;m=p.match(t);b=v;w=0;k=0;p="<c "+(p.slice(0,1)=="<"?">":"")+p;if(m!=null&&m.length===2){b=0;T=m[1];for(w=0;w!=T.length;++w){if((k=T.charCodeAt(w)-64)<1||k>26)break;b=26*b+k}--b;y=b}else++y;for(w=0;w!=p.length;++w)if(p.charCodeAt(w)===62)break;++w;E=Gr(p.slice(0,w),true);if(!E.r)E.r=Ra({r:C-1,c:y});T=p.slice(w);A={t:""};if((m=T.match(i))!=null&&m[1]!=="")A.v=Yr(m[1]);if(c.cellFormula){if((m=T.match(s))!=null&&m[1]!==""){A.f=Yr(lt(m[1])).replace(/\r\n/g,"\n");if(!c.xlfn)A.f=Nh(A.f);if(m[0].indexOf('t="array"')>-1){A.F=(T.match(n)||[])[1];if(A.F.indexOf(":")>-1)N.push([Da(A.F),A.F])}else if(m[0].indexOf('t="shared"')>-1){_=Gr(m[0]);var j=Yr(lt(m[1]));if(!c.xlfn)j=Nh(j);D[parseInt(_.si,10)]=[_,j,E.r]}}else if(m=T.match(/<f[^>]*\/>/)){_=Gr(m[0]);if(D[_.si])A.f=Rh(D[_.si][1],D[_.si][2],E.r)}var $=Oa(E.r);for(w=0;w<N.length;++w)if($.r>=N[w][0].s.r&&$.r<=N[w][0].e.r)if($.c>=N[w][0].s.c&&$.c<=N[w][0].e.c)A.F=N[w][1]}if(E.t==null&&A.v===undefined){if(A.f||A.F){A.v=0;A.t="n"}else if(!U)continue;else A.t="z"}else A.t=E.t||"n";if(h.s.c>y)h.s.c=y;if(h.e.c<y)h.e.c=y;switch(A.t){case"n":if(A.v==""||A.v==null){if(!U)continue;A.t="z"}else A.v=parseFloat(A.v);break;case"s":if(typeof A.v=="undefined"){if(!U)continue;A.t="z"}else{S=xd[parseInt(A.v,10)];A.v=S.t;A.r=S.r;if(c.cellHTML)A.h=S.h}break;case"str":A.t="s";A.v=A.v!=null?lt(A.v):"";if(c.cellHTML)A.h=et(A.v);break;case"inlineStr":m=T.match(a);A.t="s";if(m!=null&&(S=Po(m[1]))){A.v=S.t;if(c.cellHTML)A.h=S.h}else A.v="";break;case"b":A.v=nt(A.v);break;case"d":if(c.cellDates)A.v=gr(A.v,1);else{A.v=fr(gr(A.v,1));A.t="n"}break;case"e":if(!c||c.cellText!==false)A.w=A.v;A.v=jn[A.v];break;}x=O=0;I=null;if(R&&E.s!==undefined){I=d.CellXf[E.s];if(I!=null){if(I.numFmtId!=null)x=I.numFmtId;if(c.cellStyles){if(I.fillId!=null)O=I.fillId}}}Pd(A,x,O,c,u,d);if(c.cellDates&&R&&A.t=="n"&&Fe(X[x])){A.t="d";A.v=hr(A.v)}if(E.cm&&c.xlmeta){var Y=(c.xlmeta.Cell||[])[+E.cm-1];if(Y&&Y.type=="XLDAPR")A.D=true}if(F){var K=Oa(E.r);if(!l[K.r])l[K.r]=[];l[K.r][K.c]=A}else l[E.r]=A}}if(P.length>0)l["!rows"]=P}}();function vv(e,r,t,a){var n=[],i=[],s=Da(e["!ref"]),f="",o,l="",c=[],h=0,u=0,d=e["!rows"];var v=Array.isArray(e);var p={r:l},g,m=-1;for(u=s.s.c;u<=s.e.c;++u)c[u]=ya(u);for(h=s.s.r;h<=s.e.r;++h){i=[];l=Ta(h);for(u=s.s.c;u<=s.e.c;++u){o=c[u]+l;var b=v?(e[h]||[])[u]:e[o];if(b===undefined)continue;if((f=uv(b,o,e,r,t,a))!=null)i.push(f)}if(i.length>0||d&&d[h]){p={r:l};if(d&&d[h]){g=d[h];if(g.hidden)p.hidden=1;m=-1;if(g.hpx)m=Fl(g.hpx);else if(g.hpt)m=g.hpt;if(m>-1){p.ht=m;p.customHeight=1}if(g.level){p.outlineLevel=g.level}}n[n.length]=kt("row",i.join(""),p)}}if(d)for(;h<d.length;++h){if(d&&d[h]){p={r:h+1};g=d[h];if(g.hidden)p.hidden=1;m=-1;if(g.hpx)m=Fl(g.hpx);else if(g.hpt)m=g.hpt;if(m>-1){p.ht=m;p.customHeight=1}if(g.level){p.outlineLevel=g.level}n[n.length]=kt("row","",p)}}return n.join("")}function pv(e,r,t,a){var n=[Mr,kt("worksheet",null,{xmlns:St[0],"xmlns:r":yt.r})];var i=t.SheetNames[e],s=0,f="";var o=t.Sheets[i];if(o==null)o={};var l=o["!ref"]||"A1";var c=Da(l);if(c.e.c>16383||c.e.r>1048575){if(r.WTF)throw new Error("Range "+l+" exceeds format limit A1:XFD1048576");c.e.c=Math.min(c.e.c,16383);c.e.r=Math.min(c.e.c,1048575);l=Na(c)}if(!a)a={};o["!comments"]=[];var h=[];Zd(o,t,e,r,n);n[n.length]=kt("dimension",null,{ref:l});n[n.length]=hv(o,r,e,t);if(r.sheetFormat)n[n.length]=kt("sheetFormatPr",null,{defaultRowHeight:r.sheetFormat.defaultRowHeight||"16",baseColWidth:r.sheetFormat.baseColWidth||"10",outlineLevelRow:r.sheetFormat.outlineLevelRow||"7"});if(o["!cols"]!=null&&o["!cols"].length>0)n[n.length]=sv(o,o["!cols"]);n[s=n.length]="<sheetData/>";o["!links"]=[];if(o["!ref"]!=null){f=vv(o,r,e,t,a);if(f.length>0)n[n.length]=f}if(n.lLine truncated
|
||||
break;case 35:a.push(l);n=true;break;case 36:a.pop();n=false;break;case 37:a.push(l);n=true;break;case 38:a.pop();n=false;break;case 16:break;default:if(o.T){}else if(!n||r.WTF&&a[a.length-1]!=37&&a[a.length-1]!=35)throw new Error("Unexpected record 0x"+l.toString(16));}},r);$p(t);t.Names=i;t.supbooks=s;return t}function lg(e,r){pa(e,143);for(var t=0;t!=r.SheetNames.length;++t){var a=r.Workbook&&r.Workbook.Sheets&&r.Workbook.Sheets[t]&&r.Workbook.Sheets[t].Hidden||0;var n={Hidden:a,iTabID:t+1,strRelID:"rId"+(t+1),name:r.SheetNames[t]};pa(e,156,ag(n))}pa(e,144)}function cg(r,t){if(!t)t=ua(127);for(var a=0;a!=4;++a)t._W(4,0);za("SheetJS",t);za(e.version,t);za(e.version,t);za("7262",t);return t.length>t.l?t.slice(0,t.l):t}function hg(e,r){if(!r)r=ua(29);r._W(-4,0);r._W(-4,460);r._W(4,28800);r._W(4,17600);r._W(4,500);r._W(4,e);r._W(4,e);var t=120;r._W(1,t);return r.length>r.l?r.slice(0,r.l):r}function ug(e,r){if(!r.Workbook||!r.Workbook.Sheets)return;var t=r.Workbook.Sheets;var a=0,n=-1,i=-1;for(;a<t.length;++a){if(!t[a]||!t[a].Hidden&&n==-1)n=a;else if(t[a].Hidden==1&&i==-1)i=a}if(i>n)return;pa(e,135);pa(e,158,hg(n));pa(e,136)}function dg(e,r){var t=va();pa(t,131);pa(t,128,cg());pa(t,153,ig(e.Workbook&&e.Workbook.WBProps||null));ug(t,e,r);lg(t,e,r);pa(t,132);return t.end()}function vg(e,r,t){if(r.slice(-4)===".bin")return og(e,t);return eg(e,t)}function pg(e,r,t,a,n,i,s,f){if(r.slice(-4)===".bin")return Tp(e,a,t,n,i,s,f);return Yd(e,a,t,n,i,s,f)}function gg(e,r,t,a,n,i,s,f){if(r.slice(-4)===".bin")return Bp(e,a,t,n,i,s,f);return Lp(e,a,t,n,i,s,f)}function mg(e,r,t,a,n,i,s,f){if(r.slice(-4)===".bin")return Ch(e,a,t,n,i,s,f);return yh(e,a,t,n,i,s,f)}function bg(e,r,t,a,n,i,s,f){if(r.slice(-4)===".bin")return Ah(e,a,t,n,i,s,f);return Eh(e,a,t,n,i,s,f)}function wg(e,r,t,a){if(r.slice(-4)===".bin")return lc(e,t,a);return Xl(e,t,a)}function kg(e,r,t){return Rc(e,t)}function Tg(e,r,t){if(r.slice(-4)===".bin")return Vo(e,t);return Bo(e,t)}function Ag(e,r,t){if(r.slice(-4)===".bin")return gh(e,t);return sh(e,t)}function Eg(e,r,t){if(r.slice(-4)===".bin")return Qc(e,r,t);return qc(e,r,t)}function Cg(e,r,t,a){if(t.slice(-4)===".bin")return rh(e,r,t,a);return eh(e,r,t,a)}function yg(e,r,t){if(r.slice(-4)===".bin")return $c(e,r,t);return Kc(e,r,t)}function Sg(e,r,t){return(r.slice(-4)===".bin"?dg:rg)(e,t)}function _g(e,r,t,a,n){return(r.slice(-4)===".bin"?Dp:pv)(e,t,a,n)}function xg(e,r,t,a,n){return(r.slice(-4)===".bin"?Wp:Mp)(e,t,a,n)}function Og(e,r,t){return(r.slice(-4)===".bin"?kc:$l)(e,t)}function Rg(e,r,t){return(r.slice(-4)===".bin"?Xo:Ho)(e,t)}function Ig(e,r,t){return(r.slice(-4)===".bin"?mh:fh)(e,t)}function Ng(e){return(e.slice(-4)===".bin"?Yc:Jc)()}var Dg=/([\w:]+)=((?:")([^"]*)(?:")|(?:')([^']*)(?:'))/g;var Fg=/([\w:]+)=((?:")(?:[^"]*)(?:")|(?:')(?:[^']*)(?:'))/;function Pg(e,r){var t=e.split(/\s+/);var a=[];if(!r)a[0]=t[0];if(t.length===1)return a;var n=e.match(Dg),i,s,f,o;if(n)for(o=0;o!=n.length;++o){i=n[o].match(Fg);if((s=i[1].indexOf(":"))===-1)a[i[1]]=i[2].slice(1,i[2].length-1);else{if(i[1].slice(0,6)==="xmlns:")f="xmlns"+i[1].slice(6);else f=i[1].slice(s+1);a[f]=i[2].slice(1,i[2].length-1)}}return a}function Lg(e){var r=e.split(/\s+/);var t={};if(r.length===1)return t;var a=e.match(Dg),n,i,s,f;if(a)for(f=0;f!=a.length;++f){n=a[f].match(Fg);if((i=n[1].indexOf(":"))===-1)t[n[1]]=n[2].slice(1,n[2].length-1);else{if(n[1].slice(0,6)==="xmlns:")s="xmlns"+n[1].slice(6);else s=n[1].slice(i+1);t[s]=n[2].slice(1,n[2].length-1)}}return t}var Mg;function Ug(e,r){var t=Mg[e]||Yr(e);if(t==="General")return fe(r);return Be(t,r)}function Bg(e,r,t,a){var n=a;switch((t[0].match(/dt:dt="([\w.]+)"/)||["",""])[1]){case"boolean":n=nt(a);break;case"i2":;case"int":n=parseInt(a,10);break;case"r4":;case"float":n=parseFloat(a);break;case"date":;case"dateTime.tz":n=gr(a);break;case"i8":;case"string":;case"fixed":;case"uuid":;case"bin.base64":break;default:throw new Error("bad custprop:"+t[0]);}e[Yr(r)]=n}function Wg(e,r,t){if(e.t==="z")return;if(!t||t.cellText!==false)try{if(e.t==="e"){e.w=e.w||Gn[e.v]}else if(r==="General"){if(e.t==="n"){if((e.v|0)===e.v)e.w=e.v.toString(10);else e.w=se(e.v)}else e.w=fe(e.v)}else e.w=Ug(r||"General",e.v)}catch(a){if(t.WTF)throw a}try{var n=Mg[r]||r||"General";if(t.cellNF)e.z=n;if(t.cellDates&&e.t=="n"&&Fe(n)){var i=q(e.v);if(i){e.t="d";e.v=new Date(i.y,i.m-1,i.d,i.H,i.M,i.S,i.u)}}}catch(a){if(t.WTF)throw a}}function Hg(e,r,t){if(t.cellStyles){if(r.Interior){var a=r.Interior;if(a.Pattern)a.patternType=Ll[a.Pattern]||a.Pattern}}e[r.ID]=r}function zg(e,r,t,a,n,i,s,f,o,l){var c="General",h=a.StyleID,u={};l=l||{};var d=[];var v=0;if(h===undefined&&f)h=f.StyleID;if(h===undefined&&s)h=s.StyleID;while(i[h]!==undefined){if(i[h].nf)c=i[h].nf;if(i[h].Interior)d.push(i[h].Interior);if(!i[h].Parent)break;h=i[h].Parent}switch(t.Type){case"Boolean":a.t="b";a.v=nt(e);break;case"String":a.t="s";a.r=tt(Yr(e));a.v=e.indexOf("<")>-1?Yr(r||e).replace(/<.*?>/g,""):a.r;break;case"DateTime":if(e.slice(-1)!="Z")e+="Z"Line truncated
|
||||
t.opts["CalcIter"]=q;break;case 13:t.opts["CalcMode"]=q;break;case 14:t.opts["CalcPrecision"]=q;break;case 95:t.opts["CalcSaveRecalc"]=q;break;case 15:R.CalcRefMode=q;break;case 2211:t.opts.FullCalc=q;break;case 129:if(q.fDialog)n["!type"]="dialog";if(!q.fBelow)(n["!outline"]||(n["!outline"]={})).above=true;if(!q.fRight)(n["!outline"]||(n["!outline"]={})).left=true;break;case 224:E.push(q);break;case 430:M.push([q]);M[M.length-1].XTI=[];break;case 35:;case 547:M[M.length-1].push(q);break;case 24:;case 536:V={Name:q.Name,Ref:fd(q.rgce,s,null,M,R)};if(q.itab>0)V.Sheet=q.itab-1;M.names.push(V);if(!M[0]){M[0]=[];M[0].XTI=[]}M[M.length-1].push(q);if(q.Name=="_xlnm._FilterDatabase"&&q.itab>0)if(q.rgce&&q.rgce[0]&&q.rgce[0][0]&&q.rgce[0][0][0]=="PtgArea3d")z[q.itab-1]={ref:Na(q.rgce[0][0][1][2])};break;case 22:R.ExternCount=q;break;case 23:if(M.length==0){M[0]=[];M[0].XTI=[]}M[M.length-1].XTI=M[M.length-1].XTI.concat(q);M.XTI=M.XTI.concat(q);break;case 2196:if(R.biff<8)break;if(V!=null)V.Comment=q[1];break;case 18:n["!protect"]=q;break;case 19:if(q!==0&&R.WTF)console.error("Password verifier: "+q);break;case 133:{i[q.pos]=q;R.snames.push(q.name)}break;case 10:{if(--B)break;if(s.e){if(s.e.r>0&&s.e.c>0){s.e.r--;s.e.c--;n["!ref"]=Na(s);if(r.sheetRows&&r.sheetRows<=s.e.r){var Q=s.e.r;s.e.r=r.sheetRows-1;n["!fullref"]=n["!ref"];n["!ref"]=Na(s);s.e.r=Q}s.e.r++;s.e.c++}if(N.length>0)n["!merges"]=N;if(D.length>0)n["!objects"]=D;if(F.length>0)n["!cols"]=F;if(P.length>0)n["!rows"]=P;y.Sheets.push(S)}if(c==="")h=n;else a[c]=n;n=r.dense?[]:{}}break;case 9:;case 521:;case 1033:;case 2057:{if(R.biff===8)R.biff={9:2,521:3,1033:4}[$]||{512:2,768:3,1024:4,1280:5,1536:8,2:2,7:2}[q.BIFFVer]||8;R.biffguess=q.BIFFVer==0;if(q.BIFFVer==0&&q.dt==4096){R.biff=5;G=true;o(R.codepage=28591)}if(R.biff==8&&q.BIFFVer==0&&q.dt==16)R.biff=2;if(B++)break;n=r.dense?[]:{};if(R.biff<8&&!G){G=true;o(R.codepage=r.codepage||1252)}if(R.biff<5||q.BIFFVer==0&&q.dt==4096){if(c==="")c="Sheet1";s={s:{r:0,c:0},e:{r:0,c:0}};var ee={pos:e.l-Y,name:c};i[ee.pos]=ee;R.snames.push(c)}else c=(i[j]||{name:""}).name;if(q.dt==32)n["!type"]="chart";if(q.dt==64)n["!type"]="macro";N=[];D=[];R.arrayf=k=[];F=[];P=[];L=false;S={Hidden:(i[j]||{hs:0}).hs,name:c}}break;case 515:;case 3:;case 2:{if(n["!type"]=="chart")if(r.dense?(n[q.r]||[])[q.c]:n[Ra({c:q.c,r:q.r})])++q.c;T={ixfe:q.ixfe,XF:E[q.ixfe]||{},v:q.val,t:"n"};if(W>0)T.z=H[T.ixfe>>8&63];om(T,r,t.opts.Date1904);O({c:q.c,r:q.r},T,r)}break;case 5:;case 517:{T={ixfe:q.ixfe,XF:E[q.ixfe],v:q.val,t:q.t};if(W>0)T.z=H[T.ixfe>>8&63];om(T,r,t.opts.Date1904);O({c:q.c,r:q.r},T,r)}break;case 638:{T={ixfe:q.ixfe,XF:E[q.ixfe],v:q.rknum,t:"n"};if(W>0)T.z=H[T.ixfe>>8&63];om(T,r,t.opts.Date1904);O({c:q.c,r:q.r},T,r)}break;case 189:{for(var re=q.c;re<=q.C;++re){var te=q.rkrec[re-q.c][0];T={ixfe:te,XF:E[te],v:q.rkrec[re-q.c][1],t:"n"};if(W>0)T.z=H[T.ixfe>>8&63];om(T,r,t.opts.Date1904);O({c:re,r:q.r},T,r)}}break;case 6:;case 518:;case 1030:{if(q.val=="String"){f=q;break}T=lm(q.val,q.cell.ixfe,q.tt);T.XF=E[T.ixfe];if(r.cellFormula){var ae=q.formula;if(ae&&ae[0]&&ae[0][0]&&ae[0][0][0]=="PtgExp"){var ne=ae[0][0][1][0],ie=ae[0][0][1][1];var se=Ra({r:ne,c:ie});if(w[se])T.f=""+fd(q.formula,s,q.cell,M,R);else T.F=((r.dense?(n[ne]||[])[ie]:n[se])||{}).F}else T.f=""+fd(q.formula,s,q.cell,M,R)}if(W>0)T.z=H[T.ixfe>>8&63];om(T,r,t.opts.Date1904);O(q.cell,T,r);f=q}break;case 7:;case 519:{if(f){f.val=q;T=lm(q,f.cell.ixfe,"s");T.XF=E[T.ixfe];if(r.cellFormula){T.f=""+fd(f.formula,s,f.cell,M,R)}if(W>0)T.z=H[T.ixfe>>8&63];om(T,r,t.opts.Date1904);O(f.cell,T,r);f=null}else throw new Error("String record expects Formula")}break;case 33:;case 545:{k.push(q);var fe=Ra(q[0].s);v=r.dense?(n[q[0].s.r]||[])[q[0].s.c]:n[fe];if(r.cellFormula&&v){if(!f)break;if(!fe||!v)break;v.f=""+fd(q[1],s,q[0],M,R);v.F=Na(q[0])}}break;case 1212:{if(!r.cellFormula)break;if(d){if(!f)break;w[Ra(f.cell)]=q[0];v=r.dense?(n[f.cell.r]||[])[f.cell.c]:n[Ra(f.cell)];(v||{}).f=""+fd(q[0],s,u,M,R)}}break;case 253:T=lm(l[q.isst].t,q.ixfe,"s");if(l[q.isst].h)T.h=l[q.isst].h;T.XF=E[T.ixfe];if(W>0)T.z=H[T.ixfe>>8&63];om(T,r,t.opts.Date1904);O({c:q.c,r:q.r},T,r);break;case 513:if(r.sheetStubs){T={ixfe:q.ixfe,XF:E[q.ixfe],t:"z"};if(W>0)T.z=H[T.ixfe>>8&63];om(T,r,t.opts.Date1904);O({c:q.c,r:q.r},T,r)}break;case 190:if(r.sheetStubs){for(var oe=q.c;oe<=q.C;++oe){var le=q.ixfe[oe-q.c];T={ixfe:le,XF:E[le],t:"z"};if(W>0)T.z=H[T.ixfe>>8&63];om(T,r,t.opts.Date1904);O({c:oe,r:q.r},T,r)}}break;case 214:;case 516:;case 4:T=lm(q.val,q.ixfe,"s");T.XF=E[T.ixfe];if(W>0)T.z=H[T.ixfe>>8&63];om(T,r,t.opts.Date1904);O({c:q.c,r:q.r},T,r);break;case 0:;case 512:{if(B===1)s=q}break;case 252:{l=q}break;case 1054:{if(R.biff==4){H[W++]=q[1];for(var ce=0;ce<W+163;++ce)if(X[ce]==q[1])break;if(ce>=163)We(q[1],W+163)}else We(q[1],q[0])}break;case 30:{H[W++]=q;for(var he=0;he<W+163;++he)if(X[he]==q)break;if(he>=163)We(q,W+163)}break;case 229:N=N.concat(q);break;case 93:D[q.cmo[0]]=R.lastobj=q;break;case 438:R.lastobj.TxO=q;break;caLine truncated
|
||||
var o=Math.min(a.sheetRows||1e7,f.length);var l={s:{r:0,c:0},e:{r:n,c:i}};if(e["!ref"]){var c=Ia(e["!ref"]);l.s.r=Math.min(l.s.r,c.s.r);l.s.c=Math.min(l.s.c,c.s.c);l.e.r=Math.max(l.e.r,c.e.r);l.e.c=Math.max(l.e.c,c.e.c);if(n==-1)l.e.r=n=c.e.r+1}var h=[],u=0;var d=e["!rows"]||(e["!rows"]=[]);var v=0,p=0,m=0,b=0,w=0,k=0;if(!e["!cols"])e["!cols"]=[];for(;v<f.length&&p<o;++v){var T=f[v];if($m(T)){if(a.display)continue;d[p]={hidden:true}}var A=T.children;for(m=b=0;m<A.length;++m){var E=A[m];if(a.display&&$m(E))continue;var C=E.hasAttribute("data-v")?E.getAttribute("data-v"):E.hasAttribute("v")?E.getAttribute("v"):ut(E.innerHTML);var y=E.getAttribute("data-z")||E.getAttribute("z");for(u=0;u<h.length;++u){var S=h[u];if(S.s.c==b+i&&S.s.r<p+n&&p+n<=S.e.r){b=S.e.c+1-i;u=-1}}k=+E.getAttribute("colspan")||1;if((w=+E.getAttribute("rowspan")||1)>1||k>1)h.push({s:{r:p+n,c:b+i},e:{r:p+n+(w||1)-1,c:b+i+(k||1)-1}});var _={t:"s",v:C};var x=E.getAttribute("data-t")||E.getAttribute("t")||"";if(C!=null){if(C.length==0)_.t=x||"z";else if(a.raw||C.trim().length==0||x=="s"){}else if(C==="TRUE")_={t:"b",v:true};else if(C==="FALSE")_={t:"b",v:false};else if(!isNaN(kr(C)))_={t:"n",v:kr(C)};else if(!isNaN(Ar(C).getDate())){_={t:"d",v:gr(C)};if(!a.cellDates)_={t:"n",v:fr(_.v)};_.z=a.dateNF||X[14]}}if(_.z===undefined&&y!=null)_.z=y;var O="",R=E.getElementsByTagName("A");if(R&&R.length)for(var I=0;I<R.length;++I)if(R[I].hasAttribute("href")){O=R[I].getAttribute("href");if(O.charAt(0)!="#")break}if(O&&O.charAt(0)!="#")_.l={Target:O};if(a.dense){if(!e[p+n])e[p+n]=[];e[p+n][b+i]=_}else e[Ra({c:b+i,r:p+n})]=_;if(l.e.c<b+i)l.e.c=b+i;b+=k}++p}if(h.length)e["!merges"]=(e["!merges"]||[]).concat(h);l.e.r=Math.max(l.e.r,p-1+n);e["!ref"]=Na(l);if(p>=o)e["!fullref"]=Na((l.e.r=f.length-v+p-1+n,l));return e}function jm(e,r){var t=r||{};var a=t.dense?[]:{};return Gm(a,e,r)}function Xm(e,r){return La(jm(e,r),r)}function $m(e){var r="";var t=Ym(e);if(t)r=t(e).getPropertyValue("display");if(!r)r=e.style&&e.style.display;return r==="none"}function Ym(e){if(e.ownerDocument.defaultView&&typeof e.ownerDocument.defaultView.getComputedStyle==="function")return e.ownerDocument.defaultView.getComputedStyle;if(typeof getComputedStyle==="function")return getComputedStyle;return null}function Km(e){var r=e.replace(/[\t\r\n]/g," ").trim().replace(/ +/g," ").replace(/<text:s\/>/g," ").replace(/<text:s text:c="(\d+)"\/>/g,function(e,r){return Array(parseInt(r,10)+1).join(" ")}).replace(/<text:tab[^>]*\/>/g,"\t").replace(/<text:line-break\/>/g,"\n");var t=Yr(r.replace(/<[^>]*>/g,""));return[t]}var Jm={day:["d","dd"],month:["m","mm"],year:["y","yy"],hours:["h","hh"],minutes:["m","mm"],seconds:["s","ss"],"am-pm":["A/P","AM/PM"],"day-of-week":["ddd","dddd"],era:["e","ee"],quarter:["\\Qm",'m\\"th quarter"']};function qm(e,r){var t=r||{};if(g!=null&&t.dense==null)t.dense=g;var a=Et(e);var n=[],i;var s;var f={name:""},o="",l=0;var c;var h;var u={},d=[];var v=t.dense?[]:{};var p,m;var b={value:""};var w="",k=0,T;var A=[];var E=-1,C=-1,y={s:{r:1e6,c:1e7},e:{r:0,c:0}};var S=0;var _={};var x=[],O={},R=0,I=0;var N=[],D=1,F=1;var P=[];var L={Names:[]};var M={};var U=["",""];var B=[],W={};var H="",z=0;var V=false,G=false;var j=0;Ct.lastIndex=0;a=a.replace(/<!--([\s\S]*?)-->/gm,"").replace(/<!DOCTYPE[^\[]*\[[^\]]*\]>/gm,"");while(p=Ct.exec(a))switch(p[3]=p[3].replace(/_.*$/,"")){case"table":;case"工作表":if(p[1]==="/"){if(y.e.c>=y.s.c&&y.e.r>=y.s.r)v["!ref"]=Na(y);else v["!ref"]="A1:A1";if(t.sheetRows>0&&t.sheetRows<=y.e.r){v["!fullref"]=v["!ref"];y.e.r=t.sheetRows-1;v["!ref"]=Na(y)}if(x.length)v["!merges"]=x;if(N.length)v["!rows"]=N;c.name=c["名称"]||c.name;if(typeof JSON!=="undefined")JSON.stringify(c);d.push(c.name);u[c.name]=v;G=false}else if(p[0].charAt(p[0].length-2)!=="/"){c=Gr(p[0],false);E=C=-1;y.s.r=y.s.c=1e7;y.e.r=y.e.c=0;v=t.dense?[]:{};x=[];N=[];G=true}break;case"table-row-group":if(p[1]==="/")--S;else++S;break;case"table-row":;case"行":if(p[1]==="/"){E+=D;D=1;break}h=Gr(p[0],false);if(h["行号"])E=h["行号"]-1;else if(E==-1)E=0;D=+h["number-rows-repeated"]||1;if(D<10)for(j=0;j<D;++j)if(S>0)N[E+j]={level:S};C=-1;break;case"covered-table-cell":if(p[1]!=="/")++C;if(t.sheetStubs){if(t.dense){if(!v[E])v[E]=[];v[E][C]={t:"z"}}else v[Ra({r:E,c:C})]={t:"z"}}w="";A=[];break;case"table-cell":;case"数据":if(p[0].charAt(p[0].length-2)==="/"){++C;b=Gr(p[0],false);F=parseInt(b["number-columns-repeated"]||"1",10);m={t:"z",v:null};if(b.formula&&t.cellFormula!=false)m.f=Cd(Yr(b.formula));if((b["数据类型"]||b["value-type"])=="string"){m.t="s";m.v=Yr(b["string-value"]||"");if(t.dense){if(!v[E])v[E]=[];v[E][C]=m}else{v[Ra({r:E,c:C})]=m}}C+=F-1}else if(p[1]!=="/"){++C;w="";k=0;A=[];F=1;var X=D?E+D-1:E;if(C>y.e.c)y.e.c=C;if(C<y.s.c)y.s.c=C;if(E<y.s.r)y.s.r=E;if(X>y.e.r)y.e.r=X;b=Gr(p[0],false);B=[];W={};m={t:b["数据类型"]||b["value-type"],v:null};if(t.cellFormula){if(b.formula)b.formula=Yr(b.formula);if(b["number-matrix-columns-spanned"]&&b["number-matrix-rows-spanned"]){R=Line truncated
|
||||
var o=((a=s[17])==null?void 0:a[0])?Ob(e,e[_b(s[17][0].data)][0]):[];var l=vb(s[3][0].data);var c=0;l[1].forEach(function(r){var a=vb(r.data);var n=e[_b(a[2][0].data)][0];var i=db(n.meta[1][0].data);if(i!=6002)throw new Error("6001 unexpected reference to ".concat(i));var s=Ib(e,n);s.data.forEach(function(e,r){e.forEach(function(e,a){var n=Ra({r:c+r,c:a});var i=Sb(e,f,o);if(i)t[n]=i})});c+=s.nrows})}function Db(e,r){var t=vb(r.data);var a={"!ref":"A1"};var n=e[_b(t[2][0].data)];var i=db(n[0].meta[1][0].data);if(i!=6001)throw new Error("6000 unexpected reference to ".concat(i));Nb(e,n[0],a);return a}function Fb(e,r){var t;var a=vb(r.data);var n={name:((t=a[1])==null?void 0:t[0])?nb(a[1][0].data):"",sheets:[]};var i=gb(a[2],_b);i.forEach(function(r){e[r].forEach(function(r){var t=db(r.meta[1][0].data);if(t==6e3)n.sheets.push(Db(e,r))})});return n}function Pb(e,r){var t=Nw();var a=vb(r.data);var n=gb(a[1],_b);n.forEach(function(r){e[r].forEach(function(r){var a=db(r.meta[1][0].data);if(a==2){var n=Fb(e,r);n.sheets.forEach(function(e,r){Dw(t,e,r==0?n.name:n.name+"_"+r,true)})}})});if(t.SheetNames.length==0)throw new Error("Empty NUMBERS file");return t}function Lb(e){var r,t,a,n;var i={},s=[];e.FullPaths.forEach(function(e){if(e.match(/\.iwpv2/))throw new Error("Unsupported password protection")});e.FileIndex.forEach(function(e){if(!e.name.match(/\.iwa$/))return;var r;try{r=kb(e.content)}catch(t){return console.log("?? "+e.content.length+" "+(t.message||t))}var a;try{a=mb(r)}catch(t){return console.log("## "+(t.message||t))}a.forEach(function(e){i[e.id]=e.messages;s.push(e.id)})});if(!s.length)throw new Error("File has no messages");var f=((n=(a=(t=(r=i==null?void 0:i[1])==null?void 0:r[0])==null?void 0:t.meta)==null?void 0:a[1])==null?void 0:n[0].data)&&db(i[1][0].meta[1][0].data)==1&&i[1][0];if(!f)s.forEach(function(e){i[e].forEach(function(e){var r=db(e.meta[1][0].data)>>>0;if(r==1){if(!f)f=e;else throw new Error("Document has multiple roots")}})});if(!f)throw new Error("Cannot find Document root");return Pb(i,f)}function Mb(e,r,t){var a,n,i,s;if(!((a=e[6])==null?void 0:a[0])||!((n=e[7])==null?void 0:n[0]))throw"Mutation only works on post-BNC storages!";var f=((s=(i=e[8])==null?void 0:i[0])==null?void 0:s.data)&&db(e[8][0].data)>0||false;if(f)throw"Math only works with normal offsets";var o=0;var l=ab(e[7][0].data),c=0,h=[];var u=ab(e[4][0].data),d=0,v=[];for(var p=0;p<r.length;++p){if(r[p]==null){l.setUint16(p*2,65535,true);u.setUint16(p*2,65535);continue}l.setUint16(p*2,c,true);u.setUint16(p*2,d,true);var g,m;switch(typeof r[p]){case"string":g=Cb({t:"s",v:r[p]},t);m=yb({t:"s",v:r[p]},t);break;case"number":g=Cb({t:"n",v:r[p]},t);m=yb({t:"n",v:r[p]},t);break;case"boolean":g=Cb({t:"b",v:r[p]},t);m=yb({t:"b",v:r[p]},t);break;default:throw new Error("Unsupported value "+r[p]);}h.push(g);c+=g.length;v.push(m);d+=m.length;++o}e[2][0].data=ub(o);for(;p<e[7][0].data.length/2;++p){l.setUint16(p*2,65535,true);u.setUint16(p*2,65535,true)}e[6][0].data=fb(h);e[3][0].data=fb(v);return o}function Ub(e,r){if(!r||!r.numbers)throw new Error("Must pass a `numbers` option -- check the README");var t=e.Sheets[e.SheetNames[0]];if(e.SheetNames.length>1)console.error("The Numbers writer currently writes only the first table");var a=Ia(t["!ref"]);a.s.r=a.s.c=0;var n=false;if(a.e.c>9){n=true;a.e.c=9}if(a.e.r>49){n=true;a.e.r=49}if(n)console.error("The Numbers writer is currently limited to ".concat(Na(a)));var i=Aw(t,{range:a,header:1});var s=["~Sh33tJ5~"];i.forEach(function(e){return e.forEach(function(e){if(typeof e=="string")s.push(e)})});var f={};var o=[];var l=Ke.read(r.numbers,{type:"base64"});l.FileIndex.map(function(e,r){return[e,l.FullPaths[r]]}).forEach(function(e){var r=e[0],t=e[1];if(r.type!=2)return;if(!r.name.match(/\.iwa/))return;var a=r.content;var n=kb(a);var i=mb(n);i.forEach(function(e){o.push(e.id);f[e.id]={deps:[],location:t,type:db(e.messages[0].meta[1][0].data)}})});o.sort(function(e,r){return e-r});var c=o.filter(function(e){return e>1}).map(function(e){return[e,ub(e)]});l.FileIndex.map(function(e,r){return[e,l.FullPaths[r]]}).forEach(function(e){var r=e[0],t=e[1];if(!r.name.match(/\.iwa/))return;var a=mb(kb(r.content));a.forEach(function(e){e.messages.forEach(function(r){c.forEach(function(r){if(e.messages.some(function(e){return db(e.meta[1][0].data)!=11006&&sb(e.data,r[1])})){f[r[0]].deps.push(e.id)}})})})});function h(){for(var e=927262;e<2e6;++e)if(!f[e])return e;throw new Error("Too many messages")}var u=Ke.find(l,f[1].location);var d=mb(kb(u.content));var v;for(var p=0;p<d.length;++p){var g=d[p];if(g.id==1)v=g}var m=_b(vb(v.messages[0].data)[1][0].data);u=Ke.find(l,f[m].location);d=mb(kb(u.content));for(p=0;p<d.length;++p){g=d[p];if(g.id==m)v=g}m=_b(vb(v.messages[0].data)[2][0].data);u=Ke.find(l,f[m].location);d=mb(kb(u.content));for(p=0;p<d.length;++p){g=d[p];if(g.id==m)v=g}m=_b(vb(v.messages[0].data)[2][0].data);u=Ke.find(l,f[m].location);d=mb(kb(u.content));for(p=0;p<d.length;++p){g=d[p];if(g.id==Line truncated
|
||||
var o=[];var l=Array.isArray(e);for(f=n.s.c;f<=n.e.c;++f)s[f]=ya(f);for(var c=n.s.r;c<=n.e.r;++c){i=Ta(c);for(f=n.s.c;f<=n.e.c;++f){r=s[f]+i;t=l?(e[c]||[])[f]:e[r];a="";if(t===undefined)continue;else if(t.F!=null){r=t.F;if(!t.f)continue;a=t.f;if(r.indexOf(":")==-1)r=r+":"+r}if(t.f!=null)a=t.f;else if(t.t=="z")continue;else if(t.t=="n"&&t.v!=null)a=""+t.v;else if(t.t=="b")a=t.v?"TRUE":"FALSE";else if(t.w!==undefined)a="'"+t.w;else if(t.v===undefined)continue;else if(t.t=="s")a="'"+t.v;else a=""+t.v;o[o.length]=r+"="+a}}return o}function xw(e,r,t){var a=t||{};var n=+!a.skipHeader;var i=e||{};var s=0,f=0;if(i&&a.origin!=null){if(typeof a.origin=="number")s=a.origin;else{var o=typeof a.origin=="string"?Oa(a.origin):a.origin;s=o.r;f=o.c}}var l;var c={s:{c:0,r:0},e:{c:f,r:s+r.length-1+n}};if(i["!ref"]){var h=Da(i["!ref"]);c.e.c=Math.max(c.e.c,h.e.c);c.e.r=Math.max(c.e.r,h.e.r);if(s==-1){s=h.e.r+1;c.e.r=s+r.length-1+n}}else{if(s==-1){s=0;c.e.r=r.length-1+n}}var u=a.header||[],d=0;r.forEach(function(e,r){rr(e).forEach(function(t){if((d=u.indexOf(t))==-1)u[d=u.length]=t;var o=e[t];var c="z";var h="";var v=Ra({c:f+d,r:s+r+n});l=Rw(i,v);if(o&&typeof o==="object"&&!(o instanceof Date)){i[v]=o}else{if(typeof o=="number")c="n";else if(typeof o=="boolean")c="b";else if(typeof o=="string")c="s";else if(o instanceof Date){c="d";if(!a.cellDates){c="n";o=fr(o)}h=a.dateNF||X[14]}else if(o===null&&a.nullError){c="e";o=0}if(!l)i[v]=l={t:c,v:o};else{l.t=c;l.v=o;delete l.w;delete l.R;if(h)l.z=h}if(h)l.z=h}})});c.e.c=Math.max(c.e.c,f+u.length-1);var v=Ta(s);if(n)for(d=0;d<u.length;++d)i[ya(d+f)+v]={t:"s",v:u[d]};i["!ref"]=Na(c);return i}function Ow(e,r){return xw(null,e,r)}function Rw(e,r,t){if(typeof r=="string"){if(Array.isArray(e)){var a=Oa(r);if(!e[a.r])e[a.r]=[];return e[a.r][a.c]||(e[a.r][a.c]={t:"z"})}return e[r]||(e[r]={t:"z"})}if(typeof r!="number")return Rw(e,Ra(r));return Rw(e,Ra({r:r,c:t||0}))}function Iw(e,r){if(typeof r=="number"){if(r>=0&&e.SheetNames.length>r)return r;throw new Error("Cannot find sheet # "+r)}else if(typeof r=="string"){var t=e.SheetNames.indexOf(r);if(t>-1)return t;throw new Error("Cannot find sheet name |"+r+"|")}else throw new Error("Cannot find sheet |"+r+"|")}function Nw(){return{SheetNames:[],Sheets:{}}}function Dw(e,r,t,a){var n=1;if(!t)for(;n<=65535;++n,t=undefined)if(e.SheetNames.indexOf(t="Sheet"+n)==-1)break;if(!t||e.SheetNames.length>=65535)throw new Error("Too many worksheets");if(a&&e.SheetNames.indexOf(t)>=0){var i=t.match(/(^.*?)(\d+)$/);n=i&&+i[2]||0;var s=i&&i[1]||t;for(++n;n<=65535;++n)if(e.SheetNames.indexOf(t=s+n)==-1)break}Jp(t);if(e.SheetNames.indexOf(t)>=0)throw new Error("Worksheet with name |"+t+"| already exists!");e.SheetNames.push(t);e.Sheets[t]=r;return t}function Fw(e,r,t){if(!e.Workbook)e.Workbook={};if(!e.Workbook.Sheets)e.Workbook.Sheets=[];var a=Iw(e,r);if(!e.Workbook.Sheets[a])e.Workbook.Sheets[a]={};switch(t){case 0:;case 1:;case 2:break;default:throw new Error("Bad sheet visibility setting "+t);}e.Workbook.Sheets[a].Hidden=t}function Pw(e,r){e.z=r;return e}function Lw(e,r,t){if(!r){delete e.l}else{e.l={Target:r};if(t)e.l.Tooltip=t}return e}function Mw(e,r,t){return Lw(e,"#"+r,t)}function Uw(e,r,t){if(!e.c)e.c=[];e.c.push({t:r,a:t||"SheetJS"})}function Bw(e,r,t,a){var n=typeof r!="string"?r:Da(r);var i=typeof r=="string"?r:Na(r);for(var s=n.s.r;s<=n.e.r;++s)for(var f=n.s.c;f<=n.e.c;++f){var o=Rw(e,s,f);o.t="n";o.F=i;delete o.v;if(s==n.s.r&&f==n.s.c){o.f=t;if(a)o.D=true}}return e}var Ww={encode_col:ya,encode_row:Ta,encode_cell:Ra,encode_range:Na,decode_col:Ca,decode_row:ka,split_cell:xa,decode_cell:Oa,decode_range:Ia,format_cell:Pa,sheet_add_aoa:Ma,sheet_add_json:xw,sheet_add_dom:Gm,aoa_to_sheet:Ua,json_to_sheet:Ow,table_to_sheet:jm,table_to_book:Xm,sheet_to_csv:yw,sheet_to_txt:Sw,sheet_to_json:Aw,sheet_to_html:Vm,sheet_to_formulae:_w,sheet_to_row_object_array:Aw,sheet_get_cell:Rw,book_new:Nw,book_append_sheet:Dw,book_set_sheet_visibility:Fw,cell_set_number_format:Pw,cell_set_hyperlink:Lw,cell_set_internal_link:Mw,cell_add_comment:Uw,sheet_set_array_formula:Bw,consts:{SHEET_VISIBLE:0,SHEET_HIDDEN:1,SHEET_VERY_HIDDEN:2}};var Hw;function zw(e){Hw=e}function Vw(e,r){var t=Hw();var a=r==null?{}:r;if(e==null||e["!ref"]==null){t.push(null);return t}var n=Da(e["!ref"]);var i=a.FS!==undefined?a.FS:",",s=i.charCodeAt(0);var f=a.RS!==undefined?a.RS:"\n",o=f.charCodeAt(0);var l=new RegExp((i=="|"?"\\|":i)+"+$");var c="",h=[];a.dense=Array.isArray(e);var u=a.skipHidden&&e["!cols"]||[];var d=a.skipHidden&&e["!rows"]||[];for(var v=n.s.c;v<=n.e.c;++v)if(!(u[v]||{}).hidden)h[v]=ya(v);var p=n.s.r;var g=false,m=0;t._read=function(){if(!g){g=true;return t.push("\ufeff")}while(p<=n.e.r){++p;if((d[p-1]||{}).hidden)continue;c=Cw(e,n,p-1,h,s,o,i,a);if(c!=null){if(a.strip)c=c.replace(l,"");if(c||a.blankrows!==false)return t.push((m++?f:"")+c)}}return t.push(null)};return t}function Gw(e,r){var t=Hw();var a=r||{};var n=a.header!=null?a.header:Bm;var i=a.footer!=null?a.footer:Wm;t.push(n)Line truncated
|
||||
@@ -0,0 +1,67 @@
|
||||
{#
|
||||
Gemeinsame Auditlog-Zeilendarstellung -- von activity_log.html (Erstladung)
|
||||
UND _audit_log_rows.html (AJAX-Nachladen über "Weitere 500 laden")
|
||||
importiert, damit beide garantiert dieselbe Darstellung erzeugen und
|
||||
action_labels/action_icons/category_of nicht an zwei Stellen gepflegt
|
||||
werden müssen.
|
||||
#}
|
||||
{% set action_labels = {
|
||||
"settings.update": "Einstellung geändert",
|
||||
"device.create": "Gerät angelegt",
|
||||
"device.edit": "Gerät bearbeitet",
|
||||
"device.delete": "Gerät gelöscht",
|
||||
"device.activate": "Gerät aktiviert",
|
||||
"device.deactivate": "Gerät deaktiviert",
|
||||
"switch.create": "Switch angelegt",
|
||||
"switch.edit": "Switch bearbeitet",
|
||||
"switch.delete": "Switch gelöscht",
|
||||
"credential.create": "Zugangsdaten angelegt",
|
||||
"credential.edit": "Zugangsdaten bearbeitet",
|
||||
"credential.delete": "Zugangsdaten gelöscht",
|
||||
"user.create": "Benutzer angelegt",
|
||||
"user.edit": "Benutzer bearbeitet",
|
||||
"user.delete": "Benutzer gelöscht",
|
||||
"user.assign_group": "Gruppe zugewiesen",
|
||||
"group.create": "Gruppe angelegt",
|
||||
"group.edit": "Gruppe bearbeitet",
|
||||
"group.delete": "Gruppe gelöscht",
|
||||
"group.assign_admins": "Admin-Zuweisung geändert",
|
||||
"profile.update": "Profil aktualisiert",
|
||||
"profile.password": "Passwort geändert",
|
||||
"check.run_now": "Prüfung manuell gestartet",
|
||||
"fileshare.upload": "Datei(en) hochgeladen",
|
||||
"fileshare.mkdir": "Ordner angelegt",
|
||||
"fileshare.delete": "Datei/Ordner gelöscht",
|
||||
"fileshare.rename": "Datei/Ordner umbenannt",
|
||||
"fileshare.download": "Als ZIP heruntergeladen",
|
||||
"auditlog.archive": "Auditlog archiviert",
|
||||
"auditlog.export_delete": "Auditlog-Archiv exportiert",
|
||||
} %}
|
||||
{% set action_icons = {
|
||||
"delete": '<path d="M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2m3 0l-1 14a2 2 0 01-2 2H7a2 2 0 01-2-2L4 6"/>',
|
||||
"create": '<path d="M12 5v14M5 12h14"/>',
|
||||
"edit": '<path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 013 3L7 19l-4 1 1-4z"/>',
|
||||
"activate": '<path d="M20 6L9 17l-5-5"/>',
|
||||
"deactivate": '<circle cx="12" cy="12" r="9"/><path d="M15 9l-6 6M9 9l6 6"/>',
|
||||
} %}
|
||||
{% set create_kinds = ("create", "upload", "mkdir") %}
|
||||
|
||||
{% macro category_of(kind) %}{% if kind in create_kinds %}create{% elif kind == "delete" %}delete{% else %}edit{% endif %}{% endmacro %}
|
||||
|
||||
{% macro audit_row(e) %}
|
||||
{% set kind = e['action'].split('.')[-1] %}
|
||||
{% set cat = category_of(kind)|trim %}
|
||||
<tr data-category="{{ cat }}" data-sort-ts="{{ e['ts'] }}" data-sort-user="{{ e['username']|lower }}"
|
||||
data-sort-action="{{ action_labels.get(e['action'], e['action'])|lower }}" data-sort-target="{{ (e['target'] or '')|lower }}">
|
||||
<td class="text-dim mono" style="font-size:12.5px;">{{ e['ts'] }}</td>
|
||||
<td class="cell-name">{{ e['username'] }}</td>
|
||||
<td>
|
||||
<span class="pill action-pill action-pill--{{ cat }}">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">{{ (action_icons.get(kind) or action_icons['edit'])|safe }}</svg>
|
||||
{{ action_labels.get(e['action'], e['action']) }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ e['target'] or '—' }}</td>
|
||||
<td class="text-dim">{{ e['details'] or '—' }}</td>
|
||||
</tr>
|
||||
{% endmacro %}
|
||||
@@ -0,0 +1,5 @@
|
||||
{# Reines Zeilenfragment fürs AJAX-Nachladen (activity_log_more()) -- kein
|
||||
umschließendes <table>/<tbody>, wird per insertAdjacentHTML direkt an das
|
||||
bestehende tbody von #auditTable angehängt. #}
|
||||
{% import "_audit_log_macros.html" as m %}
|
||||
{% for e in entries %}{{ m.audit_row(e) }}{% endfor %}
|
||||
@@ -0,0 +1,9 @@
|
||||
{#
|
||||
Gemeinsames "i"-Hinweis-Icon: Hover zeigt den Text als Tooltip neben dem
|
||||
Mauszeiger, Klick/Tap öffnet dasselbe (wiederverwendete) Modal mit dem
|
||||
vollen Text -- Mechanik lebt zentral in app.js (initHintIcons), hier wird
|
||||
nur das Icon mit seinen Daten-Attributen erzeugt. title = Kontext für die
|
||||
Modal-Überschrift (z.B. das zugehörige Feld-Label), text = der eigentliche
|
||||
Erklärungstext.
|
||||
#}
|
||||
{% macro hint_icon(text, title='Hinweis') %}<button type="button" class="hint-icon" data-hint="{{ text }}" data-hint-title="{{ title }}" aria-label="Hinweis: {{ title }}">i</button>{% endmacro %}
|
||||
@@ -1,75 +1,224 @@
|
||||
{% extends "base.html" %}
|
||||
{% set active_page = "logs" %}
|
||||
{% block page_title %}Änderungen{% endblock %}
|
||||
{% block page_sub %}<div class="topbar-sub">{{ entries|length }} Einträge (letzte 500)</div>{% endblock %}
|
||||
{% block page_title %}Auditlog{% endblock %}
|
||||
{% block page_sub %}
|
||||
<div class="topbar-sub" id="auditEntryCount">
|
||||
{% if has_more %}{{ entries|length }} von {{ total_count }} Einträgen geladen{% else %}{{ entries|length }} Eintrag{{ 'e' if entries|length != 1 else '' }}{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% import "_audit_log_macros.html" as m %}
|
||||
|
||||
{% set action_labels = {
|
||||
"settings.update": "Einstellung geändert",
|
||||
"device.create": "Gerät angelegt",
|
||||
"device.edit": "Gerät bearbeitet",
|
||||
"device.delete": "Gerät gelöscht",
|
||||
"device.activate": "Gerät aktiviert",
|
||||
"device.deactivate": "Gerät deaktiviert",
|
||||
"switch.create": "Switch angelegt",
|
||||
"switch.edit": "Switch bearbeitet",
|
||||
"switch.delete": "Switch gelöscht",
|
||||
"credential.create": "Zugangsdaten angelegt",
|
||||
"credential.edit": "Zugangsdaten bearbeitet",
|
||||
"credential.delete": "Zugangsdaten gelöscht",
|
||||
"user.create": "Benutzer angelegt",
|
||||
"user.edit": "Benutzer bearbeitet",
|
||||
"user.delete": "Benutzer gelöscht",
|
||||
"user.assign_group": "Gruppe zugewiesen",
|
||||
"group.create": "Gruppe angelegt",
|
||||
"group.edit": "Gruppe bearbeitet",
|
||||
"group.delete": "Gruppe gelöscht",
|
||||
"group.assign_admins": "Admin-Zuweisung geändert",
|
||||
"profile.update": "Profil aktualisiert",
|
||||
"profile.password": "Passwort geändert",
|
||||
"check.run_now": "Prüfung manuell gestartet",
|
||||
} %}
|
||||
{% set action_icons = {
|
||||
"delete": '<path d="M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2m3 0l-1 14a2 2 0 01-2 2H7a2 2 0 01-2-2L4 6"/>',
|
||||
"create": '<path d="M12 5v14M5 12h14"/>',
|
||||
"edit": '<path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 013 3L7 19l-4 1 1-4z"/>',
|
||||
"activate": '<path d="M20 6L9 17l-5-5"/>',
|
||||
"deactivate": '<circle cx="12" cy="12" r="9"/><path d="M15 9l-6 6M9 9l6 6"/>',
|
||||
} %}
|
||||
{% set ns = namespace(create=0, delete=0, edit=0) %}
|
||||
{% for e in entries %}
|
||||
{% set cat = m.category_of(e['action'].split('.')[-1])|trim %}
|
||||
{% if cat == "create" %}{% set ns.create = ns.create + 1 %}
|
||||
{% elif cat == "delete" %}{% set ns.delete = ns.delete + 1 %}
|
||||
{% else %}{% set ns.edit = ns.edit + 1 %}{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% macro fmt_count(loaded, total) %}{% if loaded < total %}{{ loaded }} / {{ total }}{% else %}{{ loaded }}{% endif %}{% endmacro %}
|
||||
|
||||
<div class="stat-row" style="margin-bottom:18px;">
|
||||
<div class="stat-card" data-category-filter="">
|
||||
<div class="stat-label">Alle</div>
|
||||
<div class="stat-value" id="statAll">{{ fmt_count(entries|length, total_count) }}</div>
|
||||
</div>
|
||||
<div class="stat-card" data-category-filter="create">
|
||||
<div class="stat-label">Hinzufügen</div>
|
||||
<div class="stat-value" id="statCreate" style="color:var(--success);">{{ fmt_count(ns.create, total_create) }}</div>
|
||||
</div>
|
||||
<div class="stat-card" data-category-filter="edit">
|
||||
<div class="stat-label">Änderungen</div>
|
||||
<div class="stat-value" id="statEdit" style="color:var(--accent-strong);">{{ fmt_count(ns.edit, total_edit) }}</div>
|
||||
</div>
|
||||
<div class="stat-card" data-category-filter="delete">
|
||||
<div class="stat-label">Löschungen</div>
|
||||
<div class="stat-value" id="statDelete" style="color:var(--danger);">{{ fmt_count(ns.delete, total_delete) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-wrap">
|
||||
<div class="table-toolbar">
|
||||
<div class="search-input">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg>
|
||||
<input type="text" id="auditSearch" placeholder="Benutzer, Aktion, Ziel oder Details durchsuchen...">
|
||||
</div>
|
||||
</div>
|
||||
<div style="overflow-x:auto;">
|
||||
<table class="data-table">
|
||||
<thead><tr><th style="width:1%;"></th><th>Zeitpunkt</th><th>Benutzer</th><th>Aktion</th><th>Ziel</th><th>Details</th></tr></thead>
|
||||
<table class="data-table" id="auditTable" data-sortable>
|
||||
<thead><tr>
|
||||
<th data-sort-key="ts" style="width:1%; white-space:nowrap;">Zeitpunkt</th>
|
||||
<th data-sort-key="user" style="width:1%; white-space:nowrap;">Benutzer</th>
|
||||
<th data-sort-key="action" style="width:1%; white-space:nowrap;">Aktion</th>
|
||||
<th data-sort-key="target" style="width:1%; white-space:nowrap;">Ziel</th>
|
||||
<th>Details</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
{% for e in entries %}
|
||||
{% set kind = e['action'].split('.')[-1] %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if avatars.get(e['username']) %}
|
||||
<img class="avatar-sm" src="{{ url_for('static', filename='uploads/avatars/' + avatars[e['username']]) }}" alt="">
|
||||
{% else %}
|
||||
<span class="avatar-sm avatar-placeholder">{{ e['username'][:1]|upper }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-dim mono" style="font-size:12.5px;">{{ e['ts'] }}</td>
|
||||
<td class="cell-name">{{ e['username'] }}</td>
|
||||
<td>
|
||||
<span class="pill action-pill">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">{{ (action_icons.get(kind) or action_icons['edit'])|safe }}</svg>
|
||||
{{ action_labels.get(e['action'], e['action']) }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ e['target'] or '—' }}</td>
|
||||
<td class="text-dim">{{ e['details'] or '—' }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr class="empty-row"><td colspan="6">Noch keine Änderungen protokolliert.</td></tr>
|
||||
{% for e in entries %}{{ m.audit_row(e) }}{% else %}
|
||||
<tr class="empty-row"><td colspan="5">Noch keine Änderungen protokolliert.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<p id="auditNoResults" class="text-faint hidden" style="padding:16px; text-align:center; font-size:12.5px;">Keine Einträge für diese Auswahl.</p>
|
||||
<div id="auditLoadMoreWrap" class="{{ 'hidden' if not has_more }}" style="padding:16px; text-align:center; display:flex; gap:8px; justify-content:center;">
|
||||
<button type="button" id="auditLoadMoreBtn" class="btn btn-primary" data-oldest-id="{{ oldest_loaded_id or '' }}">
|
||||
Mehr laden (300)
|
||||
</button>
|
||||
<button type="button" id="auditLoadAllBtn" class="btn btn-secondary">
|
||||
Alle laden
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const searchInput = document.getElementById("auditSearch");
|
||||
const noResults = document.getElementById("auditNoResults");
|
||||
let activeCategory = null;
|
||||
|
||||
function applyFilters() {
|
||||
const q = (searchInput ? searchInput.value : "").trim().toLowerCase();
|
||||
const rows = Array.from(document.querySelectorAll("#auditTable tbody tr"));
|
||||
let anyVisible = false;
|
||||
rows.forEach(function (row) {
|
||||
if (row.classList.contains("empty-row")) return;
|
||||
const categoryMatches = !activeCategory || row.dataset.category === activeCategory;
|
||||
const textMatches = !q || row.innerText.toLowerCase().includes(q);
|
||||
const match = categoryMatches && textMatches;
|
||||
row.style.display = match ? "" : "none";
|
||||
if (match) anyVisible = true;
|
||||
});
|
||||
if (noResults) noResults.classList.toggle("hidden", anyVisible);
|
||||
document.querySelectorAll(".stat-card[data-category-filter]").forEach(function (card) {
|
||||
const isTotal = card.dataset.categoryFilter === "";
|
||||
const active = activeCategory === null ? isTotal : card.dataset.categoryFilter === activeCategory;
|
||||
card.classList.toggle("active", active);
|
||||
});
|
||||
}
|
||||
|
||||
if (searchInput) searchInput.addEventListener("input", applyFilters);
|
||||
|
||||
document.querySelectorAll(".stat-card[data-category-filter]").forEach(function (card) {
|
||||
card.addEventListener("click", function () {
|
||||
const key = this.dataset.categoryFilter;
|
||||
activeCategory = (!key || activeCategory === key) ? null : key;
|
||||
applyFilters();
|
||||
});
|
||||
});
|
||||
|
||||
/* ---- Nachladen älterer Einträge (Mehr laden / Alle laden) ---- */
|
||||
const totalCount = {{ total_count }};
|
||||
const totalCreate = {{ total_create }};
|
||||
const totalEdit = {{ total_edit }};
|
||||
const totalDelete = {{ total_delete }};
|
||||
const entryCountLabel = document.getElementById("auditEntryCount");
|
||||
|
||||
function fmtCount(loaded, total) {
|
||||
return loaded < total ? loaded + " / " + total : String(loaded);
|
||||
}
|
||||
|
||||
function recomputeStats() {
|
||||
const rows = Array.from(document.querySelectorAll("#auditTable tbody tr[data-category]"));
|
||||
const counts = { create: 0, edit: 0, delete: 0 };
|
||||
rows.forEach(function (r) { counts[r.dataset.category] = (counts[r.dataset.category] || 0) + 1; });
|
||||
document.getElementById("statAll").textContent = fmtCount(rows.length, totalCount);
|
||||
document.getElementById("statCreate").textContent = fmtCount(counts.create, totalCreate);
|
||||
document.getElementById("statEdit").textContent = fmtCount(counts.edit, totalEdit);
|
||||
document.getElementById("statDelete").textContent = fmtCount(counts.delete, totalDelete);
|
||||
if (entryCountLabel) {
|
||||
entryCountLabel.textContent = rows.length < totalCount
|
||||
? rows.length + " von " + totalCount + " Einträgen geladen"
|
||||
: rows.length + " Eintrag" + (rows.length === 1 ? "" : "e");
|
||||
}
|
||||
return rows.length;
|
||||
}
|
||||
|
||||
const loadMoreBtn = document.getElementById("auditLoadMoreBtn");
|
||||
const loadAllBtn = document.getElementById("auditLoadAllBtn");
|
||||
const loadMoreWrap = document.getElementById("auditLoadMoreWrap");
|
||||
const tbody = document.querySelector("#auditTable tbody");
|
||||
const MORE_URL = "{{ url_for('activity_log_more') }}";
|
||||
|
||||
// Laedt genau einen weiteren 300er-Block nach und haengt ihn an -- von
|
||||
// beiden Buttons genutzt: "Mehr laden" ruft das einmal auf, "Alle laden"
|
||||
// ruft es wiederholt auf, bis der Server "keine weiteren mehr" meldet.
|
||||
function loadMoreOnce() {
|
||||
const oldestId = loadMoreBtn.dataset.oldestId;
|
||||
if (!oldestId) return Promise.resolve({ hasMore: false });
|
||||
return fetch(MORE_URL + "?before_id=" + encodeURIComponent(oldestId))
|
||||
.then(function (r) {
|
||||
const hasMore = r.headers.get("X-Has-More") === "1";
|
||||
const newOldestId = r.headers.get("X-Oldest-Id");
|
||||
return r.text().then(function (html) {
|
||||
return { html: html, hasMore: hasMore, newOldestId: newOldestId };
|
||||
});
|
||||
})
|
||||
.then(function (result) {
|
||||
const emptyRow = tbody.querySelector(".empty-row");
|
||||
if (emptyRow) emptyRow.remove();
|
||||
tbody.insertAdjacentHTML("beforeend", result.html);
|
||||
if (result.newOldestId) loadMoreBtn.dataset.oldestId = result.newOldestId;
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
if (loadMoreBtn) {
|
||||
loadMoreBtn.addEventListener("click", function () {
|
||||
loadMoreBtn.disabled = true;
|
||||
if (loadAllBtn) loadAllBtn.disabled = true;
|
||||
const originalText = loadMoreBtn.textContent;
|
||||
loadMoreBtn.textContent = "Lädt …";
|
||||
loadMoreOnce()
|
||||
.then(function (result) {
|
||||
if (!result.hasMore) loadMoreWrap.classList.add("hidden");
|
||||
recomputeStats();
|
||||
applyFilters();
|
||||
})
|
||||
.catch(function () { /* Button unten wird trotzdem wieder aktiviert */ })
|
||||
.then(function () {
|
||||
loadMoreBtn.disabled = false;
|
||||
if (loadAllBtn) loadAllBtn.disabled = false;
|
||||
loadMoreBtn.textContent = originalText;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (loadAllBtn) {
|
||||
loadAllBtn.addEventListener("click", function () {
|
||||
loadMoreBtn.disabled = true;
|
||||
loadAllBtn.disabled = true;
|
||||
const originalText = loadAllBtn.textContent;
|
||||
|
||||
function step() {
|
||||
loadAllBtn.textContent = "Lädt … (" + recomputeStats() + " von " + totalCount + ")";
|
||||
return loadMoreOnce().then(function (result) {
|
||||
return result.hasMore ? step() : null;
|
||||
});
|
||||
}
|
||||
|
||||
step()
|
||||
.then(function () {
|
||||
loadMoreWrap.classList.add("hidden");
|
||||
})
|
||||
.catch(function () { /* teilweise geladene Eintraege bleiben stehen */ })
|
||||
.then(function () {
|
||||
recomputeStats();
|
||||
applyFilters();
|
||||
loadMoreBtn.disabled = false;
|
||||
loadAllBtn.disabled = false;
|
||||
loadAllBtn.textContent = originalText;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
applyFilters();
|
||||
recomputeStats();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -22,6 +22,7 @@
|
||||
"clock": '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 3"/>',
|
||||
"shield": '<path d="M12 2l8 4v6c0 5-3.5 8.5-8 10-4.5-1.5-8-5-8-10V6z"/><path d="M9 12l2 2 4-4"/>',
|
||||
"server": '<rect x="2" y="3" width="20" height="7" rx="1.5"/><rect x="2" y="14" width="20" height="7" rx="1.5"/><path d="M6 6.5h.01M6 17.5h.01"/>',
|
||||
"folder": '<path d="M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z"/>',
|
||||
"sliders": '<path d="M4 6h9M17 6h3M4 12h3M11 12h9M4 18h13M20 18h0"/><circle cx="15" cy="6" r="2"/><circle cx="9" cy="12" r="2"/><circle cx="17" cy="18" r="2"/>',
|
||||
"logout": '<path d="M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h4"/><path d="M16 17l5-5-5-5"/><path d="M21 12H9"/>',
|
||||
"gear": '<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 11-2.83 2.83l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 11-2.83-2.83l.06-.06A1.65 1.65 0 004.6 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 112.83-2.83l.06.06A1.65 1.65 0 009 4.6a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 112.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/>',
|
||||
@@ -128,6 +129,12 @@
|
||||
</button>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% if license_topbar %}
|
||||
<a href="{{ url_for('settings_license') }}" class="timer-pill" title="{{ license_topbar.text }}" style="text-decoration:none;">
|
||||
<span class="dot {% if license_topbar.blink %}dot--blink-{{ license_topbar.level }}{% endif %}"
|
||||
style="{% if not license_topbar.blink %}animation:none; box-shadow:none;{% endif %} background:{{ 'var(--danger)' if license_topbar.level == 'danger' else 'var(--warning)' }};"></span>{{ license_topbar.text }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if dhcp_topbar_active is not none %}
|
||||
<span class="timer-pill" title="DHCP-Dienst: {{ 'aktiv' if dhcp_topbar_active else 'inaktiv' }}">
|
||||
<span class="dot" style="animation:none; background:{{ 'var(--success)' if dhcp_topbar_active else 'var(--danger)' }}; box-shadow:none;"></span>DHCP
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
{% block page_sub %}<div class="topbar-sub">{{ credentials|length }} Zugangsdaten</div>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% import "_hint_icon.html" as hi %}
|
||||
|
||||
<div class="section-head">
|
||||
<div>
|
||||
@@ -95,13 +96,12 @@
|
||||
<div class="field"><label>Username</label>
|
||||
<input type="text" name="username" required placeholder="z.B. admin">
|
||||
</div>
|
||||
<div class="field"><label>Kategorie</label>
|
||||
<div class="field"><label>Kategorie {{ hi.hint_icon("Bestimmt u.a., ob diese Zugangsdaten unter „Wartung“ für Bulk-Updates nutzbar sind.", "Kategorie") }}</label>
|
||||
<select name="category">
|
||||
{% for key, label in categories %}
|
||||
<option value="{{ key }}">{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div class="hint">Bestimmt u.a., ob diese Zugangsdaten unter „Wartung“ für Bulk-Updates nutzbar sind.</div>
|
||||
</div>
|
||||
<div class="field"><label>Passwort</label>
|
||||
<input type="password" id="password_add" name="password" required>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
{% block page_sub %}<div class="topbar-sub">{{ devices|length }} Geräte</div>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% import "_hint_icon.html" as hi %}
|
||||
|
||||
<div class="section-head">
|
||||
<div>
|
||||
@@ -125,9 +126,8 @@
|
||||
<input type="text" name="mac" required placeholder="z.B. AA:BB:CC:DD:EE:FF">
|
||||
<div class="invalid-feedback">Bitte eine gültige MAC-Adresse eingeben (xx:xx:xx:xx:xx:xx).</div>
|
||||
</div>
|
||||
<div class="field"><label>Switchport</label>
|
||||
<div class="field"><label>Switchport {{ hi.hint_icon("Physische Port-Nummer AM SWITCH (z.B. Port 3 von 48), an dem das Gerät eingesteckt ist — für den PoE-Neustart. Kein Netzwerk-/TCP-Port, keine SSH-Anmeldung.", "Switchport") }}</label>
|
||||
<input type="text" name="port" placeholder="z.B. 3">
|
||||
<div class="field-hint">Physische Port-Nummer AM SWITCH (z.B. Port 3 von 48), an dem das Gerät eingesteckt ist — für den PoE-Neustart. Kein Netzwerk-/TCP-Port, keine SSH-Anmeldung.</div>
|
||||
</div>
|
||||
<div class="field"><label>Switch (optional)</label>
|
||||
<select name="switch_hostname">
|
||||
@@ -136,9 +136,8 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="permission-group-title">SSH-Zugriff (optional, z.B. für Update-Aktionen unter „Wartung“)</div>
|
||||
<div class="field"><label>SSH-Port</label>
|
||||
<div class="field"><label>SSH-Port {{ hi.hint_icon("TCP-Netzwerk-Port für die SSH-Anmeldung an der IP-Adresse dieses Geräts (siehe Feld „IP-Adresse“ oben). Leer lassen, wenn Standard-Port 22 verwendet wird.", "SSH-Port") }}</label>
|
||||
<input type="number" name="ssh_port" min="1" max="65535" placeholder="22 (Standard)">
|
||||
<div class="field-hint">TCP-Netzwerk-Port für die SSH-Anmeldung an der IP-Adresse dieses Geräts (siehe Feld „IP-Adresse“ oben). Leer lassen, wenn Standard-Port 22 verwendet wird.</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Zugangsdaten</label>
|
||||
@@ -201,14 +200,12 @@
|
||||
<input type="text" name="mac" id="edit_mac" required>
|
||||
<div class="invalid-feedback">Bitte eine gültige MAC-Adresse eingeben (xx:xx:xx:xx:xx:xx).</div>
|
||||
</div>
|
||||
<div class="field"><label>Switchport</label>
|
||||
<div class="field"><label>Switchport {{ hi.hint_icon("Portnummer am zugeordneten Switch, für den PoE-Neustart. Hat nichts mit dem SSH-Port unten zu tun.", "Switchport") }}</label>
|
||||
<input type="text" name="port" id="edit_port">
|
||||
<div class="field-hint">Portnummer am zugeordneten Switch, für den PoE-Neustart. Hat nichts mit dem SSH-Port unten zu tun.</div>
|
||||
</div>
|
||||
<div class="permission-group-title">SSH-Zugriff (optional, z.B. für Update-Aktionen unter „Wartung“)</div>
|
||||
<div class="field"><label>SSH-Port</label>
|
||||
<div class="field"><label>SSH-Port {{ hi.hint_icon("TCP-Netzwerk-Port für die SSH-Anmeldung an der IP-Adresse dieses Geräts (siehe Feld „IP-Adresse“ oben). Leer lassen, wenn Standard-Port 22 verwendet wird.", "SSH-Port") }}</label>
|
||||
<input type="number" name="ssh_port" id="edit_ssh_port" min="1" max="65535" placeholder="22 (Standard)">
|
||||
<div class="field-hint">TCP-Netzwerk-Port für die SSH-Anmeldung an der IP-Adresse dieses Geräts (siehe Feld „IP-Adresse“ oben). Leer lassen, wenn Standard-Port 22 verwendet wird.</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Zugangsdaten</label>
|
||||
|
||||
@@ -0,0 +1,617 @@
|
||||
{% extends "base.html" %}
|
||||
{% set active_page = "fileshare" %}
|
||||
{% block page_title %}Dateifreigaben{% endblock %}
|
||||
{% block page_sub %}<div class="topbar-sub">{{ selected_share }}{% if rel_path %} / {{ rel_path }}{% endif %}</div>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% import "_hint_icon.html" as hi %}
|
||||
|
||||
{% macro render_tree_node(share, path, name, expanded_map, active_path) %}
|
||||
<li class="tree-node" data-share="{{ share }}" data-path="{{ path }}">
|
||||
<div class="tree-row{{ ' active' if path == active_path else '' }}">
|
||||
{% if path in expanded_map %}
|
||||
<button type="button" class="tree-toggle" aria-expanded="true">▼</button>
|
||||
{% else %}
|
||||
<button type="button" class="tree-toggle" aria-expanded="false">▶</button>
|
||||
{% endif %}
|
||||
<span class="tree-label">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:14px; height:14px; margin-right:5px; vertical-align:-2px; color:var(--accent);"><path d="M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z"/></svg>
|
||||
{{ name }}
|
||||
</span>
|
||||
</div>
|
||||
<ul class="tree-children{{ '' if path in expanded_map else ' hidden' }}"{% if path in expanded_map %} data-loaded="1"{% endif %}>
|
||||
{% if path in expanded_map %}
|
||||
{% for child in expanded_map[path] %}
|
||||
{{ render_tree_node(share, (path ~ '/' ~ child) if path else child, child, expanded_map, active_path) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endmacro %}
|
||||
|
||||
<div class="section-head">
|
||||
<div>
|
||||
<h2 style="font-size:16px;">Dateifreigaben</h2>
|
||||
<div class="hint">Freigaben je nach AD-Gruppenmitgliedschaft für diese Sitzung gemountet — wird beim Abmelden wieder ausgehängt.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fileshare-layout">
|
||||
<div class="fileshare-tree card">
|
||||
<div class="fileshare-tree-title">Freigaben</div>
|
||||
<ul class="tree-root" id="fileshareTree">
|
||||
{% for s in shares %}
|
||||
{{ render_tree_node(s, '', s, tree_expanded if s == selected_share else {}, rel_path if s == selected_share else None) }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="fileshare-main">
|
||||
<div class="table-wrap">
|
||||
<div class="table-toolbar">
|
||||
<div class="search-input">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg>
|
||||
<input type="text" id="fileSearch" placeholder="Datei/Ordner suchen..." oninput="filterTable('fileSearch', 'fileTable')">
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
{% if can_create %}
|
||||
<button type="button" class="btn btn-secondary btn-sm" data-open-modal="mkdirModal">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z"/><path d="M12 11v4M10 13h4"/></svg>
|
||||
Neuer Ordner
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary btn-sm" data-open-modal="uploadModal">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><path d="M17 8l-5-5-5 5"/><path d="M12 3v12"/></svg>
|
||||
Hochladen
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="bulkActionsBar" class="flex gap-2 hidden" data-share="{{ selected_share }}" data-path="{{ rel_path }}" style="align-items:center; margin-bottom:10px; flex-wrap:wrap;">
|
||||
<span id="bulkSelectedCount" class="text-faint" style="font-size:12.5px; font-weight:600;"></span>
|
||||
<button type="button" class="btn btn-secondary btn-sm" onclick="bulkDownload()">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><path d="M7 10l5 5 5-5"/><path d="M12 15V3"/></svg>
|
||||
Herunterladen (ZIP)
|
||||
</button>
|
||||
{% if can_edit %}
|
||||
<button type="button" class="btn btn-sm" style="color:var(--danger); background:transparent; border-color:var(--danger-dim);" onclick="bulkDelete()">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2m3 0l-1 14a2 2 0 01-2 2H7a2 2 0 01-2-2L4 6"/></svg>
|
||||
Löschen
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if entries %}
|
||||
<div style="overflow-x:auto;">
|
||||
<table class="data-table" id="fileTable" data-sortable>
|
||||
<thead><tr>
|
||||
<th style="width:1%;"><input type="checkbox" id="selectAllFiles"></th>
|
||||
<th data-sort-key="name">Name</th>
|
||||
<th data-sort-key="size">Größe</th>
|
||||
<th data-sort-key="mtime">Geändert</th>
|
||||
<th style="width:1%;">Aktionen</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
{% for e in entries %}
|
||||
<tr data-sort-name="{{ e.name|lower }}" data-sort-size="{{ 0 if e.is_dir else e.size_str }}" data-sort-mtime="{{ e.mtime_str }}">
|
||||
<td><input type="checkbox" class="row-select" value="{{ e.name }}"></td>
|
||||
<td class="cell-name">
|
||||
{% if e.is_dir %}
|
||||
<a href="{{ url_for('fileshare', share=selected_share, path=(rel_path ~ '/' ~ e.name) if rel_path else e.name) }}">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:15px; height:15px; margin-right:6px; vertical-align:-2px; color:var(--accent);"><path d="M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z"/></svg>
|
||||
{{ e.name }}
|
||||
</a>
|
||||
{% else %}
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:15px; height:15px; margin-right:6px; vertical-align:-2px; color:var(--text-faint);"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><path d="M14 2v6h6"/></svg>
|
||||
{{ e.name }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-faint">{{ e.size_str }}</td>
|
||||
<td class="text-faint">{{ e.mtime_str }}</td>
|
||||
<td>
|
||||
<div class="row-actions">
|
||||
{% if e.preview_kind %}
|
||||
<button type="button" class="icon-btn" title="Vorschau"
|
||||
data-preview-kind="{{ e.preview_kind }}"
|
||||
data-preview-name="{{ e.name }}"
|
||||
data-preview-url="{{ url_for('fileshare_view', share=selected_share, path=(rel_path ~ '/' ~ e.name) if rel_path else e.name) }}">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if not e.is_dir %}
|
||||
<a class="icon-btn" title="Herunterladen" href="{{ url_for('fileshare_download', share=selected_share, path=(rel_path ~ '/' ~ e.name) if rel_path else e.name) }}">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><path d="M7 10l5 5 5-5"/><path d="M12 15V3"/></svg>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if can_edit %}
|
||||
<button type="button" class="icon-btn" title="Umbenennen" onclick="openRenameModal('{{ e.name|e }}')">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
|
||||
</button>
|
||||
<form method="post" action="{{ url_for('fileshare_delete') }}" data-confirm="„{{ e.name }}“ wirklich endgültig löschen?">
|
||||
<input type="hidden" name="share" value="{{ selected_share }}">
|
||||
<input type="hidden" name="path" value="{{ rel_path }}">
|
||||
<input type="hidden" name="name" value="{{ e.name }}">
|
||||
<button type="submit" class="icon-btn" style="color:var(--danger);" title="Löschen">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2m3 0l-1 14a2 2 0 01-2 2H7a2 2 0 01-2-2L4 6"/></svg>
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div style="padding:40px 16px; text-align:center; color:var(--text-faint);">Dieser Ordner ist leer.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if can_create %}
|
||||
<div class="modal-overlay" id="uploadModal">
|
||||
<div class="modal">
|
||||
<form method="post" action="{{ url_for('fileshare_upload') }}" enctype="multipart/form-data">
|
||||
<input type="hidden" name="share" value="{{ selected_share }}">
|
||||
<input type="hidden" name="path" value="{{ rel_path }}">
|
||||
<div class="modal-header">
|
||||
<h3>Datei(en) hochladen</h3>
|
||||
<button type="button" class="modal-close" data-close-modal>×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="field">
|
||||
<label>Datei(en) {{ hi.hint_icon("Insgesamt maximal 15 MB pro Upload-Vorgang. Mehrfachauswahl möglich (auch mehrmals nacheinander — bereits hinzugefügte Dateien bleiben dabei erhalten).", "Datei(en)") }}</label>
|
||||
<input type="file" name="file" id="uploadFileInput" multiple required>
|
||||
<div id="uploadFileList" class="upload-file-list"></div>
|
||||
</div>
|
||||
<div class="field-hint">Wird in „{{ selected_share }}{% if rel_path %} / {{ rel_path }}{% endif %}“ hochgeladen. Eine bereits vorhandene Datei gleichen Namens wird überschrieben.</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-close-modal>Abbrechen</button>
|
||||
<button type="submit" class="btn btn-primary">Hochladen</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-overlay" id="mkdirModal">
|
||||
<div class="modal">
|
||||
<form method="post" action="{{ url_for('fileshare_mkdir') }}">
|
||||
<input type="hidden" name="share" value="{{ selected_share }}">
|
||||
<input type="hidden" name="path" value="{{ rel_path }}">
|
||||
<div class="modal-header">
|
||||
<h3>Neuer Ordner</h3>
|
||||
<button type="button" class="modal-close" data-close-modal>×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="field">
|
||||
<label>Ordnername</label>
|
||||
<input type="text" name="name" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-close-modal>Abbrechen</button>
|
||||
<button type="submit" class="btn btn-primary">Anlegen</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if can_edit %}
|
||||
<div class="modal-overlay" id="renameModal">
|
||||
<div class="modal">
|
||||
<form method="post" action="{{ url_for('fileshare_rename') }}">
|
||||
<input type="hidden" name="share" value="{{ selected_share }}">
|
||||
<input type="hidden" name="path" value="{{ rel_path }}">
|
||||
<input type="hidden" name="old_name" id="renameOldName">
|
||||
<div class="modal-header">
|
||||
<h3>Umbenennen</h3>
|
||||
<button type="button" class="modal-close" data-close-modal>×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="field">
|
||||
<label>Neuer Name</label>
|
||||
<input type="text" name="new_name" id="renameNewName" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-close-modal>Abbrechen</button>
|
||||
<button type="submit" class="btn btn-primary">Umbenennen</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="modal-overlay" id="previewModal">
|
||||
<div class="modal" style="max-width:900px; width:90vw;">
|
||||
<div class="modal-header">
|
||||
<h3 id="previewTitle">Vorschau</h3>
|
||||
<button type="button" class="modal-close" data-close-modal>×</button>
|
||||
</div>
|
||||
<div class="modal-body" id="previewBody" style="max-height:75vh; overflow:auto;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='js/vendor/mammoth.browser.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/vendor/xlsx.full.min.js') }}"></script>
|
||||
<script>
|
||||
const FILESHARE_SUBFOLDERS_URL = "{{ url_for('fileshare_subfolders') }}";
|
||||
const FILESHARE_BASE_URL = "{{ url_for('fileshare') }}";
|
||||
const FILESHARE_DOWNLOAD_MULTI_URL = "{{ url_for('fileshare_download_multi') }}";
|
||||
const FILESHARE_DELETE_MULTI_URL = "{{ url_for('fileshare_delete_multi') }}";
|
||||
|
||||
function openRenameModal(name) {
|
||||
document.getElementById("renameOldName").value = name;
|
||||
document.getElementById("renameNewName").value = name;
|
||||
PoeUI.openModal("renameModal");
|
||||
}
|
||||
|
||||
function filterTable(inputId, tableId) {
|
||||
const q = document.getElementById(inputId).value.trim().toLowerCase();
|
||||
document.querySelectorAll(`#${tableId} tbody tr`).forEach(row => {
|
||||
if (row.classList.contains("empty-row")) return;
|
||||
row.style.display = row.innerText.toLowerCase().includes(q) ? "" : "none";
|
||||
});
|
||||
}
|
||||
|
||||
/* ---------------- Mehrfachauswahl (Herunterladen als ZIP / Löschen) ---------------- */
|
||||
|
||||
function getSelectedFileNames() {
|
||||
return Array.from(document.querySelectorAll("#fileTable .row-select:checked")).map(function (cb) { return cb.value; });
|
||||
}
|
||||
|
||||
function updateBulkBar() {
|
||||
const names = getSelectedFileNames();
|
||||
const bar = document.getElementById("bulkActionsBar");
|
||||
const countEl = document.getElementById("bulkSelectedCount");
|
||||
if (!bar) return;
|
||||
if (names.length > 0) {
|
||||
bar.classList.remove("hidden");
|
||||
countEl.textContent = names.length + " ausgewählt";
|
||||
} else {
|
||||
bar.classList.add("hidden");
|
||||
}
|
||||
const selectAll = document.getElementById("selectAllFiles");
|
||||
const allBoxes = document.querySelectorAll("#fileTable .row-select");
|
||||
if (selectAll && allBoxes.length) {
|
||||
selectAll.checked = names.length === allBoxes.length;
|
||||
selectAll.indeterminate = names.length > 0 && names.length < allBoxes.length;
|
||||
}
|
||||
}
|
||||
|
||||
function submitBulkForm(actionUrl, names) {
|
||||
// data-share/data-path statt Jinja-Werte direkt in einen JS-String-
|
||||
// Literal zu setzen -- Ordnernamen kommen von der echten Freigabe und
|
||||
// koennten Anfuehrungszeichen o.ae. enthalten, ueber HTML-Attribute
|
||||
// (von Jinja automatisch escaped) ist das unproblematisch.
|
||||
const bar = document.getElementById("bulkActionsBar");
|
||||
const form = document.createElement("form");
|
||||
form.method = "post";
|
||||
form.action = actionUrl;
|
||||
form.style.display = "none";
|
||||
[["share", bar.dataset.share], ["path", bar.dataset.path]].forEach(function (pair) {
|
||||
const input = document.createElement("input");
|
||||
input.type = "hidden";
|
||||
input.name = pair[0];
|
||||
input.value = pair[1];
|
||||
form.appendChild(input);
|
||||
});
|
||||
names.forEach(function (name) {
|
||||
const input = document.createElement("input");
|
||||
input.type = "hidden";
|
||||
input.name = "names";
|
||||
input.value = name;
|
||||
form.appendChild(input);
|
||||
});
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
|
||||
function bulkDownload() {
|
||||
const names = getSelectedFileNames();
|
||||
if (!names.length) return;
|
||||
submitBulkForm(FILESHARE_DOWNLOAD_MULTI_URL, names);
|
||||
}
|
||||
|
||||
function bulkDelete() {
|
||||
const names = getSelectedFileNames();
|
||||
if (!names.length) return;
|
||||
window.confirmAction(
|
||||
names.length + " ausgewählte Elemente wirklich endgültig löschen?",
|
||||
function () { submitBulkForm(FILESHARE_DELETE_MULTI_URL, names); },
|
||||
"Auswahl löschen?"
|
||||
);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const selectAll = document.getElementById("selectAllFiles");
|
||||
if (selectAll) {
|
||||
selectAll.addEventListener("change", function () {
|
||||
document.querySelectorAll("#fileTable .row-select").forEach(function (cb) { cb.checked = selectAll.checked; });
|
||||
updateBulkBar();
|
||||
});
|
||||
}
|
||||
document.querySelectorAll("#fileTable .row-select").forEach(function (cb) {
|
||||
cb.addEventListener("change", updateBulkBar);
|
||||
});
|
||||
});
|
||||
|
||||
/* ---------------- Mehrfach-Upload (Multiauswahl + mehrmals nacheinander) ---------------- */
|
||||
/* Ein <input type=file multiple> ERSETZT bei jeder erneuten Dateiauswahl
|
||||
die vorherige -- fuer "mehrmals nacheinander hinzufuegen" wird deshalb
|
||||
selbst eine "angesammelte" Auswahl per DataTransfer gepflegt und nach
|
||||
jeder Aenderung zurueck auf das Input-Feld geschrieben, sodass das
|
||||
normale <form>-Submit (kein fetch() noetig) am Ende alle gesammelten
|
||||
Dateien mitschickt. DataTransfer-Zuweisung an .files wird von allen
|
||||
gaengigen Mobil-Browsern (Android Chrome, iOS Safari) mitgetragen; falls
|
||||
nicht, faellt es einfach auf das native Verhalten (letzte Auswahl zaehlt)
|
||||
zurueck, ohne den Upload an sich zu verhindern. */
|
||||
(function () {
|
||||
const input = document.getElementById("uploadFileInput");
|
||||
const listEl = document.getElementById("uploadFileList");
|
||||
if (!input || !listEl) return;
|
||||
let staged = null;
|
||||
try { staged = new DataTransfer(); } catch (e) { staged = null; }
|
||||
|
||||
function render() {
|
||||
listEl.innerHTML = "";
|
||||
if (!staged) return;
|
||||
Array.from(staged.files).forEach(function (file, idx) {
|
||||
const row = document.createElement("div");
|
||||
row.className = "upload-file-row";
|
||||
const name = document.createElement("span");
|
||||
name.textContent = file.name;
|
||||
const removeBtn = document.createElement("button");
|
||||
removeBtn.type = "button";
|
||||
removeBtn.className = "upload-file-remove";
|
||||
removeBtn.setAttribute("aria-label", "Entfernen");
|
||||
removeBtn.textContent = "×";
|
||||
removeBtn.addEventListener("click", function () {
|
||||
const dt = new DataTransfer();
|
||||
Array.from(staged.files).forEach(function (f, i) {
|
||||
if (i !== idx) dt.items.add(f);
|
||||
});
|
||||
staged = dt;
|
||||
input.files = staged.files;
|
||||
render();
|
||||
});
|
||||
row.appendChild(name);
|
||||
row.appendChild(removeBtn);
|
||||
listEl.appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
input.addEventListener("change", function () {
|
||||
if (!staged) return; // kein DataTransfer-Support -- natives Verhalten greift
|
||||
Array.from(input.files).forEach(function (file) { staged.items.add(file); });
|
||||
input.files = staged.files;
|
||||
render();
|
||||
});
|
||||
|
||||
// Beim (Wieder-)Oeffnen des Modals eine frische Sammlung starten, statt
|
||||
// Dateien aus einem vorherigen, bereits abgeschickten Upload-Vorgang
|
||||
// versehentlich mitzuschleppen.
|
||||
document.querySelectorAll('[data-open-modal="uploadModal"]').forEach(function (btn) {
|
||||
btn.addEventListener("click", function () {
|
||||
try { staged = new DataTransfer(); } catch (e) { staged = null; }
|
||||
input.value = "";
|
||||
render();
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
/* ---------------- Baum-Navigation (Freigaben links) ---------------- */
|
||||
|
||||
function buildTreeNode(share, path, name) {
|
||||
const li = document.createElement("li");
|
||||
li.className = "tree-node";
|
||||
li.dataset.share = share;
|
||||
li.dataset.path = path;
|
||||
|
||||
const row = document.createElement("div");
|
||||
row.className = "tree-row";
|
||||
|
||||
const toggle = document.createElement("button");
|
||||
toggle.type = "button";
|
||||
toggle.className = "tree-toggle";
|
||||
toggle.textContent = "▶";
|
||||
toggle.setAttribute("aria-expanded", "false");
|
||||
|
||||
const label = document.createElement("span");
|
||||
label.className = "tree-label";
|
||||
label.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:14px;height:14px;margin-right:5px;vertical-align:-2px;color:var(--accent);"><path d="M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z"/></svg>';
|
||||
label.append(document.createTextNode(name));
|
||||
|
||||
row.appendChild(toggle);
|
||||
row.appendChild(label);
|
||||
|
||||
const childUl = document.createElement("ul");
|
||||
childUl.className = "tree-children hidden";
|
||||
|
||||
li.appendChild(row);
|
||||
li.appendChild(childUl);
|
||||
return li;
|
||||
}
|
||||
|
||||
function toggleTreeNode(btn) {
|
||||
const li = btn.closest(".tree-node");
|
||||
const childUl = li.querySelector(":scope > .tree-children");
|
||||
if (childUl.classList.contains("hidden")) {
|
||||
if (childUl.dataset.loaded === "1") {
|
||||
childUl.classList.remove("hidden");
|
||||
btn.textContent = "▼";
|
||||
btn.setAttribute("aria-expanded", "true");
|
||||
} else {
|
||||
loadTreeChildren(li, childUl, btn);
|
||||
}
|
||||
} else {
|
||||
childUl.classList.add("hidden");
|
||||
btn.textContent = "▶";
|
||||
btn.setAttribute("aria-expanded", "false");
|
||||
}
|
||||
}
|
||||
|
||||
function loadTreeChildren(li, childUl, btn) {
|
||||
const share = li.dataset.share;
|
||||
const path = li.dataset.path;
|
||||
const prevLabel = btn.textContent;
|
||||
btn.textContent = "…";
|
||||
fetch(FILESHARE_SUBFOLDERS_URL + "?share=" + encodeURIComponent(share) + "&path=" + encodeURIComponent(path))
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
childUl.innerHTML = "";
|
||||
(data.folders || []).forEach(name => {
|
||||
const childPath = path ? path + "/" + name : name;
|
||||
childUl.appendChild(buildTreeNode(share, childPath, name));
|
||||
});
|
||||
childUl.dataset.loaded = "1";
|
||||
childUl.classList.remove("hidden");
|
||||
btn.textContent = "▼";
|
||||
btn.setAttribute("aria-expanded", "true");
|
||||
})
|
||||
.catch(() => { btn.textContent = prevLabel; });
|
||||
}
|
||||
|
||||
function navigateTree(share, path) {
|
||||
window.location.href = FILESHARE_BASE_URL + "?share=" + encodeURIComponent(share) + "&path=" + encodeURIComponent(path);
|
||||
}
|
||||
|
||||
document.getElementById("fileshareTree").addEventListener("click", function (e) {
|
||||
const toggle = e.target.closest(".tree-toggle");
|
||||
if (toggle) { toggleTreeNode(toggle); return; }
|
||||
const label = e.target.closest(".tree-label");
|
||||
if (label) {
|
||||
const li = label.closest(".tree-node");
|
||||
navigateTree(li.dataset.share, li.dataset.path);
|
||||
}
|
||||
});
|
||||
|
||||
/* ---------------- Datei-Vorschau ---------------- */
|
||||
|
||||
const fileTableEl = document.getElementById("fileTable");
|
||||
if (fileTableEl) {
|
||||
fileTableEl.addEventListener("click", function (e) {
|
||||
const btn = e.target.closest("[data-preview-kind]");
|
||||
if (!btn) return;
|
||||
openPreview(btn.dataset.previewKind, btn.dataset.previewName, btn.dataset.previewUrl);
|
||||
});
|
||||
}
|
||||
|
||||
function openPreview(kind, name, url) {
|
||||
document.getElementById("previewTitle").textContent = name;
|
||||
const body = document.getElementById("previewBody");
|
||||
body.innerHTML = "";
|
||||
PoeUI.openModal("previewModal");
|
||||
|
||||
if (kind === "pdf") {
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.src = url;
|
||||
iframe.style.width = "100%";
|
||||
iframe.style.height = "70vh";
|
||||
iframe.style.border = "0";
|
||||
body.appendChild(iframe);
|
||||
} else if (kind === "image") {
|
||||
const img = document.createElement("img");
|
||||
img.src = url;
|
||||
img.alt = name;
|
||||
img.style.maxWidth = "100%";
|
||||
img.style.display = "block";
|
||||
img.style.margin = "0 auto";
|
||||
body.appendChild(img);
|
||||
} else if (kind === "text") {
|
||||
body.textContent = "Lade …";
|
||||
// Nur die ersten 512KB anfordern -- bei sehr großen Textdateien
|
||||
// (Logs etc.) reicht das für eine Vorschau, ohne alles auf einmal
|
||||
// laden zu müssen. Server unterstützt Range ueber send_file(conditional=True).
|
||||
fetch(url, { headers: { "Range": "bytes=0-524287" } })
|
||||
.then(r => {
|
||||
// r.status ist bei einem Range-Request praktisch immer 206,
|
||||
// auch wenn die Datei kleiner als die angefragten 512KB ist
|
||||
// (der Server liefert dann trotzdem "206" mit der kompletten
|
||||
// Datei) -- ob wirklich abgeschnitten wurde, steht nur im
|
||||
// Content-Range-Header ("bytes 0-524287/<Gesamtgroesse>").
|
||||
const contentRange = r.headers.get("Content-Range") || "";
|
||||
const m = contentRange.match(/\/(\d+)$/);
|
||||
const truncated = !!m && parseInt(m[1], 10) > 524288;
|
||||
return r.text().then(text => ({ text: text, truncated: truncated }));
|
||||
})
|
||||
.then(({ text, truncated }) => {
|
||||
body.innerHTML = "";
|
||||
const pre = document.createElement("pre");
|
||||
pre.style.whiteSpace = "pre-wrap";
|
||||
pre.style.wordBreak = "break-word";
|
||||
pre.style.fontSize = "12.5px";
|
||||
pre.textContent = text;
|
||||
body.appendChild(pre);
|
||||
if (truncated) {
|
||||
const hint = document.createElement("div");
|
||||
hint.className = "text-faint";
|
||||
hint.style.marginTop = "10px";
|
||||
hint.style.fontSize = "12px";
|
||||
hint.textContent = "Nur die ersten 512 KB angezeigt — bitte herunterladen für die komplette Datei.";
|
||||
body.appendChild(hint);
|
||||
}
|
||||
})
|
||||
.catch(() => { body.textContent = "Vorschau konnte nicht geladen werden."; });
|
||||
} else if (kind === "docx") {
|
||||
body.textContent = "Lade …";
|
||||
fetch(url).then(r => r.arrayBuffer())
|
||||
.then(buf => mammoth.convertToHtml({ arrayBuffer: buf }))
|
||||
.then(result => {
|
||||
body.innerHTML = "";
|
||||
const wrap = document.createElement("div");
|
||||
wrap.className = "docx-preview";
|
||||
// mammoth erzeugt kontrolliertes HTML aus Words eigenem,
|
||||
// begrenztem Formatierungsmodell (Absätze/Tabellen/Listen/
|
||||
// Bilder) -- kein beliebiges, aus der Datei uebernommenes
|
||||
// Skript kann darin stecken, anders als bei generischem
|
||||
// "fremdes HTML direkt einbetten".
|
||||
wrap.innerHTML = result.value;
|
||||
body.appendChild(wrap);
|
||||
})
|
||||
.catch(() => { body.textContent = "Vorschau konnte nicht geladen werden (Format evtl. nicht unterstützt)."; });
|
||||
} else if (kind === "xlsx") {
|
||||
body.textContent = "Lade …";
|
||||
fetch(url).then(r => r.arrayBuffer())
|
||||
.then(buf => {
|
||||
const wb = XLSX.read(buf, { type: "array" });
|
||||
body.innerHTML = "";
|
||||
wb.SheetNames.forEach(function (sheetName, idx) {
|
||||
const rows = XLSX.utils.sheet_to_json(wb.Sheets[sheetName], { header: 1, defval: "" });
|
||||
const h4 = document.createElement("div");
|
||||
h4.className = "xlsx-preview-sheet-title";
|
||||
h4.textContent = sheetName;
|
||||
body.appendChild(h4);
|
||||
const wrap = document.createElement("div");
|
||||
wrap.style.overflowX = "auto";
|
||||
const table = document.createElement("table");
|
||||
table.className = "data-table";
|
||||
// Zellenwerte bewusst per textContent statt ueber die
|
||||
// eingebaute HTML-Ausgabe von SheetJS gesetzt -- so ist
|
||||
// die Vorschau unabhaengig von deren Escaping-Verhalten
|
||||
// garantiert sicher gegen Inhalte in den Zellen.
|
||||
rows.forEach(function (row) {
|
||||
const tr = document.createElement("tr");
|
||||
row.forEach(function (cell) {
|
||||
const td = document.createElement("td");
|
||||
td.textContent = (cell === null || cell === undefined) ? "" : String(cell);
|
||||
tr.appendChild(td);
|
||||
});
|
||||
table.appendChild(tr);
|
||||
});
|
||||
wrap.appendChild(table);
|
||||
body.appendChild(wrap);
|
||||
});
|
||||
})
|
||||
.catch(() => { body.textContent = "Vorschau konnte nicht geladen werden."; });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
+101
-76
@@ -4,6 +4,7 @@
|
||||
{% block page_sub %}<div class="topbar-sub">{{ groups|length + 1 }} Gruppen · Rechteverwaltung</div>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% import "_hint_icon.html" as hi %}
|
||||
|
||||
{% macro permission_table(group, group_key, checked_keys, readonly) %}
|
||||
{% set row_types = group_row_types[group_key] %}
|
||||
@@ -69,11 +70,7 @@
|
||||
|
||||
<div class="section-head">
|
||||
<div>
|
||||
<h2 style="font-size:16px;">Gruppen</h2>
|
||||
<div class="hint">
|
||||
Rechte je Gruppe granular vergeben, Mitgliedschaft in mehreren Gruppen addiert sich.
|
||||
„Admin“ und „Benutzer“ sind feste Systemgruppen. Legende direkt bei den Rechten.
|
||||
</div>
|
||||
<h2 style="font-size:16px;">Gruppen {{ hi.hint_icon("Rechte je Gruppe granular vergeben, Mitgliedschaft in mehreren Gruppen addiert sich. „Admin“ und „Benutzer“ sind feste Systemgruppen. Legende direkt bei den Rechten.", "Gruppen") }}</h2>
|
||||
</div>
|
||||
{% if current_user.has_permission('groups.create') %}
|
||||
<button type="button" class="btn btn-primary" data-open-modal="addGroupModal">
|
||||
@@ -104,8 +101,8 @@
|
||||
<td class="text-dim">{{ admin_virtual_group.member_names|length }}</td>
|
||||
<td>
|
||||
<div class="row-actions">
|
||||
<button class="icon-btn" title="Rechte anzeigen" onclick="toggleDetail('detail-admin')">
|
||||
<svg id="chev-admin" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>
|
||||
<button class="icon-btn" title="Rechte anzeigen" data-open-modal="adminGroupModal">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
|
||||
</button>
|
||||
<button class="icon-btn" title="Mitglieder verwalten" data-open-modal="adminMembersModal">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="8" r="3.2"/><path d="M2.5 20c0-3.6 2.9-6 6.5-6s6.5 2.4 6.5 6"/><circle cx="17.5" cy="8.5" r="2.4"/><path d="M15.8 14.2c2.7.3 4.7 2.4 4.7 5.3"/></svg>
|
||||
@@ -113,16 +110,11 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="group-detail-row hidden" id="detail-admin">
|
||||
<td colspan="3">
|
||||
{{ permission_tree(admin_virtual_group.permissions, true, true) }}
|
||||
<p class="text-faint" style="font-size:11.5px; margin:12px 0 0;">Admins dürfen immer alles — diese Rechte sind fest und nicht änderbar.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
{% for g in groups %}
|
||||
{% set can_edit_this = current_user.has_permission('groups.edit') and not g.is_system %}
|
||||
{% set can_unlock_system = g.is_system and current_user.is_admin %}
|
||||
<tbody data-sort-name="{{ g.name|lower }}" data-sort-members="{{ g.member_names|length }}">
|
||||
<tr>
|
||||
<td class="cell-name">
|
||||
@@ -132,8 +124,12 @@
|
||||
<td class="text-dim">{{ g.member_names|length }}</td>
|
||||
<td>
|
||||
<div class="row-actions">
|
||||
<button class="icon-btn" title="Rechte anzeigen{{ '/bearbeiten' if can_edit_this else '' }}" onclick="toggleDetail('detail-{{ g.id }}')">
|
||||
<svg id="chev-{{ g.id }}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>
|
||||
<button class="icon-btn" title="{{ 'Bearbeiten' if (can_edit_this or can_unlock_system) else 'Anzeigen' }}" data-open-modal="editGroupModal{{ loop.index }}">
|
||||
{% if can_edit_this or can_unlock_system %}
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
|
||||
{% else %}
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
|
||||
{% endif %}
|
||||
</button>
|
||||
<button class="icon-btn" title="Mitglieder verwalten" data-open-modal="membersModal{{ loop.index }}">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="8" r="3.2"/><path d="M2.5 20c0-3.6 2.9-6 6.5-6s6.5 2.4 6.5 6"/><circle cx="17.5" cy="8.5" r="2.4"/><path d="M15.8 14.2c2.7.3 4.7 2.4 4.7 5.3"/></svg>
|
||||
@@ -149,57 +145,6 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% set can_unlock_system = g.is_system and current_user.is_admin %}
|
||||
<tr class="group-detail-row hidden" id="detail-{{ g.id }}">
|
||||
<td colspan="3">
|
||||
{% if can_edit_this %}
|
||||
<form method="post">
|
||||
<input type="hidden" name="save_group" value="1">
|
||||
<input type="hidden" name="permissions_submitted" value="1">
|
||||
<input type="hidden" name="group_id" value="{{ g.id }}">
|
||||
<input type="hidden" name="name" value="{{ g.name }}">
|
||||
{{ permission_tree(g.permissions, false, true) }}
|
||||
<div class="flex" style="justify-content:flex-end; margin-top:16px;">
|
||||
<button type="submit" class="btn btn-primary btn-sm">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
|
||||
Rechte speichern
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% elif can_unlock_system %}
|
||||
<div id="readonly-{{ g.id }}">
|
||||
{{ permission_tree(g.permissions, true, true) }}
|
||||
<div class="flex" style="justify-content:space-between; align-items:center; margin-top:12px;">
|
||||
<p class="text-faint" style="font-size:11.5px; margin:0;">Die Standardgruppe „Benutzer“ ist eine Systemgruppe — ihre Rechte sind normalerweise fest.</p>
|
||||
<button type="button" class="btn btn-secondary btn-sm" onclick="unlockSystemGroup({{ g.id }})">Freischalten</button>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" class="hidden" id="unlock-{{ g.id }}">
|
||||
<input type="hidden" name="save_group" value="1">
|
||||
<input type="hidden" name="permissions_submitted" value="1">
|
||||
<input type="hidden" name="unlock_system_group" value="1">
|
||||
<input type="hidden" name="group_id" value="{{ g.id }}">
|
||||
<input type="hidden" name="name" value="{{ g.name }}">
|
||||
{{ permission_tree(g.permissions, false, true) }}
|
||||
<p class="text-faint" style="font-size:11.5px; margin:12px 0;">
|
||||
⚠ Diese Gruppe ist die Standardgruppe für neue Benutzer (auch neu angelegte AD/LDAP-Konten). Zu restriktive
|
||||
Rechte hier können den Erst-Login neuer Konten einschränken.
|
||||
</p>
|
||||
<div class="flex" style="justify-content:flex-end;">
|
||||
<button type="submit" class="btn btn-primary btn-sm">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
|
||||
Rechte speichern
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% else %}
|
||||
{{ permission_tree(g.permissions, true, true) }}
|
||||
{% if g.is_system %}
|
||||
<p class="text-faint" style="font-size:11.5px; margin:12px 0 0;">Die Standardgruppe „Benutzer“ ist eine Systemgruppe — ihre Rechte sind fest und nicht änderbar.</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
{% else %}
|
||||
<tbody data-sort-pinned>
|
||||
@@ -210,6 +155,19 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-overlay" id="adminGroupModal">
|
||||
<div class="modal" style="max-width:1000px;">
|
||||
<div class="modal-header">
|
||||
<h3>Admin — Rechte</h3>
|
||||
<button type="button" class="modal-close" data-close-modal>×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{{ permission_tree(admin_virtual_group.permissions, true, true) }}
|
||||
<p class="text-faint" style="font-size:11.5px; margin:12px 0 0;">Admins dürfen immer alles — diese Rechte sind fest und nicht änderbar.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-overlay" id="adminMembersModal">
|
||||
<div class="modal" style="max-width:380px;">
|
||||
<form method="post">
|
||||
@@ -238,6 +196,8 @@
|
||||
</div>
|
||||
|
||||
{% for g in groups %}
|
||||
{% set can_edit_this = current_user.has_permission('groups.edit') and not g.is_system %}
|
||||
{% set can_unlock_system = g.is_system and current_user.is_admin %}
|
||||
<div class="modal-overlay" id="membersModal{{ loop.index }}">
|
||||
<div class="modal" style="max-width:380px;">
|
||||
<form method="post">
|
||||
@@ -268,6 +228,80 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-overlay" id="editGroupModal{{ loop.index }}">
|
||||
<div class="modal" style="max-width:1000px;">
|
||||
{% if can_edit_this %}
|
||||
<form method="post">
|
||||
<input type="hidden" name="save_group" value="1">
|
||||
<input type="hidden" name="permissions_submitted" value="1">
|
||||
<input type="hidden" name="group_id" value="{{ g.id }}">
|
||||
<div class="modal-header">
|
||||
<h3>Gruppe bearbeiten</h3>
|
||||
<button type="button" class="modal-close" data-close-modal>×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="field">
|
||||
<label>Name</label>
|
||||
<input type="text" name="name" value="{{ g.name }}" required>
|
||||
</div>
|
||||
{{ permission_tree(g.permissions, false, true) }}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-close-modal>Abbrechen</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
|
||||
Speichern
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% elif can_unlock_system %}
|
||||
<div class="modal-header">
|
||||
<h3>Gruppe „{{ g.name }}“</h3>
|
||||
<button type="button" class="modal-close" data-close-modal>×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="field"><label>Name</label><input type="text" value="{{ g.name }}" disabled></div>
|
||||
<div id="readonly-{{ g.id }}">
|
||||
{{ permission_tree(g.permissions, true, true) }}
|
||||
<div class="flex" style="justify-content:space-between; align-items:center; margin-top:12px; flex-wrap:wrap;">
|
||||
<p class="text-faint" style="font-size:11.5px; margin:0;">Die Standardgruppe „Benutzer“ ist eine Systemgruppe — ihre Rechte sind normalerweise fest.</p>
|
||||
<button type="button" class="btn btn-secondary btn-sm" onclick="unlockSystemGroup({{ g.id }})">Freischalten</button>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" class="hidden" id="unlock-{{ g.id }}">
|
||||
<input type="hidden" name="save_group" value="1">
|
||||
<input type="hidden" name="permissions_submitted" value="1">
|
||||
<input type="hidden" name="unlock_system_group" value="1">
|
||||
<input type="hidden" name="group_id" value="{{ g.id }}">
|
||||
<input type="hidden" name="name" value="{{ g.name }}">
|
||||
{{ permission_tree(g.permissions, false, true) }}
|
||||
<p class="text-faint" style="font-size:11.5px; margin:12px 0;">
|
||||
⚠ Diese Gruppe ist die Standardgruppe für neue Benutzer (auch neu angelegte AD/LDAP-Konten). Zu restriktive
|
||||
Rechte hier können den Erst-Login neuer Konten einschränken.
|
||||
</p>
|
||||
<div class="flex" style="justify-content:flex-end;">
|
||||
<button type="submit" class="btn btn-primary btn-sm">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
|
||||
Rechte speichern
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="modal-header">
|
||||
<h3>Gruppe „{{ g.name }}“</h3>
|
||||
<button type="button" class="modal-close" data-close-modal>×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{{ permission_tree(g.permissions, true, true) }}
|
||||
{% if g.is_system %}
|
||||
<p class="text-faint" style="font-size:11.5px; margin:12px 0 0;">Die Standardgruppe „Benutzer“ ist eine Systemgruppe — ihre Rechte sind fest und nicht änderbar.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<div class="modal-overlay" id="addGroupModal">
|
||||
@@ -280,9 +314,8 @@
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="add_group" value="1">
|
||||
<div class="field">
|
||||
<label>Name</label>
|
||||
<label>Name {{ hi.hint_icon("Mitglieder werden danach über die Gruppentabelle zugeordnet.", "Name") }}</label>
|
||||
<input type="text" name="name" required placeholder="z.B. Facility-Team">
|
||||
<div class="field-hint">Mitglieder werden danach über die Gruppentabelle zugeordnet.</div>
|
||||
</div>
|
||||
{{ permission_tree([], false) }}
|
||||
</div>
|
||||
@@ -317,14 +350,6 @@ function unlockSystemGroup(id) {
|
||||
);
|
||||
}
|
||||
|
||||
function toggleDetail(id) {
|
||||
const row = document.getElementById(id);
|
||||
if (!row) return;
|
||||
row.classList.toggle("hidden");
|
||||
const chev = document.getElementById(id.replace("detail-", "chev-"));
|
||||
if (chev) chev.style.transform = row.classList.contains("hidden") ? "" : "rotate(180deg)";
|
||||
}
|
||||
|
||||
function applyPermissionGating() {
|
||||
document.querySelectorAll(".permission-group-col").forEach(function (area) {
|
||||
const toggle = area.querySelector(".permission-area-toggle-cb");
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
{% block page_sub %}<div class="topbar-sub">{{ selected_file.range_label if selected_file else "kein Log" }}</div>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% import "_hint_icon.html" as hi %}
|
||||
|
||||
<div class="section-head">
|
||||
<div>
|
||||
<h2 style="font-size:16px;">Log-Verlauf</h2>
|
||||
<div class="hint">Aktuelles Live-Log sowie ältere, von logrotate rotierte Kopien davon — {{ files|length }} Stand{{ 'e' if files|length != 1 else '' }} verfügbar, auswählbar nach Zeitraum. Aus Performance-Gründen unformatiert (RAW) dargestellt, ohne farbliche Aufbereitung.</div>
|
||||
<div class="hint">Zusätzlich weiter unten: bereits archivierte Auditlog-Tage (das Auditlog wird ab {{ "{:,}".format(audit_threshold).replace(",", ".") }} Einträgen automatisch tageweise archiviert, bis {{ "{:,}".format(audit_target).replace(",", ".") }} Einträge unterschritten sind).</div>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<a href="{{ url_for('logs') }}" class="btn btn-secondary">
|
||||
@@ -16,7 +18,7 @@
|
||||
Zurück zur Live-Ansicht
|
||||
</a>
|
||||
<button type="button" class="btn btn-secondary"
|
||||
onclick="openRawLogModal('{{ url_for('logs_history_raw', file=selected_name) if selected_name else '' }}', 'Komplettes Log (RAW) — {{ selected_file.range_label if selected_file else '' }}')"
|
||||
onclick="openRawLogModal('{{ url_for('logs_history_raw', file=selected_name) if selected_name else '' }}', 'Komplettes Log (RAW) — {{ selected_file.range_label if selected_file else '' }}', true)"
|
||||
{% if not selected_name %}disabled{% endif %}>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><path d="M14 2v6h6"/></svg>
|
||||
Komplettes Log (RAW)
|
||||
@@ -50,6 +52,54 @@
|
||||
<pre id="log-box" class="raw-log-content" style="height:100%;">{{ log_content or "" }}</pre>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="section-head" style="margin-top:28px;">
|
||||
<div>
|
||||
<h2 style="font-size:16px;">Archivierte Auditlog-Tage {{ hi.hint_icon("Ältere Auditlog-Einträge, tageweise als eigene Datei ausgelagert, sobald die laufende Tabelle den Schwellenwert überschreitet — die Dateien selbst bleiben unbegrenzt erhalten, bis sie hier bewusst exportiert werden.", "Archivierte Auditlog-Tage") }}</h2>
|
||||
</div>
|
||||
{% if current_user.can_manage_log_history and audit_files %}
|
||||
<form method="post" action="{{ url_for('logs_history_audit_export') }}"
|
||||
data-confirm="Alle {{ audit_files|length }} archivierten Auditlog-Datei(en) als ZIP herunterladen und danach vom Server löschen?"
|
||||
data-confirm-title="Export & Löschen">
|
||||
<button type="submit" class="btn btn-secondary">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><path d="M7 10l5 5 5-5"/><path d="M12 15V3"/></svg>
|
||||
Alle exportieren & löschen (ZIP)
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if disk_warning %}
|
||||
<div class="notice-banner notice-banner--warning" style="margin-bottom:14px;">{{ disk_warning }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if not audit_files %}
|
||||
<p class="text-faint" style="font-size:12.5px;">Noch keine archivierten Auditlog-Tage vorhanden.</p>
|
||||
{% else %}
|
||||
<div class="table-wrap">
|
||||
<table class="data-table">
|
||||
<thead><tr>
|
||||
<th style="width:1%; white-space:nowrap;">Tag</th>
|
||||
<th style="width:1%; white-space:nowrap;">Größe</th>
|
||||
<th></th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
{% for f in audit_files %}
|
||||
<tr>
|
||||
<td class="mono">{{ f.day }}</td>
|
||||
<td class="text-dim mono" style="font-size:12.5px;">{{ (f.size / 1024)|round(1) }} KB</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-secondary btn-sm"
|
||||
onclick="openRawLogModal('{{ url_for('logs_history_audit_raw', file=f.filename) }}', 'Auditlog-Archiv — {{ f.day }}', true)">
|
||||
Ansehen (RAW)
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
|
||||
@@ -4,15 +4,11 @@
|
||||
{% block page_sub %}<div class="topbar-sub">{{ devices|length }} Linux-Client{{ 's' if devices|length != 1 else '' }} mit SSH-Zugangsdaten</div>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% import "_hint_icon.html" as hi %}
|
||||
|
||||
<div class="section-head">
|
||||
<div>
|
||||
<h2 style="font-size:16px;">Wartung (SSH)</h2>
|
||||
<div class="hint">
|
||||
Bulk-Update (<code>apt update & upgrade -y</code>) und Neustart per SSH direkt auf dem Gerät —
|
||||
unabhängig vom PoE-Neustart über den Switch (siehe Dashboard). Nur Geräte mit hinterlegten
|
||||
SSH-Zugangsdaten der Kategorie „Linux-Client“ erscheinen hier.
|
||||
</div>
|
||||
<h2 style="font-size:16px;">Wartung (SSH) {{ hi.hint_icon("Bulk-Update (apt update & upgrade -y) und Neustart per SSH direkt auf dem Gerät — unabhängig vom PoE-Neustart über den Switch (siehe Dashboard). Nur Geräte mit hinterlegten SSH-Zugangsdaten der Kategorie „Linux-Client“ erscheinen hier.", "Wartung (SSH)") }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -87,7 +83,9 @@
|
||||
|
||||
{% if can_run %}
|
||||
<div class="section-head" style="margin-top:16px;">
|
||||
<div class="hint">Ausgewählte Geräte aktualisieren (nicht-interaktiv, unbeaufsichtigte Konfig-Rückfragen werden automatisch mit den bisherigen Werten beantwortet) oder neu starten.</div>
|
||||
<div style="display:flex; align-items:center; gap:6px; font-size:12.5px; color:var(--text-faint);">
|
||||
Bulk-Aktionen {{ hi.hint_icon("Ausgewählte Geräte aktualisieren (nicht-interaktiv, unbeaufsichtigte Konfig-Rückfragen werden automatisch mit den bisherigen Werten beantwortet) oder neu starten.", "Bulk-Aktionen") }}
|
||||
</div>
|
||||
<div style="display:flex; gap:8px;">
|
||||
<button type="button" class="btn btn-secondary" onclick="bulkReboot()">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 11-3.2-6.9M21 4v5h-5"/></svg>
|
||||
|
||||
@@ -17,6 +17,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if not license_active() %}
|
||||
<div class="notice-banner notice-banner--warning" style="margin-bottom:14px;">
|
||||
Wiederherstellen und endgültiges Löschen sind nur mit gültiger Lizenz verfügbar — siehe
|
||||
<a href="{{ url_for('settings_license') }}">Lizenz</a>. Ansehen ist weiterhin uneingeschränkt möglich.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div style="display:flex; flex-direction:column; gap:16px;">
|
||||
|
||||
<div class="card card-pad">
|
||||
@@ -44,7 +51,7 @@
|
||||
<td class="mono">{{ d['ip'] }}</td>
|
||||
<td class="text-faint" style="font-size:12px;">{{ d['deleted_at'] }}</td>
|
||||
<td>
|
||||
{% if current_user.has_permission('papierkorb.edit') %}
|
||||
{% if current_user.has_permission('papierkorb.edit') and license_active() %}
|
||||
<div class="row-actions">
|
||||
<form method="post" action="{{ url_for('restore_device', mac=d['mac']) }}">
|
||||
<button type="submit" class="btn btn-sm btn-secondary">Wiederherstellen</button>
|
||||
@@ -92,7 +99,7 @@
|
||||
<td class="mono">{{ s['ip'] }}</td>
|
||||
<td class="text-faint" style="font-size:12px;">{{ s['deleted_at'] }}</td>
|
||||
<td>
|
||||
{% if current_user.has_permission('papierkorb.edit') %}
|
||||
{% if current_user.has_permission('papierkorb.edit') and license_active() %}
|
||||
<div class="row-actions">
|
||||
<form method="post" action="{{ url_for('restore_switch', hostname=s['hostname']) }}">
|
||||
<button type="submit" class="btn btn-sm btn-secondary">Wiederherstellen</button>
|
||||
@@ -140,7 +147,7 @@
|
||||
<td class="mono">{{ c['username'] }}</td>
|
||||
<td class="text-faint" style="font-size:12px;">{{ c['deleted_at'] }}</td>
|
||||
<td>
|
||||
{% if current_user.has_permission('papierkorb.edit') %}
|
||||
{% if current_user.has_permission('papierkorb.edit') and license_active() %}
|
||||
<div class="row-actions">
|
||||
<form method="post" action="{{ url_for('restore_credential', cred_id=c['id']) }}">
|
||||
<button type="submit" class="btn btn-sm btn-secondary">Wiederherstellen</button>
|
||||
@@ -189,7 +196,7 @@
|
||||
<td class="text-dim">{{ full_name or '—' }}</td>
|
||||
<td class="text-faint" style="font-size:12px;">{{ u['deleted_at'] }}</td>
|
||||
<td>
|
||||
{% if current_user.has_permission('papierkorb.edit') %}
|
||||
{% if current_user.has_permission('papierkorb.edit') and license_active() %}
|
||||
<div class="row-actions">
|
||||
<form method="post" action="{{ url_for('restore_user', user_id=u['id']) }}">
|
||||
<button type="submit" class="btn btn-sm btn-secondary">Wiederherstellen</button>
|
||||
@@ -235,7 +242,7 @@
|
||||
<td class="cell-name">{{ g['name'] }}</td>
|
||||
<td class="text-faint" style="font-size:12px;">{{ g['deleted_at'] }}</td>
|
||||
<td>
|
||||
{% if current_user.has_permission('papierkorb.edit') %}
|
||||
{% if current_user.has_permission('papierkorb.edit') and license_active() %}
|
||||
<div class="row-actions">
|
||||
<form method="post" action="{{ url_for('restore_group', group_id=g['id']) }}">
|
||||
<button type="submit" class="btn btn-sm btn-secondary">Wiederherstellen</button>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
{% block page_sub %}<div class="topbar-sub">Prüfintervall und Netzwerkkonfiguration dieses Hosts</div>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% import "_hint_icon.html" as hi %}
|
||||
<div class="settings-grid">
|
||||
|
||||
<div class="card card-pad">
|
||||
@@ -13,7 +14,8 @@
|
||||
<div class="hint">Name und Zeitzone dieses Hosts.</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if current_user.has_permission('settings_system.edit') %}
|
||||
{% set can_edit_host = current_user.has_permission('settings_system.edit') %}
|
||||
{% if can_edit_host %}
|
||||
<form method="post">
|
||||
<div class="field">
|
||||
<label for="hostname">Hostname</label>
|
||||
@@ -27,31 +29,18 @@
|
||||
<hr style="border:none; border-top:1px solid var(--border-soft); margin:18px 0;">
|
||||
<form method="post">
|
||||
<div class="field">
|
||||
<label for="timezone">Zeitzone</label>
|
||||
<label for="timezone">Zeitzone {{ hi.hint_icon("Bestimmt die lokale Zeit in allen Logs und im Änderungsverlauf — wirkt sofort für diese laufende App-Instanz, ohne Dienst-Neustart.", "Zeitzone") }}</label>
|
||||
<select name="timezone" id="timezone" required>
|
||||
{% for tz in timezones %}
|
||||
<option value="{{ tz }}" {% if tz == current_timezone %}selected{% endif %}>{{ tz }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div class="field-hint">Bestimmt die lokale Zeit in allen Logs und im Änderungsverlauf — wirkt sofort für diese laufende App-Instanz, ohne Dienst-Neustart.</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
|
||||
Zeitzone setzen
|
||||
</button>
|
||||
</form>
|
||||
<hr style="border:none; border-top:1px solid var(--border-soft); margin:18px 0;">
|
||||
<form method="post">
|
||||
<div class="field">
|
||||
<label for="interval">Prüfintervall (Minuten)</label>
|
||||
<input type="number" name="interval" id="interval" value="{{ interval }}" min="1" required>
|
||||
<div class="field-hint">Wie oft Geräte auf Erreichbarkeit geprüft werden. Der Hintergrund-Dienst (tesm-check.service) wird nach dem Speichern automatisch neu gestartet.</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
|
||||
Speichern & Service neustarten
|
||||
</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<div class="field">
|
||||
<label>Hostname</label>
|
||||
@@ -61,10 +50,30 @@
|
||||
<label>Zeitzone</label>
|
||||
<input type="text" value="{{ current_timezone or '' }}" disabled>
|
||||
</div>
|
||||
{% endif %}
|
||||
<hr style="border:none; border-top:1px solid var(--border-soft); margin:18px 0;">
|
||||
{% if can_edit_host and license_active() %}
|
||||
<form method="post">
|
||||
<div class="field">
|
||||
<label for="interval">Prüfintervall (Minuten) {{ hi.hint_icon("Wie oft Geräte auf Erreichbarkeit geprüft werden. Der Hintergrund-Dienst (tesm-check.service) wird nach dem Speichern automatisch neu gestartet.", "Prüfintervall (Minuten)") }}</label>
|
||||
<input type="number" name="interval" id="interval" value="{{ interval }}" min="1" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
|
||||
Speichern & Service neustarten
|
||||
</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<div class="field">
|
||||
<label>Prüfintervall (Minuten)</label>
|
||||
<input type="number" value="{{ interval }}" disabled>
|
||||
<div class="field-hint">Nur Lesezugriff — für Änderungen fehlt das Recht „Systemeinstellungen ändern“.</div>
|
||||
<div class="field-hint">
|
||||
{% if not can_edit_host %}
|
||||
Nur Lesezugriff — für Änderungen fehlt das Recht „Systemeinstellungen ändern“.
|
||||
{% else %}
|
||||
Nur mit gültiger Lizenz änderbar — siehe <a href="{{ url_for('settings_license') }}">Lizenz</a>.
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -118,13 +127,12 @@
|
||||
{% elif net_backend != 'unknown' and current_user.has_permission('settings_system.edit') %}
|
||||
<form method="post" data-confirm="Netzwerkkonfiguration wirklich ändern? Falls die Verbindung danach abbricht, wird die vorherige Konfiguration automatisch nach {{ net_revert_seconds }} Sekunden wiederhergestellt.">
|
||||
<input type="hidden" name="apply_network" value="1">
|
||||
<div class="field"><label>Interface</label>
|
||||
<div class="field"><label>Interface {{ hi.hint_icon("Jedes Interface hat seine eigene Konfiguration — ein Wechsel hier lädt unten dessen tatsächlichen Ist-Zustand, ändert aber noch nichts.", "Interface") }}</label>
|
||||
<select name="net_interface" id="netInterfaceSelect" onchange="loadNetworkState(this.value)">
|
||||
{% for iface in net_interfaces %}
|
||||
<option value="{{ iface }}" {% if iface == net_interface %}selected{% endif %}>{{ iface }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div class="field-hint">Jedes Interface hat seine eigene Konfiguration — ein Wechsel hier lädt unten dessen tatsächlichen Ist-Zustand, ändert aber noch nichts.</div>
|
||||
</div>
|
||||
<div class="field"><label>Modus</label>
|
||||
<select name="net_mode" id="netModeSelect" onchange="document.getElementById('netStaticFields').classList.toggle('hidden', this.value !== 'static')">
|
||||
@@ -143,9 +151,8 @@
|
||||
<input type="text" name="net_gateway" id="netGatewayInput" value="{{ net_state.gateway if net_state.mode == 'static' else '' }}" placeholder="z.B. 192.168.1.1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field"><label>DNS-Server</label>
|
||||
<div class="field"><label>DNS-Server {{ hi.hint_icon("Kommagetrennt. Leer lassen, um die per DHCP zugewiesenen DNS-Server zu verwenden.", "DNS-Server") }}</label>
|
||||
<input type="text" name="net_dns" id="netDnsInput" value="{{ net_state.dns|join(', ') if net_state and net_state.dns else '' }}" placeholder="z.B. 1.1.1.1, 8.8.8.8">
|
||||
<div class="field-hint">Kommagetrennt. Leer lassen, um die per DHCP zugewiesenen DNS-Server zu verwenden.</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
|
||||
@@ -184,9 +191,8 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="log_rotation_keep">Aufbewahrung (Anzahl Rotationen)</label>
|
||||
<label for="log_rotation_keep">Aufbewahrung (Anzahl Rotationen) {{ hi.hint_icon("Standard: wöchentlich, 4 Rotationen (≈ 1 Monat Historie je Log).", "Aufbewahrung (Anzahl Rotationen)") }}</label>
|
||||
<input type="number" name="log_rotation_keep" id="log_rotation_keep" value="{{ log_rotation_keep }}" min="1" required>
|
||||
<div class="field-hint">Standard: wöchentlich, 4 Rotationen (≈ 1 Monat Historie je Log).</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
|
||||
@@ -221,9 +227,8 @@
|
||||
{% if current_user.has_permission('settings_system.edit') %}
|
||||
<form method="post">
|
||||
<div class="field">
|
||||
<label for="trash_retention_days">Aufbewahrungsdauer (Tage)</label>
|
||||
<label for="trash_retention_days">Aufbewahrungsdauer (Tage) {{ hi.hint_icon("Danach werden Papierkorb-Einträge automatisch unwiderruflich gelöscht (AD/LDAP-Benutzer sind nie im Papierkorb, da sie sich beim nächsten Login automatisch neu anlegen).", "Aufbewahrungsdauer (Tage)") }}</label>
|
||||
<input type="number" name="trash_retention_days" id="trash_retention_days" value="{{ trash_retention_days }}" min="1" required>
|
||||
<div class="field-hint">Danach werden Papierkorb-Einträge automatisch unwiderruflich gelöscht (AD/LDAP-Benutzer sind nie im Papierkorb, da sie sich beim nächsten Login automatisch neu anlegen).</div>
|
||||
</div>
|
||||
<button type="submit" name="save_trash_retention" value="1" class="btn btn-primary btn-block">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
{% block page_sub %}<div class="topbar-sub">Reservierungen und eigene Options aus den Client-Stammdaten (Kea DHCP)</div>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% import "_hint_icon.html" as hi %}
|
||||
<div class="settings-grid">
|
||||
|
||||
<div class="card card-pad">
|
||||
@@ -81,9 +82,8 @@
|
||||
<div class="field"><label>Lease-Zeit Maximum (Sek.)</label>
|
||||
<input type="number" name="dhcp_lease_max" value="{{ cfg.dhcp_lease_max }}">
|
||||
</div>
|
||||
<div class="field"><label>Ausgabepfad (Kea-Konfigurationsdatei)</label>
|
||||
<div class="field"><label>Ausgabepfad (Kea-Konfigurationsdatei) {{ hi.hint_icon("Standardmäßig die aktive Kea-Konfiguration. Läuft der Dienst gerade, schreibt „Speichern“ die Datei neu und startet ihn automatisch neu.", "Ausgabepfad (Kea-Konfigurationsdatei)") }}</label>
|
||||
<input type="text" name="dhcp_output_path" value="{{ cfg.dhcp_output_path }}" class="mono">
|
||||
<div class="field-hint">Standardmäßig die aktive Kea-Konfiguration. Läuft der Dienst gerade, schreibt „Speichern“ die Datei neu und startet ihn automatisch neu.</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
|
||||
@@ -98,11 +98,7 @@
|
||||
<div class="card card-pad" style="grid-column:1 / -1;">
|
||||
<div class="section-head" style="margin-bottom:16px;">
|
||||
<div>
|
||||
<h2 style="font-size:16px;">Subnetze</h2>
|
||||
<div class="hint">
|
||||
Ein Host kann mehrere IPs/Interfaces mit jeweils eigenem Netz bedienen — je Subnetz eigene Range,
|
||||
optional eigenes Gateway/DNS. Subnet/Netzmaske werden live vom Interface übernommen, nicht manuell gepflegt.
|
||||
</div>
|
||||
<h2 style="font-size:16px;">Subnetze {{ hi.hint_icon("Ein Host kann mehrere IPs/Interfaces mit jeweils eigenem Netz bedienen — je Subnetz eigene Range, optional eigenes Gateway/DNS. Subnet/Netzmaske werden live vom Interface übernommen, nicht manuell gepflegt.", "Subnetze") }}</h2>
|
||||
</div>
|
||||
{% if can_edit %}
|
||||
<button type="button" class="btn btn-primary" data-open-modal="addSubnetModal">
|
||||
@@ -175,11 +171,7 @@
|
||||
<div class="card card-pad" style="grid-column:1 / -1;">
|
||||
<div class="section-head" style="margin-bottom:16px;">
|
||||
<div>
|
||||
<h2 style="font-size:16px;">DHCP-Options</h2>
|
||||
<div class="hint">
|
||||
Standard-Optionen (analog den "Predefined Options" eines Windows-DHCP-Servers) sind bereits vorbefüllt,
|
||||
eigene/herstellerspezifische Options lassen sich zusätzlich anlegen — beide global oder pro Client überschreibbar.
|
||||
</div>
|
||||
<h2 style="font-size:16px;">DHCP-Options {{ hi.hint_icon('Standard-Optionen (analog den "Predefined Options" eines Windows-DHCP-Servers) sind bereits vorbefüllt, eigene/herstellerspezifische Options lassen sich zusätzlich anlegen — beide global oder pro Client überschreibbar.', "DHCP-Options") }}</h2>
|
||||
</div>
|
||||
{% if can_edit %}
|
||||
<button type="button" class="btn btn-primary" data-open-modal="addOptionModal">
|
||||
@@ -217,7 +209,9 @@
|
||||
<input type="text" name="value" value="{{ option_values.get(d.id, {}).get('', '') }}" style="max-width:220px;" placeholder="(nicht gesetzt)">
|
||||
<button type="submit" class="btn btn-secondary btn-sm">Speichern</button>
|
||||
</form>
|
||||
<div class="field-hint">Leeren Wert speichern, um eine Standard-Option wieder auszublenden.</div>
|
||||
<div class="text-faint" style="font-size:11.5px; display:flex; align-items:center; gap:6px;">
|
||||
Standard-Option zurücksetzen {{ hi.hint_icon("Leeren Wert speichern, um eine Standard-Option wieder auszublenden.", "Standard-Option zurücksetzen") }}
|
||||
</div>
|
||||
{% else %}
|
||||
{{ option_values.get(d.id, {}).get('', '—') }}
|
||||
{% endif %}
|
||||
@@ -272,11 +266,7 @@
|
||||
<div class="card card-pad" style="grid-column:1 / -1;">
|
||||
<div class="section-head" style="margin-bottom:16px;">
|
||||
<div>
|
||||
<h2 style="font-size:16px;">Aktive Leases</h2>
|
||||
<div class="hint">
|
||||
Direkt aus Kea gelesen (nicht aus den Reservierungen) — zeigt auch Clients OHNE eigene Reservierung,
|
||||
die sich einfach eine freie IP aus dem Pool genommen haben.
|
||||
</div>
|
||||
<h2 style="font-size:16px;">Aktive Leases {{ hi.hint_icon("Direkt aus Kea gelesen (nicht aus den Reservierungen) — zeigt auch Clients OHNE eigene Reservierung, die sich einfach eine freie IP aus dem Pool genommen haben.", "Aktive Leases") }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -497,9 +487,8 @@
|
||||
<div class="field"><label>Code</label>
|
||||
<input type="number" name="code" min="1" max="254" required placeholder="z.B. 225">
|
||||
</div>
|
||||
<div class="field"><label>Name</label>
|
||||
<div class="field"><label>Name {{ hi.hint_icon("Nur Kleinbuchstaben, Ziffern und Bindestrich, muss mit einem Buchstaben beginnen.", "Name") }}</label>
|
||||
<input type="text" name="name" required placeholder="z.B. terminal-url" pattern="[a-z][a-z0-9-]*">
|
||||
<div class="field-hint">Nur Kleinbuchstaben, Ziffern und Bindestrich, muss mit einem Buchstaben beginnen.</div>
|
||||
</div>
|
||||
<div class="field"><label>Datentyp</label>
|
||||
<select name="type">
|
||||
@@ -539,13 +528,11 @@
|
||||
<div class="field"><label>MAC-Adresse</label>
|
||||
<input type="text" name="mac" required placeholder="z.B. AA:BB:CC:DD:EE:FF">
|
||||
</div>
|
||||
<div class="field"><label>IP-Adresse</label>
|
||||
<div class="field"><label>IP-Adresse {{ hi.hint_icon("Muss in einem der oben konfigurierten Subnetze liegen, sonst wird sie beim Schreiben übersprungen.", "IP-Adresse") }}</label>
|
||||
<input type="text" name="ip" required placeholder="z.B. 192.168.1.50">
|
||||
<div class="field-hint">Muss in einem der oben konfigurierten Subnetze liegen, sonst wird sie beim Schreiben übersprungen.</div>
|
||||
</div>
|
||||
<div class="field"><label>Name</label>
|
||||
<div class="field"><label>Name {{ hi.hint_icon("Wird zum Hostname der Reservierung. DHCP-Optionen (z.B. Hostname erzwingen) lassen sich danach über das Options-Symbol in der Tabelle setzen.", "Name") }}</label>
|
||||
<input type="text" name="name" required placeholder="z.B. Empfangsdrucker">
|
||||
<div class="field-hint">Wird zum Hostname der Reservierung. DHCP-Optionen (z.B. Hostname erzwingen) lassen sich danach über das Options-Symbol in der Tabelle setzen.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@@ -575,16 +562,14 @@
|
||||
<div class="field"><label>Range Start</label>
|
||||
<input type="text" name="range_start" required placeholder="z.B. 192.168.1.100">
|
||||
</div>
|
||||
<div class="field"><label>Range Ende</label>
|
||||
<div class="field"><label>Range Ende {{ hi.hint_icon("Muss im tatsächlich am Interface erkannten Netz liegen — sonst wird das Subnetz abgelehnt.", "Range Ende") }}</label>
|
||||
<input type="text" name="range_end" required placeholder="z.B. 192.168.1.200">
|
||||
<div class="field-hint">Muss im tatsächlich am Interface erkannten Netz liegen — sonst wird das Subnetz abgelehnt.</div>
|
||||
</div>
|
||||
<div class="field"><label>Gateway (optional)</label>
|
||||
<input type="text" name="gateway" placeholder="Automatisch: erkanntes Gateway des Interfaces">
|
||||
</div>
|
||||
<div class="field"><label>DNS-Server (optional)</label>
|
||||
<div class="field"><label>DNS-Server (optional) {{ hi.hint_icon("Kommagetrennt, falls mehrere.", "DNS-Server (optional)") }}</label>
|
||||
<input type="text" name="dns" placeholder="z.B. 1.1.1.1, 8.8.8.8">
|
||||
<div class="field-hint">Kommagetrennt, falls mehrere.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@@ -617,16 +602,14 @@
|
||||
<div class="field"><label>Range Start</label>
|
||||
<input type="text" name="range_start" value="{{ s.range_start }}" required>
|
||||
</div>
|
||||
<div class="field"><label>Range Ende</label>
|
||||
<div class="field"><label>Range Ende {{ hi.hint_icon("Muss im tatsächlich am Interface erkannten Netz liegen — sonst wird das Subnetz abgelehnt.", "Range Ende") }}</label>
|
||||
<input type="text" name="range_end" value="{{ s.range_end }}" required>
|
||||
<div class="field-hint">Muss im tatsächlich am Interface erkannten Netz liegen — sonst wird das Subnetz abgelehnt.</div>
|
||||
</div>
|
||||
<div class="field"><label>Gateway (optional)</label>
|
||||
<input type="text" name="gateway" value="{{ s.gateway or '' }}" placeholder="Automatisch: erkanntes Gateway des Interfaces">
|
||||
</div>
|
||||
<div class="field"><label>DNS-Server (optional)</label>
|
||||
<div class="field"><label>DNS-Server (optional) {{ hi.hint_icon("Kommagetrennt, falls mehrere.", "DNS-Server (optional)") }}</label>
|
||||
<input type="text" name="dns" value="{{ s.dns or '' }}" placeholder="z.B. 1.1.1.1, 8.8.8.8">
|
||||
<div class="field-hint">Kommagetrennt, falls mehrere.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
{% block page_sub %}<div class="topbar-sub">Umzug auf eine neue Umgebung</div>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% import "_hint_icon.html" as hi %}
|
||||
<div class="settings-grid">
|
||||
|
||||
<div class="card card-pad">
|
||||
@@ -34,14 +35,10 @@
|
||||
{% if s.key not in admin_only_sections or current_user.is_admin %}checked{% endif %}
|
||||
{% if s.admin_only and not current_user.is_admin %}disabled{% endif %}>
|
||||
{{ s.label }} <span class="text-faint">({{ s.count }})</span>
|
||||
{% if s.admin_only %}<span class="pill user" style="font-size:10px; padding:2px 7px;">Nur Admin</span>{% endif %}
|
||||
{% if export_section_hints and s.key in export_section_hints %}{{ hi.hint_icon(export_section_hints[s.key], s.label) }}{% endif %}
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="field-hint">
|
||||
AD/LDAP-Benutzerkonten sind hiervon unberührt — sie werden über Active Directory/Windows verwaltet,
|
||||
nicht über diese App, und beim nächsten Login automatisch neu angelegt.
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
|
||||
@@ -74,7 +71,7 @@
|
||||
<div class="hint">Ausgewählte Kategorien verschlüsselt sichern.</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if current_user.has_permission('settings_importexport.export') %}
|
||||
{% if current_user.has_permission('settings_importexport.export') and license_export_allowed() %}
|
||||
<form method="post" action="{{ url_for('export_data') }}">
|
||||
<div class="field">
|
||||
<label>Was exportieren?</label>
|
||||
@@ -85,26 +82,22 @@
|
||||
{% if key not in admin_only_sections or current_user.is_admin %}checked{% endif %}
|
||||
{% if key in admin_only_sections and not current_user.is_admin %}disabled{% endif %}>
|
||||
{{ label }}
|
||||
{% if key in admin_only_sections %}<span class="pill user" style="font-size:10px; padding:2px 7px;">Nur Admin</span>{% endif %}
|
||||
{% if export_section_hints and key in export_section_hints %}{{ hi.hint_icon(export_section_hints[key], label) }}{% endif %}
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="field-hint">
|
||||
AD/LDAP-Benutzerkonten werden nie mitexportiert — sie werden über Active Directory/Windows verwaltet
|
||||
(nicht über diese App) und legen sich beim nächsten Login automatisch wieder an. Beim LDAP-Export wird
|
||||
lediglich das Bind-Konto (verschlüsselt) sowie die Gruppenzuordnungen gesichert.
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="export_passphrase">Passphrase</label>
|
||||
<label for="export_passphrase">Passphrase {{ hi.hint_icon("Wird zum Verschlüsseln der Export-Datei benötigt — für den späteren Import dieselbe Passphrase erneut eingeben.", "Passphrase") }}</label>
|
||||
<input type="password" name="export_passphrase" id="export_passphrase" required>
|
||||
<div class="field-hint">Wird zum Verschlüsseln der Export-Datei benötigt — für den späteren Import dieselbe Passphrase erneut eingeben.</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-secondary btn-block">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><path d="M7 10l5 5 5-5"/><path d="M12 15V3"/></svg>
|
||||
Export herunterladen
|
||||
</button>
|
||||
</form>
|
||||
{% elif current_user.has_permission('settings_importexport.export') %}
|
||||
<p class="text-faint" style="font-size:12.5px;">Für den Export ist keine gültige Lizenz vorhanden — siehe <a href="{{ url_for('settings_license') }}">Lizenz</a>.</p>
|
||||
{% else %}
|
||||
<p class="text-faint" style="font-size:12.5px;">Für den Export fehlt das Recht „Daten exportieren“.</p>
|
||||
{% endif %}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{% extends "base.html" %}
|
||||
{% set active_page = "settings_ldap" %}
|
||||
{% set can_edit = current_user.has_permission('settings_ldap.edit') %}
|
||||
{% set can_edit = current_user.has_permission('settings_ldap.edit') and license_active() %}
|
||||
{% block page_title %}LDAP / Active Directory{% endblock %}
|
||||
{% block page_sub %}<div class="topbar-sub">Anmeldung mit dem Domänen-Passwort, zusätzlich zu lokalen Konten</div>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% import "_hint_icon.html" as hi %}
|
||||
<div class="settings-grid settings-grid--wide">
|
||||
|
||||
<div class="card card-pad">
|
||||
@@ -23,18 +24,16 @@
|
||||
<span>LDAP-Anmeldung aktivieren</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="field"><label>Server</label>
|
||||
<div class="field"><label>Server {{ hi.hint_icon("DNS-Name oder IP-Adresse — beides wird genau so gespeichert und beim Verbinden verwendet.", "Server") }}</label>
|
||||
<input type="text" name="ldap_server" value="{{ ldap.server }}" placeholder="z.B. 192.168.1.1 oder dc01.firma.local">
|
||||
<div class="field-hint">DNS-Name oder IP-Adresse — beides wird genau so gespeichert und beim Verbinden verwendet.</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="switch-check" style="display:inline-flex; align-items:center; gap:8px;">
|
||||
<input type="checkbox" name="ldap_use_ssl" id="ldap_use_ssl" {% if ldap.use_ssl %}checked{% endif %}
|
||||
onchange="document.getElementById('ldap_port').value = this.checked ? 636 : 389;">
|
||||
<span class="track"></span>
|
||||
<span>LDAPS/TLS verwenden</span>
|
||||
<span>LDAPS/TLS verwenden {{ hi.hint_icon("Ohne LDAPS wird das Passwort unverschlüsselt übertragen — nur für interne Tests geeignet, vor Produktivbetrieb LDAPS auf dem Domain Controller einrichten. Stellt beim Umschalten den Port automatisch auf 636/389 — unten weiterhin manuell änderbar.", "LDAPS/TLS verwenden") }}</span>
|
||||
</label>
|
||||
<div class="field-hint">Ohne LDAPS wird das Passwort unverschlüsselt übertragen — nur für interne Tests geeignet, vor Produktivbetrieb LDAPS auf dem Domain Controller einrichten. Stellt beim Umschalten den Port automatisch auf 636/389 — unten weiterhin manuell änderbar.</div>
|
||||
</div>
|
||||
<div class="field"><label>Port</label>
|
||||
<input type="number" name="ldap_port" id="ldap_port" min="1" max="65535" value="{{ ldap.port }}">
|
||||
@@ -43,13 +42,11 @@
|
||||
<label class="switch-check" style="display:inline-flex; align-items:center; gap:8px;">
|
||||
<input type="checkbox" name="ldap_tls_skip_verify" {% if ldap.tls_skip_verify %}checked{% endif %}>
|
||||
<span class="track"></span>
|
||||
<span>Zertifikatsprüfung überspringen (nur LDAPS)</span>
|
||||
<span>Zertifikatsprüfung überspringen (nur LDAPS) {{ hi.hint_icon("Akzeptiert jedes Server-Zertifikat, auch selbstsignierte/nicht vertrauenswürdige — praktisch für interne Tests, schützt dann aber nicht mehr vor einem gefälschten Server. Vor Produktivbetrieb ein echtes, vertrauenswürdiges Zertifikat einrichten und diese Option deaktivieren.", "Zertifikatsprüfung überspringen") }}</span>
|
||||
</label>
|
||||
<div class="field-hint">Akzeptiert jedes Server-Zertifikat, auch selbstsignierte/nicht vertrauenswürdige — praktisch für interne Tests, schützt dann aber nicht mehr vor einem gefälschten Server. Vor Produktivbetrieb ein echtes, vertrauenswürdiges Zertifikat einrichten und diese Option deaktivieren.</div>
|
||||
</div>
|
||||
<div class="field"><label>Bind-Konto (Service-Account)</label>
|
||||
<div class="field"><label>Bind-Konto (Service-Account) {{ hi.hint_icon("Ein normales, unprivilegiertes Domänenkonto reicht — es wird nur zum Suchen von Benutzern verwendet, keine Admin-Rechte nötig. Ein neu gespeichertes Konto ersetzt das bisherige vollständig.", "Bind-Konto (Service-Account)") }}</label>
|
||||
<input type="text" name="ldap_bind_dn" value="{{ ldap.bind_dn }}" placeholder="z.B. ldap@ad.firma.local">
|
||||
<div class="field-hint">Ein normales, unprivilegiertes Domänenkonto reicht — es wird nur zum Suchen von Benutzern verwendet, keine Admin-Rechte nötig. Ein neu gespeichertes Konto ersetzt das bisherige vollständig.</div>
|
||||
</div>
|
||||
<div class="field"><label>Bind-Passwort</label>
|
||||
<input type="password" name="ldap_bind_password" placeholder="{{ '(unverändert lassen)' if ldap.bind_password_enc else '' }}">
|
||||
@@ -57,18 +54,28 @@
|
||||
<div class="field"><label>Base-DN</label>
|
||||
<input type="text" name="ldap_base_dn" value="{{ ldap.base_dn }}" placeholder="Leer = automatisch ermitteln">
|
||||
</div>
|
||||
<div class="field"><label>Attribut für Benutzername</label>
|
||||
<div class="field"><label>Attribut für Benutzername {{ hi.hint_icon("Für Active Directory: sAMAccountName. Für generisches LDAP (z.B. OpenLDAP): meist uid. Anmeldung per userPrincipalName (E-Mail/UPN) funktioniert unabhängig davon immer zusätzlich.", "Attribut für Benutzername") }}</label>
|
||||
<input type="text" name="ldap_user_filter_attr" value="{{ ldap.filter_attr }}" placeholder="sAMAccountName">
|
||||
<div class="field-hint">Für Active Directory: sAMAccountName. Für generisches LDAP (z.B. OpenLDAP): meist uid. Anmeldung per userPrincipalName (E-Mail/UPN) funktioniert unabhängig davon immer zusätzlich.</div>
|
||||
</div>
|
||||
<div class="field"><label>Standardgruppe für neue AD-Benutzer</label>
|
||||
<div class="field"><label>Standardgruppe für neue AD-Benutzer {{ hi.hint_icon("Wird nur zugewiesen, wenn unten keine AD-Gruppenzuordnung greift — siehe Karte „AD-Gruppenzuordnungen“.", "Standardgruppe für neue AD-Benutzer") }}</label>
|
||||
<select name="ldap_default_group">
|
||||
<option value="">Systemstandard ({{ ldap_groups|selectattr('is_default')|map(attribute='name')|first or 'Benutzer' }})</option>
|
||||
{% for g in ldap_groups %}
|
||||
<option value="{{ g['id'] }}" {% if ldap.default_group == g['id']|string %}selected{% endif %}>{{ g['name'] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div class="field-hint">Wird nur zugewiesen, wenn unten keine AD-Gruppenzuordnung greift — siehe Karte „AD-Gruppenzuordnungen“.</div>
|
||||
</div>
|
||||
<div class="field"><label>Erforderliche AD-Gruppe für Login (optional)</label>
|
||||
<div class="flex gap-2">
|
||||
<select name="ldap_required_login_group" id="requiredGroupSelect" style="flex:1;">
|
||||
<option value="">Keine (jeder gültige AD-Benutzer darf sich anmelden)</option>
|
||||
{% if ldap.required_login_group %}
|
||||
<option value="{{ ldap.required_login_group }}" selected>{{ ldap.required_login_group }}</option>
|
||||
{% endif %}
|
||||
</select>
|
||||
<button type="button" class="btn btn-secondary btn-sm" id="requiredGroupLoadBtn">Gruppen laden</button>
|
||||
</div>
|
||||
<div class="field-hint" id="requiredGroupLoadStatus">Ist hier eine Gruppe ausgewählt, scheitert die Anmeldung für alle Benutzer, die ihr NICHT angehören (rekursiv, auch über verschachtelte Gruppen) — wie bei falschen Zugangsdaten, ohne Hinweis auf den eigentlichen Grund.</div>
|
||||
</div>
|
||||
<div class="flex gap-2" style="flex-wrap:wrap;">
|
||||
<button type="submit" name="save_ldap" value="1" class="btn btn-primary">
|
||||
@@ -89,19 +96,20 @@
|
||||
</form>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p class="text-faint" style="font-size:12.5px;">Für Änderungen fehlt das Recht „LDAP/AD-Konfiguration speichern“.</p>
|
||||
<p class="text-faint" style="font-size:12.5px;">
|
||||
{% if current_user.has_permission('settings_ldap.edit') %}
|
||||
Nur mit Lizenz verfügbar — siehe <a href="{{ url_for('settings_license') }}">Lizenz</a>.
|
||||
{% else %}
|
||||
Für Änderungen fehlt das Recht „LDAP/AD-Konfiguration speichern“.
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="card card-pad">
|
||||
<div class="section-head" style="margin-bottom:16px;">
|
||||
<div>
|
||||
<h2 style="font-size:16px;">AD-Gruppenzuordnungen</h2>
|
||||
<div class="hint">
|
||||
Ist ein AD-Benutzer (rekursiv, auch über verschachtelte Gruppen) Mitglied einer hier zugeordneten
|
||||
AD-Gruppe, erhält er beim Login zusätzlich die zugeordnete App-Rechtegruppe — additiv, mehrere
|
||||
Zuordnungen können gleichzeitig greifen. Wird keine Zuordnung getroffen, gilt die Standardgruppe oben.
|
||||
</div>
|
||||
<h2 style="font-size:16px;">AD-Gruppenzuordnungen {{ hi.hint_icon("Ist ein AD-Benutzer (rekursiv, auch über verschachtelte Gruppen) Mitglied einer hier zugeordneten AD-Gruppe, erhält er beim Login zusätzlich die zugeordnete App-Rechtegruppe — additiv, mehrere Zuordnungen können gleichzeitig greifen. Wird keine Zuordnung getroffen, gilt die Standardgruppe oben.", "AD-Gruppenzuordnungen") }}</h2>
|
||||
</div>
|
||||
{% if can_edit %}
|
||||
<button type="button" class="btn btn-primary" data-open-modal="addLdapMappingModal">
|
||||
@@ -123,12 +131,18 @@
|
||||
<td>{{ m.app_group_name or '—' }}</td>
|
||||
<td>
|
||||
{% if can_edit %}
|
||||
<div class="row-actions">
|
||||
<button type="button" class="icon-btn" title="Bearbeiten"
|
||||
onclick="openEditLdapMappingModal('{{ m.id }}','{{ m.ad_group_dn|e }}','{{ m.ad_group_name|e }}','{{ m.app_group_id }}')">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 013 3L7 19l-4 1 1-4z"/></svg>
|
||||
</button>
|
||||
<form method="post" data-confirm="Zuordnung „{{ m.ad_group_name }} → {{ m.app_group_name }}“ löschen?">
|
||||
<input type="hidden" name="delete_ldap_group_mapping" value="{{ m.id }}">
|
||||
<button type="submit" class="icon-btn" style="color:var(--danger);" title="Löschen">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2m3 0l-1 14a2 2 0 01-2 2H7a2 2 0 01-2-2L4 6"/></svg>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -142,6 +156,57 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="card card-pad">
|
||||
<div class="section-head" style="margin-bottom:16px;">
|
||||
<div>
|
||||
<h2 style="font-size:16px;">Fileshare-Gruppenzuordnung {{ hi.hint_icon("Ist ein AD-Benutzer (rekursiv) Mitglied einer hier zugeordneten AD-Gruppe, wird die zugehörige Freigabe beim Login für ihn gemountet — sofern er zusätzlich das TESM-Recht „Dateifreigaben lesen“ hat (siehe Gruppen → Rechte, Bereich „Dateifreigaben“). Fehlt das Recht, wird nicht gemountet und der Menüpunkt „Dateifreigaben“ erscheint nicht, unabhängig von der AD-Gruppenmitgliedschaft.", "Fileshare-Gruppenzuordnung") }}</h2>
|
||||
</div>
|
||||
{% if can_edit %}
|
||||
<button type="button" class="btn btn-primary" data-open-modal="addFileshareMappingModal">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M12 5v14M5 12h14"/></svg>
|
||||
Zuordnung hinzufügen
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if fileshare_mappings %}
|
||||
<div class="table-wrap">
|
||||
<div style="overflow-x:auto;">
|
||||
<table class="data-table">
|
||||
<thead><tr><th>AD-Gruppe</th><th>Freigabe</th><th>Pfad</th><th style="width:1%;">Aktionen</th></tr></thead>
|
||||
<tbody>
|
||||
{% for m in fileshare_mappings %}
|
||||
<tr>
|
||||
<td>{{ m.ad_group_name }}<div class="text-faint mono" style="font-size:11px;">{{ m.ad_group_dn }}</div></td>
|
||||
<td>{{ m.share_label }}</td>
|
||||
<td class="mono" style="font-size:12px;">{{ m.share_unc }}</td>
|
||||
<td>
|
||||
{% if can_edit %}
|
||||
<div class="row-actions">
|
||||
<button type="button" class="icon-btn" title="Bearbeiten"
|
||||
onclick="openEditFileshareMappingModal('{{ m.id }}','{{ m.ad_group_dn|e }}','{{ m.ad_group_name|e }}','{{ m.share_label|e }}','{{ m.share_unc|e }}')">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 013 3L7 19l-4 1 1-4z"/></svg>
|
||||
</button>
|
||||
<form method="post" data-confirm="Fileshare-Zuordnung „{{ m.ad_group_name }} → {{ m.share_label }}“ löschen?">
|
||||
<input type="hidden" name="delete_fileshare_mapping" value="{{ m.id }}">
|
||||
<button type="submit" class="icon-btn" style="color:var(--danger);" title="Löschen">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2m3 0l-1 14a2 2 0 01-2 2H7a2 2 0 01-2-2L4 6"/></svg>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-faint" style="font-size:12.5px;">Noch keine Fileshare-Zuordnung angelegt — für niemanden wird eine Freigabe gemountet.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{% if can_edit %}
|
||||
@@ -183,15 +248,125 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-overlay" id="addFileshareMappingModal">
|
||||
<div class="modal">
|
||||
<form method="post">
|
||||
<input type="hidden" name="add_fileshare_mapping" value="1">
|
||||
<div class="modal-header">
|
||||
<h3>Fileshare-Zuordnung hinzufügen</h3>
|
||||
<button type="button" class="modal-close" data-close-modal>×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="field">
|
||||
<label>AD-Gruppe</label>
|
||||
<div class="flex gap-2">
|
||||
<select name="fs_ad_group_dn" id="fsMappingAdGroup" required style="flex:1;">
|
||||
<option value="">— zuerst laden —</option>
|
||||
</select>
|
||||
<button type="button" class="btn btn-secondary btn-sm" id="fsMappingLoadGroupsBtn">Gruppen laden</button>
|
||||
</div>
|
||||
<input type="hidden" name="fs_ad_group_name" id="fsMappingAdGroupName">
|
||||
<div class="field-hint" id="fsMappingLoadStatus">Fragt live per Bind-Konto alle Gruppen aus dem Verzeichnis ab.</div>
|
||||
</div>
|
||||
<div class="field"><label>Bezeichnung {{ hi.hint_icon("Anzeigename in der Freigaben-Auswahl — auch Ordnername unter dem Mount-Punkt.", "Bezeichnung") }}</label>
|
||||
<input type="text" name="fs_share_label" placeholder="z.B. Vertrieb" required>
|
||||
</div>
|
||||
<div class="field"><label>Freigabe-Pfad (UNC) {{ hi.hint_icon("Beide Schreibweisen funktionieren — \\\\server\\freigabe wird automatisch in das von Linux benötigte //server/freigabe umgewandelt.", "Freigabe-Pfad (UNC)") }}</label>
|
||||
<input type="text" name="fs_share_unc" placeholder="//fileserver/freigabe" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-close-modal>Abbrechen</button>
|
||||
<button type="submit" class="btn btn-primary">Speichern</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-overlay" id="editLdapMappingModal">
|
||||
<div class="modal">
|
||||
<form method="post">
|
||||
<input type="hidden" name="edit_ldap_group_mapping" id="editLdapMappingId" value="">
|
||||
<div class="modal-header">
|
||||
<h3>AD-Gruppenzuordnung bearbeiten</h3>
|
||||
<button type="button" class="modal-close" data-close-modal>×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="field">
|
||||
<label>AD-Gruppe</label>
|
||||
<div class="flex gap-2">
|
||||
<select name="ad_group_dn" id="editLdapMappingAdGroup" required style="flex:1;"></select>
|
||||
<button type="button" class="btn btn-secondary btn-sm" id="editLdapMappingLoadGroupsBtn">Gruppen laden</button>
|
||||
</div>
|
||||
<input type="hidden" name="ad_group_name" id="editLdapMappingAdGroupName">
|
||||
<div class="field-hint" id="editLdapMappingLoadStatus">Fragt live per Bind-Konto alle Gruppen aus dem Verzeichnis ab.</div>
|
||||
</div>
|
||||
<div class="field"><label>App-Rechtegruppe</label>
|
||||
<select name="app_group_id" id="editLdapMappingAppGroup" required>
|
||||
<option value="">— auswählen —</option>
|
||||
<option value="admin">Admin (alle Rechte)</option>
|
||||
{% for g in ldap_groups %}
|
||||
<option value="{{ g['id'] }}">{{ g['name'] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-close-modal>Abbrechen</button>
|
||||
<button type="submit" class="btn btn-primary">Speichern</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-overlay" id="editFileshareMappingModal">
|
||||
<div class="modal">
|
||||
<form method="post">
|
||||
<input type="hidden" name="edit_fileshare_mapping" id="editFsMappingId" value="">
|
||||
<div class="modal-header">
|
||||
<h3>Fileshare-Zuordnung bearbeiten</h3>
|
||||
<button type="button" class="modal-close" data-close-modal>×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="field">
|
||||
<label>AD-Gruppe</label>
|
||||
<div class="flex gap-2">
|
||||
<select name="fs_ad_group_dn" id="editFsMappingAdGroup" required style="flex:1;"></select>
|
||||
<button type="button" class="btn btn-secondary btn-sm" id="editFsMappingLoadGroupsBtn">Gruppen laden</button>
|
||||
</div>
|
||||
<input type="hidden" name="fs_ad_group_name" id="editFsMappingAdGroupName">
|
||||
<div class="field-hint" id="editFsMappingLoadStatus">Fragt live per Bind-Konto alle Gruppen aus dem Verzeichnis ab.</div>
|
||||
</div>
|
||||
<div class="field"><label>Bezeichnung {{ hi.hint_icon("Anzeigename in der Freigaben-Auswahl — auch Ordnername unter dem Mount-Punkt.", "Bezeichnung") }}</label>
|
||||
<input type="text" name="fs_share_label" id="editFsMappingLabel" placeholder="z.B. Vertrieb" required>
|
||||
</div>
|
||||
<div class="field"><label>Freigabe-Pfad (UNC) {{ hi.hint_icon("Beide Schreibweisen funktionieren — \\\\server\\freigabe wird automatisch in das von Linux benötigte //server/freigabe umgewandelt.", "Freigabe-Pfad (UNC)") }}</label>
|
||||
<input type="text" name="fs_share_unc" id="editFsMappingUnc" placeholder="//fileserver/freigabe" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-close-modal>Abbrechen</button>
|
||||
<button type="submit" class="btn btn-primary">Speichern</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
var btn = document.getElementById('ldapMappingLoadGroupsBtn');
|
||||
var select = document.getElementById('ldapMappingAdGroup');
|
||||
var nameField = document.getElementById('ldapMappingAdGroupName');
|
||||
var status = document.getElementById('ldapMappingLoadStatus');
|
||||
function wireAdGroupLoader(btnId, selectId, nameFieldId, statusId, includeEmptyOption) {
|
||||
var btn = document.getElementById(btnId);
|
||||
var select = document.getElementById(selectId);
|
||||
var nameField = nameFieldId ? document.getElementById(nameFieldId) : null;
|
||||
var status = document.getElementById(statusId);
|
||||
if (!btn) return;
|
||||
|
||||
btn.addEventListener('click', function () {
|
||||
// Aktuellen Wert (z.B. die vorher gespeicherte, noch nicht per
|
||||
// Klartext-Namen aufgeloeste Gruppe) merken -- bleibt nach dem
|
||||
// Neuaufbau der Optionsliste ausgewaehlt, falls sie unter den
|
||||
// geladenen Gruppen auftaucht.
|
||||
var currentValue = select.value;
|
||||
status.textContent = 'Lade Gruppen …';
|
||||
fetch("{{ url_for('settings_ldap_ad_groups') }}")
|
||||
.then(function (r) { return r.json(); })
|
||||
@@ -201,8 +376,14 @@
|
||||
return;
|
||||
}
|
||||
select.innerHTML = '';
|
||||
if (includeEmptyOption) {
|
||||
var emptyOpt = document.createElement('option');
|
||||
emptyOpt.value = '';
|
||||
emptyOpt.textContent = 'Keine (jeder gültige AD-Benutzer darf sich anmelden)';
|
||||
select.appendChild(emptyOpt);
|
||||
}
|
||||
if (!groups.length) {
|
||||
select.innerHTML = '<option value="">Keine Gruppen gefunden</option>';
|
||||
if (!includeEmptyOption) select.innerHTML = '<option value="">Keine Gruppen gefunden</option>';
|
||||
status.textContent = 'Keine Gruppen gefunden — Verbindung/Bind-Konto prüfen.';
|
||||
return;
|
||||
}
|
||||
@@ -211,19 +392,63 @@
|
||||
opt.value = g.dn;
|
||||
opt.textContent = g.name;
|
||||
opt.dataset.name = g.name;
|
||||
if (g.dn === currentValue) opt.selected = true;
|
||||
select.appendChild(opt);
|
||||
});
|
||||
nameField.value = select.options[select.selectedIndex].dataset.name || '';
|
||||
if (nameField) nameField.value = (select.options[select.selectedIndex] && select.options[select.selectedIndex].dataset.name) || '';
|
||||
status.textContent = groups.length + ' Gruppe(n) geladen.';
|
||||
})
|
||||
.catch(function () { status.textContent = 'Fehler beim Laden — Verbindung/Bind-Konto prüfen.'; });
|
||||
});
|
||||
|
||||
select.addEventListener('change', function () {
|
||||
if (!nameField) return;
|
||||
var opt = select.options[select.selectedIndex];
|
||||
nameField.value = (opt && opt.dataset.name) || '';
|
||||
});
|
||||
}
|
||||
|
||||
wireAdGroupLoader('ldapMappingLoadGroupsBtn', 'ldapMappingAdGroup', 'ldapMappingAdGroupName', 'ldapMappingLoadStatus', false);
|
||||
wireAdGroupLoader('fsMappingLoadGroupsBtn', 'fsMappingAdGroup', 'fsMappingAdGroupName', 'fsMappingLoadStatus', false);
|
||||
wireAdGroupLoader('requiredGroupLoadBtn', 'requiredGroupSelect', null, 'requiredGroupLoadStatus', true);
|
||||
wireAdGroupLoader('editLdapMappingLoadGroupsBtn', 'editLdapMappingAdGroup', 'editLdapMappingAdGroupName', 'editLdapMappingLoadStatus', false);
|
||||
wireAdGroupLoader('editFsMappingLoadGroupsBtn', 'editFsMappingAdGroup', 'editFsMappingAdGroupName', 'editFsMappingLoadStatus', false);
|
||||
})();
|
||||
|
||||
// Bearbeiten-Modals vorbefuellen -- die AD-Gruppe steht dabei zunaechst nur
|
||||
// als DN+Name aus der Datenbank zur Verfuegung (ohne erneute LDAP-Abfrage);
|
||||
// "Gruppen laden" ersetzt die Auswahlliste bei Bedarf durch die vollstaendige,
|
||||
// aktuelle AD-Gruppenliste und behaelt den bisherigen Wert dabei bei (siehe
|
||||
// wireAdGroupLoader oben).
|
||||
function seedMappingSelect(selectId, dn, name) {
|
||||
var select = document.getElementById(selectId);
|
||||
select.innerHTML = '';
|
||||
var opt = document.createElement('option');
|
||||
opt.value = dn;
|
||||
opt.textContent = name || dn;
|
||||
opt.dataset.name = name || dn;
|
||||
opt.selected = true;
|
||||
select.appendChild(opt);
|
||||
}
|
||||
|
||||
function openEditLdapMappingModal(id, dn, name, appGroupId) {
|
||||
document.getElementById('editLdapMappingId').value = id;
|
||||
seedMappingSelect('editLdapMappingAdGroup', dn, name);
|
||||
document.getElementById('editLdapMappingAdGroupName').value = name;
|
||||
document.getElementById('editLdapMappingAppGroup').value = appGroupId;
|
||||
document.getElementById('editLdapMappingLoadStatus').textContent = 'Fragt live per Bind-Konto alle Gruppen aus dem Verzeichnis ab.';
|
||||
PoeUI.openModal('editLdapMappingModal');
|
||||
}
|
||||
|
||||
function openEditFileshareMappingModal(id, dn, name, label, unc) {
|
||||
document.getElementById('editFsMappingId').value = id;
|
||||
seedMappingSelect('editFsMappingAdGroup', dn, name);
|
||||
document.getElementById('editFsMappingAdGroupName').value = name;
|
||||
document.getElementById('editFsMappingLabel').value = label;
|
||||
document.getElementById('editFsMappingUnc').value = unc;
|
||||
document.getElementById('editFsMappingLoadStatus').textContent = 'Fragt live per Bind-Konto alle Gruppen aus dem Verzeichnis ab.';
|
||||
PoeUI.openModal('editFileshareMappingModal');
|
||||
}
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,184 @@
|
||||
{% extends "base.html" %}
|
||||
{% set active_page = "settings_license" %}
|
||||
{% set can_edit = current_user.has_permission('settings_license.edit') %}
|
||||
{% block page_title %}Lizenz{% endblock %}
|
||||
{% block page_sub %}<div class="topbar-sub">Lizenzstatus, Module und Aktivierung dieses Systems</div>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% import "_hint_icon.html" as hi %}
|
||||
<div class="settings-grid">
|
||||
|
||||
<div class="card card-pad">
|
||||
<div class="section-head" style="margin-bottom:16px;">
|
||||
<div>
|
||||
<h2 style="font-size:16px;">Status</h2>
|
||||
<div class="hint">Aktuell installierte Lizenz und ihr Zustand.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if license_error %}
|
||||
<div class="notice-banner notice-banner--warning" style="margin-bottom:14px;">{{ license_error }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if not license_file %}
|
||||
<p class="text-faint" style="font-size:12.5px;">Keine Lizenzdatei vorhanden — bitte unten eine Lizenzdatei hochladen.</p>
|
||||
{% else %}
|
||||
<div class="field">
|
||||
<label>Kunde</label>
|
||||
<input type="text" value="{{ license_file.customer.name or '' }}" disabled>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Lizenztyp</label>
|
||||
<input type="text" value="{{ {'trial': 'Trial', 'standard': 'Standard', 'custom': 'Custom', 'enterprise': 'Enterprise'}.get(license_file.type, license_file.type) }}" disabled>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Module {{ hi.hint_icon("Zusätzlich zu den immer verfügbaren Standard-Funktionen freigeschaltete Vollmodule.", "Module") }}</label>
|
||||
<input type="text" value="{{ (license_file.modules or [])|join(', ') if license_file.modules else 'keine' }}" disabled>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Ausgestellt am</label>
|
||||
<input type="text" value="{{ license_file.issued_at or '' }}" disabled>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Läuft ab am</label>
|
||||
<input type="text" value="{{ license_file.expires_at or '' }}" disabled>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Aktivierungsstatus</label>
|
||||
<input type="text" value="{{ 'Aktiviert' if license_is_activated else 'Nicht aktiviert' }}" disabled>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Letzter Heartbeat</label>
|
||||
<input type="text" value="{{ license_last_heartbeat_at or 'noch nie' }}" disabled>
|
||||
</div>
|
||||
{% if license_last_error %}
|
||||
<div class="field">
|
||||
<label>Letzter Fehler</label>
|
||||
<input type="text" value="{{ license_last_error }}" disabled>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="field">
|
||||
<label>System-Fingerabdruck {{ hi.hint_icon("Eindeutiger Identifikator dieses Systems, an den die Lizenz bei der Aktivierung gebunden wird.", "System-Fingerabdruck") }}</label>
|
||||
<input type="text" class="mono" value="{{ license_fingerprint }}" disabled>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if license_file and license_valid %}
|
||||
<div class="card card-pad">
|
||||
<div class="section-head" style="margin-bottom:16px;">
|
||||
<div>
|
||||
<h2 style="font-size:16px;">Aktivierung</h2>
|
||||
<div class="hint">
|
||||
Ein Protokoll, zwei Wege: automatisch online, oder — falls kein Netzwerkzugriff auf den
|
||||
Lizenzserver besteht — per Code manuell mit dem Lizenzserver-Administrator ausgetauscht.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if not can_edit %}
|
||||
<p class="text-faint" style="font-size:12.5px;">Für Aktivierung/Deaktivierung fehlt das Recht „Systemeinstellungen ändern“.</p>
|
||||
|
||||
{% elif pending_action %}
|
||||
<p class="text-faint" style="font-size:12.5px; margin-bottom:10px;">
|
||||
Offene {{ 'Aktivierungs' if pending_action.action == 'activate' else 'Deaktivierungs' }}-Anfrage — folgenden Code
|
||||
beim Lizenzserver-Administrator eingeben:
|
||||
</p>
|
||||
<div class="field">
|
||||
<textarea class="mono" rows="4" readonly onclick="this.select()" style="width:100%; resize:vertical;">{{ pending_code }}</textarea>
|
||||
</div>
|
||||
<form method="post" action="{{ url_for('license_activate_confirm' if pending_action.action == 'activate' else 'license_deactivate_confirm') }}">
|
||||
<div class="field">
|
||||
<label for="confirmation_code">Bestätigungscode vom Lizenzserver</label>
|
||||
<textarea class="mono" name="confirmation_code" id="confirmation_code" rows="4" style="width:100%; resize:vertical;" required></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
|
||||
Code bestätigen
|
||||
</button>
|
||||
</form>
|
||||
<form method="post" action="{{ url_for('license_activate' if pending_action.action == 'activate' else 'license_deactivate') }}" style="margin-top:10px;">
|
||||
<button type="submit" class="btn btn-secondary btn-block">Erneut online versuchen</button>
|
||||
</form>
|
||||
|
||||
{% elif license_is_activated %}
|
||||
<p class="text-faint" style="font-size:12.5px; margin-bottom:10px;">
|
||||
Lizenz ist aktiviert. Bei einem Systemwechsel zuerst hier deaktivieren — danach kann der Kunde sich beim
|
||||
Anbieter selbst eine neue Lizenz für das neue System ausstellen lassen.
|
||||
</p>
|
||||
<form method="post" action="{{ url_for('license_deactivate') }}"
|
||||
data-confirm="Lizenz wirklich deaktivieren? Alle lizenzpflichtigen Module/Funktionen werden danach sofort inaktiv (Export bleibt bis zur nächsten Aktivierung möglich).">
|
||||
<button type="submit" class="btn btn-danger btn-block">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M15 9l-6 6M9 9l6 6"/></svg>
|
||||
Lizenz deaktivieren (Systemwechsel)
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{% else %}
|
||||
<form method="post" action="{{ url_for('license_activate') }}">
|
||||
<button type="submit" class="btn btn-primary btn-block">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
|
||||
Jetzt aktivieren
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card card-pad">
|
||||
<div class="section-head" style="margin-bottom:16px;">
|
||||
<div>
|
||||
<h2 style="font-size:16px;">Lizenzdatei hochladen</h2>
|
||||
<div class="hint">Vom Anbieter erhaltene Lizenzdatei einspielen — muss danach noch aktiviert werden.</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if can_edit %}
|
||||
<form method="post" action="{{ url_for('license_upload') }}" enctype="multipart/form-data">
|
||||
<div class="field">
|
||||
<label for="license_file">Lizenzdatei</label>
|
||||
<input type="file" name="license_file" id="license_file" accept=".json,.lic" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-secondary btn-block">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><path d="M17 8l-5-5-5 5"/><path d="M12 3v12"/></svg>
|
||||
Hochladen
|
||||
</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<p class="text-faint" style="font-size:12.5px;">Für den Upload fehlt das Recht „Systemeinstellungen ändern“.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if license_file and license_file.vendor %}
|
||||
<div class="card card-pad">
|
||||
<div class="section-head" style="margin-bottom:16px;">
|
||||
<div>
|
||||
<h2 style="font-size:16px;">Anbieter</h2>
|
||||
<div class="hint">Kontakt für Rückfragen zu dieser Lizenz.</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if license_file.vendor.logo_base64 %}
|
||||
<div class="field" style="text-align:center;">
|
||||
<img src="{{ license_file.vendor.logo_base64 }}" alt="{{ license_file.vendor.name or 'Anbieter' }}" style="max-width:220px; max-height:90px; margin:0 auto 8px;">
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="field">
|
||||
<label>Firma</label>
|
||||
<input type="text" value="{{ license_file.vendor.name or '' }}" disabled>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Telefon</label>
|
||||
<input type="text" value="{{ license_file.vendor.phone or '' }}" disabled>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>E-Mail</label>
|
||||
<input type="text" value="{{ license_file.vendor.email or '' }}" disabled>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Anschrift</label>
|
||||
<input type="text" value="{{ license_file.vendor.address or '' }}" disabled>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,10 +1,11 @@
|
||||
{% extends "base.html" %}
|
||||
{% set active_page = "settings_nginx" %}
|
||||
{% set can_edit = current_user.has_permission('settings_nginx.edit') %}
|
||||
{% set can_edit = current_user.has_permission('settings_nginx.edit') and license_active() %}
|
||||
{% block page_title %}NGINX{% endblock %}
|
||||
{% block page_sub %}<div class="topbar-sub">Reverse-Proxy: Domain, Ports, SSL/HSTS und Zertifikat</div>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% import "_hint_icon.html" as hi %}
|
||||
<div class="settings-grid settings-grid--wide">
|
||||
|
||||
<div class="card card-pad">
|
||||
@@ -38,24 +39,19 @@
|
||||
<form method="post"
|
||||
data-confirm="nginx-Konfiguration wirklich ändern? Falls die Verbindung danach abbricht, wird die vorherige Konfiguration automatisch nach {{ nginx_revert_seconds }} Sekunden wiederhergestellt.">
|
||||
<div class="field">
|
||||
<label for="server_name">Domain (server_name)</label>
|
||||
<label for="server_name">Domain (server_name) {{ hi.hint_icon("\"_\" ist nginx' Catch-all (Standard für interne Instanzen ohne eigene Domain) — für Let's Encrypt muss hier die tatsächliche, öffentlich auflösbare Domain stehen.", "Domain (server_name)") }}</label>
|
||||
<input type="text" name="server_name" id="server_name" value="{{ server_name }}" placeholder="_ (kein bestimmter Hostname) oder z.B. tesm.example.com">
|
||||
<div class="field-hint">"_" ist nginx' Catch-all (Standard für interne Instanzen ohne eigene Domain) — für Let's Encrypt muss hier die tatsächliche, öffentlich auflösbare Domain stehen.</div>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<div class="field" style="flex:1;">
|
||||
<div class="field field--half">
|
||||
<label for="http_port">HTTP-Port</label>
|
||||
<input type="number" name="http_port" id="http_port" min="1" max="65535" value="{{ http_port }}" required>
|
||||
</div>
|
||||
<div class="field" style="flex:1;">
|
||||
<label for="https_port">HTTPS-Port</label>
|
||||
<div class="field field--half">
|
||||
<label for="https_port">HTTPS-Port {{ hi.hint_icon("Let's Encrypt validiert IMMER über Port 80 (protokollbedingt, unabhängig vom hier eingestellten HTTP-Port) — weicht der HTTP-Port von 80 ab, wird dafür automatisch zusätzlich ein minimaler Port-80-Block mitgeschrieben.", "HTTP-Port / HTTPS-Port") }}</label>
|
||||
<input type="number" name="https_port" id="https_port" min="1" max="65535" value="{{ https_port }}" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field-hint" style="margin-top:-8px; margin-bottom:14px;">
|
||||
Let's Encrypt validiert IMMER über Port 80 (protokollbedingt, unabhängig vom hier eingestellten HTTP-Port) —
|
||||
weicht der HTTP-Port von 80 ab, wird dafür automatisch zusätzlich ein minimaler Port-80-Block mitgeschrieben.
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="switch-check" style="display:inline-flex; align-items:center; gap:8px;">
|
||||
<input type="checkbox" name="ssl_enabled" id="ssl_enabled_check" {% if ssl_enabled %}checked{% endif %} {% if not cert_info %}disabled{% endif %}>
|
||||
@@ -68,9 +64,8 @@
|
||||
<label class="switch-check" style="display:inline-flex; align-items:center; gap:8px;">
|
||||
<input type="checkbox" name="hsts_enabled" id="hsts_enabled_check" {% if hsts_enabled %}checked{% endif %} {% if not ssl_enabled %}disabled{% endif %}>
|
||||
<span class="track"></span>
|
||||
<span>HSTS (Strict-Transport-Security)</span>
|
||||
<span>HSTS (Strict-Transport-Security) {{ hi.hint_icon("Weist Browser an, diese Instanz künftig NUR noch über HTTPS aufzurufen — bleibt auch bei einem späteren Zurückschalten auf HTTP im Browser für 1 Jahr bestehen. Nur aktivieren, wenn das Zertifikat dauerhaft gepflegt wird.", "HSTS") }}</span>
|
||||
</label>
|
||||
<div class="field-hint">Weist Browser an, diese Instanz künftig NUR noch über HTTPS aufzurufen — bleibt auch bei einem späteren Zurückschalten auf HTTP im Browser für 1 Jahr bestehen. Nur aktivieren, wenn das Zertifikat dauerhaft gepflegt wird.</div>
|
||||
</div>
|
||||
<button type="submit" name="apply_nginx" value="1" class="btn btn-primary btn-block">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 12l2 2 4-4"/><path d="M12 2l8 4v6c0 5-3.5 8.5-8 10-4.5-1.5-8-5-8-10V6z"/></svg>
|
||||
@@ -78,7 +73,13 @@
|
||||
</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<p class="text-faint" style="font-size:12.5px;">Für Änderungen fehlt das Recht „NGINX ändern“.</p>
|
||||
<p class="text-faint" style="font-size:12.5px;">
|
||||
{% if current_user.has_permission('settings_nginx.edit') %}
|
||||
Nur mit Lizenz verfügbar — siehe <a href="{{ url_for('settings_license') }}">Lizenz</a>.
|
||||
{% else %}
|
||||
Für Änderungen fehlt das Recht „NGINX ändern“.
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@@ -165,9 +166,8 @@
|
||||
<input type="file" name="cert_file" id="cert_file" accept=".pem,.crt,.cer" required>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="key_file">Privater Schlüssel (PEM, unverschlüsselt)</label>
|
||||
<label for="key_file">Privater Schlüssel (PEM, unverschlüsselt) {{ hi.hint_icon("Ein passwortgeschütztes Schlüssel wird abgelehnt — nginx könnte ihn beim Start ohnehin nicht ohne manuelle Passworteingabe laden.", "Privater Schlüssel") }}</label>
|
||||
<input type="file" name="key_file" id="key_file" accept=".pem,.key" required>
|
||||
<div class="field-hint">Ein passwortgeschütztes Schlüssel wird abgelehnt — nginx könnte ihn beim Start ohnehin nicht ohne manuelle Passworteingabe laden.</div>
|
||||
</div>
|
||||
<button type="submit" name="upload_cert" value="1" class="btn btn-secondary btn-block">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><path d="M17 8l-5-5-5 5"/><path d="M12 3v12"/></svg>
|
||||
@@ -183,7 +183,13 @@
|
||||
<p class="text-faint" style="font-size:11.5px;">SSL muss zuerst deaktiviert werden, bevor das Zertifikat entfernt werden kann.</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p class="text-faint" style="font-size:12.5px;">Für Anfordern/Upload/Entfernen fehlt das Recht „NGINX ändern“.</p>
|
||||
<p class="text-faint" style="font-size:12.5px;">
|
||||
{% if current_user.has_permission('settings_nginx.edit') %}
|
||||
Nur mit Lizenz verfügbar — siehe <a href="{{ url_for('settings_license') }}">Lizenz</a>.
|
||||
{% else %}
|
||||
Für Anfordern/Upload/Entfernen fehlt das Recht „NGINX ändern“.
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
{% block page_sub %}<div class="topbar-sub">{{ switches|length }} Switche</div>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% import "_hint_icon.html" as hi %}
|
||||
|
||||
<div class="section-head">
|
||||
<div>
|
||||
@@ -102,9 +103,8 @@
|
||||
<input type="text" name="ip" value="{{ s['ip'] }}" required placeholder="z.B. 192.168.1.100">
|
||||
<div class="invalid-feedback">Bitte eine gültige IP-Adresse eingeben.</div>
|
||||
</div>
|
||||
<div class="field"><label>SSH-Port</label>
|
||||
<div class="field"><label>SSH-Port {{ hi.hint_icon("Leer lassen, wenn der Switch den Standard-Port 22 verwendet.", "SSH-Port") }}</label>
|
||||
<input type="number" name="ssh_port" value="{{ s['ssh_port'] or '' }}" min="1" max="65535" placeholder="22 (Standard)">
|
||||
<div class="field-hint">Leer lassen, wenn der Switch den Standard-Port 22 verwendet.</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Zugangsdaten</label>
|
||||
@@ -162,9 +162,8 @@
|
||||
<input type="text" name="ip" required placeholder="z.B. 192.168.1.100">
|
||||
<div class="invalid-feedback">Bitte eine gültige IP-Adresse eingeben.</div>
|
||||
</div>
|
||||
<div class="field"><label>SSH-Port</label>
|
||||
<div class="field"><label>SSH-Port {{ hi.hint_icon("Leer lassen, wenn der Switch den Standard-Port 22 verwendet.", "SSH-Port") }}</label>
|
||||
<input type="number" name="ssh_port" min="1" max="65535" placeholder="22 (Standard)">
|
||||
<div class="field-hint">Leer lassen, wenn der Switch den Standard-Port 22 verwendet.</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Zugangsdaten</label>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
{% block page_sub %}<div class="topbar-sub">{{ users|length }} Benutzer</div>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% import "_hint_icon.html" as hi %}
|
||||
|
||||
<div class="section-head">
|
||||
<div>
|
||||
@@ -205,13 +206,12 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="field">
|
||||
<label>Gruppe</label>
|
||||
<label>Gruppe {{ hi.hint_icon("Ersetzt die bisherige Gruppen-/Rollenzuordnung dieses Benutzers.", "Gruppe") }}</label>
|
||||
<select name="group_id" id="group_select">
|
||||
<option value="">Keine Gruppe</option>
|
||||
{% for g in all_groups %}<option value="{{ g['id'] }}">{{ g['name'] }}</option>{% endfor %}
|
||||
{% if current_user.is_admin %}<option value="admin">Admin</option>{% endif %}
|
||||
</select>
|
||||
<div class="field-hint">Ersetzt die bisherige Gruppen-/Rollenzuordnung dieses Benutzers.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
@@ -34,11 +34,8 @@ REPO_NAME="${TESM_REPO_NAME:-tesm}"
|
||||
RELEASE_TAG="${TESM_RELEASE_TAG:-latest}"
|
||||
GITEA_USER="${TESM_GITEA_USER:-}"
|
||||
GITEA_TOKEN="${TESM_GITEA_TOKEN:-}"
|
||||
PACKAGE_NAME="tesm-${RELEASE_TAG}.tar.gz"
|
||||
DOWNLOAD_URL="${GITEA_BASE}/${REPO_OWNER}/${REPO_NAME}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}"
|
||||
|
||||
WORK_DIR="/tmp/tesm-update-$(date +%s)"
|
||||
PACKAGE_FILE="$WORK_DIR/${PACKAGE_NAME}"
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
@@ -50,6 +47,38 @@ if [ "$(id -u)" -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CURL_AUTH=()
|
||||
if [ -n "$GITEA_USER" ] && [ -n "$GITEA_TOKEN" ]; then
|
||||
CURL_AUTH=(-u "${GITEA_USER}:${GITEA_TOKEN}")
|
||||
fi
|
||||
|
||||
# ---- "latest" IMMER über die Gitea-API auflösen, nie als Literal in die
|
||||
# Download-URL einsetzen ----
|
||||
# Live reproduziert: /releases/download/latest/tesm-latest.tar.gz liefert
|
||||
# HTTP 200 -- aber keinen echten Release-Asset, sondern (weil kein Asset
|
||||
# exakt "tesm-latest.tar.gz" heißt) Giteas automatisch generiertes
|
||||
# Quellcode-Archiv des AKTUELLEN Default-Branch-Stands, kommentarlos und
|
||||
# ohne Fehlermeldung. Ohne diesen Auflösungsschritt würde ein einfaches
|
||||
# "sudo ./update.sh" (ohne explizites TESM_RELEASE_TAG) also lautlos einen
|
||||
# unversionierten, potenziell halbfertigen Zwischenstand installieren
|
||||
# statt des tatsächlich neuesten Releases.
|
||||
if [ "$RELEASE_TAG" == "latest" ]; then
|
||||
echo -e "${RED}→${NC} Ermittle aktuellsten Release-Tag von ${GITEA_BASE}/${REPO_OWNER}/${REPO_NAME}..."
|
||||
RESOLVED_TAG="$(curl -fsSL "${CURL_AUTH[@]}" "${GITEA_BASE}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest" \
|
||||
| python3 -c "import json,sys; print(json.load(sys.stdin).get('tag_name',''))" 2>/dev/null)"
|
||||
if [ -z "$RESOLVED_TAG" ]; then
|
||||
echo "✖ Konnte den aktuellsten Release-Tag nicht über die Gitea-API ermitteln -- Abbruch." >&2
|
||||
echo " (Alternative: TESM_RELEASE_TAG=vX.Y.Z explizit setzen.)" >&2
|
||||
exit 1
|
||||
fi
|
||||
RELEASE_TAG="$RESOLVED_TAG"
|
||||
echo -e "${GREEN}✔${NC} Aktuellster Release ist ${RELEASE_TAG}."
|
||||
fi
|
||||
|
||||
PACKAGE_NAME="tesm-${RELEASE_TAG}.tar.gz"
|
||||
DOWNLOAD_URL="${GITEA_BASE}/${REPO_OWNER}/${REPO_NAME}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}"
|
||||
PACKAGE_FILE="$WORK_DIR/${PACKAGE_NAME}"
|
||||
|
||||
echo -e "${YELLOW}============================================================================${NC}"
|
||||
echo -e "${YELLOW} TESM Update/Reinstall — Release \"${RELEASE_TAG}\"${NC}"
|
||||
echo -e "${YELLOW}============================================================================${NC}"
|
||||
@@ -68,16 +97,22 @@ fi
|
||||
|
||||
echo -e "${RED}→${NC} Lade Paket von ${DOWNLOAD_URL}..."
|
||||
mkdir -p "$WORK_DIR"
|
||||
CURL_AUTH=()
|
||||
if [ -n "$GITEA_USER" ] && [ -n "$GITEA_TOKEN" ]; then
|
||||
CURL_AUTH=(-u "${GITEA_USER}:${GITEA_TOKEN}")
|
||||
fi
|
||||
curl -fsSL "${CURL_AUTH[@]}" -o "$PACKAGE_FILE" "$DOWNLOAD_URL"
|
||||
echo -e "${GREEN}✔${NC} Paket heruntergeladen ($(du -h "$PACKAGE_FILE" | cut -f1))."
|
||||
|
||||
echo -e "${RED}→${NC} Entpacke Paket..."
|
||||
mkdir -p "$WORK_DIR/pkg"
|
||||
tar xzf "$PACKAGE_FILE" -C "$WORK_DIR/pkg" --strip-components=1
|
||||
|
||||
# Absicherung gegen genau das oben beschriebene Fallback-Verhalten: statt
|
||||
# eines späten, kryptischen "No such file or directory" beim Aufruf von
|
||||
# install.sh hier klar benennen, WENN das heruntergeladene Paket nicht das
|
||||
# erwartete ist (z.B. weil sich Giteas Verhalten wieder ändert).
|
||||
if [ ! -f "$WORK_DIR/pkg/install.sh" ]; then
|
||||
echo "✖ Entpacktes Paket enthält kein install.sh -- vermutlich wurde nicht das" >&2
|
||||
echo " erwartete Release-Asset heruntergeladen (siehe ${DOWNLOAD_URL})." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}✔${NC} Paket entpackt."
|
||||
|
||||
echo -e "${RED}→${NC} Starte install.sh aus dem Paket..."
|
||||
|
||||
@@ -80,6 +80,7 @@ EOF
|
||||
function check_device() {
|
||||
local rpi_ip=$1 dev_name=$2 switch_ip=$3 switch_ssh_port=$4
|
||||
local switch_hostname=$5 switch_port=$6 switch_user=$7 switch_pass=$8
|
||||
local license_active=$9
|
||||
|
||||
if ping -c 1 -W 2 "$rpi_ip" &> /dev/null; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name ist erreichbar!" >> "$LOGFILE"
|
||||
@@ -92,6 +93,15 @@ function check_device() {
|
||||
return
|
||||
fi
|
||||
|
||||
# Automatischer PoE-Neustart bei Ausfall ist ein lizenzpflichtiges
|
||||
# Feature (siehe Lizenzsystem) -- der manuelle Neustart-Button im
|
||||
# Dashboard (manual_restart() weiter unten) ruft disable_poe/enable_poe
|
||||
# dagegen IMMER direkt auf und bleibt davon bewusst unberührt.
|
||||
if [ "$license_active" != "1" ]; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name: automatischer PoE-Neustart übersprungen -- keine gültige Lizenz." >> "$LOGFILE"
|
||||
return
|
||||
fi
|
||||
|
||||
if ! disable_poe "$switch_ip" "$switch_port" "$switch_user" "$switch_pass" "$switch_ssh_port"; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name: Switch $switch_hostname nicht erreichbar oder Kommando abgelehnt — PoE-Deaktivierung fehlgeschlagen, kein Neustart durchgeführt." >> "$LOGFILE"
|
||||
return
|
||||
@@ -146,8 +156,19 @@ fi
|
||||
while true; do
|
||||
echo "--------------------------------------------------------------------" >> "$LOGFILE"
|
||||
|
||||
# Einmal pro Schleifendurchlauf gelesen (wie SLEEP oben beim Skriptstart),
|
||||
# nicht pro Gerät -- reicht als Shell-Variable an check_device() durch.
|
||||
LICENSE_ACTIVE=$(python3 - <<'END'
|
||||
import sys
|
||||
sys.path.insert(0, "/srv/tesm")
|
||||
from app import license_active
|
||||
print("1" if license_active() else "0")
|
||||
END
|
||||
)
|
||||
LICENSE_ACTIVE=${LICENSE_ACTIVE:-0}
|
||||
|
||||
while IFS='|' read -r rpi_ip dev_name switch_ip switch_ssh_port switch_hostname switch_port switch_user switch_pass mac; do
|
||||
check_device "$rpi_ip" "$dev_name" "$switch_ip" "$switch_ssh_port" "$switch_hostname" "$switch_port" "$switch_user" "$switch_pass" &
|
||||
check_device "$rpi_ip" "$dev_name" "$switch_ip" "$switch_ssh_port" "$switch_hostname" "$switch_port" "$switch_user" "$switch_pass" "$LICENSE_ACTIVE" &
|
||||
done < <(python3 /srv/tesm/generate_ips.py)
|
||||
|
||||
wait
|
||||
|
||||
Reference in new issue
Block a user