commit edc4ec5bf3600ec4ce451a1479b31006c0792a3a Author: Florian Otto Date: Wed Apr 15 14:30:36 2026 +0200 Initialer Upload diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4330e77 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# Node.js / Standard-Entwicklungsdateien +node_modules/ +dist/ +build/ +.env + +# Editor-Dateien +*.log +*.swp +*.swo +*.tmp +.idea/ +.vscode/ +.DS_Store + +# Browser-Erweiterung spezifisch +.sass-cache/ +.cache/ +#*.zip + +# Betriebssystem-Cache +Thumbs.db +ehthumbs.db +Desktop.ini + +# Andere temporäre Dateien +*.bak diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..9c8589f --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,21 @@ +# 📋 Changelog + +Format: +- **[Version]** - Datum + - Kategorie: Beschreibung + +--- + +## [0.0.1] - 2025-04-28 + +### Hinzugefügt +- Erstveröffentlichung von CookieKop (Beta-Version). +- Screenshot-Funktion zum Erfassen von Webseiten mit Cookie-Verstößen. +- Automatische E-Mail-Erstellung an Datenschutzbehörden. +- Auswahl des Bundeslandes in den Optionen. +- Lokale Speicherung aller Daten ohne Serververbindung. +- Optionaler Projektbanner in E-Mails. + +### Geplant +- Erstellung einer PDF-Meldung. +- Teilen von anonymisierten Statistiken (nur mit aktiver Zustimmung). diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a201967 --- /dev/null +++ b/LICENSE @@ -0,0 +1,16 @@ +Creative Commons Lizenzvertrag + +CookieKop (c) 2025 1337gut und Mitwirkende + +Dieses Werk ist lizenziert unter der Creative Commons Namensnennung - Nicht-kommerziell - Weitergabe unter gleichen Bedingungen 4.0 International Lizenz (CC BY-NC-SA 4.0). + +Du darfst: +- Teilen — das Material in jedwedem Format oder Medium vervielfältigen und weiterverbreiten +- Bearbeiten — das Material remixen, verändern und darauf aufbauen + +Unter den folgenden Bedingungen: +- Namensnennung — Du musst angemessene Urheber- und Rechteangaben machen. +- Nicht kommerziell — Du darfst das Material nicht für kommerzielle Zwecke nutzen. +- Weitergabe unter gleichen Bedingungen — Wenn du das Material veränderst, musst du deine Beiträge unter derselben Lizenz verbreiten. + +Lizenzdetails: https://creativecommons.org/licenses/by-nc-sa/4.0/deed.de diff --git a/README.md b/README.md new file mode 100644 index 0000000..c60ea08 --- /dev/null +++ b/README.md @@ -0,0 +1,117 @@ +# CookieKop +![Logo](icons/logo_alt.png) +CookieKop ist ein datenschutzfreundliches Open-Source-Plugin, um Webseiten mit rechtswidrigen Cookie-Bannern schnell und unkompliziert zu melden. + +## 🔹 Funktionen + +- 📸 Screenshot erstellen: Speichere Screenshots von Webseiten mit problematischen Cookie-Bannern. +- 📧 E-Mail-Vorbereitung: Erzeuge eine fertige E-Mail an die zuständige Datenschutzbehörde deines Bundeslandes. +- 🛡️ Projektbanner (optional): Automatischer Hinweis auf CookieKop am Ende der E-Mail. +- 🔒 Datenschutzfreundlich: Alle Daten werden ausschließlich lokal gespeichert. Keine externe Serverkommunikation, kein Tracking. +- 🏛️ Bundeslandauswahl: CookieKop findet automatisch die richtige Behörde für dein Bundesland. + +## 🚧 Geplante Funktionen + +- 📝 Erstellung einer PDF-Datei mit allen Meldungsdaten (aktuell deaktiviert). +- 📈 Teilen von anonymisierten Statistiken (nur mit aktiver Zustimmung, aktuell deaktiviert). + +## ❌ Bekannte Fehler + +- 🦊 Plugin läuft noch nicht in Firefox (Unterstützung ist hier herzlich willkommen!) + +## 📦 Installation lokal + +### Für Chromium-basierte Browser (Chrome, Edge, Brave, Vivaldi) + +1. ZIP herunterladen + - Entweder eine Release-ZIP von GitLab herunterladen (zum Beispiel `cookiekop_v0.0.1_chromium.zip`), + - oder eine aktuelle ZIP selbst erstellen (siehe Abschnitt "ZIP selbst erstellen"). + +2. ZIP entpacken + - Entpacke die ZIP-Datei in einen beliebigen Ordner auf deinem Computer. + +3. Erweiterungen-Seite öffnen + - Gehe zu: `chrome://extensions/` + - Entwicklermodus oben rechts aktivieren. + +4. Entpackte Erweiterung laden + - Klicke auf "Entpackte Erweiterung laden". + - Wähle den entpackten CookieKop-Ordner aus. + +### Für Mozilla Firefox + +1. ZIP herunterladen + - Entweder eine Release-ZIP von GitLab herunterladen (zum Beispiel `cookiekop_v0.0.1_firefox.zip`), + - oder eine aktuelle ZIP selbst erstellen (siehe Abschnitt "ZIP selbst erstellen"). + +2. ZIP entpacken + - Entpacke die ZIP-Datei in einen beliebigen Ordner auf deinem Computer. + +3. Erweiterung temporär laden + - Öffne Firefox und gehe zu: `about:debugging#/runtime/this-firefox` + - Klicke auf "Temporäres Add-on laden". + - Wähle die `manifest.json` Datei im entpackten CookieKop-Ordner aus. + +Hinweis: In Firefox bleibt die Erweiterung nur bis zum nächsten Browserneustart aktiv. + +## 📦 ZIP selbst erstellen (optional) + +Wenn du keine fertige ZIP-Datei verwenden möchtest, kannst du die ZIP-Dateien auch selbst erstellen. + +1. Terminal oder Konsole öffnen (Git Bash, macOS Terminal, Linux Terminal). + +2. In den Projektordner wechseln: +```bash +cd /pfad/zum/cookiekop/ +``` + +3. Master-Skript ausführen: +```bash +./build_all_zips.sh +``` + +Das Skript erstellt automatisch: +- `cookiekop_vX.Y.Z_chromium.zip` +- `cookiekop_vX.Y.Z_firefox.zip` + +Die ZIP-Dateien befinden sich anschließend im Projektordner und können wie oben beschrieben installiert werden. + +## 📜 Lizenz + +Dieses Projekt steht unter der Creative Commons BY-NC-SA 4.0 Lizenz. + +- Namensnennung erforderlich ("CookieKop by 1337gut und Mitwirkende"). +- Keine kommerzielle Nutzung erlaubt. +- Veränderungen und Weiterentwicklungen müssen unter gleicher Lizenz veröffentlicht werden. + +Der vollständige Lizenztext liegt unter `/LICENSE`. + +## 📣 Haftungsausschluss + +Es wurde größtmögliche Sorgfalt auf die Entwicklung und rechtliche Konformität gelegt. +Dennoch erfolgt die Nutzung auf eigene Verantwortung. +Es wird keine Haftung für eventuelle Schäden, Fehlfunktionen oder rechtliche Folgen übernommen. + +## 🙌 Mitmachen + +CookieKop ist ein Community-Projekt. +Fehler gefunden? Ideen für neue Features? +Erstelle gerne Issues oder Merge Requests auf GitLab! + +### 💬 Feedback und Fehler melden + +Dein Feedback hilft uns, CookieKop noch besser zu machen! + +Wenn du einen Fehler gefunden hast oder eine Idee zur Verbesserung hast, +erstelle bitte ein [neues Issue mit unserem Feedback-Formular](https://gitlab.com/1337gut/cookiekop/-/issues/new?issue[template]=Feedback). + +Vielen Dank für deine Unterstützung! 🚀 + + +## 📢 Hinweis + +CookieKop befindet sich derzeit in der Beta-Phase (Version 0.0.1). +Funktionen wie die PDF-Erstellung und das Teilen von Statistiken sind geplant, aber aktuell deaktiviert. + +## Disclaimer: KI-Nutzung +Dieses Plugin wurde unter Mitwirkung von ChatGPT erstellt, da meine Programmierkenntnisse nicht ausreichen, um das alleine zu schaffen. \ No newline at end of file diff --git a/background.js b/background.js new file mode 100644 index 0000000..4ff986e --- /dev/null +++ b/background.js @@ -0,0 +1,3 @@ +chrome.runtime.onInstalled.addListener(() => { + console.log("CookieKop wurde erfolgreich installiert."); +}); \ No newline at end of file diff --git a/behoerden_liste.txt b/behoerden_liste.txt new file mode 100644 index 0000000..9dce708 --- /dev/null +++ b/behoerden_liste.txt @@ -0,0 +1,74 @@ +# Datenschutzbehörden Deutschland (Stand: 2025) +# Quelle: Offizielle Webseiten der Landesdatenschutzbehörden und BfDI (Bundesbeauftragter für den Datenschutz und die Informationsfreiheit) + +Baden-Württemberg +E-Mail: poststelle@lfdi.bwl.de +Website: https://www.baden-wuerttemberg.datenschutz.de/ + +Bayern (Landesamt für Datenschutzaufsicht - Unternehmen/Private) +E-Mail: poststelle@lda.bayern.de +Website: https://www.lda.bayern.de/ + +Bayern (Landesbeauftragter für den Datenschutz - öffentliche Stellen) +E-Mail: poststelle@datenschutz-bayern.de +Website: https://www.datenschutz-bayern.de/ + +Berlin +E-Mail: mailbox@datenschutz-berlin.de +Website: https://www.datenschutz-berlin.de/ + +Brandenburg +E-Mail: poststelle@lda.brandenburg.de +Website: https://www.lda.brandenburg.de/ + +Bremen +E-Mail: office@datenschutz.bremen.de +Website: https://www.datenschutz.bremen.de/ + +Hamburg +E-Mail: mailbox@datenschutz.hamburg.de +Website: https://www.datenschutz-hamburg.de/ + +Hessen +E-Mail: poststelle@datenschutz.hessen.de +Website: https://datenschutz.hessen.de/ + +Mecklenburg-Vorpommern +E-Mail: info@datenschutz-mv.de +Website: https://www.datenschutz-mv.de/ + +Niedersachsen +E-Mail: poststelle@lfd.niedersachsen.de +Website: https://lfd.niedersachsen.de/ + +Nordrhein-Westfalen +E-Mail: poststelle@ldi.nrw.de +Website: https://www.ldi.nrw.de/ + +Rheinland-Pfalz +E-Mail: poststelle@datenschutz.rlp.de +Website: https://www.datenschutz.rlp.de/ + +Saarland +E-Mail: poststelle@datenschutz.saarland.de +Website: https://datenschutz.saarland.de/ + +Sachsen +E-Mail: saechsdsb@slt.sachsen.de +Website: https://www.saechsdsb.de/ + +Sachsen-Anhalt +E-Mail: poststelle@lfd.sachsen-anhalt.de +Website: https://datenschutz.sachsen-anhalt.de/ + +Schleswig-Holstein (ULD) +E-Mail: mail@datenschutzzentrum.de +Website: https://www.datenschutzzentrum.de/ + +Thüringen +E-Mail: poststelle@datenschutz.thueringen.de +Website: https://www.datenschutz.thueringen.de/ + +Bund (BfDI - Telekommunikation, Post, Bundesbehörden) +E-Mail: poststelle@bfdi.bund.de +Website: https://www.bfdi.bund.de/ diff --git a/build_all_zips.sh b/build_all_zips.sh new file mode 100644 index 0000000..0ec90b1 --- /dev/null +++ b/build_all_zips.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +# Exit bei Fehler +set -e + +# --- Prüfen, ob "zip" installiert ist --- +if ! command -v zip &> /dev/null +then + echo "❌ Fehler: 'zip' ist nicht installiert." + echo "Bitte installiere 'zip', z.B. unter Ubuntu: sudo apt install zip" + exit 1 +fi + +# Versionsnummer aus manifest.json auslesen +VERSION=$(grep '"version"' manifest.json | head -n 1 | sed -E 's/.*"([0-9]+\.[0-9]+\.[0-9]+)".*/\1/') +echo "Gefundene Version: $VERSION" + +# Variablen +CHROMIUM_ZIPNAME="cookiekop_v${VERSION}_chromium.zip" +FIREFOX_ZIPNAME="cookiekop_v${VERSION}_firefox.zip" + +# Temp-Ordner vorbereiten +TEMPDIR_CHROMIUM="temp_cookiekop_chromium" +TEMPDIR_FIREFOX="temp_cookiekop_firefox" + +rm -f $CHROMIUM_ZIPNAME $FIREFOX_ZIPNAME +rm -rf $TEMPDIR_CHROMIUM $TEMPDIR_FIREFOX + +mkdir -p $TEMPDIR_CHROMIUM +mkdir -p $TEMPDIR_FIREFOX + +# --- Chromium Build --- +cp manifest.json popup.html popup.js options.html options.js background.js README.md LICENSE "$TEMPDIR_CHROMIUM/" +cp -r util "$TEMPDIR_CHROMIUM/" +cp -r data "$TEMPDIR_CHROMIUM/" +cp -r styles "$TEMPDIR_CHROMIUM/" +cp -r icons "$TEMPDIR_CHROMIUM/" +cp -r libs "$TEMPDIR_CHROMIUM/" + +cd $TEMPDIR_CHROMIUM +zip -r ../$CHROMIUM_ZIPNAME * +cd .. + +# --- Firefox Build --- +cp popup.html popup.js options.html options.js background.js README.md LICENSE "$TEMPDIR_FIREFOX/" +cp -r util "$TEMPDIR_FIREFOX/" +cp -r data "$TEMPDIR_FIREFOX/" +cp -r styles "$TEMPDIR_FIREFOX/" +cp -r icons "$TEMPDIR_FIREFOX/" +cp -r libs "$TEMPDIR_FIREFOX/" + +# Temporäres Firefox-spezifisches manifest.json erstellen +cat << EOF > $TEMPDIR_FIREFOX/manifest.json +{ + "manifest_version": 2, + "name": "CookieKop", + "version": "${VERSION}", + "description": "Melde rechtswidrige Cookie-Banner schnell und einfach.", + "background": { + "scripts": ["background.js"], + "persistent": false + }, + "browser_action": { + "default_popup": "popup.html", + "default_icon": "icons/icon48.png" + }, + "permissions": [ + "storage", + "activeTab", + "tabs", + "downloads" + ], + "options_ui": { + "page": "options.html", + "open_in_tab": true + }, + "icons": { + "48": "icons/icon48.png", + "128": "icons/icon128.png" + } +} +EOF + +# ZIP-Datei für Firefox erstellen +cd $TEMPDIR_FIREFOX +zip -r ../$FIREFOX_ZIPNAME * +cd .. + +# Temp-Ordner löschen +rm -rf $TEMPDIR_CHROMIUM $TEMPDIR_FIREFOX + +# Erfolgsmeldung +echo "✅ Erfolgreich erstellt:" +echo " → $CHROMIUM_ZIPNAME" +echo " → $FIREFOX_ZIPNAME" \ No newline at end of file diff --git a/build_chromium_zip.sh b/build_chromium_zip.sh new file mode 100644 index 0000000..214e0c7 --- /dev/null +++ b/build_chromium_zip.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# Setze Variablen +ZIPNAME="cookiekop_chromium.zip" +TEMPDIR="temp_cookiekop_chromium" + +# Alte ZIP löschen +rm -f $ZIPNAME + +# Temp-Ordner erstellen +mkdir -p $TEMPDIR + +# Wichtige Dateien kopieren +cp manifest.json popup.html popup.js options.html options.js background.js README.md LICENSE $TEMPDIR/ +cp -r util $TEMPDIR/ +cp -r data $TEMPDIR/ +cp -r styles $TEMPDIR/ +cp -r icons $TEMPDIR/ +cp -r libs $TEMPDIR/ + +# In Temp-Ordner wechseln und zippen +cd $TEMPDIR +zip -r ../$ZIPNAME * +cd .. + +# Temp-Ordner löschen +rm -rf $TEMPDIR + +echo "Chromium-ZIP erfolgreich erstellt: $ZIPNAME" diff --git a/build_firefox_zip.sh b/build_firefox_zip.sh new file mode 100644 index 0000000..f07cef5 --- /dev/null +++ b/build_firefox_zip.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# Setze Variablen +ZIPNAME="cookiekop_firefox.zip" +TEMPDIR="temp_cookiekop" + +# Alte ZIP löschen +rm -f $ZIPNAME + +# Temp-Ordner erstellen +mkdir -p $TEMPDIR + +# Wichtige Dateien kopieren +cp manifest.json popup.html popup.js options.html options.js background.js README.md LICENSE $TEMPDIR/ +cp -r util $TEMPDIR/ +cp -r data $TEMPDIR/ +cp -r styles $TEMPDIR/ +cp -r icons $TEMPDIR/ +cp -r libs $TEMPDIR/ + +# In Temp-Ordner wechseln und zippen +cd $TEMPDIR +zip -r ../$ZIPNAME * +cd .. + +# Temp-Ordner löschen +rm -rf $TEMPDIR + +echo "Firefox-ZIP erfolgreich erstellt: $ZIPNAME" diff --git a/cookiekop_v0.0.1_chromium.zip b/cookiekop_v0.0.1_chromium.zip new file mode 100644 index 0000000..a26b9e7 Binary files /dev/null and b/cookiekop_v0.0.1_chromium.zip differ diff --git a/cookiekop_v0.0.1_firefox.zip b/cookiekop_v0.0.1_firefox.zip new file mode 100644 index 0000000..18635c7 Binary files /dev/null and b/cookiekop_v0.0.1_firefox.zip differ diff --git a/data/datenschutzbehoerden.json b/data/datenschutzbehoerden.json new file mode 100644 index 0000000..1539fdb --- /dev/null +++ b/data/datenschutzbehoerden.json @@ -0,0 +1,104 @@ +[ + { + "bundesland": "Baden-Württemberg", + "behoerde": "Der Landesbeauftragte für den Datenschutz und die Informationsfreiheit", + "email": "poststelle@lfdi.bwl.de", + "website": "https://www.baden-wuerttemberg.datenschutz.de/" + }, + { + "bundesland": "Bayern", + "behoerde": "Bayerisches Landesamt für Datenschutzaufsicht", + "email": "poststelle@lda.bayern.de", + "website": "https://www.lda.bayern.de/" + }, + { + "bundesland": "Berlin", + "behoerde": "Berliner Beauftragte für Datenschutz und Informationsfreiheit", + "email": "mailbox@datenschutz-berlin.de", + "website": "https://www.datenschutz-berlin.de/" + }, + { + "bundesland": "Brandenburg", + "behoerde": "Die Landesbeauftragte für den Datenschutz und für das Recht auf Akteneinsicht Brandenburg", + "email": "poststelle@lda.brandenburg.de", + "website": "https://www.lda.brandenburg.de/" + }, + { + "bundesland": "Bremen", + "behoerde": "Der Landesbeauftragte für Datenschutz und Informationsfreiheit", + "email": "office@datenschutz.bremen.de", + "website": "https://www.datenschutz.bremen.de/" + }, + { + "bundesland": "Hamburg", + "behoerde": "Der Hamburgische Beauftragte für Datenschutz und Informationsfreiheit", + "email": "mailbox@datenschutz.hamburg.de", + "website": "https://www.datenschutz-hamburg.de/" + }, + { + "bundesland": "Hessen", + "behoerde": "Hess. Beauftragter für Datenschutz und Informationsfreiheit", + "email": "poststelle@datenschutz.hessen.de", + "website": "https://datenschutz.hessen.de/" + }, + { + "bundesland": "Mecklenburg-Vorpommern", + "behoerde": "Der Landesbeauftragte für Datenschutz und Informationsfreiheit Mecklenburg-Vorpommern", + "email": "info@datenschutz-mv.de", + "website": "https://www.datenschutz-mv.de/" + }, + { + "bundesland": "Niedersachsen", + "behoerde": "Der Landesbeauftragte für den Datenschutz Niedersachsen", + "email": "poststelle@lfd.niedersachsen.de", + "website": "https://lfd.niedersachsen.de/" + }, + { + "bundesland": "Nordrhein-Westfalen", + "behoerde": "Landesbeauftragte für Datenschutz und Informationsfreiheit in NRW", + "email": "poststelle@ldi.nrw.de", + "website": "https://www.ldi.nrw.de/" + }, + { + "bundesland": "Rheinland-Pfalz", + "behoerde": "Der Landesbeauftragte für den Datenschutz und die Informationsfreiheit Rheinland-Pfalz", + "email": "poststelle@datenschutz.rlp.de", + "website": "https://www.datenschutz.rlp.de/" + }, + { + "bundesland": "Saarland", + "behoerde": "Die Landesbeauftragte für Datenschutz und Informationsfreiheit", + "email": "poststelle@datenschutz.saarland.de", + "website": "https://datenschutz.saarland.de/" + }, + { + "bundesland": "Sachsen", + "behoerde": "Sächsische Datenschutz- und Transparenzbeauftragte", + "email": "saechsdsb@slt.sachsen.de", + "website": "https://www.saechsdsb.de/" + }, + { + "bundesland": "Sachsen-Anhalt", + "behoerde": "Landesbeauftragte für den Datenschutz", + "email": "poststelle@lfd.sachsen-anhalt.de", + "website": "https://datenschutz.sachsen-anhalt.de/" + }, + { + "bundesland": "Schleswig-Holstein (ULD)", + "behoerde": "Unabhängiges Landeszentrum für Datenschutz Schleswig-Holstein", + "email": "mail@datenschutzzentrum.de", + "website": "https://www.datenschutzzentrum.de/" + }, + { + "bundesland": "Thüringen", + "behoerde": "Thüringer Landesbeauftragter für den Datenschutz und die Informationsfreiheit", + "email": "poststelle@datenschutz.thueringen.de", + "website": "https://www.datenschutz.thueringen.de/" + }, + { + "bundesland": "Bund", + "behoerde": "BfDI - Bundesbeauftragter für den Datenschutz und die Informationsfreiheit", + "email": "poststelle@bfdi.bund.de", + "website": "https://www.bfdi.bund.de/" + } +] \ No newline at end of file diff --git a/icons/icon128.png b/icons/icon128.png new file mode 100644 index 0000000..4dc753d Binary files /dev/null and b/icons/icon128.png differ diff --git a/icons/icon16.png b/icons/icon16.png new file mode 100644 index 0000000..aa350f0 Binary files /dev/null and b/icons/icon16.png differ diff --git a/icons/icon32.png b/icons/icon32.png new file mode 100644 index 0000000..a308eab Binary files /dev/null and b/icons/icon32.png differ diff --git a/icons/icon48.png b/icons/icon48.png new file mode 100644 index 0000000..cd13318 Binary files /dev/null and b/icons/icon48.png differ diff --git a/icons/logo.png b/icons/logo.png new file mode 100644 index 0000000..910635c Binary files /dev/null and b/icons/logo.png differ diff --git a/icons/logo_alt.png b/icons/logo_alt.png new file mode 100644 index 0000000..72515e4 Binary files /dev/null and b/icons/logo_alt.png differ diff --git a/libs/jspdf.umd.min.js b/libs/jspdf.umd.min.js new file mode 100644 index 0000000..39fc02e --- /dev/null +++ b/libs/jspdf.umd.min.js @@ -0,0 +1,398 @@ +/** @license + * + * jsPDF - PDF Document creation from JavaScript + * Version 3.0.1 Built on 2025-03-17T14:19:36.870Z + * CommitID 00000000 + * + * Copyright (c) 2010-2021 James Hall , https://github.com/MrRio/jsPDF + * 2015-2021 yWorks GmbH, http://www.yworks.com + * 2015-2021 Lukas Holländer , https://github.com/HackbrettXXX + * 2016-2018 Aras Abbasi + * 2010 Aaron Spike, https://github.com/acspike + * 2012 Willow Systems Corporation, https://github.com/willowsystems + * 2012 Pablo Hess, https://github.com/pablohess + * 2012 Florian Jenett, https://github.com/fjenett + * 2013 Warren Weckesser, https://github.com/warrenweckesser + * 2013 Youssef Beddad, https://github.com/lifof + * 2013 Lee Driscoll, https://github.com/lsdriscoll + * 2013 Stefan Slonevskiy, https://github.com/stefslon + * 2013 Jeremy Morel, https://github.com/jmorel + * 2013 Christoph Hartmann, https://github.com/chris-rock + * 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria + * 2014 James Makes, https://github.com/dollaruw + * 2014 Diego Casorran, https://github.com/diegocr + * 2014 Steven Spungin, https://github.com/Flamenco + * 2014 Kenneth Glassey, https://github.com/Gavvers + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributor(s): + * siefkenj, ahwolf, rickygu, Midnith, saintclair, eaparango, + * kim3er, mfo, alnorth, Flamenco + */ + +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).jspdf={})}(this,(function(t){"use strict";function e(t){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}var r=function(){return"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this}();function n(){r.console&&"function"==typeof r.console.log&&r.console.log.apply(r.console,arguments)}var i={log:n,warn:function(t){r.console&&("function"==typeof r.console.warn?r.console.warn.apply(r.console,arguments):n.call(null,arguments))},error:function(t){r.console&&("function"==typeof r.console.error?r.console.error.apply(r.console,arguments):n(t))}};function a(t,e,r){var n=new XMLHttpRequest;n.open("GET",t),n.responseType="blob",n.onload=function(){l(n.response,e,r)},n.onerror=function(){i.error("could not download file")},n.send()}function o(t){var e=new XMLHttpRequest;e.open("HEAD",t,!1);try{e.send()}catch(t){}return e.status>=200&&e.status<=299}function s(t){try{t.dispatchEvent(new MouseEvent("click"))}catch(r){var e=document.createEvent("MouseEvents");e.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),t.dispatchEvent(e)}}var c,u,l=r.saveAs||("object"!==("undefined"==typeof window?"undefined":e(window))||window!==r?function(){}:"undefined"!=typeof HTMLAnchorElement&&"download"in HTMLAnchorElement.prototype?function(t,e,n){var i=r.URL||r.webkitURL,c=document.createElement("a");e=e||t.name||"download",c.download=e,c.rel="noopener","string"==typeof t?(c.href=t,c.origin!==location.origin?o(c.href)?a(t,e,n):s(c,c.target="_blank"):s(c)):(c.href=i.createObjectURL(t),setTimeout((function(){i.revokeObjectURL(c.href)}),4e4),setTimeout((function(){s(c)}),0))}:"msSaveOrOpenBlob"in navigator?function(t,r,n){if(r=r||t.name||"download","string"==typeof t)if(o(t))a(t,r,n);else{var c=document.createElement("a");c.href=t,c.target="_blank",setTimeout((function(){s(c)}))}else navigator.msSaveOrOpenBlob(function(t,r){return void 0===r?r={autoBom:!1}:"object"!==e(r)&&(i.warn("Deprecated: Expected third argument to be a object"),r={autoBom:!r}),r.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(t.type)?new Blob([String.fromCharCode(65279),t],{type:t.type}):t}(t,n),r)}:function(t,n,i,o){if((o=o||open("","_blank"))&&(o.document.title=o.document.body.innerText="downloading..."),"string"==typeof t)return a(t,n,i);var s="application/octet-stream"===t.type,c=/constructor/i.test(r.HTMLElement)||r.safari,u=/CriOS\/[\d]+/.test(navigator.userAgent);if((u||s&&c)&&"object"===("undefined"==typeof FileReader?"undefined":e(FileReader))){var l=new FileReader;l.onloadend=function(){var t=l.result;t=u?t:t.replace(/^data:[^;]*;/,"data:attachment/file;"),o?o.location.href=t:location=t,o=null},l.readAsDataURL(t)}else{var h=r.URL||r.webkitURL,f=h.createObjectURL(t);o?o.location=f:location.href=f,o=null,setTimeout((function(){h.revokeObjectURL(f)}),4e4)}}); +/** + * A class to parse color values + * @author Stoyan Stefanov + * {@link http://www.phpied.com/rgb-color-parser-in-javascript/} + * @license Use it if you like it + */function h(t){var e;t=t||"",this.ok=!1,"#"==t.charAt(0)&&(t=t.substr(1,6));t={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"00ffff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000000",blanchedalmond:"ffebcd",blue:"0000ff",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"00ffff",darkblue:"00008b",darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3",deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dodgerblue:"1e90ff",feldspar:"d19275",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"ff00ff",gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgrey:"d3d3d3",lightgreen:"90ee90",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa",lightslateblue:"8470ff",lightslategray:"778899",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"00ff00",limegreen:"32cd32",linen:"faf0e6",magenta:"ff00ff",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370d8",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc",mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080",oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"d87093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",red:"ff0000",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd",slategray:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",violetred:"d02090",wheat:"f5deb3",white:"ffffff",whitesmoke:"f5f5f5",yellow:"ffff00",yellowgreen:"9acd32"}[t=(t=t.replace(/ /g,"")).toLowerCase()]||t;for(var r=[{re:/^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,example:["rgb(123, 234, 45)","rgb(255,234,245)"],process:function(t){return[parseInt(t[1]),parseInt(t[2]),parseInt(t[3])]}},{re:/^(\w{2})(\w{2})(\w{2})$/,example:["#00ff00","336699"],process:function(t){return[parseInt(t[1],16),parseInt(t[2],16),parseInt(t[3],16)]}},{re:/^(\w{1})(\w{1})(\w{1})$/,example:["#fb0","f0f"],process:function(t){return[parseInt(t[1]+t[1],16),parseInt(t[2]+t[2],16),parseInt(t[3]+t[3],16)]}}],n=0;n255?255:this.r,this.g=this.g<0||isNaN(this.g)?0:this.g>255?255:this.g,this.b=this.b<0||isNaN(this.b)?0:this.b>255?255:this.b,this.toRGB=function(){return"rgb("+this.r+", "+this.g+", "+this.b+")"},this.toHex=function(){var t=this.r.toString(16),e=this.g.toString(16),r=this.b.toString(16);return 1==t.length&&(t="0"+t),1==e.length&&(e="0"+e),1==r.length&&(r="0"+r),"#"+t+e+r}} +/** + * @license + * Joseph Myers does not specify a particular license for his work. + * + * Author: Joseph Myers + * Accessed from: http://www.myersdaily.org/joseph/javascript/md5.js + * + * Modified by: Owen Leong + */ +function f(t,e){var r=t[0],n=t[1],i=t[2],a=t[3];r=p(r,n,i,a,e[0],7,-680876936),a=p(a,r,n,i,e[1],12,-389564586),i=p(i,a,r,n,e[2],17,606105819),n=p(n,i,a,r,e[3],22,-1044525330),r=p(r,n,i,a,e[4],7,-176418897),a=p(a,r,n,i,e[5],12,1200080426),i=p(i,a,r,n,e[6],17,-1473231341),n=p(n,i,a,r,e[7],22,-45705983),r=p(r,n,i,a,e[8],7,1770035416),a=p(a,r,n,i,e[9],12,-1958414417),i=p(i,a,r,n,e[10],17,-42063),n=p(n,i,a,r,e[11],22,-1990404162),r=p(r,n,i,a,e[12],7,1804603682),a=p(a,r,n,i,e[13],12,-40341101),i=p(i,a,r,n,e[14],17,-1502002290),r=g(r,n=p(n,i,a,r,e[15],22,1236535329),i,a,e[1],5,-165796510),a=g(a,r,n,i,e[6],9,-1069501632),i=g(i,a,r,n,e[11],14,643717713),n=g(n,i,a,r,e[0],20,-373897302),r=g(r,n,i,a,e[5],5,-701558691),a=g(a,r,n,i,e[10],9,38016083),i=g(i,a,r,n,e[15],14,-660478335),n=g(n,i,a,r,e[4],20,-405537848),r=g(r,n,i,a,e[9],5,568446438),a=g(a,r,n,i,e[14],9,-1019803690),i=g(i,a,r,n,e[3],14,-187363961),n=g(n,i,a,r,e[8],20,1163531501),r=g(r,n,i,a,e[13],5,-1444681467),a=g(a,r,n,i,e[2],9,-51403784),i=g(i,a,r,n,e[7],14,1735328473),r=m(r,n=g(n,i,a,r,e[12],20,-1926607734),i,a,e[5],4,-378558),a=m(a,r,n,i,e[8],11,-2022574463),i=m(i,a,r,n,e[11],16,1839030562),n=m(n,i,a,r,e[14],23,-35309556),r=m(r,n,i,a,e[1],4,-1530992060),a=m(a,r,n,i,e[4],11,1272893353),i=m(i,a,r,n,e[7],16,-155497632),n=m(n,i,a,r,e[10],23,-1094730640),r=m(r,n,i,a,e[13],4,681279174),a=m(a,r,n,i,e[0],11,-358537222),i=m(i,a,r,n,e[3],16,-722521979),n=m(n,i,a,r,e[6],23,76029189),r=m(r,n,i,a,e[9],4,-640364487),a=m(a,r,n,i,e[12],11,-421815835),i=m(i,a,r,n,e[15],16,530742520),r=v(r,n=m(n,i,a,r,e[2],23,-995338651),i,a,e[0],6,-198630844),a=v(a,r,n,i,e[7],10,1126891415),i=v(i,a,r,n,e[14],15,-1416354905),n=v(n,i,a,r,e[5],21,-57434055),r=v(r,n,i,a,e[12],6,1700485571),a=v(a,r,n,i,e[3],10,-1894986606),i=v(i,a,r,n,e[10],15,-1051523),n=v(n,i,a,r,e[1],21,-2054922799),r=v(r,n,i,a,e[8],6,1873313359),a=v(a,r,n,i,e[15],10,-30611744),i=v(i,a,r,n,e[6],15,-1560198380),n=v(n,i,a,r,e[13],21,1309151649),r=v(r,n,i,a,e[4],6,-145523070),a=v(a,r,n,i,e[11],10,-1120210379),i=v(i,a,r,n,e[2],15,718787259),n=v(n,i,a,r,e[9],21,-343485551),t[0]=S(r,t[0]),t[1]=S(n,t[1]),t[2]=S(i,t[2]),t[3]=S(a,t[3])}function d(t,e,r,n,i,a){return e=S(S(e,t),S(n,a)),S(e<>>32-i,r)}function p(t,e,r,n,i,a,o){return d(e&r|~e&n,t,e,i,a,o)}function g(t,e,r,n,i,a,o){return d(e&n|r&~n,t,e,i,a,o)}function m(t,e,r,n,i,a,o){return d(e^r^n,t,e,i,a,o)}function v(t,e,r,n,i,a,o){return d(r^(e|~n),t,e,i,a,o)}function b(t){var e,r=t.length,n=[1732584193,-271733879,-1732584194,271733878];for(e=64;e<=t.length;e+=64)f(n,y(t.substring(e-64,e)));t=t.substring(e-64);var i=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];for(e=0;e>2]|=t.charCodeAt(e)<<(e%4<<3);if(i[e>>2]|=128<<(e%4<<3),e>55)for(f(n,i),e=0;e<16;e++)i[e]=0;return i[14]=8*r,f(n,i),n}function y(t){var e,r=[];for(e=0;e<64;e+=4)r[e>>2]=t.charCodeAt(e)+(t.charCodeAt(e+1)<<8)+(t.charCodeAt(e+2)<<16)+(t.charCodeAt(e+3)<<24);return r}c=r.atob.bind(r),u=r.btoa.bind(r);var w="0123456789abcdef".split("");function N(t){for(var e="",r=0;r<4;r++)e+=w[t>>8*r+4&15]+w[t>>8*r&15];return e}function L(t){return String.fromCharCode((255&t)>>0,(65280&t)>>8,(16711680&t)>>16,(4278190080&t)>>24)}function A(t){return function(t){return t.map(L).join("")}(b(t))}var x="5d41402abc4b2a76b9719d911017c592"!=function(t){for(var e=0;e>16)+(e>>16)+(r>>16)<<16|65535&r}return t+e&4294967295} +/** + * @license + * FPDF is released under a permissive license: there is no usage restriction. + * You may embed it freely in your application (commercial or not), with or + * without modifications. + * + * Reference: http://www.fpdf.org/en/script/script37.php + */function _(t,e){var r,n,i,a;if(t!==r){for(var o=(i=t,a=1+(256/t.length>>0),new Array(a+1).join(i)),s=[],c=0;c<256;c++)s[c]=c;var u=0;for(c=0;c<256;c++){var l=s[c];u=(u+l+o.charCodeAt(c))%256,s[c]=s[u],s[u]=l}r=t,n=s}else s=n;var h=e.length,f=0,d=0,p="";for(c=0;c€/\f©þdSiz";var a=(e+this.padding).substr(0,32),o=(r+this.padding).substr(0,32);this.O=this.processOwnerPassword(a,o),this.P=-(1+(255^i)),this.encryptionKey=A(a+this.O+this.lsbFirstWord(this.P)+this.hexToBytes(n)).substr(0,5),this.U=_(this.encryptionKey,this.padding)}function F(t){if(/[^\u0000-\u00ff]/.test(t))throw new Error("Invalid PDF Name Object: "+t+", Only accept ASCII characters.");for(var e="",r=t.length,n=0;n126)e+="#"+("0"+i.toString(16)).slice(-2);else e+=t[n]}return e}function I(t){if("object"!==e(t))throw new Error("Invalid Context passed to initialize PubSub (jsPDF-module)");var n={};this.subscribe=function(t,e,r){if(r=r||!1,"string"!=typeof t||"function"!=typeof e||"boolean"!=typeof r)throw new Error("Invalid arguments passed to PubSub.subscribe (jsPDF-module)");n.hasOwnProperty(t)||(n[t]={});var i=Math.random().toString(35);return n[t][i]=[e,!!r],i},this.unsubscribe=function(t){for(var e in n)if(n[e][t])return delete n[e][t],0===Object.keys(n[e]).length&&delete n[e],!0;return!1},this.publish=function(e){if(n.hasOwnProperty(e)){var a=Array.prototype.slice.call(arguments,1),o=[];for(var s in n[e]){var c=n[e][s];try{c[0].apply(t,a)}catch(t){r.console&&i.error("jsPDF PubSub Error",t.message,t)}c[1]&&o.push(s)}o.length&&o.forEach(this.unsubscribe)}},this.getTopics=function(){return n}}function C(t){if(!(this instanceof C))return new C(t);var e="opacity,stroke-opacity".split(",");for(var r in t)t.hasOwnProperty(r)&&e.indexOf(r)>=0&&(this[r]=t[r]);this.id="",this.objectNumber=-1}function j(t,e){this.gState=t,this.matrix=e,this.id="",this.objectNumber=-1}function O(t,e,r,n,i){if(!(this instanceof O))return new O(t,e,r,n,i);this.type="axial"===t?2:3,this.coords=e,this.colors=r,j.call(this,n,i)}function B(t,e,r,n,i){if(!(this instanceof B))return new B(t,e,r,n,i);this.boundingBox=t,this.xStep=e,this.yStep=r,this.stream="",this.cloneIndex=0,j.call(this,n,i)}function M(t){var n,a="string"==typeof arguments[0]?arguments[0]:"p",o=arguments[1],s=arguments[2],c=arguments[3],f=[],d=1,p=16,g="S",m=null;"object"===e(t=t||{})&&(a=t.orientation,o=t.unit||o,s=t.format||s,c=t.compress||t.compressPdf||c,null!==(m=t.encryption||null)&&(m.userPassword=m.userPassword||"",m.ownerPassword=m.ownerPassword||"",m.userPermissions=m.userPermissions||[]),d="number"==typeof t.userUnit?Math.abs(t.userUnit):1,void 0!==t.precision&&(n=t.precision),void 0!==t.floatPrecision&&(p=t.floatPrecision),g=t.defaultPathOperation||"S"),f=t.filters||(!0===c?["FlateEncode"]:f),o=o||"mm",a=(""+(a||"P")).toLowerCase();var v=t.putOnlyUsedFonts||!1,b={},y={internal:{},__private__:{}};y.__private__.PubSub=I;var w="1.3",N=y.__private__.getPdfVersion=function(){return w};y.__private__.setPdfVersion=function(t){w=t};var L={a0:[2383.94,3370.39],a1:[1683.78,2383.94],a2:[1190.55,1683.78],a3:[841.89,1190.55],a4:[595.28,841.89],a5:[419.53,595.28],a6:[297.64,419.53],a7:[209.76,297.64],a8:[147.4,209.76],a9:[104.88,147.4],a10:[73.7,104.88],b0:[2834.65,4008.19],b1:[2004.09,2834.65],b2:[1417.32,2004.09],b3:[1000.63,1417.32],b4:[708.66,1000.63],b5:[498.9,708.66],b6:[354.33,498.9],b7:[249.45,354.33],b8:[175.75,249.45],b9:[124.72,175.75],b10:[87.87,124.72],c0:[2599.37,3676.54],c1:[1836.85,2599.37],c2:[1298.27,1836.85],c3:[918.43,1298.27],c4:[649.13,918.43],c5:[459.21,649.13],c6:[323.15,459.21],c7:[229.61,323.15],c8:[161.57,229.61],c9:[113.39,161.57],c10:[79.37,113.39],dl:[311.81,623.62],letter:[612,792],"government-letter":[576,756],legal:[612,1008],"junior-legal":[576,360],ledger:[1224,792],tabloid:[792,1224],"credit-card":[153,243]};y.__private__.getPageFormats=function(){return L};var A=y.__private__.getPageFormat=function(t){return L[t]};s=s||"a4";var x={COMPAT:"compat",ADVANCED:"advanced"},S=x.COMPAT;function _(){this.saveGraphicsState(),ht(new Vt(_t,0,0,-_t,0,Rr()*_t).toString()+" cm"),this.setFontSize(this.getFontSize()/_t),g="n",S=x.ADVANCED}function P(){this.restoreGraphicsState(),g="S",S=x.COMPAT}var j=y.__private__.combineFontStyleAndFontWeight=function(t,e){if("bold"==t&&"normal"==e||"bold"==t&&400==e||"normal"==t&&"italic"==e||"bold"==t&&"italic"==e)throw new Error("Invalid Combination of fontweight and fontstyle");return e&&(t=400==e||"normal"===e?"italic"===t?"italic":"normal":700!=e&&"bold"!==e||"normal"!==t?(700==e?"bold":e)+""+t:"bold"),t};y.advancedAPI=function(t){var e=S===x.COMPAT;return e&&_.call(this),"function"!=typeof t||(t(this),e&&P.call(this)),this},y.compatAPI=function(t){var e=S===x.ADVANCED;return e&&P.call(this),"function"!=typeof t||(t(this),e&&_.call(this)),this},y.isAdvancedAPI=function(){return S===x.ADVANCED};var E,q=function(t){if(S!==x.ADVANCED)throw new Error(t+" is only available in 'advanced' API mode. You need to call advancedAPI() first.")},D=y.roundToPrecision=y.__private__.roundToPrecision=function(t,e){var r=n||e;if(isNaN(t)||isNaN(r))throw new Error("Invalid argument passed to jsPDF.roundToPrecision");return t.toFixed(r).replace(/0+$/,"")};E=y.hpf=y.__private__.hpf="number"==typeof p?function(t){if(isNaN(t))throw new Error("Invalid argument passed to jsPDF.hpf");return D(t,p)}:"smart"===p?function(t){if(isNaN(t))throw new Error("Invalid argument passed to jsPDF.hpf");return D(t,t>-1&&t<1?16:5)}:function(t){if(isNaN(t))throw new Error("Invalid argument passed to jsPDF.hpf");return D(t,16)};var R=y.f2=y.__private__.f2=function(t){if(isNaN(t))throw new Error("Invalid argument passed to jsPDF.f2");return D(t,2)},T=y.__private__.f3=function(t){if(isNaN(t))throw new Error("Invalid argument passed to jsPDF.f3");return D(t,3)},U=y.scale=y.__private__.scale=function(t){if(isNaN(t))throw new Error("Invalid argument passed to jsPDF.scale");return S===x.COMPAT?t*_t:S===x.ADVANCED?t:void 0},z=function(t){return S===x.COMPAT?Rr()-t:S===x.ADVANCED?t:void 0},H=function(t){return U(z(t))};y.__private__.setPrecision=y.setPrecision=function(t){"number"==typeof parseInt(t,10)&&(n=parseInt(t,10))};var W,V="00000000000000000000000000000000",G=y.__private__.getFileId=function(){return V},Y=y.__private__.setFileId=function(t){return V=void 0!==t&&/^[a-fA-F0-9]{32}$/.test(t)?t.toUpperCase():V.split("").map((function(){return"ABCDEF0123456789".charAt(Math.floor(16*Math.random()))})).join(""),null!==m&&(Ye=new k(m.userPermissions,m.userPassword,m.ownerPassword,V)),V};y.setFileId=function(t){return Y(t),this},y.getFileId=function(){return G()};var J=y.__private__.convertDateToPDFDate=function(t){var e=t.getTimezoneOffset(),r=e<0?"+":"-",n=Math.floor(Math.abs(e/60)),i=Math.abs(e%60),a=[r,Q(n),"'",Q(i),"'"].join("");return["D:",t.getFullYear(),Q(t.getMonth()+1),Q(t.getDate()),Q(t.getHours()),Q(t.getMinutes()),Q(t.getSeconds()),a].join("")},X=y.__private__.convertPDFDateToDate=function(t){var e=parseInt(t.substr(2,4),10),r=parseInt(t.substr(6,2),10)-1,n=parseInt(t.substr(8,2),10),i=parseInt(t.substr(10,2),10),a=parseInt(t.substr(12,2),10),o=parseInt(t.substr(14,2),10);return new Date(e,r,n,i,a,o,0)},K=y.__private__.setCreationDate=function(t){var e;if(void 0===t&&(t=new Date),t instanceof Date)e=J(t);else{if(!/^D:(20[0-2][0-9]|203[0-7]|19[7-9][0-9])(0[0-9]|1[0-2])([0-2][0-9]|3[0-1])(0[0-9]|1[0-9]|2[0-3])(0[0-9]|[1-5][0-9])(0[0-9]|[1-5][0-9])(\+0[0-9]|\+1[0-4]|-0[0-9]|-1[0-1])'(0[0-9]|[1-5][0-9])'?$/.test(t))throw new Error("Invalid argument passed to jsPDF.setCreationDate");e=t}return W=e},Z=y.__private__.getCreationDate=function(t){var e=W;return"jsDate"===t&&(e=X(W)),e};y.setCreationDate=function(t){return K(t),this},y.getCreationDate=function(t){return Z(t)};var $,Q=y.__private__.padd2=function(t){return("0"+parseInt(t)).slice(-2)},tt=y.__private__.padd2Hex=function(t){return("00"+(t=t.toString())).substr(t.length)},et=0,rt=[],nt=[],it=0,at=[],ot=[],st=!1,ct=nt,ut=function(){et=0,it=0,nt=[],rt=[],at=[],Qt=Kt(),te=Kt()};y.__private__.setCustomOutputDestination=function(t){st=!0,ct=t};var lt=function(t){st||(ct=t)};y.__private__.resetCustomOutputDestination=function(){st=!1,ct=nt};var ht=y.__private__.out=function(t){return t=t.toString(),it+=t.length+1,ct.push(t),ct},ft=y.__private__.write=function(t){return ht(1===arguments.length?t.toString():Array.prototype.join.call(arguments," "))},dt=y.__private__.getArrayBuffer=function(t){for(var e=t.length,r=new ArrayBuffer(e),n=new Uint8Array(r);e--;)n[e]=t.charCodeAt(e);return r},pt=[["Helvetica","helvetica","normal","WinAnsiEncoding"],["Helvetica-Bold","helvetica","bold","WinAnsiEncoding"],["Helvetica-Oblique","helvetica","italic","WinAnsiEncoding"],["Helvetica-BoldOblique","helvetica","bolditalic","WinAnsiEncoding"],["Courier","courier","normal","WinAnsiEncoding"],["Courier-Bold","courier","bold","WinAnsiEncoding"],["Courier-Oblique","courier","italic","WinAnsiEncoding"],["Courier-BoldOblique","courier","bolditalic","WinAnsiEncoding"],["Times-Roman","times","normal","WinAnsiEncoding"],["Times-Bold","times","bold","WinAnsiEncoding"],["Times-Italic","times","italic","WinAnsiEncoding"],["Times-BoldItalic","times","bolditalic","WinAnsiEncoding"],["ZapfDingbats","zapfdingbats","normal",null],["Symbol","symbol","normal",null]];y.__private__.getStandardFonts=function(){return pt};var gt=t.fontSize||16;y.__private__.setFontSize=y.setFontSize=function(t){return gt=S===x.ADVANCED?t/_t:t,this};var mt,vt=y.__private__.getFontSize=y.getFontSize=function(){return S===x.COMPAT?gt:gt*_t},bt=t.R2L||!1;y.__private__.setR2L=y.setR2L=function(t){return bt=t,this},y.__private__.getR2L=y.getR2L=function(){return bt};var yt,wt=y.__private__.setZoomMode=function(t){var e=[void 0,null,"fullwidth","fullheight","fullpage","original"];if(/^(?:\d+\.\d*|\d*\.\d+|\d+)%$/.test(t))mt=t;else if(isNaN(t)){if(-1===e.indexOf(t))throw new Error('zoom must be Integer (e.g. 2), a percentage Value (e.g. 300%) or fullwidth, fullheight, fullpage, original. "'+t+'" is not recognized.');mt=t}else mt=parseInt(t,10)};y.__private__.getZoomMode=function(){return mt};var Nt,Lt=y.__private__.setPageMode=function(t){if(-1==[void 0,null,"UseNone","UseOutlines","UseThumbs","FullScreen"].indexOf(t))throw new Error('Page mode must be one of UseNone, UseOutlines, UseThumbs, or FullScreen. "'+t+'" is not recognized.');yt=t};y.__private__.getPageMode=function(){return yt};var At=y.__private__.setLayoutMode=function(t){if(-1==[void 0,null,"continuous","single","twoleft","tworight","two"].indexOf(t))throw new Error('Layout mode must be one of continuous, single, twoleft, tworight. "'+t+'" is not recognized.');Nt=t};y.__private__.getLayoutMode=function(){return Nt},y.__private__.setDisplayMode=y.setDisplayMode=function(t,e,r){return wt(t),At(e),Lt(r),this};var xt={title:"",subject:"",author:"",keywords:"",creator:""};y.__private__.getDocumentProperty=function(t){if(-1===Object.keys(xt).indexOf(t))throw new Error("Invalid argument passed to jsPDF.getDocumentProperty");return xt[t]},y.__private__.getDocumentProperties=function(){return xt},y.__private__.setDocumentProperties=y.setProperties=y.setDocumentProperties=function(t){for(var e in xt)xt.hasOwnProperty(e)&&t[e]&&(xt[e]=t[e]);return this},y.__private__.setDocumentProperty=function(t,e){if(-1===Object.keys(xt).indexOf(t))throw new Error("Invalid arguments passed to jsPDF.setDocumentProperty");return xt[t]=e};var St,_t,Pt,kt,Ft,It={},Ct={},jt=[],Ot={},Bt={},Mt={},Et={},qt=null,Dt=0,Rt=[],Tt=new I(y),Ut=t.hotfixes||[],zt={},Ht={},Wt=[],Vt=function t(e,r,n,i,a,o){if(!(this instanceof t))return new t(e,r,n,i,a,o);isNaN(e)&&(e=1),isNaN(r)&&(r=0),isNaN(n)&&(n=0),isNaN(i)&&(i=1),isNaN(a)&&(a=0),isNaN(o)&&(o=0),this._matrix=[e,r,n,i,a,o]};Object.defineProperty(Vt.prototype,"sx",{get:function(){return this._matrix[0]},set:function(t){this._matrix[0]=t}}),Object.defineProperty(Vt.prototype,"shy",{get:function(){return this._matrix[1]},set:function(t){this._matrix[1]=t}}),Object.defineProperty(Vt.prototype,"shx",{get:function(){return this._matrix[2]},set:function(t){this._matrix[2]=t}}),Object.defineProperty(Vt.prototype,"sy",{get:function(){return this._matrix[3]},set:function(t){this._matrix[3]=t}}),Object.defineProperty(Vt.prototype,"tx",{get:function(){return this._matrix[4]},set:function(t){this._matrix[4]=t}}),Object.defineProperty(Vt.prototype,"ty",{get:function(){return this._matrix[5]},set:function(t){this._matrix[5]=t}}),Object.defineProperty(Vt.prototype,"a",{get:function(){return this._matrix[0]},set:function(t){this._matrix[0]=t}}),Object.defineProperty(Vt.prototype,"b",{get:function(){return this._matrix[1]},set:function(t){this._matrix[1]=t}}),Object.defineProperty(Vt.prototype,"c",{get:function(){return this._matrix[2]},set:function(t){this._matrix[2]=t}}),Object.defineProperty(Vt.prototype,"d",{get:function(){return this._matrix[3]},set:function(t){this._matrix[3]=t}}),Object.defineProperty(Vt.prototype,"e",{get:function(){return this._matrix[4]},set:function(t){this._matrix[4]=t}}),Object.defineProperty(Vt.prototype,"f",{get:function(){return this._matrix[5]},set:function(t){this._matrix[5]=t}}),Object.defineProperty(Vt.prototype,"rotation",{get:function(){return Math.atan2(this.shx,this.sx)}}),Object.defineProperty(Vt.prototype,"scaleX",{get:function(){return this.decompose().scale.sx}}),Object.defineProperty(Vt.prototype,"scaleY",{get:function(){return this.decompose().scale.sy}}),Object.defineProperty(Vt.prototype,"isIdentity",{get:function(){return 1===this.sx&&(0===this.shy&&(0===this.shx&&(1===this.sy&&(0===this.tx&&0===this.ty))))}}),Vt.prototype.join=function(t){return[this.sx,this.shy,this.shx,this.sy,this.tx,this.ty].map(E).join(t)},Vt.prototype.multiply=function(t){var e=t.sx*this.sx+t.shy*this.shx,r=t.sx*this.shy+t.shy*this.sy,n=t.shx*this.sx+t.sy*this.shx,i=t.shx*this.shy+t.sy*this.sy,a=t.tx*this.sx+t.ty*this.shx+this.tx,o=t.tx*this.shy+t.ty*this.sy+this.ty;return new Vt(e,r,n,i,a,o)},Vt.prototype.decompose=function(){var t=this.sx,e=this.shy,r=this.shx,n=this.sy,i=this.tx,a=this.ty,o=Math.sqrt(t*t+e*e),s=(t/=o)*r+(e/=o)*n;r-=t*s,n-=e*s;var c=Math.sqrt(r*r+n*n);return s/=c,t*(n/=c)>16&255,i=u>>8&255,a=255&u}if(void 0===i||void 0===o&&n===i&&i===a)if("string"==typeof n)r=n+" "+s[0];else switch(t.precision){case 2:r=R(n/255)+" "+s[0];break;case 3:default:r=T(n/255)+" "+s[0]}else if(void 0===o||"object"===e(o)){if(o&&!isNaN(o.a)&&0===o.a)return r=["1.","1.","1.",s[1]].join(" ");if("string"==typeof n)r=[n,i,a,s[1]].join(" ");else switch(t.precision){case 2:r=[R(n/255),R(i/255),R(a/255),s[1]].join(" ");break;default:case 3:r=[T(n/255),T(i/255),T(a/255),s[1]].join(" ")}}else if("string"==typeof n)r=[n,i,a,o,s[2]].join(" ");else switch(t.precision){case 2:r=[R(n),R(i),R(a),R(o),s[2]].join(" ");break;case 3:default:r=[T(n),T(i),T(a),T(o),s[2]].join(" ")}return r},ne=y.__private__.getFilters=function(){return f},ie=y.__private__.putStream=function(t){var e=(t=t||{}).data||"",r=t.filters||ne(),n=t.alreadyAppliedFilters||[],i=t.addLength1||!1,a=e.length,o=t.objectId,s=function(t){return t};if(null!==m&&void 0===o)throw new Error("ObjectId must be passed to putStream for file encryption");null!==m&&(s=Ye.encryptor(o,0));var c={};!0===r&&(r=["FlateEncode"]);var u=t.additionalKeyValues||[],l=(c=void 0!==M.API.processDataByFilters?M.API.processDataByFilters(e,r):{data:e,reverseChain:[]}).reverseChain+(Array.isArray(n)?n.join(" "):n.toString());if(0!==c.data.length&&(u.push({key:"Length",value:c.data.length}),!0===i&&u.push({key:"Length1",value:a})),0!=l.length)if(l.split("/").length-1==1)u.push({key:"Filter",value:l});else{u.push({key:"Filter",value:"["+l+"]"});for(var h=0;h>"),0!==c.data.length&&(ht("stream"),ht(s(c.data)),ht("endstream"))},ae=y.__private__.putPage=function(t){var e=t.number,r=t.data,n=t.objId,i=t.contentsObjId;Zt(n,!0),ht("<>"),ht("endobj");var a=r.join("\n");return S===x.ADVANCED&&(a+="\nQ"),Zt(i,!0),ie({data:a,filters:ne(),objectId:i}),ht("endobj"),n},oe=y.__private__.putPages=function(){var t,e,r=[];for(t=1;t<=Dt;t++)Rt[t].objId=Kt(),Rt[t].contentsObjId=Kt();for(t=1;t<=Dt;t++)r.push(ae({number:t,data:ot[t],objId:Rt[t].objId,contentsObjId:Rt[t].contentsObjId,mediaBox:Rt[t].mediaBox,cropBox:Rt[t].cropBox,bleedBox:Rt[t].bleedBox,trimBox:Rt[t].trimBox,artBox:Rt[t].artBox,userUnit:Rt[t].userUnit,rootDictionaryObjId:Qt,resourceDictionaryObjId:te}));Zt(Qt,!0),ht("<>"),ht("endobj"),Tt.publish("postPutPages")},se=function(t){Tt.publish("putFont",{font:t,out:ht,newObject:Xt,putStream:ie}),!0!==t.isAlreadyPutted&&(t.objectNumber=Xt(),ht("<<"),ht("/Type /Font"),ht("/BaseFont /"+F(t.postScriptName)),ht("/Subtype /Type1"),"string"==typeof t.encoding&&ht("/Encoding /"+t.encoding),ht("/FirstChar 32"),ht("/LastChar 255"),ht(">>"),ht("endobj"))},ce=function(){for(var t in It)It.hasOwnProperty(t)&&(!1===v||!0===v&&b.hasOwnProperty(t))&&se(It[t])},ue=function(t){t.objectNumber=Xt();var e=[];e.push({key:"Type",value:"/XObject"}),e.push({key:"Subtype",value:"/Form"}),e.push({key:"BBox",value:"["+[E(t.x),E(t.y),E(t.x+t.width),E(t.y+t.height)].join(" ")+"]"}),e.push({key:"Matrix",value:"["+t.matrix.toString()+"]"});var r=t.pages[1].join("\n");ie({data:r,additionalKeyValues:e,objectId:t.objectNumber}),ht("endobj")},le=function(){for(var t in zt)zt.hasOwnProperty(t)&&ue(zt[t])},he=function(t,e){var r,n=[],i=1/(e-1);for(r=0;r<1;r+=i)n.push(r);if(n.push(1),0!=t[0].offset){var a={offset:0,color:t[0].color};t.unshift(a)}if(1!=t[t.length-1].offset){var o={offset:1,color:t[t.length-1].color};t.push(o)}for(var s="",c=0,u=0;ut[c+1].offset;)c++;var l=t[c].offset,h=(r-l)/(t[c+1].offset-l),f=t[c].color,d=t[c+1].color;s+=tt(Math.round((1-h)*f[0]+h*d[0]).toString(16))+tt(Math.round((1-h)*f[1]+h*d[1]).toString(16))+tt(Math.round((1-h)*f[2]+h*d[2]).toString(16))}return s.trim()},fe=function(t,e){e||(e=21);var r=Xt(),n=he(t.colors,e),i=[];i.push({key:"FunctionType",value:"0"}),i.push({key:"Domain",value:"[0.0 1.0]"}),i.push({key:"Size",value:"["+e+"]"}),i.push({key:"BitsPerSample",value:"8"}),i.push({key:"Range",value:"[0.0 1.0 0.0 1.0 0.0 1.0]"}),i.push({key:"Decode",value:"[0.0 1.0 0.0 1.0 0.0 1.0]"}),ie({data:n,additionalKeyValues:i,alreadyAppliedFilters:["/ASCIIHexDecode"],objectId:r}),ht("endobj"),t.objectNumber=Xt(),ht("<< /ShadingType "+t.type),ht("/ColorSpace /DeviceRGB");var a="/Coords ["+E(parseFloat(t.coords[0]))+" "+E(parseFloat(t.coords[1]))+" ";2===t.type?a+=E(parseFloat(t.coords[2]))+" "+E(parseFloat(t.coords[3])):a+=E(parseFloat(t.coords[2]))+" "+E(parseFloat(t.coords[3]))+" "+E(parseFloat(t.coords[4]))+" "+E(parseFloat(t.coords[5])),ht(a+="]"),t.matrix&&ht("/Matrix ["+t.matrix.toString()+"]"),ht("/Function "+r+" 0 R"),ht("/Extend [true true]"),ht(">>"),ht("endobj")},de=function(t,e){var r=Kt(),n=Xt();e.push({resourcesOid:r,objectOid:n}),t.objectNumber=n;var i=[];i.push({key:"Type",value:"/Pattern"}),i.push({key:"PatternType",value:"1"}),i.push({key:"PaintType",value:"1"}),i.push({key:"TilingType",value:"1"}),i.push({key:"BBox",value:"["+t.boundingBox.map(E).join(" ")+"]"}),i.push({key:"XStep",value:E(t.xStep)}),i.push({key:"YStep",value:E(t.yStep)}),i.push({key:"Resources",value:r+" 0 R"}),t.matrix&&i.push({key:"Matrix",value:"["+t.matrix.toString()+"]"}),ie({data:t.stream,additionalKeyValues:i,objectId:t.objectNumber}),ht("endobj")},pe=function(t){var e;for(e in Ot)Ot.hasOwnProperty(e)&&(Ot[e]instanceof O?fe(Ot[e]):Ot[e]instanceof B&&de(Ot[e],t))},ge=function(t){for(var e in t.objectNumber=Xt(),ht("<<"),t)switch(e){case"opacity":ht("/ca "+R(t[e]));break;case"stroke-opacity":ht("/CA "+R(t[e]))}ht(">>"),ht("endobj")},me=function(){var t;for(t in Mt)Mt.hasOwnProperty(t)&&ge(Mt[t])},ve=function(){for(var t in ht("/XObject <<"),zt)zt.hasOwnProperty(t)&&zt[t].objectNumber>=0&&ht("/"+t+" "+zt[t].objectNumber+" 0 R");Tt.publish("putXobjectDict"),ht(">>")},be=function(){Ye.oid=Xt(),ht("<<"),ht("/Filter /Standard"),ht("/V "+Ye.v),ht("/R "+Ye.r),ht("/U <"+Ye.toHexString(Ye.U)+">"),ht("/O <"+Ye.toHexString(Ye.O)+">"),ht("/P "+Ye.P),ht(">>"),ht("endobj")},ye=function(){for(var t in ht("/Font <<"),It)It.hasOwnProperty(t)&&(!1===v||!0===v&&b.hasOwnProperty(t))&&ht("/"+t+" "+It[t].objectNumber+" 0 R");ht(">>")},we=function(){if(Object.keys(Ot).length>0){for(var t in ht("/Shading <<"),Ot)Ot.hasOwnProperty(t)&&Ot[t]instanceof O&&Ot[t].objectNumber>=0&&ht("/"+t+" "+Ot[t].objectNumber+" 0 R");Tt.publish("putShadingPatternDict"),ht(">>")}},Ne=function(t){if(Object.keys(Ot).length>0){for(var e in ht("/Pattern <<"),Ot)Ot.hasOwnProperty(e)&&Ot[e]instanceof y.TilingPattern&&Ot[e].objectNumber>=0&&Ot[e].objectNumber>")}},Le=function(){if(Object.keys(Mt).length>0){var t;for(t in ht("/ExtGState <<"),Mt)Mt.hasOwnProperty(t)&&Mt[t].objectNumber>=0&&ht("/"+t+" "+Mt[t].objectNumber+" 0 R");Tt.publish("putGStateDict"),ht(">>")}},Ae=function(t){Zt(t.resourcesOid,!0),ht("<<"),ht("/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]"),ye(),we(),Ne(t.objectOid),Le(),ve(),ht(">>"),ht("endobj")},xe=function(){var t=[];ce(),me(),le(),pe(t),Tt.publish("putResources"),t.forEach(Ae),Ae({resourcesOid:te,objectOid:Number.MAX_SAFE_INTEGER}),Tt.publish("postPutResources")},Se=function(){Tt.publish("putAdditionalObjects");for(var t=0;t>8&&(c=!0);t=s.join("")}for(r=t.length;void 0===c&&0!==r;)t.charCodeAt(r-1)>>8&&(c=!0),r--;if(!c)return t;for(s=e.noBOM?[]:[254,255],r=0,n=t.length;r>8)>>8)throw new Error("Character at position "+r+" of string '"+t+"' exceeds 16bits. Cannot be encoded into UCS-2 BE");s.push(l),s.push(u-(l<<8))}return String.fromCharCode.apply(void 0,s)},Ce=y.__private__.pdfEscape=y.pdfEscape=function(t,e){return Ie(t,e).replace(/\\/g,"\\\\").replace(/\(/g,"\\(").replace(/\)/g,"\\)")},je=y.__private__.beginPage=function(t){ot[++Dt]=[],Rt[Dt]={objId:0,contentsObjId:0,userUnit:Number(d),artBox:null,bleedBox:null,cropBox:null,trimBox:null,mediaBox:{bottomLeftX:0,bottomLeftY:0,topRightX:Number(t[0]),topRightY:Number(t[1])}},Me(Dt),lt(ot[$])},Oe=function(t,e){var r,n,o;switch(a=e||a,"string"==typeof t&&(r=A(t.toLowerCase()),Array.isArray(r)&&(n=r[0],o=r[1])),Array.isArray(t)&&(n=t[0]*_t,o=t[1]*_t),isNaN(n)&&(n=s[0],o=s[1]),(n>14400||o>14400)&&(i.warn("A page in a PDF can not be wider or taller than 14400 userUnit. jsPDF limits the width/height to 14400"),n=Math.min(14400,n),o=Math.min(14400,o)),s=[n,o],a.substr(0,1)){case"l":o>n&&(s=[o,n]);break;case"p":n>o&&(s=[o,n])}je(s),pr(fr),ht(Lr),0!==kr&&ht(kr+" J"),0!==Fr&&ht(Fr+" j"),Tt.publish("addPage",{pageNumber:Dt})},Be=function(t){t>0&&t<=Dt&&(ot.splice(t,1),Rt.splice(t,1),Dt--,$>Dt&&($=Dt),this.setPage($))},Me=function(t){t>0&&t<=Dt&&($=t)},Ee=y.__private__.getNumberOfPages=y.getNumberOfPages=function(){return ot.length-1},qe=function(t,e,r){var n,a=void 0;return r=r||{},t=void 0!==t?t:It[St].fontName,e=void 0!==e?e:It[St].fontStyle,n=t.toLowerCase(),void 0!==Ct[n]&&void 0!==Ct[n][e]?a=Ct[n][e]:void 0!==Ct[t]&&void 0!==Ct[t][e]?a=Ct[t][e]:!1===r.disableWarning&&i.warn("Unable to look up font label for font '"+t+"', '"+e+"'. Refer to getFontList() for available fonts."),a||r.noFallback||null==(a=Ct.times[e])&&(a=Ct.times.normal),a},De=y.__private__.putInfo=function(){var t=Xt(),e=function(t){return t};for(var r in null!==m&&(e=Ye.encryptor(t,0)),ht("<<"),ht("/Producer ("+Ce(e("jsPDF "+M.version))+")"),xt)xt.hasOwnProperty(r)&&xt[r]&&ht("/"+r.substr(0,1).toUpperCase()+r.substr(1)+" ("+Ce(e(xt[r]))+")");ht("/CreationDate ("+Ce(e(W))+")"),ht(">>"),ht("endobj")},Re=y.__private__.putCatalog=function(t){var e=(t=t||{}).rootDictionaryObjId||Qt;switch(Xt(),ht("<<"),ht("/Type /Catalog"),ht("/Pages "+e+" 0 R"),mt||(mt="fullwidth"),mt){case"fullwidth":ht("/OpenAction [3 0 R /FitH null]");break;case"fullheight":ht("/OpenAction [3 0 R /FitV null]");break;case"fullpage":ht("/OpenAction [3 0 R /Fit]");break;case"original":ht("/OpenAction [3 0 R /XYZ null null 1]");break;default:var r=""+mt;"%"===r.substr(r.length-1)&&(mt=parseInt(mt)/100),"number"==typeof mt&&ht("/OpenAction [3 0 R /XYZ null null "+R(mt)+"]")}switch(Nt||(Nt="continuous"),Nt){case"continuous":ht("/PageLayout /OneColumn");break;case"single":ht("/PageLayout /SinglePage");break;case"two":case"twoleft":ht("/PageLayout /TwoColumnLeft");break;case"tworight":ht("/PageLayout /TwoColumnRight")}yt&&ht("/PageMode /"+yt),Tt.publish("putCatalog"),ht(">>"),ht("endobj")},Te=y.__private__.putTrailer=function(){ht("trailer"),ht("<<"),ht("/Size "+(et+1)),ht("/Root "+et+" 0 R"),ht("/Info "+(et-1)+" 0 R"),null!==m&&ht("/Encrypt "+Ye.oid+" 0 R"),ht("/ID [ <"+V+"> <"+V+"> ]"),ht(">>")},Ue=y.__private__.putHeader=function(){ht("%PDF-"+w),ht("%ºß¬à")},ze=y.__private__.putXRef=function(){var t="0000000000";ht("xref"),ht("0 "+(et+1)),ht("0000000000 65535 f ");for(var e=1;e<=et;e++){"function"==typeof rt[e]?ht((t+rt[e]()).slice(-10)+" 00000 n "):void 0!==rt[e]?ht((t+rt[e]).slice(-10)+" 00000 n "):ht("0000000000 00000 n ")}},He=y.__private__.buildDocument=function(){ut(),lt(nt),Tt.publish("buildDocument"),Ue(),oe(),Se(),xe(),null!==m&&be(),De(),Re();var t=it;return ze(),Te(),ht("startxref"),ht(""+t),ht("%%EOF"),lt(ot[$]),nt.join("\n")},We=y.__private__.getBlob=function(t){return new Blob([dt(t)],{type:"application/pdf"})},Ve=y.output=y.__private__.output=Fe((function(t,e){switch("string"==typeof(e=e||{})?e={filename:e}:e.filename=e.filename||"generated.pdf",t){case void 0:return He();case"save":y.save(e.filename);break;case"arraybuffer":return dt(He());case"blob":return We(He());case"bloburi":case"bloburl":if(void 0!==r.URL&&"function"==typeof r.URL.createObjectURL)return r.URL&&r.URL.createObjectURL(We(He()))||void 0;i.warn("bloburl is not supported by your system, because URL.createObjectURL is not supported by your browser.");break;case"datauristring":case"dataurlstring":var n="",a=He();try{n=u(a)}catch(t){n=u(unescape(encodeURIComponent(a)))}return"data:application/pdf;filename="+e.filename+";base64,"+n;case"pdfobjectnewwindow":if("[object Window]"===Object.prototype.toString.call(r)){var o="https://cdnjs.cloudflare.com/ajax/libs/pdfobject/2.1.1/pdfobject.min.js",s=' integrity="sha512-4ze/a9/4jqu+tX9dfOqJYSvyYd5M6qum/3HpCLr+/Jqf0whc37VUbkpNGHR7/8pSnCFw47T1fmIpwBV7UySh3g==" crossorigin="anonymous"';e.pdfObjectUrl&&(o=e.pdfObjectUrl,s="");var c=' + + + + +
+

Einstellungen

+
+ +
+ + + + + + + + Diese Funktion wird in einer zukünftigen Version verfügbar sein. + + + +
+ + \ No newline at end of file diff --git a/options.js b/options.js new file mode 100644 index 0000000..723ac38 --- /dev/null +++ b/options.js @@ -0,0 +1,30 @@ +// options.js + +document.addEventListener("DOMContentLoaded", async () => { + const options = await loadOptions(); + + document.getElementById("state-selector").value = options.state || ""; + document.getElementById("pdf-path").value = options.pdfPath || "cookiekop/pdfs/"; + document.getElementById("screenshot-path").value = options.screenshotPath || "cookiekop/screenshots/"; + document.getElementById("show-banner").checked = options.showBanner ?? true; + document.getElementById("share-stats").checked = options.shareStats ?? false; + + document.getElementById("save-options").addEventListener("click", saveOptions); +}); + +// Optionen speichern +async function saveOptions() { + const options = { + state: document.getElementById("state-selector").value, + showBanner: document.getElementById("show-banner").checked + // pdfPath und screenshotPath werden NICHT mehr gespeichert! + }; + await chrome.storage.local.set({ options }); + alert("Einstellungen gespeichert!"); +} + +// Optionen laden +async function loadOptions() { + const result = await chrome.storage.local.get('options'); + return result.options || {}; +} \ No newline at end of file diff --git a/popup.html b/popup.html new file mode 100644 index 0000000..225bdb3 --- /dev/null +++ b/popup.html @@ -0,0 +1,43 @@ + + + + + Meldung erstellen – CookieKop + + + + + + + + + + +
+

+
+ +
+ + + + +
+ + + + +
+ + + + + +
+
+ +
+ +
+ + \ No newline at end of file diff --git a/popup.js b/popup.js new file mode 100644 index 0000000..65e6657 --- /dev/null +++ b/popup.js @@ -0,0 +1,135 @@ +// popup.js + +document.addEventListener("DOMContentLoaded", async () => { + document.getElementById("project-name").innerText = PROJECT_NAME; + + const options = await loadOptions(); + const bannerElement = document.getElementById("project-banner"); + if (bannerElement && (options.showBanner ?? SHOW_BANNER_DEFAULT)) { + bannerElement.innerText = `Erstellt mit ${PROJECT_NAME} – deinem datenschutzfreundlichen Helfer gegen unerlaubte Cookie-Banner.`; + } + + const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); + document.getElementById("current-url").value = tab.url; + + const violationsDiv = document.getElementById("violations"); + VIOLATION_TYPES.forEach(type => { + const label = document.createElement("label"); + label.innerHTML = ` ${type}`; + violationsDiv.appendChild(label); + }); + + document.getElementById("screenshot-btn").addEventListener("click", createScreenshot); + // PDF-Funktion aktuell deaktiviert: + // document.getElementById("create-pdf-btn").addEventListener("click", preparePDF); + document.getElementById("create-email-btn").addEventListener("click", prepareEmail); + document.getElementById("open-options-btn").addEventListener("click", openOptions); +}); + +// Optionen laden +async function loadOptions() { + const result = await chrome.storage.local.get('options'); + return result.options || {}; +} + +// Screenshot erstellen und speichern +async function createScreenshot() { + try { + const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); + const imageUri = await chrome.tabs.captureVisibleTab(tab.windowId, { format: "png" }); + + const url = new URL(tab.url); + const domain = url.hostname.replace('www.', ''); + + const now = new Date(); + const timestamp = now.getFullYear().toString() + + String(now.getMonth() + 1).padStart(2, '0') + + String(now.getDate()).padStart(2, '0') + + String(now.getHours()).padStart(2, '0') + + String(now.getMinutes()).padStart(2, '0'); + + const filename = `${timestamp}_${domain}.png`; + + const response = await fetch(imageUri); + const blob = await response.blob(); + const blobUrl = URL.createObjectURL(blob); + + await chrome.downloads.download({ + url: blobUrl, + filename: `cookiekop/screenshots/${filename}`, // Vorschlagpfad + saveAs: true + }); + + // Kein alert mehr notwendig! + } catch (error) { + console.error("Fehler beim Screenshot:", error); + alert("Screenshot konnte nicht gespeichert werden."); + } +} + +// (PDF erstellen Funktion bleibt auskommentiert – später aktivieren) + +// E-Mail vorbereiten +async function prepareEmail() { + const url = document.getElementById("current-url").value; + const violations = Array.from(document.querySelectorAll('#violations input:checked')).map(input => input.value); + const note = document.getElementById("custom-note").value; + const options = await loadOptions(); + + let body = `Sehr geehrte Damen und Herren,\n\n`; + body += `ich möchte folgende Verstöße gegen die DSGVO im Zusammenhang mit dem Cookie-Banner melden:\n\n`; + violations.forEach(v => { + body += `- ${v}\n`; + }); + body += `\nURL: ${url}\n`; + if (note) { + body += `\nZusätzliche Hinweise: ${note}\n`; + } + + if (options.showBanner ?? true) { + body += `\n\nErstellt mit CookieKop – https://gitlab.com/1337gut/cookiekop`; + } + + body += `\n\nMit freundlichen Grüßen\n\n`; + + const subject = encodeURIComponent("Meldung eines Cookie-Banner-Verstoßes"); + const bodyEncoded = encodeURIComponent(body); + + let recipient = "poststelle@lda.bayern.de"; // Fallback + if (options?.state) { + try { + const response = await fetch('data/datenschutzbehoerden.json'); + const authorities = await response.json(); + const found = authorities.find(entry => entry.bundesland === options.state); + if (found && found.email) { + recipient = found.email; + } + } catch (error) { + console.error("Fehler beim Laden der Behördenliste:", error); + } + } + + const mailtoLink = `mailto:${recipient}?subject=${subject}&body=${bodyEncoded}`; + + try { + await chrome.tabs.create({ url: mailtoLink }); + } catch (error) { + console.error("Fehler beim Öffnen von mailto:", error); + fallbackCopyEmailText(body); + } +} + +// Fallback: E-Mail-Text in Zwischenablage kopieren +function fallbackCopyEmailText(text) { + navigator.clipboard.writeText(text).then(() => { + alert("E-Mail-Text wurde kopiert. Bitte öffne dein E-Mail-Programm und füge ihn ein (Strg+V)."); + }).catch(err => { + console.error("Kopieren fehlgeschlagen:", err); + alert("Fehler beim Kopieren des E-Mail-Texts."); + }); +} + +// Optionen-Seite öffnen +function openOptions() { + chrome.runtime.openOptionsPage(); +} \ No newline at end of file diff --git a/statistics.html b/statistics.html new file mode 100644 index 0000000..b1f9c7e --- /dev/null +++ b/statistics.html @@ -0,0 +1,29 @@ + + + + + Statistik – CookieKop + + + + + +
+

Deine Statistik

+
+ +
+
+

Gesendete Meldungen: 0

+
+ +
+ +
+
+ + + + diff --git a/statistics.js b/statistics.js new file mode 100644 index 0000000..f8e91ae --- /dev/null +++ b/statistics.js @@ -0,0 +1,32 @@ +// statistics.js + +document.addEventListener("DOMContentLoaded", async () => { + const count = await loadFromStorage('reportCount') || 0; + document.getElementById("report-count").innerText = count; + + document.getElementById("share-stats-btn").addEventListener("click", shareStats); +}); + +// Statistik anonym teilen +async function shareStats() { + const options = await loadFromStorage('options'); + if (!options?.shareStats) { + alert("Das Teilen von Statistiken ist deaktiviert."); + return; + } + + const count = await loadFromStorage('reportCount') || 0; + const payload = { + reports: count, + browser: navigator.userAgent, + timestamp: new Date().toISOString() + }; + + try { + // Dummy Upload (kann später erweitert werden) + alert("Statistik erfolgreich (lokal simuliert) geteilt."); + } catch (error) { + console.error("Fehler beim Teilen der Statistik:", error); + alert("Fehler beim Senden der Statistik."); + } +} diff --git a/styles/options.css b/styles/options.css new file mode 100644 index 0000000..2aefdd3 --- /dev/null +++ b/styles/options.css @@ -0,0 +1,33 @@ +/* styles/options.css */ + +body { + font-family: sans-serif; + padding: 10px; + width: 300px; + } + + header h1 { + font-size: 20px; + margin-bottom: 10px; + } + + label { + display: block; + margin-top: 10px; + font-weight: bold; + } + + input[type="text"], + select { + width: 100%; + box-sizing: border-box; + margin-top: 5px; + } + + button { + margin-top: 15px; + padding: 8px; + font-size: 14px; + cursor: pointer; + } + \ No newline at end of file diff --git a/styles/popup.css b/styles/popup.css new file mode 100644 index 0000000..7cf2018 --- /dev/null +++ b/styles/popup.css @@ -0,0 +1,49 @@ +/* styles/popup.css */ + +body { + font-family: sans-serif; + padding: 10px; + width: 300px; + } + + header h1 { + font-size: 20px; + margin-bottom: 10px; + } + + label { + display: block; + margin-top: 10px; + font-weight: bold; + } + + input[type="text"], + textarea { + width: 100%; + box-sizing: border-box; + margin-top: 5px; + } + + .actions { + margin-top: 15px; + display: flex; + flex-direction: column; + gap: 5px; + } + + button { + padding: 8px; + font-size: 14px; + cursor: pointer; + } + + footer { + margin-top: 15px; + font-size: 10px; + text-align: center; + color: #666; + } + + #project-banner { + display: none; + } \ No newline at end of file diff --git a/styles/statistics.css b/styles/statistics.css new file mode 100644 index 0000000..41be530 --- /dev/null +++ b/styles/statistics.css @@ -0,0 +1,35 @@ +/* styles/statistics.css */ + +body { + font-family: sans-serif; + padding: 10px; + width: 300px; + } + + header h1 { + font-size: 20px; + margin-bottom: 10px; + } + + #stats p { + font-size: 16px; + margin: 10px 0; + } + + #share-option { + margin-top: 20px; + } + + button { + padding: 8px; + font-size: 14px; + cursor: pointer; + } + + footer { + margin-top: 15px; + font-size: 10px; + text-align: center; + color: #666; + } + \ No newline at end of file diff --git a/util/constants.js b/util/constants.js new file mode 100644 index 0000000..9ed9b02 --- /dev/null +++ b/util/constants.js @@ -0,0 +1,23 @@ +// constants.js + +// Projektname und zentrale Konfigurationen +const PROJECT_NAME = "CookieKop"; + +// Standardwert: Soll der Banner angezeigt werden? +const SHOW_BANNER_DEFAULT = true; + +// Standardwert: Soll die Statistik anonymisiert geteilt werden? +const SHARE_STATS_DEFAULT = false; + +// (Platzhalter) URL für anonymen Statistik-Upload +const STATS_UPLOAD_URL = "https://your-stats-endpoint.example.com/upload"; // aktuell deaktiviert + +// Verfügbare Cookie-Verstoßtypen +const VIOLATION_TYPES = [ + "Kein 'Ablehnen'-Button sichtbar", + "Voreingestellte Zustimmung (Checkboxen vorausgefüllt)", + "Intransparente Sprache", + "Keine echte Auswahlmöglichkeit", + "Tracking startet vor Einwilligung", + "Andere (bitte in Kommentar beschreiben)" +]; diff --git a/util/pdf.js b/util/pdf.js new file mode 100644 index 0000000..083d1c7 --- /dev/null +++ b/util/pdf.js @@ -0,0 +1,46 @@ +// util/pdf.js + +async function generatePDF(url, violations, note, screenshotDataUrl) { + const { jsPDF } = window.jspdf; + const doc = new jsPDF(); + + let y = 10; + + doc.setFontSize(16); + doc.text("Cookie-Banner Verstoßmeldung", 10, y); + y += 10; + + doc.setFontSize(12); + doc.text(`Datum: ${new Date().toLocaleString()}`, 10, y); + y += 10; + + doc.text(`URL:`, 10, y); + doc.text(url, 30, y); + y += 10; + + doc.text("Verstöße:", 10, y); + violations.forEach(v => { + y += 7; + doc.text(`- ${v}`, 15, y); + }); + + y += 10; + doc.text("Zusätzliche Hinweise:", 10, y); + y += 7; + const splitNote = doc.splitTextToSize(note || "Keine Angabe", 180); + doc.text(splitNote, 10, y); + y += splitNote.length * 7; + + if (screenshotDataUrl) { + y += 10; + doc.text("Screenshot:", 10, y); + y += 5; + try { + doc.addImage(screenshotDataUrl, 'PNG', 10, y, 180, 100); + } catch (error) { + console.error("Fehler beim Einfügen des Screenshots ins PDF:", error); + } + } + + doc.save('cookiekop_meldung.pdf'); +} diff --git a/util/screenshot.js b/util/screenshot.js new file mode 100644 index 0000000..ed674c9 --- /dev/null +++ b/util/screenshot.js @@ -0,0 +1,25 @@ +// util/screenshot.js + +// Macht einen Screenshot vom aktiven Tab und speichert ihn lokal +async function takeScreenshotAndStore() { + try { + const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); + const imageUri = await chrome.tabs.captureVisibleTab(tab.windowId, { format: "png" }); + await saveToStorage('screenshot', imageUri); + return imageUri; + } catch (error) { + console.error("Fehler beim Screenshot erstellen:", error); + throw error; + } + } + + // Lädt den Screenshot aus dem Storage + async function loadScreenshot() { + return await loadFromStorage('screenshot'); + } + + // Löscht gespeicherten Screenshot + async function clearScreenshot() { + await removeFromStorage('screenshot'); + } + \ No newline at end of file diff --git a/util/storage.js b/util/storage.js new file mode 100644 index 0000000..b83c192 --- /dev/null +++ b/util/storage.js @@ -0,0 +1,25 @@ +// util/storage.js + +// Speichert beliebige Daten in chrome.storage.local +async function saveToStorage(key, value) { + const data = {}; + data[key] = value; + return chrome.storage.local.set(data); + } + + // Holt Daten aus chrome.storage.local + async function loadFromStorage(key) { + const result = await chrome.storage.local.get(key); + return result[key]; + } + + // Löscht einen bestimmten Schlüssel aus dem Storage + async function removeFromStorage(key) { + return chrome.storage.local.remove(key); + } + + // Löscht ALLE gespeicherten Daten (zurücksetzen) + async function clearStorage() { + return chrome.storage.local.clear(); + } + \ No newline at end of file