diff --git a/bin/wappalyzer-build b/bin/wappalyzer-build index 3f973b736..6a18d6bb6 100755 --- a/bin/wappalyzer-build +++ b/bin/wappalyzer-build @@ -66,6 +66,13 @@ pushd $WAPPALYZER_ROOT/src/drivers/chrome > /dev/null zip -qr $WAPPALYZER_ROOT/build/wappalyzer_chrome.zip . +# Google Chrome +echo "Building WebExtension driver..." + +pushd $WAPPALYZER_ROOT/src/drivers/webextension > /dev/null + +zip -qr $WAPPALYZER_ROOT/build/wappalyzer_webextension.zip . + popd > /dev/null echo "Done. Builds have been created in $WAPPALYZER_ROOT/build." diff --git a/bin/wappalyzer-links b/bin/wappalyzer-links index 25f589406..dbb52a483 100755 --- a/bin/wappalyzer-links +++ b/bin/wappalyzer-links @@ -46,6 +46,16 @@ if [ "$(compgen -G "$path/icons/converted/*.png" | head -n1)" ]; then ln -f $path/icons/converted/*.png $path/drivers/chrome/images/icons/converted fi +ln -f $path/wappalyzer.js $path/drivers/webextension/js +ln -f $path/apps.json $path/drivers/webextension +ln -f $path/icons/*.png $path/drivers/webextension/images/icons +ln -f $path/icons/*.svg $path/drivers/webextension/images/icons +ln -f $path/utils/*.js $path/drivers/webextension/js + +if [ "$(compgen -G "$path/icons/converted/*.png" | head -n1)" ]; then + ln -f $path/icons/converted/*.png $path/drivers/webextension/images/icons/converted +fi + echo "OK" exit 0 diff --git a/bin/wappalyzer-validate b/bin/wappalyzer-validate index d9d09a617..0106c4d18 100755 --- a/bin/wappalyzer-validate +++ b/bin/wappalyzer-validate @@ -1,23 +1,20 @@ #!/bin/bash -path=$1 +path="$1" -if [ -z $path ] -then - if [ -z $WAPPALYZER_ROOT ] - then +if [ -z "$path" ]; then + if [ -z "$WAPPALYZER_ROOT" ]; then echo "-$(basename $0): No path specified" exit 1 fi - path=$WAPPALYZER_ROOT + path="$WAPPALYZER_ROOT" fi set -eu -if [ ! -d $path/src ] -then +if [ ! -d "$path/src" ]; then echo "-$(basename $0): Incorrect path" exit 1 @@ -27,7 +24,7 @@ path="$path/src" echo "Validating apps.json..." -node $WAPPALYZER_NODE_PATH/node_modules/jsonlint/lib/cli.js --quiet -V $WAPPALYZER_ROOT/schema.json $path/apps.json +node "$WAPPALYZER_NODE_PATH/node_modules/jsonlint/lib/cli.js" --quiet -V "$WAPPALYZER_ROOT/schema.json" "$path/apps.json" echo "Validating regular expressions..." diff --git a/src/drivers/webextension/.gitignore b/src/drivers/webextension/.gitignore new file mode 100644 index 000000000..602df1590 --- /dev/null +++ b/src/drivers/webextension/.gitignore @@ -0,0 +1,7 @@ +apps.json +images/icons/converted/*.png +images/icons/*.png +images/icons/*.svg +js/wappalyzer.js +js/iframe.js +js/network.js diff --git a/src/drivers/webextension/_locales/el/messages.json b/src/drivers/webextension/_locales/el/messages.json new file mode 100644 index 000000000..8b63568f5 --- /dev/null +++ b/src/drivers/webextension/_locales/el/messages.json @@ -0,0 +1,60 @@ +{ + "github": { "message": "Κάνε fork το Wappalyzer στο GitHub!" }, + "twitter": { "message": "Ακολούθησε το Wappalyzer στο Twitter" }, + "website": { "message": "Πήγαινε στο wappalyzer.com" }, + "options": { "message": "Ρυθμίσεις Wappalyzer" }, + "optionsSave": { "message": "Ρυθμίσεις αποθήκευσης" }, + "optionsSaved": { "message": "Αποθηκεύτηκε" }, + "optionUpgradeMessage": { "message": "Ενημερώστε με για αναβαθμίσεις" }, + "optionTracking": { "message": "Ανώνυμη αποστολή αναφορών για εντοπισμένες εφαρμογές στο wappalyzer.com για έρευνα" }, + "nothingToDo": { "message": "Καμία ενέργεια." }, + "noAppsDetected": { "message": "Δεν ανιχνεύθηκαν εφαρμογές." }, + "categoryName1": { "message": "CMS" }, + "categoryName2": { "message": "Διαδικτυακό Φόρουμ" }, + "categoryName3": { "message": "Διαχειριστής Βάσης Δεδομένων" }, + "categoryName4": { "message": "Εργαλείο Τεκμηρίωσης" }, + "categoryName5": { "message": "Widget" }, + "categoryName10": { "message": "Analytics" }, + "categoryName11": { "message": "Blog" }, + "categoryName12": { "message": "Framework της JavaScript" }, + "categoryName13": { "message": "Issue Tracker" }, + "categoryName14": { "message": "Πρόγραμμα αναπαραγωγής Βίντεο" }, + "categoryName15": { "message": "Σύστημα Σχολίων" }, + "categoryName16": { "message": "Captcha" }, + "categoryName17": { "message": "Script Γραμματοσειράς" }, + "categoryName18": { "message": "Framework Διαδικτύου" }, + "categoryName19": { "message": "Διάφορα" }, + "categoryName20": { "message": "Επεξεργαστής Κειμένου" }, + "categoryName21": { "message": "LMS" }, + "categoryName22": { "message": "Διακομιστής Διαδικτύου" }, + "categoryName23": { "message": "Εργαλείο Μνήμης Cache" }, + "categoryName24": { "message": "Επεξεργαστής Εμπλουτισμένου Κειμένου" }, + "categoryName25": { "message": "Γραφικά JavaScript" }, + "categoryName26": { "message": "Framework για Κινητά" }, + "categoryName27": { "message": "Γλώσσα Προγραμματισμού" }, + "categoryName28": { "message": "Λειτουργικό Σύστημα" }, + "categoryName29": { "message": "Μηχανή Αναζήτησης" }, + "categoryName30": { "message": "Web Mail" }, + "categoryName31": { "message": "CDN" }, + "categoryName32": { "message": "Αυτοματοποίηση Marketing" }, + "categoryName33": { "message": "Επέκταση Διακομιστή Διαδικτύου" }, + "categoryName34": { "message": "Βάση Δεδομένων" }, + "categoryName35": { "message": "Χάρτης" }, + "categoryName36": { "message": "Δίκτυο Διαφημίσεων" }, + "categoryName37": { "message": "Υπηρεσία Δικτύου" }, + "categoryName38": { "message": "Διακομιστής Πολυμέσων" }, + "categoryName39": { "message": "Διαδικτυακή κάμερα" }, + "categoryName40": { "message": "Εκτυπωτής" }, + "categoryName41": { "message": "Σύστημα Επεξεργασίας Πληρωμών" }, + "categoryName42": { "message": "Σύστημα Διαχείρισης Tags" }, + "categoryName43": { "message": "Paywall" }, + "categoryName44": { "message": "Σύστημα Build/CI" }, + "categoryName45": { "message": "Σύστημα SCADA" }, + "categoryName46": { "message": "Απομακρυσμένη Πρόσβαση" }, + "categoryName47": { "message": "Εργαλείο Ανάπτυξης" }, + "categoryName48": { "message": "Δικτυακός Αποθηκευτικός Χώρος" }, + "categoryName49": { "message": "Feed Readers" }, + "categoryName50": { "message": "Συστήματα Διαχειρίσης Εγγράφων" }, + "categoryName51": { "message": "Σύστημα Κατασκευής Σελίδων Υποδοχής" }, + "categoryName52": { "message": "Live Chat" } +} diff --git a/src/drivers/webextension/_locales/en/messages.json b/src/drivers/webextension/_locales/en/messages.json new file mode 100644 index 000000000..ba2c15174 --- /dev/null +++ b/src/drivers/webextension/_locales/en/messages.json @@ -0,0 +1,60 @@ +{ + "github": { "message": "Fork Wappalyzer on GitHub!" }, + "twitter": { "message": "Follow Wappalyzer on Twitter" }, + "website": { "message": "Go to wappalyzer.com" }, + "options": { "message": "Options" }, + "optionsSave": { "message": "Save options" }, + "optionsSaved": { "message": "Saved" }, + "optionUpgradeMessage": { "message": "Tell me about upgrades" }, + "optionTracking": { "message": "Anonymously send reports on detected applications to wappalyzer.com for research" }, + "nothingToDo": { "message": "Nothing to do here." }, + "noAppsDetected": { "message": "No applications detected." }, + "categoryName1": { "message": "CMS" }, + "categoryName2": { "message": "Message Board" }, + "categoryName3": { "message": "Database Manager" }, + "categoryName4": { "message": "Documentation Tool" }, + "categoryName5": { "message": "Widget" }, + "categoryName10": { "message": "Analytics" }, + "categoryName11": { "message": "Blog" }, + "categoryName12": { "message": "JavaScript Framework" }, + "categoryName13": { "message": "Issue Tracker" }, + "categoryName14": { "message": "Video Player" }, + "categoryName15": { "message": "Comment System" }, + "categoryName16": { "message": "Captcha" }, + "categoryName17": { "message": "Font Script" }, + "categoryName18": { "message": "Web Framework" }, + "categoryName19": { "message": "Miscellaneous" }, + "categoryName20": { "message": "Editor" }, + "categoryName21": { "message": "LMS" }, + "categoryName22": { "message": "Web Server" }, + "categoryName23": { "message": "Cache Tool" }, + "categoryName24": { "message": "Rich Text Editor" }, + "categoryName25": { "message": "JavaScript Graphics" }, + "categoryName26": { "message": "Mobile Framework" }, + "categoryName27": { "message": "Programming Language" }, + "categoryName28": { "message": "Operating System" }, + "categoryName29": { "message": "Search Engine" }, + "categoryName30": { "message": "Web Mail" }, + "categoryName31": { "message": "CDN" }, + "categoryName32": { "message": "Marketing Automation" }, + "categoryName33": { "message": "Web Server Extension" }, + "categoryName34": { "message": "Database" }, + "categoryName35": { "message": "Map" }, + "categoryName36": { "message": "Advertising Network" }, + "categoryName37": { "message": "Network Service" }, + "categoryName38": { "message": "Media Server" }, + "categoryName39": { "message": "Webcam" }, + "categoryName40": { "message": "Printer" }, + "categoryName41": { "message": "Payment Processor" }, + "categoryName42": { "message": "Tag Manager" }, + "categoryName43": { "message": "Paywall" }, + "categoryName44": { "message": "Build/CI System" }, + "categoryName45": { "message": "SCADA System" }, + "categoryName46": { "message": "Remote Access" }, + "categoryName47": { "message": "Development Tool" }, + "categoryName48": { "message": "Network Storage" }, + "categoryName49": { "message": "Feed Readers" }, + "categoryName50": { "message": "Document Management Systems" }, + "categoryName51": { "message": "Landing Page Builder" }, + "categoryName52": { "message": "Live Chat" } +} diff --git a/src/drivers/webextension/_locales/es/messages.json b/src/drivers/webextension/_locales/es/messages.json new file mode 100644 index 000000000..666deb714 --- /dev/null +++ b/src/drivers/webextension/_locales/es/messages.json @@ -0,0 +1,64 @@ +{ + "github": { "message": "¡Forkea Wappalyzer en GitHub!" }, + "twitter": { "message": "Sigue Wappalyzer en Twitter" }, + "website": { "message": "Ir a wappalyzer.com" }, + "options": { "message": "Opciones" }, + "optionsSave": { "message": "Guardar opciones" }, + "optionsSaved": { "message": "Guardado" }, + "optionUpgradeMessage": { "message": "Indicarme actualizaciones" }, + "optionTracking": { "message": "Enviar informes anónimos sobre las aplicaciones detectadas a wappalyzer.com para análisis" }, + "nothingToDo": { "message": "Nada que hacer aquí." }, + "noAppsDetected": { "message": "Aplicaciones no detectadas." }, + "categoryName1": { "message": "Gestor de Contenido" }, + "categoryName2": { "message": "Foro" }, + "categoryName3": { "message": "Gestor de Bases de Datos" }, + "categoryName4": { "message": "Herramienta de Documentación" }, + "categoryName5": { "message": "Widget" }, + "categoryName6": { "message": "Tienda Web" }, + "categoryName7": { "message": "Galería fotográfica" }, + "categoryName8": { "message": "Wiki" }, + "categoryName9": { "message": "Panel de Hosting" }, + "categoryName10": { "message": "Analítica" }, + "categoryName11": { "message": "Blog" }, + "categoryName12": { "message": "Framework JavaScript" }, + "categoryName13": { "message": "Gestor de Incidencias" }, + "categoryName14": { "message": "Reproductor de Vídeo" }, + "categoryName15": { "message": "Sistema de Comentarios" }, + "categoryName16": { "message": "Captcha" }, + "categoryName17": { "message": "Tipografía" }, + "categoryName18": { "message": "Framework Web" }, + "categoryName19": { "message": "Miscelánea" }, + "categoryName20": { "message": "Editor" }, + "categoryName21": { "message": "LMS" }, + "categoryName22": { "message": "Servidor Web" }, + "categoryName23": { "message": "Herramienta de Cache" }, + "categoryName24": { "message": "Editor de Texto Enriquecido" }, + "categoryName25": { "message": "Gráficos JavaScript" }, + "categoryName26": { "message": "Framework Móvil" }, + "categoryName27": { "message": "Lenguaje de programación" }, + "categoryName28": { "message": "Sistema Operativo" }, + "categoryName29": { "message": "Motor de Búsqueda" }, + "categoryName30": { "message": "Correo Web" }, + "categoryName31": { "message": "CDN" }, + "categoryName32": { "message": "Automatización de Marketing" }, + "categoryName33": { "message": "Extensión de Servidor Web" }, + "categoryName34": { "message": "Base de Datos" }, + "categoryName35": { "message": "Mapa" }, + "categoryName36": { "message": "Red de Publicidad" }, + "categoryName37": { "message": "Network Sevice" }, + "categoryName38": { "message": "Media Server" }, + "categoryName39": { "message": "Webcam" }, + "categoryName40": { "message": "Printer" }, + "categoryName41": { "message": "Payment Processor" }, + "categoryName42": { "message": "Tag Manager" }, + "categoryName43": { "message": "Paywall" }, + "categoryName44": { "message": "Build/CI System" }, + "categoryName45": { "message": "SCADA System" }, + "categoryName46": { "message": "Remote Access" }, + "categoryName47": { "message": "Development Tool" }, + "categoryName48": { "message": "Network Storage" }, + "categoryName49": { "message": "Feed Readers" }, + "categoryName50": { "message": "Document Management Systems" }, + "categoryName51": { "message": "Landing Page Builder" }, + "categoryName52": { "message": "Live Chat" } +} diff --git a/src/drivers/webextension/_locales/fr/messages.json b/src/drivers/webextension/_locales/fr/messages.json new file mode 100644 index 000000000..743d79210 --- /dev/null +++ b/src/drivers/webextension/_locales/fr/messages.json @@ -0,0 +1,64 @@ +{ + "github": { "message": "Forker Wappalyzer sur GitHub!" }, + "noAppsDetected": { "message": "Pas d'applications détectées." }, + "nothingToDo": { "message": "Rien à faire ici." }, + "optionTracking": { "message": "Envoyer anonymement des rapports sur les applications détectées à wappalyzer.com pour la recherche" }, + "optionUpgradeMessage": { "message": "M'afficher les mises à jour" }, + "options": { "message": "Options" }, + "optionsSave": { "message": "Sauver les options" }, + "optionsSaved": { "message": "Sauvé" }, + "twitter": { "message": "Suivre Wappalyzer sur Twitter" }, + "website": { "message": "Aller à wappalyzer.com" }, + "categoryName1": { "message": "CMS" }, + "categoryName2": { "message": "Forum" }, + "categoryName3": { "message": "Gestionnaire de base de données" }, + "categoryName4": { "message": "Outil de documentation" }, + "categoryName5": { "message": "Widget" }, + "categoryName6": { "message": "Boutique en ligne" }, + "categoryName7": { "message": "Galerie photo" }, + "categoryName8": { "message": "Wiki" }, + "categoryName9": { "message": "Gestionnaires de serveur" }, + "categoryName10": { "message": "Outil de statistiques" }, + "categoryName11": { "message": "Blog" }, + "categoryName12": { "message": "Framework JavaScript" }, + "categoryName13": { "message": "Outil de suivi de problèmes" }, + "categoryName14": { "message": "Lecteur de vidéos" }, + "categoryName15": { "message": "Système de commentaires" }, + "categoryName16": { "message": "Captcha" }, + "categoryName17": { "message": "Script de police" }, + "categoryName18": { "message": "Framework web" }, + "categoryName19": { "message": "Divers" }, + "categoryName20": { "message": "Editeur" }, + "categoryName21": { "message": "LMS" }, + "categoryName22": { "message": "Serveur web" }, + "categoryName23": { "message": "Outil de cache" }, + "categoryName24": { "message": "Editeur WYSIWYG" }, + "categoryName25": { "message": "Graphismes JavaScript" }, + "categoryName26": { "message": "Framework pour mobiles" }, + "categoryName27": { "message": "Language de programmation" }, + "categoryName28": { "message": "Système d'exploitation" }, + "categoryName29": { "message": "Moteur de recherche" }, + "categoryName30": { "message": "Web Mail" }, + "categoryName31": { "message": "CDN" }, + "categoryName32": { "message": "Logiciel de marketing" }, + "categoryName33": { "message": "Extension de serveur web" }, + "categoryName34": { "message": "Base de données" }, + "categoryName35": { "message": "Carte" }, + "categoryName36": { "message": "Réseau publicitaire" }, + "categoryName37": { "message": "Périphérique réseau" }, + "categoryName38": { "message": "Serveur multimédia" }, + "categoryName39": { "message": "Webcam" }, + "categoryName40": { "message": "Imprimante" }, + "categoryName41": { "message": "Service de paiement" }, + "categoryName42": { "message": "Tag Manager" }, + "categoryName43": { "message": "Paywall" }, + "categoryName44": { "message": "Système CI" }, + "categoryName45": { "message": "Système SCADA" }, + "categoryName46": { "message": "Accès à distance" }, + "categoryName47": { "message": "Outil de développement" }, + "categoryName48": { "message": "Stockage réseau" }, + "categoryName49": { "message": "Lecteur RSS" }, + "categoryName50": { "message": "Système de gestion de documents" }, + "categoryName51": { "message": "Landing Page Builder" }, + "categoryName52": { "message": "Chat en direct" } +} diff --git a/src/drivers/webextension/_locales/gr/messages.json b/src/drivers/webextension/_locales/gr/messages.json new file mode 100644 index 000000000..f512dad8f --- /dev/null +++ b/src/drivers/webextension/_locales/gr/messages.json @@ -0,0 +1,60 @@ +{ + "github": { "message": "Κάνε fork το Wappalyzer στο GitHub!" }, + "twitter": { "message": "Ακολούθησε το Wappalyzer στο Twitter" }, + "website": { "message": "Πήγαινε στο wappalyzer.com" }, + "options": { "message": "Ρυθμίσεις" }, + "optionsSave": { "message": "Ρυθμίσεις αποθήκευσης" }, + "optionsSaved": { "message": "Αποθηκεύτηκε" }, + "optionUpgradeMessage": { "message": "Ενημερώστε με για αναβαθμίσεις" }, + "optionTracking": { "message": "Ανώνυμη αποστολή αναφορών για εντοπισμένες εφαρμογές στο wappalyzer.com για έρευνα" }, + "nothingToDo": { "message": "Καμία ενέργεια." }, + "noAppsDetected": { "message": "Δεν ανιχνεύθηκαν εφαρμογές." }, + "categoryName1": { "message": "CMS" }, + "categoryName2": { "message": "Διαδικτυακό Φόρουμ" }, + "categoryName3": { "message": "Διαχειριστής Βάσης Δεδομένων" }, + "categoryName4": { "message": "Εργαλείο Τεκμηρίωσης" }, + "categoryName5": { "message": "Widget" }, + "categoryName10": { "message": "Analytics" }, + "categoryName11": { "message": "Blog" }, + "categoryName12": { "message": "Framework της JavaScript" }, + "categoryName13": { "message": "Issue Tracker" }, + "categoryName14": { "message": "Πρόγραμμα αναπαραγωγής Βίντεο" }, + "categoryName15": { "message": "Σύστημα Σχολίων" }, + "categoryName16": { "message": "Captcha" }, + "categoryName17": { "message": "Script Γραμματοσειράς" }, + "categoryName18": { "message": "Framework Διαδικτύου" }, + "categoryName19": { "message": "Διάφορα" }, + "categoryName20": { "message": "Επεξεργαστής Κειμένου" }, + "categoryName21": { "message": "LMS" }, + "categoryName22": { "message": "Διακομιστής Διαδικτύου" }, + "categoryName23": { "message": "Εργαλείο Μνήμης Cache" }, + "categoryName24": { "message": "Επεξεργαστής Εμπλουτισμένου Κειμένου" }, + "categoryName25": { "message": "Γραφικά JavaScript" }, + "categoryName26": { "message": "Framework για Κινητά" }, + "categoryName27": { "message": "Γλώσσα Προγραμματισμού" }, + "categoryName28": { "message": "Λειτουργικό Σύστημα" }, + "categoryName29": { "message": "Μηχανή Αναζήτησης" }, + "categoryName30": { "message": "Web Mail" }, + "categoryName31": { "message": "CDN" }, + "categoryName32": { "message": "Αυτοματοποίηση Marketing" }, + "categoryName33": { "message": "Επέκταση Διακομιστή Διαδικτύου" }, + "categoryName34": { "message": "Βάση Δεδομένων" }, + "categoryName35": { "message": "Χάρτης" }, + "categoryName36": { "message": "Δίκτυο Διαφημίσεων" }, + "categoryName37": { "message": "Υπηρεσία Δικτύου" }, + "categoryName38": { "message": "Διακομιστής Πολυμέσων" }, + "categoryName39": { "message": "Διαδικτυακή κάμερα" }, + "categoryName40": { "message": "Εκτυπωτής" }, + "categoryName41": { "message": "Σύστημα Επεξεργασίας Πληρωμών" }, + "categoryName42": { "message": "Σύστημα Διαχείρισης Tags" }, + "categoryName43": { "message": "Paywall" }, + "categoryName44": { "message": "Σύστημα Build/CI" }, + "categoryName45": { "message": "Σύστημα SCADA" }, + "categoryName46": { "message": "Απομακρυσμένη Πρόσβαση" }, + "categoryName47": { "message": "Εργαλείο Ανάπτυξης" }, + "categoryName48": { "message": "Δικτυακός Αποθηκευτικός Χώρος" }, + "categoryName49": { "message": "Feed Readers" }, + "categoryName50": { "message": "Συστήματα Διαχειρίσης Εγγράφων" }, + "categoryName51": { "message": "Σύστημα Κατασκευής Σελίδων Υποδοχής" }, + "categoryName52": { "message": "Live Chat" } +} diff --git a/src/drivers/webextension/_locales/it/messages.json b/src/drivers/webextension/_locales/it/messages.json new file mode 100644 index 000000000..ede043b3b --- /dev/null +++ b/src/drivers/webextension/_locales/it/messages.json @@ -0,0 +1,65 @@ +{ + "github": { "message": "Fork Wappalyzer su GitHub!" }, + "twitter": { "message": "Follow Wappalyzer su Twitter" }, + "website": { "message": "Vai su wappalyzer.com" }, + "options": { "message": "Opzioni" }, + "optionsSave": { "message": "Salva opzioni" }, + "optionsSaved": { "message": "Salvato" }, + "optionUpgradeMessage": { "message": "Parlami dell'upgrade" }, + "optionTracking": { "message": "Inviare anonimamente un report sulle applicazioni rilevate a wappalyzer.com per l'analisi" }, + "nothingToDo": { "message": "Niente da fare qui." }, + "noAppsDetected": { "message": "Nessuna applicazione rilevata." }, + "categoryName1": { "message": "CMS" }, + "categoryName2": { "message": "Forum" }, + "categoryName3": { "message": "Gestore di Database" }, + "categoryName4": { "message": "Strumento di documentazione" }, + "categoryName5": { "message": "Widget" }, + "categoryName6": { "message": "eCommerce" }, + "categoryName7": { "message": "Galleria fotografica" }, + "categoryName8": { "message": "Wiki" }, + "categoryName9": { "message": "Pannello Hosting" }, + "categoryName10": { "message": "Analytics" }, + "categoryName11": { "message": "Blog" }, + "categoryName12": { "message": "Framework JavaScript" }, + "categoryName13": { "message": "Issue Tracker" }, + "categoryName14": { "message": "Player Video" }, + "categoryName15": { "message": "Sistema di commenti" }, + "categoryName16": { "message": "Captcha" }, + "categoryName17": { "message": "Font Script" }, + "categoryName18": { "message": "Framework Web" }, + "categoryName19": { "message": "Miscellanea" }, + "categoryName20": { "message": "Editor" }, + "categoryName21": { "message": "LMS" }, + "categoryName22": { "message": "Web Server" }, + "categoryName23": { "message": "Cache Tool" }, + "categoryName24": { "message": "Editor di Testo Ricco" }, + "categoryName25": { "message": "JavaScript Graphics" }, + "categoryName26": { "message": "Framework Mobile" }, + "categoryName27": { "message": "Linguaggio di programmazione" }, + "categoryName28": { "message": "Sistema Operativo" }, + "categoryName29": { "message": "Motore di Ricerca" }, + "categoryName30": { "message": "Web Mail" }, + "categoryName31": { "message": "CDN" }, + "categoryName32": { "message": "Marketing Automation" }, + "categoryName33": { "message": "Estensione Web Server" }, + "categoryName34": { "message": "Database" }, + "categoryName35": { "message": "Mappa" }, + "categoryName36": { "message": "Network Pubblicitario" }, + "categoryName37": { "message": "Network Service" }, + "categoryName38": { "message": "Media Server" }, + "categoryName39": { "message": "Webcam" }, + "categoryName40": { "message": "Stampante" }, + "categoryName41": { "message": "Payment Processor" }, + "categoryName42": { "message": "Tag Manager" }, + "categoryName43": { "message": "Paywall" }, + "categoryName44": { "message": "Sistema Build/CI" }, + "categoryName45": { "message": "SCADA System" }, + "categoryName46": { "message": "Accesso" }, + "categoryName47": { "message": "Strumenti di Sviluppo" }, + "categoryName48": { "message": "Network Storage" }, + "categoryName49": { "message": "Lettore di Feed" }, + "categoryName50": { "message": "Sistema di Gestione Documenti" }, + "categoryName51": { "message": "Landing Page Builder" }, + "categoryName52": { "message": "Live Chat" } +} + diff --git a/src/drivers/webextension/_locales/ro/messages.json b/src/drivers/webextension/_locales/ro/messages.json new file mode 100644 index 000000000..6d938bc67 --- /dev/null +++ b/src/drivers/webextension/_locales/ro/messages.json @@ -0,0 +1,60 @@ +{ + "github": { "message": "Fork-uiește Wappalyzer pe GitHub!" }, + "twitter": { "message": "Urmărește Wappalyzer pe Twitter" }, + "website": { "message": "Mergi la wappalyzer.com" }, + "options": { "message": "Opțiuni" }, + "optionsSave": { "message": "Salvează opțiuni" }, + "optionsSaved": { "message": "Salvat" }, + "optionUpgradeMessage": { "message": "Anunță-mă dacă sunt actualizări" }, + "optionTracking": { "message": "Trimite rapoarte anonime despre aplicațiile detectate către wappalyzer.com pentru cercetare" }, + "nothingToDo": { "message": "Nimic de făcut pe pagina curentă." }, + "noAppsDetected": { "message": "Nici o aplicație detectată." }, + "categoryName1": { "message": "CMS" }, + "categoryName2": { "message": "Forum de discuții" }, + "categoryName3": { "message": "Manager baze de date" }, + "categoryName4": { "message": "Unealtă pentru documentare" }, + "categoryName5": { "message": "Widget" }, + "categoryName10": { "message": "Analiză trafic web" }, + "categoryName11": { "message": "Blog" }, + "categoryName12": { "message": "Framework JavaScript" }, + "categoryName13": { "message": "Tracker probleme" }, + "categoryName14": { "message": "Player Video" }, + "categoryName15": { "message": "Sistem de comentarii" }, + "categoryName16": { "message": "Verificare Captcha" }, + "categoryName17": { "message": "Script pentru fonturi" }, + "categoryName18": { "message": "Framework Web" }, + "categoryName19": { "message": "Divers" }, + "categoryName20": { "message": "Editor" }, + "categoryName21": { "message": "LMS" }, + "categoryName22": { "message": "Server Web" }, + "categoryName23": { "message": "Unealtă Cache" }, + "categoryName24": { "message": "Editor Texte Rich" }, + "categoryName25": { "message": "Grafică JavaScript" }, + "categoryName26": { "message": "Framework Mobile" }, + "categoryName27": { "message": "Limbaj de programare" }, + "categoryName28": { "message": "Sistem de operare" }, + "categoryName29": { "message": "Motor de căutare" }, + "categoryName30": { "message": "Poștă electronică" }, + "categoryName31": { "message": "CDN" }, + "categoryName32": { "message": "Automatizare marketing" }, + "categoryName33": { "message": "Extensie server web" }, + "categoryName34": { "message": "Bază de date" }, + "categoryName35": { "message": "Hartă" }, + "categoryName36": { "message": "Rețea de advertising" }, + "categoryName37": { "message": "Serviciu rețea" }, + "categoryName38": { "message": "Server Media" }, + "categoryName39": { "message": "Webcam" }, + "categoryName40": { "message": "Imprimantă" }, + "categoryName41": { "message": "Sistem de plată" }, + "categoryName42": { "message": "Manager cuvinte cheie" }, + "categoryName43": { "message": "Paywall" }, + "categoryName44": { "message": "Build/CI System" }, + "categoryName45": { "message": "SCADA System" }, + "categoryName46": { "message": "Remote Access" }, + "categoryName47": { "message": "Development Tool" }, + "categoryName48": { "message": "Network Storage" }, + "categoryName49": { "message": "Feed Readers" }, + "categoryName50": { "message": "Document Management Systems" }, + "categoryName51": { "message": "Landing Page Builder" }, + "categoryName52": { "message": "Live Chat" } +} diff --git a/src/drivers/webextension/_locales/ru/messages.json b/src/drivers/webextension/_locales/ru/messages.json new file mode 100644 index 000000000..4cf46ecbc --- /dev/null +++ b/src/drivers/webextension/_locales/ru/messages.json @@ -0,0 +1,60 @@ +{ + "categoryName1" : { "message" : "CMS" }, + "categoryName2" : { "message" : "Форум" }, + "categoryName3" : { "message" : "Менеджер БД" }, + "categoryName4" : { "message" : "Документация" }, + "categoryName5" : { "message" : "Виджет" }, + "categoryName10" : { "message" : "Аналитика" }, + "categoryName11" : { "message" : "Блог" }, + "categoryName12" : { "message" : "JS фреймворк" }, + "categoryName13" : { "message" : "Баг трекер" }, + "categoryName14" : { "message" : "Видео плеер" }, + "categoryName15" : { "message" : "Система комментариев" }, + "categoryName16" : { "message" : "Капча" }, + "categoryName17" : { "message" : "Шрифт" }, + "categoryName18" : { "message" : "Веб фреймворк" }, + "categoryName19" : { "message" : "Прочее" }, + "categoryName20" : { "message" : "HTML редактор" }, + "categoryName21" : { "message" : "LMS" }, + "categoryName22" : { "message" : "Веб сервер" }, + "categoryName23" : { "message" : "Кеширование" }, + "categoryName24" : { "message" : "WYSIWYG редактор" }, + "categoryName25" : { "message" : "JS графика" }, + "categoryName26" : { "message" : "Мобильный фреймворк" }, + "categoryName27" : { "message" : "Язык программирования" }, + "categoryName28" : { "message" : "Операционная система" }, + "categoryName29" : { "message" : "Поисковый движок" }, + "categoryName30" : { "message" : "Веб почта" }, + "categoryName31" : { "message" : "CDN" }, + "categoryName32" : { "message" : "Управление маркетингом" }, + "categoryName33" : { "message" : "Расширение Веб сервера" }, + "categoryName34" : { "message" : "База данных" }, + "categoryName35" : { "message" : "Карта" }, + "categoryName36" : { "message" : "Рекламная сеть" }, + "categoryName37" : { "message" : "Сетевая служба" }, + "categoryName38" : { "message" : "Медиа сервер" }, + "categoryName39" : { "message" : "Вебкамера" }, + "categoryName40" : { "message" : "Принтер" }, + "categoryName41" : { "message" : "Провайдер платежей" }, + "categoryName42" : { "message" : "Менеджер тэгов" }, + "categoryName43" : { "message" : "Paywall" }, + "categoryName44" : { "message" : "Система непрерывной интеграции" }, + "categoryName45" : { "message" : "Система SCADA" }, + "categoryName46" : { "message" : "Удаленное управление" }, + "categoryName47" : { "message" : "Утилита для разработчиков" }, + "categoryName48" : { "message" : "Сетевое хранилище" }, + "categoryName49" : { "message" : "Граббер контента" }, + "categoryName50" : { "message" : "Управление документами" }, + "categoryName51" : { "message": "Генератор лендингов" }, + "categoryName52" : { "message": "Live Chat" }, + "github" : { "message" : "Форкнуть на GitHub!" }, + "noAppsDetected" : { "message" : "Нет данных о сайте" }, + "nothingToDo" : { "message" : "Тут нечего искать" }, + "optionTracking" : { "message" : "Анонимно отправлять статистику распознанных данных на сервер (для улучшения расширения)" }, + "optionUpgradeMessage" : { "message" : "Оповещать меня о новых обновлениях" }, + "options" : { "message" : "Настройки" }, + "optionsSave" : { "message" : "Сохранить" }, + "optionsSaved" : { "message" : "Успешно сохранено!" }, + "twitter" : { "message" : "Следите за новостями в Твиттере" }, + "website" : { "message" : "Перейти на Wappalyzer.com" } +} diff --git a/src/drivers/webextension/background.html b/src/drivers/webextension/background.html new file mode 100644 index 000000000..06e298abf --- /dev/null +++ b/src/drivers/webextension/background.html @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/drivers/webextension/css/options.css b/src/drivers/webextension/css/options.css new file mode 100644 index 000000000..bd6096f5f --- /dev/null +++ b/src/drivers/webextension/css/options.css @@ -0,0 +1,106 @@ +body { + color: #303942; + cursor: default; + font-family: Helvetica, Arial, sans-serif; + font-size: .8rem; + line-height: 1.4rem; + margin: 0; +} + +p { + margin: 0 0 1rem 0; +} + +h1, h2, h3 { + font-weight: normal; + line-height: 1; +} + +h1 { + border-bottom: 1px solid #dbdbdb; + font-size: 1.5rem; + margin: 0 0 1.5rem 0; + padding: 1rem 0 1.5rem 0; +} + +h2 { + font-size: 1.3em; + margin-bottom: 0.4em; +} + +h3 { + color: black; + font-size: 1.2em; + margin-bottom: 0.5em; +} + +a { + color: rgb(17, 85, 204); + text-decoration: underline; +} + +label { + display: block; +} + +button { + background: #4608ad; + border: none; + border-radius: .2rem; + color: white; + font-size: inherit; + padding: 0 .6rem; + line-height: 1.8rem; +} + +a:active { + color: rgb(5, 37, 119); +} + +.hero { + background: linear-gradient(160deg, #32067c, #150233); + padding: 1.5rem 0 1rem 1.5rem; +} + + .hero img { + height: 3rem; + } + +.container { + margin: 0 auto; + max-width: 800px; +} + +.content { + padding: 1.5rem; +} + +#options-saved { + display: none; + margin-left: .5rem; + -webkit-animation: fadeout 2s; +} + +#about { + border-top: 1px solid #dbdbdb; + margin-top: 2.5rem; + padding: 1.5rem 0 1.5rem 0; +} + + #about img { + margin-right: .2rem; + vertical-align: middle; + } + + #about button { + background: white; + border: 1px solid #dbdbdb; + cursor: pointer; + color: #303942; + margin: 0 1rem .5rem 0; + } + +@-webkit-keyframes fadeout { + from { opacity: 1; } + to { opacity: 0; } +} diff --git a/src/drivers/webextension/css/popup.css b/src/drivers/webextension/css/popup.css new file mode 100644 index 000000000..6c91d5888 --- /dev/null +++ b/src/drivers/webextension/css/popup.css @@ -0,0 +1,96 @@ +body { + background: #fff; + color: #4a4a4a; + font-family: Helvetica, Arial, sans-serif; + font-size: 13px; + line-height: 16px; + margin: 0; + min-width: 200px; + padding: 15px; +} + +a { + color: #4a4a4a; +} + +a:focus { + outline: 0; +} + +img { + display: inline-block; + height: 16px; + margin-right: 8px; + vertical-align: top; + width: 16px; +} + +.detected-app { + padding: 7px 0; +} + +.detected-app:first-child { + padding-top: 0; +} + +.detected-app:last-child { + border: none; + padding-bottom: 0; +} + + .detected-app a { + color: #4608ad; + display: block; + text-decoration: none; + } + + .detected-app a .label .name { + border-bottom: 1px solid transparent; + } + + .detected-app a:hover .label .name { + border-bottom: 1px solid #4608ad; + } + + .detected-app a .category .name { + color: #4a4a4a; + border-bottom: 1px solid transparent; + } + + .detected-app a:hover .category .name { + border-bottom: 1px solid #4a4a4a; + } + +.label { + font-weight: bold; +} + +.category { + display: block; + margin: 5px 0 0 24px; +} + +.empty { + color: #999; + font-style: italic; + text-align: center; +} + +#footer { + border-top: 1px solid #ccc; + margin-top: 17px; + overflow: hidden; + padding-top: 11px; +} + +#footer a { + text-decoration: none; +} + +#footer a:hover { + border-bottom: 1px solid #dbdbdb; +} + +#options { + float: right; +} diff --git a/src/drivers/webextension/images/github.png b/src/drivers/webextension/images/github.png new file mode 100644 index 000000000..037d0c336 Binary files /dev/null and b/src/drivers/webextension/images/github.png differ diff --git a/src/drivers/webextension/images/icon_128.png b/src/drivers/webextension/images/icon_128.png new file mode 100644 index 000000000..05ad20d8f Binary files /dev/null and b/src/drivers/webextension/images/icon_128.png differ diff --git a/src/drivers/webextension/images/icon_16.png b/src/drivers/webextension/images/icon_16.png new file mode 100644 index 000000000..31e9b7c86 Binary files /dev/null and b/src/drivers/webextension/images/icon_16.png differ diff --git a/src/drivers/webextension/images/icon_19.png b/src/drivers/webextension/images/icon_19.png new file mode 100644 index 000000000..9668bc65a Binary files /dev/null and b/src/drivers/webextension/images/icon_19.png differ diff --git a/src/drivers/webextension/images/icon_32.png b/src/drivers/webextension/images/icon_32.png new file mode 100644 index 000000000..cc9d30b15 Binary files /dev/null and b/src/drivers/webextension/images/icon_32.png differ diff --git a/src/drivers/webextension/images/icon_38.png b/src/drivers/webextension/images/icon_38.png new file mode 100644 index 000000000..95f220ff3 Binary files /dev/null and b/src/drivers/webextension/images/icon_38.png differ diff --git a/src/drivers/webextension/images/icons/.gitkeep b/src/drivers/webextension/images/icons/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/drivers/webextension/images/icons/converted/.gitkeep b/src/drivers/webextension/images/icons/converted/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/drivers/webextension/images/logo-white.svg b/src/drivers/webextension/images/logo-white.svg new file mode 100644 index 000000000..bfbecc41b --- /dev/null +++ b/src/drivers/webextension/images/logo-white.svg @@ -0,0 +1,29 @@ + + + + Logo-White + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/drivers/webextension/images/twitter.png b/src/drivers/webextension/images/twitter.png new file mode 100644 index 000000000..e20284ddf Binary files /dev/null and b/src/drivers/webextension/images/twitter.png differ diff --git a/src/drivers/webextension/js/browser-polyfill.js b/src/drivers/webextension/js/browser-polyfill.js new file mode 100644 index 000000000..76cb766e1 --- /dev/null +++ b/src/drivers/webextension/js/browser-polyfill.js @@ -0,0 +1,851 @@ +/* webextension-polyfill - v0.1.0 - Sat Mar 11 2017 11:35:13 */ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +if (typeof browser === "undefined") { + // Wrapping the bulk of this polyfill in a one-time-use function is a minor + // optimization for Firefox. Since Spidermonkey does not fully parse the + // contents of a function until the first time it's called, and since it will + // never actually need to be called, this allows the polyfill to be included + // in Firefox nearly for free. + const wrapAPIs = () => { + const apiMetadata = { + "alarms": { + "clear": { + "minArgs": 0, + "maxArgs": 1 + }, + "clearAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "get": { + "minArgs": 0, + "maxArgs": 1 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "bookmarks": { + "create": { + "minArgs": 1, + "maxArgs": 1 + }, + "export": { + "minArgs": 0, + "maxArgs": 0 + }, + "get": { + "minArgs": 1, + "maxArgs": 1 + }, + "getChildren": { + "minArgs": 1, + "maxArgs": 1 + }, + "getRecent": { + "minArgs": 1, + "maxArgs": 1 + }, + "getTree": { + "minArgs": 0, + "maxArgs": 0 + }, + "getSubTree": { + "minArgs": 1, + "maxArgs": 1 + }, + "import": { + "minArgs": 0, + "maxArgs": 0 + }, + "move": { + "minArgs": 2, + "maxArgs": 2 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeTree": { + "minArgs": 1, + "maxArgs": 1 + }, + "search": { + "minArgs": 1, + "maxArgs": 1 + }, + "update": { + "minArgs": 2, + "maxArgs": 2 + } + }, + "browserAction": { + "getBadgeBackgroundColor": { + "minArgs": 1, + "maxArgs": 1 + }, + "getBadgeText": { + "minArgs": 1, + "maxArgs": 1 + }, + "getPopup": { + "minArgs": 1, + "maxArgs": 1 + }, + "getTitle": { + "minArgs": 1, + "maxArgs": 1 + }, + "setIcon": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "commands": { + "getAll": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "contextMenus": { + "update": { + "minArgs": 2, + "maxArgs": 2 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeAll": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "cookies": { + "get": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAll": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAllCookieStores": { + "minArgs": 0, + "maxArgs": 0 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "set": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "downloads": { + "download": { + "minArgs": 1, + "maxArgs": 1 + }, + "cancel": { + "minArgs": 1, + "maxArgs": 1 + }, + "erase": { + "minArgs": 1, + "maxArgs": 1 + }, + "getFileIcon": { + "minArgs": 1, + "maxArgs": 2 + }, + "open": { + "minArgs": 1, + "maxArgs": 1 + }, + "pause": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeFile": { + "minArgs": 1, + "maxArgs": 1 + }, + "resume": { + "minArgs": 1, + "maxArgs": 1 + }, + "search": { + "minArgs": 1, + "maxArgs": 1 + }, + "show": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "extension": { + "isAllowedFileSchemeAccess": { + "minArgs": 0, + "maxArgs": 0 + }, + "isAllowedIncognitoAccess": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "history": { + "addUrl": { + "minArgs": 1, + "maxArgs": 1 + }, + "getVisits": { + "minArgs": 1, + "maxArgs": 1 + }, + "deleteAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "deleteRange": { + "minArgs": 1, + "maxArgs": 1 + }, + "deleteUrl": { + "minArgs": 1, + "maxArgs": 1 + }, + "search": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "i18n": { + "detectLanguage": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAcceptLanguages": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "idle": { + "queryState": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "management": { + "get": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "getSelf": { + "minArgs": 0, + "maxArgs": 0 + }, + "uninstallSelf": { + "minArgs": 0, + "maxArgs": 1 + } + }, + "notifications": { + "clear": { + "minArgs": 1, + "maxArgs": 1 + }, + "create": { + "minArgs": 1, + "maxArgs": 2 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "getPermissionLevel": { + "minArgs": 0, + "maxArgs": 0 + }, + "update": { + "minArgs": 2, + "maxArgs": 2 + } + }, + "pageAction": { + "getPopup": { + "minArgs": 1, + "maxArgs": 1 + }, + "getTitle": { + "minArgs": 1, + "maxArgs": 1 + }, + "hide": { + "minArgs": 0, + "maxArgs": 0 + }, + "setIcon": { + "minArgs": 1, + "maxArgs": 1 + }, + "show": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "runtime": { + "getBackgroundPage": { + "minArgs": 0, + "maxArgs": 0 + }, + "getBrowserInfo": { + "minArgs": 0, + "maxArgs": 0 + }, + "getPlatformInfo": { + "minArgs": 0, + "maxArgs": 0 + }, + "openOptionsPage": { + "minArgs": 0, + "maxArgs": 0 + }, + "requestUpdateCheck": { + "minArgs": 0, + "maxArgs": 0 + }, + "sendMessage": { + "minArgs": 1, + "maxArgs": 3 + }, + "sendNativeMessage": { + "minArgs": 2, + "maxArgs": 2 + }, + "setUninstallURL": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "storage": { + "local": { + "clear": { + "minArgs": 0, + "maxArgs": 0 + }, + "get": { + "minArgs": 0, + "maxArgs": 1 + }, + "getBytesInUse": { + "minArgs": 0, + "maxArgs": 1 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "set": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "managed": { + "get": { + "minArgs": 0, + "maxArgs": 1 + }, + "getBytesInUse": { + "minArgs": 0, + "maxArgs": 1 + } + }, + "sync": { + "clear": { + "minArgs": 0, + "maxArgs": 0 + }, + "get": { + "minArgs": 0, + "maxArgs": 1 + }, + "getBytesInUse": { + "minArgs": 0, + "maxArgs": 1 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "set": { + "minArgs": 1, + "maxArgs": 1 + } + } + }, + "tabs": { + "create": { + "minArgs": 1, + "maxArgs": 1 + }, + "captureVisibleTab": { + "minArgs": 0, + "maxArgs": 2 + }, + "detectLanguage": { + "minArgs": 0, + "maxArgs": 1 + }, + "duplicate": { + "minArgs": 1, + "maxArgs": 1 + }, + "executeScript": { + "minArgs": 1, + "maxArgs": 2 + }, + "get": { + "minArgs": 1, + "maxArgs": 1 + }, + "getCurrent": { + "minArgs": 0, + "maxArgs": 0 + }, + "getZoom": { + "minArgs": 0, + "maxArgs": 1 + }, + "getZoomSettings": { + "minArgs": 0, + "maxArgs": 1 + }, + "highlight": { + "minArgs": 1, + "maxArgs": 1 + }, + "insertCSS": { + "minArgs": 1, + "maxArgs": 2 + }, + "move": { + "minArgs": 2, + "maxArgs": 2 + }, + "reload": { + "minArgs": 0, + "maxArgs": 2 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "query": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeCSS": { + "minArgs": 1, + "maxArgs": 2 + }, + "sendMessage": { + "minArgs": 2, + "maxArgs": 3 + }, + "setZoom": { + "minArgs": 1, + "maxArgs": 2 + }, + "setZoomSettings": { + "minArgs": 1, + "maxArgs": 2 + }, + "update": { + "minArgs": 1, + "maxArgs": 2 + } + }, + "webNavigation": { + "getAllFrames": { + "minArgs": 1, + "maxArgs": 1 + }, + "getFrame": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "webRequest": { + "handlerBehaviorChanged": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "windows": { + "create": { + "minArgs": 0, + "maxArgs": 1 + }, + "get": { + "minArgs": 1, + "maxArgs": 2 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 1 + }, + "getCurrent": { + "minArgs": 0, + "maxArgs": 1 + }, + "getLastFocused": { + "minArgs": 0, + "maxArgs": 1 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "update": { + "minArgs": 2, + "maxArgs": 2 + } + } + }; + + /** + * A WeakMap subclass which creates and stores a value for any key which does + * not exist when accessed, but behaves exactly as an ordinary WeakMap + * otherwise. + * + * @param {function} createItem + * A function which will be called in order to create the value for any + * key which does not exist, the first time it is accessed. The + * function receives, as its only argument, the key being created. + */ + class DefaultWeakMap extends WeakMap { + constructor(createItem, items = undefined) { + super(items); + this.createItem = createItem; + } + + get(key) { + if (!this.has(key)) { + this.set(key, this.createItem(key)); + } + + return super.get(key); + } + } + + /** + * Returns true if the given object is an object with a `then` method, and can + * therefore be assumed to behave as a Promise. + * + * @param {*} value The value to test. + * @returns {boolean} True if the value is thenable. + */ + const isThenable = value => { + return value && typeof value === "object" && typeof value.then === "function"; + }; + + /** + * Creates and returns a function which, when called, will resolve or reject + * the given promise based on how it is called: + * + * - If, when called, `chrome.runtime.lastError` contains a non-null object, + * the promise is rejected with that value. + * - If the function is called with exactly one argument, the promise is + * resolved to that value. + * - Otherwise, the promise is resolved to an array containing all of the + * function's arguments. + * + * @param {object} promise + * An object containing the resolution and rejection functions of a + * promise. + * @param {function} promise.resolve + * The promise's resolution function. + * @param {function} promise.rejection + * The promise's rejection function. + * + * @returns {function} + * The generated callback function. + */ + const makeCallback = promise => { + return (...callbackArgs) => { + if (chrome.runtime.lastError) { + promise.reject(chrome.runtime.lastError); + } else if (callbackArgs.length === 1) { + promise.resolve(callbackArgs[0]); + } else { + promise.resolve(callbackArgs); + } + }; + }; + + /** + * Creates a wrapper function for a method with the given name and metadata. + * + * @param {string} name + * The name of the method which is being wrapped. + * @param {object} metadata + * Metadata about the method being wrapped. + * @param {integer} metadata.minArgs + * The minimum number of arguments which must be passed to the + * function. If called with fewer than this number of arguments, the + * wrapper will raise an exception. + * @param {integer} metadata.maxArgs + * The maximum number of arguments which may be passed to the + * function. If called with more than this number of arguments, the + * wrapper will raise an exception. + * + * @returns {function(object, ...*)} + * The generated wrapper function. + */ + const wrapAsyncFunction = (name, metadata) => { + const pluralizeArguments = (numArgs) => numArgs == 1 ? "argument" : "arguments"; + + return function asyncFunctionWrapper(target, ...args) { + if (args.length < metadata.minArgs) { + throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`); + } + + if (args.length > metadata.maxArgs) { + throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`); + } + + return new Promise((resolve, reject) => { + target[name](...args, makeCallback({resolve, reject})); + }); + }; + }; + + /** + * Wraps an existing method of the target object, so that calls to it are + * intercepted by the given wrapper function. The wrapper function receives, + * as its first argument, the original `target` object, followed by each of + * the arguments passed to the orginal method. + * + * @param {object} target + * The original target object that the wrapped method belongs to. + * @param {function} method + * The method being wrapped. This is used as the target of the Proxy + * object which is created to wrap the method. + * @param {function} wrapper + * The wrapper function which is called in place of a direct invocation + * of the wrapped method. + * + * @returns {Proxy} + * A Proxy object for the given method, which invokes the given wrapper + * method in its place. + */ + const wrapMethod = (target, method, wrapper) => { + return new Proxy(method, { + apply(targetMethod, thisObj, args) { + return wrapper.call(thisObj, target, ...args); + }, + }); + }; + + let hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty); + + /** + * Wraps an object in a Proxy which intercepts and wraps certain methods + * based on the given `wrappers` and `metadata` objects. + * + * @param {object} target + * The target object to wrap. + * + * @param {object} [wrappers = {}] + * An object tree containing wrapper functions for special cases. Any + * function present in this object tree is called in place of the + * method in the same location in the `target` object tree. These + * wrapper methods are invoked as described in {@see wrapMethod}. + * + * @param {object} [metadata = {}] + * An object tree containing metadata used to automatically generate + * Promise-based wrapper functions for asynchronous. Any function in + * the `target` object tree which has a corresponding metadata object + * in the same location in the `metadata` tree is replaced with an + * automatically-generated wrapper function, as described in + * {@see wrapAsyncFunction} + * + * @returns {Proxy} + */ + const wrapObject = (target, wrappers = {}, metadata = {}) => { + let cache = Object.create(null); + + let handlers = { + has(target, prop) { + return prop in target || prop in cache; + }, + + get(target, prop, receiver) { + if (prop in cache) { + return cache[prop]; + } + + if (!(prop in target)) { + return undefined; + } + + let value = target[prop]; + + if (typeof value === "function") { + // This is a method on the underlying object. Check if we need to do + // any wrapping. + + if (typeof wrappers[prop] === "function") { + // We have a special-case wrapper for this method. + value = wrapMethod(target, target[prop], wrappers[prop]); + } else if (hasOwnProperty(metadata, prop)) { + // This is an async method that we have metadata for. Create a + // Promise wrapper for it. + let wrapper = wrapAsyncFunction(prop, metadata[prop]); + value = wrapMethod(target, target[prop], wrapper); + } else { + // This is a method that we don't know or care about. Return the + // original method, bound to the underlying object. + value = value.bind(target); + } + } else if (typeof value === "object" && value !== null && + (hasOwnProperty(wrappers, prop) || + hasOwnProperty(metadata, prop))) { + // This is an object that we need to do some wrapping for the children + // of. Create a sub-object wrapper for it with the appropriate child + // metadata. + value = wrapObject(value, wrappers[prop], metadata[prop]); + } else { + // We don't need to do any wrapping for this property, + // so just forward all access to the underlying object. + Object.defineProperty(cache, prop, { + configurable: true, + enumerable: true, + get() { + return target[prop]; + }, + set(value) { + target[prop] = value; + }, + }); + + return value; + } + + cache[prop] = value; + return value; + }, + + set(target, prop, value, receiver) { + if (prop in cache) { + cache[prop] = value; + } else { + target[prop] = value; + } + return true; + }, + + defineProperty(target, prop, desc) { + return Reflect.defineProperty(cache, prop, desc); + }, + + deleteProperty(target, prop) { + return Reflect.deleteProperty(cache, prop); + }, + }; + + return new Proxy(target, handlers); + }; + + /** + * Creates a set of wrapper functions for an event object, which handles + * wrapping of listener functions that those messages are passed. + * + * A single wrapper is created for each listener function, and stored in a + * map. Subsequent calls to `addListener`, `hasListener`, or `removeListener` + * retrieve the original wrapper, so that attempts to remove a + * previously-added listener work as expected. + * + * @param {DefaultWeakMap} wrapperMap + * A DefaultWeakMap object which will create the appropriate wrapper + * for a given listener function when one does not exist, and retrieve + * an existing one when it does. + * + * @returns {object} + */ + const wrapEvent = wrapperMap => ({ + addListener(target, listener, ...args) { + target.addListener(wrapperMap.get(listener), ...args); + }, + + hasListener(target, listener) { + return target.hasListener(wrapperMap.get(listener)); + }, + + removeListener(target, listener) { + target.removeListener(wrapperMap.get(listener)); + }, + }); + + const onMessageWrappers = new DefaultWeakMap(listener => { + if (typeof listener !== "function") { + return listener; + } + + /** + * Wraps a message listener function so that it may send responses based on + * its return value, rather than by returning a sentinel value and calling a + * callback. If the listener function returns a Promise, the response is + * sent when the promise either resolves or rejects. + * + * @param {*} message + * The message sent by the other end of the channel. + * @param {object} sender + * Details about the sender of the message. + * @param {function(*)} sendResponse + * A callback which, when called with an arbitrary argument, sends + * that value as a response. + * @returns {boolean} + * True if the wrapped listener returned a Promise, which will later + * yield a response. False otherwise. + */ + return function onMessage(message, sender, sendResponse) { + let result = listener(message, sender); + + if (isThenable(result)) { + result.then(sendResponse, error => { + console.error(error); + sendResponse(error); + }); + + return true; + } else if (result !== undefined) { + sendResponse(result); + } + }; + }); + + const staticWrappers = { + runtime: { + onMessage: wrapEvent(onMessageWrappers), + }, + }; + + return wrapObject(chrome, staticWrappers, apiMetadata); + }; + + this.browser = wrapAPIs(); +} diff --git a/src/drivers/webextension/js/content.js b/src/drivers/webextension/js/content.js new file mode 100644 index 000000000..e394e8eb7 --- /dev/null +++ b/src/drivers/webextension/js/content.js @@ -0,0 +1,61 @@ +(function() { + var c = { + init: function() { + var html = document.documentElement.outerHTML; + + c.log('init'); + + if ( html.length > 50000 ) { + html = html.substring(0, 25000) + html.substring(html.length - 25000, html.length); + } + + browser.runtime.sendMessage({ id: 'analyze', subject: { html: html } }); + + c.getEnvironmentVars(); + }, + + log: function(message) { + browser.runtime.sendMessage({ id: 'log', message: '[ content.js ] ' + message }); + }, + + getEnvironmentVars: function() { + var container, script; + + c.log('getEnvironmentVars'); + + if ( typeof document.documentElement.innerHTML === 'undefined' ) { + return; + } + + try { + container = document.createElement('wappalyzerData'); + + container.setAttribute('id', 'wappalyzerData'); + container.setAttribute('style', 'display: none'); + + script = document.createElement('script'); + + script.setAttribute('id', 'wappalyzerEnvDetection'); + script.setAttribute('src', browser.extension.getURL('js/inject.js')); + + container.addEventListener('wappalyzerEvent', (function(event) { + var environmentVars = event.target.childNodes[0].nodeValue; + + document.documentElement.removeChild(container); + document.documentElement.removeChild(script); + + environmentVars = environmentVars.split(' ').slice(0, 500); + + browser.runtime.sendMessage({ id: 'analyze', subject: { env: environmentVars } }); + }), true); + + document.documentElement.appendChild(container); + document.documentElement.appendChild(script); + } catch(e) { + c.log('Error: ' + e); + } + } + } + + c.init(); +}()); diff --git a/src/drivers/webextension/js/defaults.js b/src/drivers/webextension/js/defaults.js new file mode 100644 index 000000000..3ec2d3e09 --- /dev/null +++ b/src/drivers/webextension/js/defaults.js @@ -0,0 +1,5 @@ +var defaults = { + autoAnalyzeHeaders: 0, + upgradeMessage: 1, + tracking: 1 +}; diff --git a/src/drivers/webextension/js/driver.js b/src/drivers/webextension/js/driver.js new file mode 100644 index 000000000..eeda6b920 --- /dev/null +++ b/src/drivers/webextension/js/driver.js @@ -0,0 +1,306 @@ +/** + * Chrome driver + */ + +(function() { + if ( wappalyzer == null ) { + return; + } + + var w = wappalyzer, + firstRun = false, + upgraded = false, + tab, + tabCache = {}, + headersCache = {}; + + w.driver = { + timeout: 1000, + + /** + * Log messages to console + */ + log: function(args) { + console.log('[wappalyzer ' + args.type + '] ' + args.message); + }, + + /** + * Initialize + */ + init: function() { + w.log('init'); + + // Load apps.json + var xhr = new XMLHttpRequest(); + + xhr.open('GET', 'apps.json', true); + + xhr.overrideMimeType('application/json'); + + xhr.onload = function() { + var json = JSON.parse(xhr.responseText); + + w.categories = json.categories; + w.apps = json.apps; + }; + + xhr.send(null); + + // Version check + try { + var version = browser.app.getDetails().version; + + if ( localStorage['version'] == null ) { + firstRun = true; + + // Set defaults + for ( option in defaults ) { + localStorage[option] = defaults[option]; + } + } else if ( version !== localStorage['version'] && parseInt(localStorage['upgradeMessage'], 10) ) { + upgraded = true; + } + + localStorage['version'] = version; + } catch(e) { } + + browser.runtime.onMessage.addListener(function(message, sender, sendResponse) { + var + hostname, + a = document.createElement('a'); + + if ( typeof message.id != 'undefined' ) { + w.log('message: ' + message.id); + + switch ( message.id ) { + case 'log': + w.log(message.message); + + break; + case 'analyze': + tab = sender.tab; + + a.href = tab.url.replace(/#.*$/, ''); + + hostname = a.hostname; + + if ( headersCache[a.href] !== undefined ) { + message.subject.headers = headersCache[a.href]; + } + + w.analyze(hostname, a.href, message.subject); + + break; + case 'ad_log': + w.adCache.push(message.subject); + + break; + case 'get_apps': + sendResponse({ + tabCache: tabCache[message.tab.id], + apps: w.apps, + categories: w.categories + }); + + break; + } + } + }); + + browser.tabs.query({}).then(function(tabs) { + tabs.forEach(function(tab) { + if ( tab.url.match(/^https?:\/\//) ) { + browser.tabs.executeScript(tab.id, { file: 'js/content.js' }); + } + }) + }); + + browser.tabs.onRemoved.addListener(function(tabId) { + w.log('remove tab'); + + tabCache[tabId] = null; + }); + + // Live intercept headers using webRequest API + browser.webRequest.onCompleted.addListener(function(details) { + var responseHeaders = {}; + + if ( details.responseHeaders ) { + var uri = details.url.replace(/#.*$/, ''); // Remove hash + + details.responseHeaders.forEach(function(header) { + responseHeaders[header.name.toLowerCase()] = header.value || '' + header.binaryValue; + }); + + if ( headersCache.length > 50 ) { + headersCache = {}; + } + + if ( /text\/html/.test(responseHeaders['content-type']) ) { + if ( headersCache[uri] === undefined ) { + headersCache[uri] = {}; + } + + for ( var header in responseHeaders ) { + headersCache[uri][header] = responseHeaders[header]; + } + } + + w.log(JSON.stringify({ uri: uri, headers: responseHeaders })); + } + }, { urls: [ 'http://*/*', 'https://*/*' ], types: [ 'main_frame' ] }, [ 'responseHeaders' ]); + + if ( firstRun ) { + w.driver.goToURL({ url: w.config.websiteURL + 'installed', medium: 'install' }); + + firstRun = false; + } + + if ( upgraded ) { + w.driver.goToURL({ url: w.config.websiteURL + 'upgraded', medium: 'upgrade', background: true }); + + upgraded = false; + } + }, + + goToURL: function(args) { + var url = args.url + ( typeof args.medium === 'undefined' ? '' : '?pk_campaign=chrome&pk_kwd=' + args.medium); + + browser.tabs.create({ url: url, active: args.background === undefined || !args.background }); + }, + + /** + * Display apps + */ + displayApps: function() { + var + url = tab.url.replace(/#.*$/, ''), + count = w.detected[url] ? Object.keys(w.detected[url]).length.toString() : '0'; + + if ( tabCache[tab.id] == null ) { + tabCache[tab.id] = { + count: 0, + appsDetected: [] + }; + } + + tabCache[tab.id].count = count; + tabCache[tab.id].appsDetected = w.detected[url]; + + if ( count > 0 ) { + // Find the main application to display + var i, appName, found = false; + + w.driver.categoryOrder.forEach(function(match) { + for ( appName in w.detected[url] ) { + w.apps[appName].cats.forEach(function(cat) { + var icon = w.apps[appName].icon; + + if ( cat == match && !found ) { + if ( /\.svg$/i.test(icon) ) { + icon = 'converted/' + icon + '.png'; + } + + browser.pageAction.setIcon({ tabId: tab.id, path: 'images/icons/' + icon }); + + found = true; + } + }); + } + }); + + if ( typeof chrome !== 'undefined' ) { + // Browser polyfill doesn't seem to work here + chrome.pageAction.show(tab.id); + } else { + browser.pageAction.show(tab.id); + } + }; + }, + + /** + * Anonymously track detected applications for research purposes + */ + ping: function() { + if ( Object.keys(w.ping.hostnames).length && parseInt(localStorage['tracking'], 10) ) { + w.driver.post('http://ping.wappalyzer.com/v2/', w.ping); + + w.log('w.driver.ping: ' + JSON.stringify(w.ping)); + + w.ping = { hostnames: {} }; + + w.driver.post('https://ad.wappalyzer.com/log/wp/', w.adCache); + + w.adCache = []; + } + }, + + /** + * Make POST request + */ + post: function(url, data) { + var xhr = new XMLHttpRequest(); + + xhr.open('POST', url, true); + + xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + + xhr.onreadystatechange = function(e) { + if ( xhr.readyState == 4 ) { + w.log('w.driver.post: status ' + xhr.status + ' (' + url + ')'); + } + }; + + xhr.send('json=' + encodeURIComponent(JSON.stringify(data))); + }, + + categoryOrder: [ // Used to pick the main application + 1, // CMS + 11, // Blog + 6, // Web Shop + 2, // Message Board + 51, // Landing Page Builder + 8, // Wiki + 13, // Issue Tracker + 30, // Web Mail + 18, // Web Framework + 21, // LMS + 7, // Photo Gallery + 38, // Media Server + 3, // Database Manager + 34, // Database + 4, // Documentation Tool + 9, // Hosting Panel + 29, // Search Engine + 12, // JavaScript Framework + 26, // Mobile Framework + 25, // JavaScript Graphics + 22, // Web Server + 27, // Programming Language + 28, // Operating System + 15, // Comment System + 20, // Editor + 41, // Payment Processor + 10, // Analytics + 32, // Marketing Automation + 31, // CDN + 23, // Cache Tool + 17, // Font Script + 24, // Rich Text Editor + 35, // Map + 5, // Widget + 14, // Video Player + 16, // Captcha + 33, // Web Server Extension + 37, // Network Device + 39, // Webcam + 40, // Printer + 36, // Advertising Network + 42, // Tag Managers + 43, // Paywalls + 19 // Miscellaneous + ] + }; + + w.init(); +}()); diff --git a/src/drivers/webextension/js/i18n.js b/src/drivers/webextension/js/i18n.js new file mode 100644 index 000000000..bf9c7e2bc --- /dev/null +++ b/src/drivers/webextension/js/i18n.js @@ -0,0 +1,11 @@ +document.addEventListener('DOMContentLoaded', function() { + var + i, value, + nodes = document.getElementsByTagName('*'); + + for ( i = 0; i < nodes.length; i ++ ) { + if ( attr = nodes[i].dataset.i18n ) { + nodes[i].innerHTML = browser.i18n.getMessage(attr); + } + } +}); diff --git a/src/drivers/webextension/js/inject.js b/src/drivers/webextension/js/inject.js new file mode 100644 index 000000000..fd734182e --- /dev/null +++ b/src/drivers/webextension/js/inject.js @@ -0,0 +1,14 @@ +(function() { + try { + var i, environmentVars, e = document.createEvent('Events'); + + e.initEvent('wappalyzerEvent', true, false); + + for ( i in window ) { + environmentVars += i + ' '; + } + + document.getElementById('wappalyzerData').appendChild(document.createComment(environmentVars)); + document.getElementById('wappalyzerData').dispatchEvent(e); + } catch(e) { } +}()); diff --git a/src/drivers/webextension/js/options.js b/src/drivers/webextension/js/options.js new file mode 100644 index 000000000..71974de97 --- /dev/null +++ b/src/drivers/webextension/js/options.js @@ -0,0 +1,54 @@ +document.addEventListener('DOMContentLoaded', function() { + var d = document; + + var options = { + opts: defaults, + + init: function() { + options.load(); + + d.getElementById('github' ).addEventListener('click', function() { window.open(wappalyzer.config.githubURL); }); + d.getElementById('twitter' ).addEventListener('click', function() { window.open(wappalyzer.config.twitterURL); }); + d.getElementById('wappalyzer').addEventListener('click', function() { window.open(wappalyzer.config.websiteURL + '?pk_campaign=chrome&pk_kwd=options'); }); + + d.getElementById('options-save').addEventListener('click', options.save); + }, + + load: function() { + var option, value; + + for ( option in options.opts ) { + if ( value = localStorage[option] ) { + options.opts[option] = value; + } + } + + if ( parseInt(options.opts.upgradeMessage) ) { + d.getElementById('option-upgrade-message').setAttribute('checked', 'checked'); + } + + if ( parseInt(options.opts.tracking) ) { + d.getElementById('option-tracking').setAttribute('checked', 'checked'); + } + }, + + save: function() { + var option; + + options.opts.upgradeMessage = d.getElementById('option-upgrade-message').checked ? 1 : 0; + options.opts.tracking = d.getElementById('option-tracking' ).checked ? 1 : 0; + + for ( option in options.opts ) { + localStorage[option] = options.opts[option]; + } + + d.getElementById('options-saved').style.display = 'inline'; + + setTimeout(function(){ + d.getElementById('options-saved').style.display = 'none'; + }, 2000); + } + }; + + options.init(); +}); diff --git a/src/drivers/webextension/js/popup.js b/src/drivers/webextension/js/popup.js new file mode 100644 index 000000000..2b0a0e84d --- /dev/null +++ b/src/drivers/webextension/js/popup.js @@ -0,0 +1,68 @@ +document.addEventListener('DOMContentLoaded', function() { + var + slugify, popup, + d = document, + detectedApps = d.getElementById('detected-apps'); + + slugify = function(string) { + return string.toLowerCase().replace(/ /g, '-').replace(/[^\w-]/g, ''); + }; + + popup = { + init: function() { + + d.getElementById('options').addEventListener('click', function() { + window.open(browser.extension.getURL('options.html')); + }); + + browser.tabs.query({ active: true }).then(function(tabs) { + if ( tabs[0].url.match(/https?:\/\//) ) { + detectedApps.innerHTML = '
' + browser.i18n.getMessage('noAppsDetected') + '
'; + } else { + detectedApps.innerHTML = '
' + browser.i18n.getMessage('nothingToDo') + '
'; + } + }); + + popup.displayApps(); + }, + + displayApps: function() { + var appName, confidence, version; + + browser.tabs.query({ active: true }).then(function(tabs) { + browser.runtime.sendMessage({ id: 'get_apps', tab: tabs[0] }, function(response) { + if ( response.tabCache && response.tabCache.count > 0 ) { + detectedApps.innerHTML = ''; + + for ( appName in response.tabCache.appsDetected ) { + confidence = response.tabCache.appsDetected[appName].confidenceTotal; + version = response.tabCache.appsDetected[appName].version; + + html = + ''; + + detectedApps.innerHTML = detectedApps.innerHTML + html; + } + } + }); + }); + } + }; + + popup.init(); +}); diff --git a/src/drivers/webextension/manifest.json b/src/drivers/webextension/manifest.json new file mode 100644 index 000000000..37879632e --- /dev/null +++ b/src/drivers/webextension/manifest.json @@ -0,0 +1,48 @@ +{ "name": "Wappalyzer", + "homepage_url": "https://wappalyzer.com/", + "description": "Identify web technologies", + "version": "3", + "default_locale": "en", + "manifest_version": 2, + "icons": { + "16": "images/icon_16.png", + "32": "images/icon_32.png", + "128": "images/icon_128.png" + }, + "page_action": { + "default_icon": "images/icon_32.png", + "default_title": "Wappalyzer", + "default_popup": "popup.html" + }, + "background": { + "page": "background.html" + }, + "content_scripts": [ { + "matches": [ "http://*/*", "https://*/*" ], + "js": [ + "js/browser-polyfill.js", + "js/content.js" + ], + "run_at": "document_idle" + }, { + "matches": [ "http://*/*", "https://*/*" ], + "js": [ + "js/browser-polyfill.js", + "js/iframe.js" + ], + "run_at": "document_start", + "all_frames": true + } ], + "web_accessible_resources": [ + "js/inject.js" + ], + "options_page": "options.html", + "permissions": [ + "tabs", + "webRequest", + "webNavigation", + "http://*/*", + "https://*/*" + ], + "content_security_policy": "script-src 'self'; object-src 'self'" +} diff --git a/src/drivers/webextension/options.html b/src/drivers/webextension/options.html new file mode 100644 index 000000000..bd7f606d0 --- /dev/null +++ b/src/drivers/webextension/options.html @@ -0,0 +1,49 @@ + + + + + + + Wappalyzer options + + + + + + + + + + + + +
+
+ +
+
+ +
+
+

Options

+ +

+ + +

+ +

+ Saved +

+ +
+

+ +

+
+
+
+ + diff --git a/src/drivers/webextension/popup.html b/src/drivers/webextension/popup.html new file mode 100644 index 000000000..35c1ea3e5 --- /dev/null +++ b/src/drivers/webextension/popup.html @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + +
+ + + +