Add PRO tab to WebExtension

main
Elbert Alias 4 years ago
parent 6625a034b1
commit 1c716621d8

@ -13,7 +13,7 @@
"software"
],
"homepage": "https://www.wappalyzer.com/",
"version": "6.7.4",
"version": "6.7.5",
"author": "Wappalyzer",
"license": "MIT",
"repository": {

@ -11,6 +11,8 @@
"optionThemeMode": { "message": "Enable dark mode compatibility" },
"optionBadge": { "message": "Show the number of identified technologies on the icon" },
"optionShowCached": { "message": "Include cached detections in results" },
"optionApiKey": { "message": "API key" },
"optionApiKeyDescription": { "message": "get your API key" },
"disableOnDomain": { "message": "Disable on this website" },
"clearCache": { "message": "Clear cached detections" },
"nothingToDo": { "message": "Nothing to do here." },
@ -22,6 +24,67 @@
"privacyPolicy": { "message": "Privacy policy" },
"createAlert": { "message": "Create an alert for this website" },
"leadLists": { "message": "Lead generation tools" },
"tabTechnologies": { "message": "Technologies" },
"tabPro": { "message": "Pro" },
"creditBalance": { "message": "Credit balance:" },
"proMessageHeading": { "message": "Unlock PRO features" },
"proMessage": { "message": "Subscribe to a PRO plan to view company and contact information of the websites you visit." },
"proButton": { "message": "Compare plans" },
"proFaq": { "message": "FAQs" },
"formSave": { "message": "Save" },
"setCompany": { "message": "Company information" },
"setKeywords": { "message": "Keywords" },
"setEmail": { "message": "Email addresses" },
"setPhone": { "message": "Phone numbers" },
"setAddress": { "message": "Addresses" },
"setContact": { "message": "Contact details" },
"setSocial": { "message": "Social media accounts" },
"setMeta": { "message": "Metadata" },
"setLocale": { "message": "Locale" },
"setTrackers": { "message": "Trackers" },
"setSecurity": { "message": "Security" },
"attributeIpCountry": { "message": "IP country" },
"attributeIpRegion": { "message": "IP region" },
"attributeLanguage": { "message": "Language" },
"attributeEmail": { "message": "Email address" },
"attributePhone": { "message": "Phone number" },
"attributeSkype": { "message": "Skype" },
"attributeWhatsapp": { "message": "WhatsApp" },
"attributeInferredCompanyName": { "message": "Inferred company name" },
"attributeTwitter": { "message": "Twitter" },
"attributeFacebook": { "message": "Facebook" },
"attributeInstagram": { "message": "Instagram" },
"attributeGithub": { "message": "GitHub" },
"attributeTiktok": { "message": "TikTok" },
"attributeYoutube": { "message": "YouTube" },
"attributePinterest": { "message": "Pinterest" },
"attributeLinkedin": { "message": "LinkedIn" },
"attributeOwler": { "message": "Owler" },
"attributeTitle": { "message": "Title" },
"attributeDescription": { "message": "Description" },
"attributeCopyright": { "message": "Copyright" },
"attributeCopyrightYear": { "message": "Copyright year" },
"attributeResponsive": { "message": "Responsive" },
"attributeCertInfo_issuer": { "message": "Cert issuer" },
"attributeCertInfo_protocol": { "message": "Cert protocol" },
"attributeCertInfo_validTo": { "message": "Cert expiry" },
"attributeHttps": { "message": "SSL/TLS enabled" },
"attributeTrackerGoogleAnalytics": { "message": "Google Analytics" },
"attributeTrackerGoogleAdSense": { "message": "Google AdSense" },
"attributeTrackerMedianet": { "message": "Medianet" },
"attributeTrackerFacebook": { "message": "Facebook" },
"attributeTrackerOptimizely": { "message": "Optimizely" },
"attributeCompanyName": { "message": "Company name" },
"attributeIndustry": { "message": "Industry" },
"attributeAbout": { "message": "About" },
"attributeLocations": { "message": "Locations" },
"attributeCompanySize": { "message": "Company size" },
"attributeCompanyType": { "message": "Company type" },
"attributeCompanyFounded": { "message": "Company founded" },
"attributeKeywords": { "message": "Keywords" },
"categoryName1": { "message": "CMS" },
"categoryName2": { "message": "Message boards" },
"categoryName3": { "message": "Database managers" },

@ -3,14 +3,16 @@
--color-primary-darken: #32067c;
--color-primary-lighten: #f4f1fa;
--color-secondary: #fafafa;
--color-secondary-darken: #f5f5f5;
--color-secondary-darken: #e0e0e0;
--color-text: #4a4a4a;
--color-text-lighten: #7a7a7a;
--color-text-dark: var(--color-primary-lighten);
--color-success: #50b154;
--color-error: #ff5252;
}
* {
box-sizing: border-box;
user-select: none;
}
body {
@ -21,7 +23,7 @@ body {
font-size: .9rem;
line-height: 1.5rem;
margin: 0;
min-width: 24rem;
width: 34rem;
}
a, a:focus, a:hover {
@ -30,6 +32,72 @@ a, a:focus, a:hover {
text-decoration: underline;
}
p {
margin: 0 0 .5rem 0;
}
.input[type="text"], .input[type="password"] {
background: white;
border: 1px solid var(--color-primary);
border-radius: 4px;
color: var(--color-text);
font-size: .9rem;
margin-bottom: 1rem;
padding: .5rem;
width: 100%;
}
.button__link, .button__link:active, .button__link:hover {
color: var(--color-primary);
cursor: pointer;
display: inline-block;
font-size: .85rem;
font-weight: bold;
margin: -.3rem -1rem -.5rem 0;
padding: .4rem 1rem .3rem 1rem;
position: relative;
text-decoration: none;
text-align: right;
}
.button__link:hover:before {
background: var(--color-primary);
border-radius: 4px;
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: .1;
}
.button__icon {
height: 1.2rem;
margin: 0 -.4rem .1rem .2rem;
width: 1.2rem;
vertical-align: middle;
}
.button__icon--left {
margin: 0 .2rem .2rem -.4rem;
}
.button__icon--right {
margin: 0 -.4rem .2rem .2rem;
}
.label {
font-weight: bold;
display: block;
margin-bottom: .5rem;
}
small {
font-weight: normal;
font-size: .8rem;
}
.header {
align-items: center;
background: var(--color-primary);
@ -74,6 +142,249 @@ a, a:focus, a:hover {
flex-grow: 1;
}
.loading {
padding: 4rem 0;
}
.loading--hidden {
display: none;
}
.progress {
display: block;
width: 100px;
height: 100px;
margin: 0 auto;
opacity: .2;
animation: progress-rotate 1.4s linear infinite;
transform-origin: center center;
transition: all .2s ease-in-out;
transform: rotate(0deg);
}
.progress__circle {
color: var(--color-primary);
fill: transparent;
cx: 40;
cy: 40;
r: 18;
stroke-width: 2;
stroke: currentColor;
transition: all .6s ease-in-out;
animation: progress-dash 1.4s ease-in-out infinite;
stroke-linecap: round;
stroke-dasharray: 80,200;
stroke-dashoffset: 0px;
}
.tabs {
align-items: center;
border-bottom: 1px solid var(--color-secondary-darken);
background: white;
display: flex;
font-size: .8rem;
}
.tab {
color: var(--color-primary);
cursor: pointer;
letter-spacing: .5px;
padding: 1rem 1.5rem .8rem 1.5rem;
text-transform: uppercase;
}
.tab--active {
border-bottom: 2px solid var(--color-primary);
color: var(--color-primary);
}
.tab-item {
background: white;
overflow: hidden;
}
.tab-item--hidden {
display: none;
}
.credits {
color: var(--color-text-lighten);
display: block;
text-align: right;
flex: 1;
padding: 0 1.5rem;
margin-bottom: -3px;
}
.credits--hidden {
display: none;
}
.credits__remaining {
font-weight: bold;
}
.panels {
background: var(--color-secondary);
overflow: hidden;
}
.panels--hidden {
display: none;
}
.panel {
background: white;
border: 1px solid var(--color-secondary-darken);
border-width: 1px 0;
margin: 1rem 0;
}
.panel__header {
font-weight: bold;
padding: 1rem 1.5rem;
}
.panel__content {
}
.panel__content table {
border-collapse: collapse;
margin-bottom: 1rem;
width: 100%;
}
.panel__content tr {
border-bottom: 1px solid var(--color-secondary-darken);
}
.panel__content tr:last-child {
border-bottom: none;
}
.panel__content th {
font-weight: normal;
text-align: left;
width: 33%;
}
.panel__content th, .panel__content td {
padding: .5rem;
}
.panel__content td strong {
display: block;
}
.panel__content th:first-child, .panel__content td:first-child {
padding-left: 1.5rem;
}
.panel__content th:last-child, .panel__content td:last-child {
padding-right: 1.5rem;
}
.panel__content td div {
border-bottom: 1px solid var(--color-secondary-darken);
padding: .5rem 1.5rem .5rem 0;
margin-right: -1.5rem;
}
.panel__content td div:first-child {
padding-top: 0;
}
.panel__content td div:last-child {
border-bottom: none;
padding-bottom: 0;
}
.chip, .chip:focus, .chip:hover {
border: 1px solid var(--color-secondary-darken);
border-radius: 4px;
margin: 0 .5rem .5rem 0;
display: inline-block;
padding: .2rem .5rem;
text-decoration: none;
}
.chip:focus, .chip:hover {
background: var(--color-primary-lighten)
}
.pro-configure {
margin: 1.5rem;
}
.pro-configure--hidden {
display: none;
}
.message {
background: var(--color-primary-lighten);
border-radius: 4px;
color: var(--color-primary);
padding: 1rem 1.5rem;
margin-bottom: 1.5rem;
}
.message__heading {
font-size: .9rem;
font-weight: bold;
margin: .5rem 0;
}
.message__heading__icon {
height: 1.2rem;
margin: 0 .2rem .2rem 0;
width: 1.2rem;
vertical-align: middle;
}
.message__button {
text-align: right;
}
.pro-configure__form {
background: var(--color-primary-lighten);
border-radius: 4px;
color: var(--color-primary);
padding: 1rem 1.5rem;
}
.pro-empty {
text-align: center;
padding: 4rem 1.5rem;
}
.pro-empty--hidden {
display: none;
}
.pro-crawl {
text-align: center;
padding: 4rem 1.5rem;
}
.pro-crawl--hidden {
display: none;
}
.pro-error {
margin: 1.5rem;
}
.pro-error--hidden {
display: none;
}
.pro-error__message {
border: 1px solid var(--color-error);
border-radius: 4px;
color: var(--color-error);
padding: 1rem 1.2rem;
}
.footer {
background: var(--color-primary-lighten);
bottom: 0;
@ -97,6 +408,7 @@ a, a:focus, a:hover {
flex: 1 0;
font-size: .9rem;
font-weight: bold;
margin-bottom: .5rem;
}
.footer--collapsed .footer__heading {
@ -106,6 +418,7 @@ a, a:focus, a:hover {
.footer--collapsed .footer__heading-text {
font-size: .9rem;
font-weight: inherit;
margin-bottom: 0;
opacity: .8;
}
@ -122,8 +435,14 @@ a, a:focus, a:hover {
display: flex;
}
.footer__icon {
height: 1.2rem;
margin: 0 -.4rem .1rem .2rem;
width: 1.2rem;
vertical-align: middle;
}
.footer__toggle {
fle
flex: 1;
text-align: right;
}
@ -133,10 +452,7 @@ a, a:focus, a:hover {
}
.footer__content {
font-size: .85rem;
letter-spacing: .5px;
flex: 1 0 auto;
opacity: .8;
max-width: 400px;
}
@ -148,47 +464,15 @@ a, a:focus, a:hover {
text-align: right;
}
.footer__button-link, .footer__button-link:active, .footer__button-link:hover {
color: var(--color-primary);
display: inline-block;
font-size: .85rem;
font-weight: bold;
margin: -.3rem -1rem -.5rem 0;
padding: .4rem 1rem .3rem 1rem;
position: relative;
text-decoration: none;
text-align: right;
}
.footer__button-link:hover:before {
background: var(--color-primary);
border-radius: 4px;
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: .1;
}
.footer--collapsed .footer__button {
display: none;
}
.footer__icon {
height: 1.2rem;
margin: 0 -.4rem .1rem .2rem;
width: 1.2rem;
vertical-align: middle;
}
.detections {
background: #fff;
columns: 2;
column-gap: 3rem;
min-height: 200px;
padding: 1.5rem 1.5rem .5rem 1.5rem;
padding: 1rem 1.5rem .5rem 1.5rem;
}
.detections--hidden {
@ -196,10 +480,9 @@ a, a:focus, a:hover {
}
.empty {
background: #fff;
height: calc(100% - 4.5rem);
padding: 2.5rem;
text-align: center;
margin: 2.5rem 0 3.5rem 0;
}
.empty__text {
@ -332,8 +615,7 @@ body.dynamic-icon .category__heading:hover .category__pin {
display: flex;
flex-direction: column;
justify-content: center;
height: 16rem;
width: 36rem;
margin: 3rem 1.5rem;
}
.terms--hidden {
@ -354,14 +636,13 @@ body.dynamic-icon .category__heading:hover .category__pin {
font-size: .9rem;
line-height: 150%;
text-align: center;
margin-bottom: 1rem;
width: 80%;
margin-bottom: 1.5rem;
}
.terms__button {
background-color: #4608ad;
border: none;
border-radius: 3px;
border-radius: 4px;
color: white;
cursor: pointer;
font-size: .9rem;
@ -377,7 +658,7 @@ body.dynamic-icon .category__heading:hover .category__pin {
}
.terms__privacy {
margin-top: 1rem;
margin-top: 1.5rem;
}
.options {
@ -387,7 +668,15 @@ body.dynamic-icon .category__heading:hover .category__pin {
.options__label {
display: block;
margin-bottom: .5rem;
margin-bottom: 1rem;
}
.options__input {
border: 1px solid var(--color-text);
border-radius: 4px;
padding: .5rem;
margin: .2rem 0 .5rem 0;
width: 100%;
}
.options__cache {
@ -466,6 +755,48 @@ body.dynamic-icon .category__heading:hover .category__pin {
color: var(--color-text-dark);
}
.dark a, .dark a:focus, .dark a:hover {
color: var(--color-primary-text);
}
.input[type="text"], .input[type="password"] {
border-color: var(--color-primary-darken);
}
.dark .chip:focus, .dark .chip:hover {
background: var(--color-primary);
}
.dark .message {
background: var(--color-primary);
color: var(--color-text-dark);
}
.dark .message__heading {
color: white;
}
.dark .pro-configure__form {
background: var(--color-primary);
color: var(--color-text-dark);
}
.dark .button__link, .dark .button__link:active, .dark .button__link:hover {
color: var(--color-text-dark);
}
.dark .button__link:hover:before {
background: white;
}
.dark .label {
color: white;
}
.dark .label__description {
color: var(--color-text-dark);
}
.dark .detections {
background: var(--color-primary-darken);
}
@ -486,6 +817,10 @@ body.dynamic-icon .category__heading:hover .category__pin {
opacity: .8;
}
.dark .technology__name:hover {
opacity: 1;
}
.dark .technology__icon {
}
@ -499,6 +834,59 @@ body.dynamic-icon .category__heading:hover .category__pin {
color: var(--color-text-dark);
}
.dark .progress__circle {
color: white;
}
.dark .tabs {
background: var(--color-primary-darken);
border-bottom-color: var(--color-primary);
}
.dark .tab {
color: var(--color-text-dark);
opacity: .8;
}
.dark .tab--active {
border-color: var(--color-text-dark);
opacity: 1;
}
.dark .tab-item {
background: var(--color-primary-darken);
}
.dark .credits {
color: var(--color-text-dark);
opacity: .5;
}
.dark .panels {
background: var(--color-primary-darken);
}
.dark .panel {
background: var(--color-primary-darken);
border-color: var(--color-primary);
}
.dark .panel__content tr {
border-bottom-color: var(--color-primary);
}
.dark .panel__content td {
opacity: .8;
}
.dark .panel__content tr:hover td {
opacity: 1;
}
.dark .panel__content td div {
border-color: var(--color-primary);
}
.dark .footer {
background: var(--color-primary-darken);
border-top: 1px solid var(--color-primary);
@ -513,6 +901,9 @@ body.dynamic-icon .category__heading:hover .category__pin {
background: var(--color-primary-lighten);
}
.dark .footer__content {
opacity: .8;
}
.dark .terms__button {
background-color: white;
@ -528,3 +919,31 @@ body.dynamic-icon .category__heading:hover .category__pin {
.dark .ttt-player-icon {
color: var(--color-primary-lighten);
}
@keyframes progress-rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes progress-dash {
0% {
stroke-dasharray: 1,200;
stroke-dashoffset: 0px;
}
50% {
stroke-dasharray: 100,200;
stroke-dashoffset: -15px;
}
100% {
stroke-dasharray: 100,200;
stroke-dashoffset: -125px;
}
}

@ -13,6 +13,12 @@
</head>
<body>
<div class="options">
<label class="options__label">
<span data-i18n="optionApiKey">&nbsp;</span>
<input class="options__input" type="password">
</label>
<label class="options__label">
<input class="options__checkbox" type="checkbox">

@ -44,6 +44,18 @@
</svg>
</div>
<div class="tabs">
<div class="tab tab--active" data-i18n="tabTechnologies"></div>
<div class="tab" data-i18n="tabPro"></div>
<div class="credits credits--hidden">
<span data-i18n="creditBalance">&nbsp;</span>
<span class="credits__remaining">&nbsp;</span>
</div>
</div>
<div class="tab-item">
<div class="empty">
<div class="empty__text" data-i18n="noAppsDetected">&nbsp;</div>
@ -138,6 +150,84 @@
</a>
</div>
</div>
</div>
<div class="tab-item tab-item--hidden">
<div class="pro-error pro-error--hidden">
<div class="pro-error__message">
</div>
</div>
<div class="loading">
<svg class="progress" viewBox="20 20 40 40">
<circle class="progress__circle"></circle>
</svg>
</div>
<div class="panels panels--hidden">
</div>
<div class="pro-configure pro-configure--hidden">
<div class="message">
<div class="message__heading">
<svg class="message__heading__icon" viewBox="0 0 24 24">
<path fill="currentColor" d="M10 13C11.1 13 12 13.89 12 15C12 16.11 11.11 17 10 17S8 16.11 8 15 8.9 13 10 13M18 1C15.24 1 13 3.24 13 6V8H4C2.9 8 2 8.9 2 10V20C2 21.1 2.9 22 4 22H16C17.1 22 18 21.1 18 20V10C18 8.9 17.1 8 16 8H15V6C15 4.34 16.34 3 18 3S21 4.34 21 6V8H23V6C23 3.24 20.76 1 18 1M16 10V20H4V10H16Z" />
</svg>
<span data-i18n="proMessageHeading">&nbsp;</span>
</div>
<p>
<span data-i18n="proMessage">&nbsp;</span>
<small>
(<a href="https://www.wappalyzer.com/faq/extension/?utm_source=popup&utm_medium=extension&utm_campaign=wappalyzer" data-i18n="proFaq">&nbsp;</a>)
</small>
</p>
<div class="message__button button">
<a class="button__link" href="https://www.wappalyzer.com/pricing/?utm_source=popup&utm_medium=extension&utm_campaign=wappalyzer">
<span class="button__text" data-i18n="proButton"></span>
<svg class="button__icon button__icon--right" viewBox="0 0 24 24">
<path fill="currentColor" d="M4,11V13H16L10.5,18.5L11.92,19.92L19.84,12L11.92,4.08L10.5,5.5L16,11H4Z" />
</svg>
</a>
</div>
</div>
<form class="pro-configure__form">
<div class="control">
<span class="label">
<span data-i18n="optionApiKey">&nbsp;</span>
<small class="label__description">
(<a href="https://www.wappalyzer.com/apikey/?utm_source=popup&utm_medium=extension&utm_campaign=wappalyzer" data-i18n="optionApiKeyDescription"></a>)
</small>
</span>
<input type="password" class="pro-configure__apikey input" />
</div>
<div class="message__button button">
<span class="pro-configure__save button__link">
<svg class="button__icon button__icon--left" viewBox="0 0 24 24">
<path fill="currentColor" d="M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z" />
</svg>
<span class="button__text" data-i18n="formSave"></span>
</span>
</div>
</form>
</div>
<div class="pro-empty pro-empty--hidden">
No results found.
</div>
<div class="pro-crawl pro-crawl--hidden">
Website is being analysed, check back later.
</div>
</div>
<div class="footer footer--hidden">
<div class="footer__heading">
@ -159,11 +249,11 @@
<p class="footer__content-body">&nbsp;</p>
</div>
<div class="footer__button">
<a class="footer__button-link" href="#">
<span class="footer__button-text">&nbsp;</span>
<div class="footer__button button">
<a class="button__link" href="#">
<span class="button__text">&nbsp;</span>
<svg class="footer__icon" viewBox="0 0 24 24">
<svg class="button__icon button__icon--right" viewBox="0 0 24 24">
<path fill="currentColor" d="M4,11V13H16L10.5,18.5L11.92,19.92L19.84,12L11.92,4.08L10.5,5.5L16,11H4Z" />
</svg>
</a>

@ -18,6 +18,7 @@ const Options = {
['badge', true],
['tracking', true],
['showCached', true],
['apiKey', ''],
].map(async ([option, defaultValue]) => {
const el = document
.querySelector(
@ -27,6 +28,7 @@ const Options = {
)
.parentNode.querySelector('input')
if (el.type === 'checkbox') {
el.checked =
!!(await getOption(option, defaultValue)) &&
(option !== 'tracking' || termsAccepted)
@ -34,8 +36,19 @@ const Options = {
el.addEventListener('click', async () => {
await setOption(option, !!el.checked)
})
} else if (el.type === 'password') {
el.value = await getOption(option, defaultValue)
}
})
document
.querySelector('[data-i18n="optionApiKey"]')
.parentNode.querySelector('input')
.addEventListener(
'input',
async (event) => await setOption('apiKey', event.target.value)
)
document
.querySelector('.options__cache')
.addEventListener('click', () => Options.driver('clearCache'))

@ -140,13 +140,15 @@ const Popup = {
})
}
let url
const tabs = await promisify(chrome.tabs, 'query', {
active: true,
currentWindow: true,
})
if (tabs && tabs.length) {
const [{ url }] = tabs
;[{ url }] = tabs
if (url.startsWith('http')) {
const { hostname } = new URL(url)
@ -185,6 +187,23 @@ const Popup = {
}
}
// PRO configuration
const apiKey = document.querySelector('.pro-configure__apikey')
apiKey.value = await getOption('apiKey', '')
document
.querySelector('.pro-configure__save')
.addEventListener('click', async (event) => {
await setOption(
'apiKey',
document.querySelector('.pro-configure__apikey').value
)
await Popup.getPro(url)
})
// Header
document
.querySelector('.header__settings')
.addEventListener('click', () => chrome.runtime.openOptionsPage())
@ -211,6 +230,27 @@ const Popup = {
})
)
// Tabs
const tabHeadings = Array.from(document.querySelectorAll('.tab'))
const tabItems = Array.from(document.querySelectorAll('.tab-item'))
const credits = document.querySelector('.credits')
tabHeadings.forEach((tab, index) => {
tab.addEventListener('click', async () => {
tabHeadings.forEach((tab) => tab.classList.remove('tab--active'))
tabItems.forEach((item) => item.classList.add('tab-item--hidden'))
tab.classList.add('tab--active')
tabItems[index].classList.remove('tab-item--hidden')
credits.classList.add('credits--hidden')
if (index === 1) {
await Popup.getPro(url)
}
})
})
// Footer
const item =
footers[
@ -221,8 +261,9 @@ const Popup = {
document.querySelector('.footer__heading-text').textContent = item.heading
document.querySelector('.footer__content-body').textContent = item.body
document.querySelector('.footer__button-text').textContent = item.buttonText
document.querySelector('.footer__button-link').href = item.buttonLink
document.querySelector('.footer .button__text').textContent =
item.buttonText
document.querySelector('.footer .button__link').href = item.buttonLink
const collapseFooter = await getOption('collapseFooter', false)
@ -252,6 +293,16 @@ const Popup = {
await setOption('collapseFooter', !collapsed)
})
Array.from(document.querySelectorAll('a')).forEach((a) =>
a.addEventListener('click', (event) => {
event.preventDefault()
open(a.href)
return false
})
)
// Apply internationalization
i18n()
},
@ -416,6 +467,232 @@ const Popup = {
i18n()
},
/**
* TODO
*/
async getPro(url) {
const apiKey = await getOption('apiKey', '')
const el = {
loading: document.querySelector('.loading'),
panels: document.querySelector('.panels'),
empty: document.querySelector('.pro-empty'),
crawl: document.querySelector('.pro-crawl'),
error: document.querySelector('.pro-error'),
errorMessage: document.querySelector('.pro-error__message'),
configure: document.querySelector('.pro-configure'),
credits: document.querySelector('.credits'),
creditsRemaining: document.querySelector('.credits__remaining'),
}
el.error.classList.add('pro-error--hidden')
if (apiKey) {
el.loading.classList.remove('loading--hidden')
el.configure.classList.add('pro-configure--hidden')
} else {
el.loading.classList.add('loading--hidden')
el.configure.classList.remove('pro-configure--hidden')
return
}
el.panels.classList.add('panels--hidden')
el.empty.classList.add('pro-empty--hidden')
el.crawl.classList.add('pro-crawl--hidden')
el.error.classList.add('pro-error--hidden')
while (el.panels.lastElementChild) {
el.panels.removeChild(el.panels.lastElementChild)
}
try {
const response = await fetch(
`https://api.wappalyzer.com/pro/v2/${encodeURIComponent(url)}`,
{
method: 'GET',
headers: {
'x-api-key': apiKey,
},
}
)
const data = await response.json()
if (!response.ok) {
const error = new Error()
error.data = data
error.response = response
throw error
}
const { attributes, creditsRemaining, crawl } = data
el.creditsRemaining.textContent = parseInt(
creditsRemaining || 0,
10
).toLocaleString()
el.credits.classList.remove('credits--hidden')
el.loading.classList.add('loading--hidden')
if (crawl) {
document
.querySelector('.pro-crawl')
.classList.remove('pro-crawl--hidden')
return
}
if (!Object.keys(attributes).length) {
el.empty.classList.remove('pro-empty--hidden')
return
}
Object.keys(attributes).forEach((set) => {
const panel = document.createElement('div')
const header = document.createElement('div')
const content = document.createElement('div')
const table = document.createElement('table')
panel.classList.add('panel')
header.classList.add('panel__header')
content.classList.add('panel__content')
header.setAttribute(
'data-i18n',
`set${set.charAt(0).toUpperCase() + set.slice(1)}`
)
Object.keys(attributes[set]).forEach((key) => {
const value = attributes[set][key]
const tr = document.createElement('tr')
const th = document.createElement('th')
const td = document.createElement('td')
th.setAttribute(
'data-i18n',
`attribute${
key.charAt(0).toUpperCase() + key.slice(1).replace('.', '_')
}`
)
if (Array.isArray(value)) {
value.forEach((value) => {
const div = document.createElement('div')
if (typeof value === 'object') {
const a = document.createElement('a')
a.href = value.to
a.textContent = value.text
if (
['social', 'keywords'].includes(set) ||
['phone', 'email'].includes(key)
) {
a.classList.add('chip')
td.appendChild(a)
} else {
div.appendChild(a)
td.appendChild(div)
}
} else if (key === 'employees') {
const [name, title] = value.split(' -- ')
const strong = document.createElement('strong')
const span = document.createElement('span')
strong.textContent = name
span.textContent = title
div.appendChild(strong)
div.appendChild(span)
td.appendChild(div)
} else {
div.textContent = value
td.appendChild(div)
}
})
} else if (key === 'companyName') {
const strong = document.createElement('strong')
strong.textContent = value
td.appendChild(strong)
} else {
td.textContent = value
}
if (key !== 'keywords') {
tr.appendChild(th)
}
tr.appendChild(td)
table.appendChild(tr)
})
content.appendChild(table)
panel.appendChild(header)
panel.appendChild(content)
el.panels.appendChild(panel)
})
el.panels.classList.remove('panels--hidden')
} catch (error) {
Popup.log(error.data)
// eslint-disable-next-line
console.log(error)
el.errorMessage.textContent = `Sorry, something went wrong${
error.response ? ` (${error.response.status})` : ''
}. Please try again later.`
if (error.response) {
if (error.response.status === 403) {
el.errorMessage.textContent =
typeof error.data === 'string'
? error.data
: 'No access. Please check your API key.'
el.configure.classList.remove('pro-configure--hidden')
} else if (error.response.status === 429) {
el.errorMessage.textContent =
'Too many requests. Please try again in a few seconds.'
} else if (
error.response.status === 400 &&
typeof error.data === 'string'
) {
el.errorMessage.textContent = error.data
}
}
el.loading.classList.add('loading--hidden')
el.error.classList.remove('pro-error--hidden')
}
Array.from(document.querySelectorAll('.panels a')).forEach((a) =>
a.addEventListener('click', (event) => {
event.preventDefault()
open(a.href)
return false
})
)
i18n()
},
}
if (/complete|interactive|loaded/.test(document.readyState)) {

@ -4,7 +4,7 @@
"author": "Wappalyzer",
"homepage_url": "https://www.wappalyzer.com/",
"description": "Identify web technologies",
"version": "6.7.4",
"version": "6.7.5",
"default_locale": "en",
"manifest_version": 2,
"icons": {

@ -13,7 +13,7 @@
"software"
],
"homepage": "https://www.wappalyzer.com/",
"version": "6.7.4",
"version": "6.7.5",
"author": "Wappalyzer",
"license": "MIT",
"repository": {

@ -5010,7 +5010,9 @@
"js": {
"corebine": ""
},
"pricing": ["poa"],
"pricing": [
"poa"
],
"website": "https://corebine.com"
},
"Cosmoshop": {
@ -6235,22 +6237,6 @@
"url": "https?://(?:[^/]+\\.)?edgecastcdn\\.net/",
"website": "http://www.edgecast.com"
},
"eDokan": {
"cats": [
6
],
"description": "eDokan is hosted ecommerce platform with drag-drop template builder and zero programming knowledge.",
"icon": "eDokan.png",
"implies": [
"Node.js",
"Angular",
"MongoDB"
],
"dom": "img[src*='cdn.edokan.co']",
"saas": true,
"pricing": ["low", "recurring"],
"website": "https://edokan.co"
},
"Elasticsearch": {
"cats": [
29
@ -9411,9 +9397,12 @@
"js": {
"Intercom": ""
},
"scripts": "(?:api\\.intercom\\.io/api|static\\.intercomcdn\\.com/intercom\\.v1)",
"pricing": [
"mid",
"recurring"
],
"saas": true,
"pricing": ["mid","recurring"],
"scripts": "(?:api\\.intercom\\.io/api|static\\.intercomcdn\\.com/intercom\\.v1)",
"website": "https://www.intercom.com"
},
"Intercom Articles": {
@ -11116,7 +11105,6 @@
6
],
"description": "Localised is local-first ecommerce platform.",
"icon": "Localised.png",
"dom": {
"img[src='.localised.com'": {
"attributes": {
@ -11129,10 +11117,13 @@
}
}
},
"xhr": "api\\.localised\\.com",
"icon": "Localised.png",
"pricing": [
"poa"
],
"saas": true,
"pricing": ["poa"],
"website": "https://www.localised.com"
"website": "https://www.localised.com",
"xhr": "api\\.localised\\.com"
},
"LocomotiveCMS": {
"cats": [
@ -17370,7 +17361,6 @@
}
},
"icon": "Shopify.svg",
"implies": "Shopify",
"scripts": [
"cdn\\.shopify\\.com/shopifycloud/shopify_pay/"
],
@ -18748,17 +18738,21 @@
1
],
"description": "Statamic is an open-source and self-hosted content management system based on the PHP programming language.",
"icon": "Statamic.svg",
"headers": {
"x-powered-by": "^Statamic$"
},
"icon": "Statamic.svg",
"implies": [
"PHP",
"Laravel"
],
"saas": false,
"oss": true,
"pricing": ["freemium", "mid", "payg"],
"pricing": [
"freemium",
"mid",
"payg"
],
"saas": false,
"website": "https://statamic.com"
},
"Statcounter": {
@ -19881,7 +19875,6 @@
"js": {
"TrackJs": ""
},
"scripts": "tracker\\.js",
"website": "http://trackjs.com"
},
"Tradedoubler": {
@ -21702,10 +21695,7 @@
6
],
"description": "WooCommerce is an open-source ecommerce plugin for WordPress.",
"html": [
"<!-- WooCommerce",
"<link rel='[^']+' id='woocommerce-(?:layout|smallscreen|general)-css' href='https?://[^/]+/wp-content/plugins/woocommerce/assets/css/woocommerce(?:-layout|-smallscreen)?\\.css?ver=([\\d.]+)'\\;version:\\1"
],
"dom": ".woocommerce, .woocommerce-no-js, link[rel*='woocommerce']",
"icon": "WooCommerce.svg",
"implies": "WordPress",
"js": {
@ -21715,7 +21705,10 @@
"generator": "WooCommerce ([\\d.]+)\\;version:\\1"
},
"oss": true,
"scripts": "/woocommerce(?:\\.min)?\\.js(?:\\?ver=([0-9.]+))?\\;version:\\1",
"scripts": [
"woocommerce",
"/woocommerce(?:\\.min)?\\.js(?:\\?ver=([0-9.]+))?\\;version:\\1"
],
"website": "https://woocommerce.com"
},
"Woopra": {
@ -22774,6 +22767,25 @@
},
"website": "https://www.eclass.com.hk"
},
"eDokan": {
"cats": [
6
],
"description": "eDokan is hosted ecommerce platform with drag-drop template builder and zero programming knowledge.",
"dom": "img[src*='cdn.edokan.co']",
"icon": "eDokan.png",
"implies": [
"Node.js",
"Angular",
"MongoDB"
],
"pricing": [
"low",
"recurring"
],
"saas": true,
"website": "https://edokan.co"
},
"eSyndiCat": {
"cats": [
1