@ -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
|
@ -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" }
|
||||
}
|
@ -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" }
|
||||
}
|
@ -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" }
|
||||
}
|
||||
|
@ -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" }
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<script src="js/browser-polyfill.js"></script>
|
||||
<script src="js/wappalyzer.js"></script>
|
||||
<script src="js/defaults.js"></script>
|
||||
<script src="js/driver.js"></script>
|
||||
<script src="js/network.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
@ -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; }
|
||||
}
|
@ -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;
|
||||
}
|
After Width: | Height: | Size: 281 B |
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 555 B |
After Width: | Height: | Size: 643 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 373 B |
@ -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<function>}
|
||||
* 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<object>}
|
||||
*/
|
||||
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<function, function>} 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();
|
||||
}
|
@ -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();
|
||||
}());
|
@ -0,0 +1,5 @@
|
||||
var defaults = {
|
||||
autoAnalyzeHeaders: 0,
|
||||
upgradeMessage: 1,
|
||||
tracking: 1
|
||||
};
|
@ -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();
|
||||
}());
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
@ -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) { }
|
||||
}());
|
@ -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();
|
||||
});
|
@ -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 = '<div class="empty">' + browser.i18n.getMessage('noAppsDetected') + '</div>';
|
||||
} else {
|
||||
detectedApps.innerHTML = '<div class="empty">' + browser.i18n.getMessage('nothingToDo') + '</div>';
|
||||
}
|
||||
});
|
||||
|
||||
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 =
|
||||
'<div class="detected-app">' +
|
||||
'<a target="_blank" href="https://wappalyzer.com/applications/' + slugify(appName) + '">' +
|
||||
'<img src="images/icons/' + response.apps[appName].icon + '"/>' +
|
||||
'<span class="label"><span class="name">' + appName + '</span>' + ( version ? ' ' + version : '' ) + ( confidence < 100 ? ' (' + confidence + '% sure)' : '' ) + '</span>' +
|
||||
'</a>';
|
||||
|
||||
response.apps[appName].cats.forEach(function(cat) {
|
||||
html +=
|
||||
'<a target="_blank" href="https://wappalyzer.com/categories/' + slugify(response.categories[cat]) + '">' +
|
||||
'<span class="category"><span class="name">' + browser.i18n.getMessage('categoryName' + cat) + '</span></span>' +
|
||||
'</a>';
|
||||
});
|
||||
|
||||
html +=
|
||||
'</a>' +
|
||||
'</div>';
|
||||
|
||||
detectedApps.innerHTML = detectedApps.innerHTML + html;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
popup.init();
|
||||
});
|
@ -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'"
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title data-i18n="options">Wappalyzer options</title>
|
||||
|
||||
<link rel="icon" href="images/icon_16.png">
|
||||
|
||||
<link rel="stylesheet" href="css/options.css">
|
||||
|
||||
<script src="js/browser-polyfill.js"></script>
|
||||
<script src="js/wappalyzer.js"></script>
|
||||
<script src="js/defaults.js"></script>
|
||||
<script src="js/options.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="hero">
|
||||
<div class="container">
|
||||
<img src="images/logo-white.svg">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div class="container">
|
||||
<h1 data-i18n="options">Options</h1>
|
||||
|
||||
<p>
|
||||
<label for="option-upgrade-message"><input id="option-upgrade-message" type="checkbox"> <span data-i18n="optionUpgradeMessage">Tell me about upgrades</span></label>
|
||||
<label for="option-tracking"><input id="option-tracking" type="checkbox"> <span data-i18n="optionTracking">Anonymously send reports on detected applications to wappalyzer.com for research</span></label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<button id="options-save" data-i18n="optionsSave">Save options</button> <span id="options-saved" data-i18n="optionsSaved">Saved</span>
|
||||
</p>
|
||||
|
||||
<div id="about">
|
||||
<p>
|
||||
<button id="github"><img src="images/github.png" width="16" height="16" alt=""/> <span data-i18n="github" >Fork Wappalyzer on GitHub!</span></button><!--
|
||||
--><button id="twitter"><img src="images/twitter.png" width="16" height="16" alt=""/> <span data-i18n="twitter">Follow Wappalyzer on Twitter</span></button><!--
|
||||
--><button id="wappalyzer"><img src="images/icon_16.png" width="16" height="16" alt=""/> <span data-i18n="website">Go to wappalyzer.com</span></button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<link rel="stylesheet" href="css/widgets.css">
|
||||
<link rel="stylesheet" href="css/popup.css">
|
||||
|
||||
<script src="js/browser-polyfill.js"></script>
|
||||
<script src="js/popup.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="detected-apps"></div>
|
||||
|
||||
<div id="footer">
|
||||
<a href="javascript: void(0);" data-i18n="options" id="options">Options</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|