Export button

main
Elbert Alias 3 years ago
parent 2d0ada890a
commit 3e8869ac47

@ -72,7 +72,7 @@ p {
text-align: right; text-align: right;
} }
.button__link:hover:before { .button__link:before {
background: var(--color-primary); background: var(--color-primary);
border-radius: 4px; border-radius: 4px;
content: ''; content: '';
@ -81,6 +81,10 @@ p {
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
opacity: 0;
}
.button__link:hover:before {
opacity: .1; opacity: .1;
} }
@ -223,16 +227,22 @@ small {
overflow: hidden; overflow: hidden;
} }
.tab-item:nth-child(2) {
min-width: 500px;
}
.tab-item--hidden { .tab-item--hidden {
display: none; display: none;
} }
.credits { .credits {
background: var(--color-secondary);
color: var(--color-text-lighten); color: var(--color-text-lighten);
display: block; display: block;
font-size: .8rem;
text-align: right; text-align: right;
flex: 1; flex: 1;
padding: 0 1.5rem; padding: 1rem 1.5rem 0 1.5rem;
margin-bottom: -3px; margin-bottom: -3px;
line-height: 1rem; line-height: 1rem;
} }
@ -393,13 +403,22 @@ small {
} }
.plus-download { .plus-download {
background: var(--color-secondary); flex: 1 0;
padding: 1rem 1.5rem 0 1.5rem;
text-align: right; text-align: right;
padding: 0 1.5rem;
white-space: nowrap;
}
.plus-download .button__link:before {
opacity: .1;
}
.plus-download .button__link:hover:before {
opacity: .2;
} }
.plus-download--hidden { .plus-download--hidden {
display: none; visibility: hidden;
} }
.plus-error { .plus-error {
@ -849,7 +868,7 @@ body.dynamic-icon .category__heading:hover .category__pin {
color: var(--color-text-dark); color: var(--color-text-dark);
} }
.dark .button__link:hover:before { .dark .button__link:before {
background: white; background: white;
} }
@ -953,6 +972,11 @@ body.dynamic-icon .category__heading:hover .category__pin {
border-color: var(--color-primary); border-color: var(--color-primary);
} }
.dark .credits {
background: var(--color-primary-darken);
}
.dark .footer { .dark .footer {
background: var(--color-primary-darken); background: var(--color-primary-darken);
border-top: 1px solid var(--color-primary); border-top: 1px solid var(--color-primary);

@ -49,187 +49,189 @@
<div class="tab tab--technologies tab--active" data-i18n="tabTechnologies">&nbsp;</div> <div class="tab tab--technologies tab--active" data-i18n="tabTechnologies">&nbsp;</div>
<div class="tab tab--plus"><span data-i18n="tabPlus">&nbsp;</span></div> <div class="tab tab--plus"><span data-i18n="tabPlus">&nbsp;</span></div>
<div class="credits credits--hidden"> <div class="plus-download plus-download--hidden">
<span data-i18n="creditBalance">&nbsp;</span> <div class="plus-download__button button">
<span class="button__link">
<svg class="button__icon button__icon--left" viewBox="0 0 24 24">
<path fill="currentColor" d="M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z" />
</svg>
<span class="credits__remaining">&nbsp;</span> <span class="button__text">
Export
</span>
</span>
</div>
</div> </div>
</div> </div>
<div class="tab-item"> <div class="tab-items">
<div class="empty empty--hidden"> <div class="tab-item">
<div class="empty__text" data-i18n="noAppsDetected">&nbsp;</div> <div class="empty empty--hidden">
<div class="empty__text" data-i18n="noAppsDetected">&nbsp;</div>
<div class="ttt-game"> <div class="ttt-game">
<div class="ttt-player"> <div class="ttt-player">
<svg class="ttt-player-icon ttt-player-icon-x ttt-player-icon--ahead" viewBox="0 0 24 24"> <svg class="ttt-player-icon ttt-player-icon-x ttt-player-icon--ahead" viewBox="0 0 24 24">
<path fill="currentColor" d="M19,10C19,11.38 16.88,12.5 15.5,12.5C14.12,12.5 12.75,11.38 12.75,10H11.25C11.25,11.38 9.88,12.5 8.5,12.5C7.12,12.5 5,11.38 5,10H4.25C4.09,10.64 4,11.31 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12C20,11.31 19.91,10.64 19.75,10H19M12,4C9.04,4 6.45,5.61 5.07,8H18.93C17.55,5.61 14.96,4 12,4M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,12M12,17.23C10.25,17.23 8.71,16.5 7.81,15.42L9.23,14C9.68,14.72 10.75,15.23 12,15.23C13.25,15.23 14.32,14.72 14.77,14L16.19,15.42C15.29,16.5 13.75,17.23 12,17.23Z" /> <path fill="currentColor" d="M19,10C19,11.38 16.88,12.5 15.5,12.5C14.12,12.5 12.75,11.38 12.75,10H11.25C11.25,11.38 9.88,12.5 8.5,12.5C7.12,12.5 5,11.38 5,10H4.25C4.09,10.64 4,11.31 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12C20,11.31 19.91,10.64 19.75,10H19M12,4C9.04,4 6.45,5.61 5.07,8H18.93C17.55,5.61 14.96,4 12,4M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,12M12,17.23C10.25,17.23 8.71,16.5 7.81,15.42L9.23,14C9.68,14.72 10.75,15.23 12,15.23C13.25,15.23 14.32,14.72 14.77,14L16.19,15.42C15.29,16.5 13.75,17.23 12,17.23Z" />
</svg> </svg>
<svg class="ttt-player-icon ttt-player-icon--behind ttt-player-icon ttt-player-icon--hidden" viewBox="0 0 24 24">
<path fill="currentColor" d="M20 12A8 8 0 1 0 12 20A8 8 0 0 0 20 12M22 12A10 10 0 1 1 12 2A10 10 0 0 1 22 12M15.5 8A1.5 1.5 0 1 1 14 9.5A1.54 1.54 0 0 1 15.5 8M10 9.5A1.5 1.5 0 1 1 8.5 8A1.54 1.54 0 0 1 10 9.5M17 15H13A4 4 0 0 0 9.53 17L7.8 16A6 6 0 0 1 13 13H17Z" />
</svg>
<div class="ttt-score ttt-score-x">0</div> <svg class="ttt-player-icon ttt-player-icon--behind ttt-player-icon ttt-player-icon--hidden" viewBox="0 0 24 24">
</div> <path fill="currentColor" d="M20 12A8 8 0 1 0 12 20A8 8 0 0 0 20 12M22 12A10 10 0 1 1 12 2A10 10 0 0 1 22 12M15.5 8A1.5 1.5 0 1 1 14 9.5A1.54 1.54 0 0 1 15.5 8M10 9.5A1.5 1.5 0 1 1 8.5 8A1.54 1.54 0 0 1 10 9.5M17 15H13A4 4 0 0 0 9.53 17L7.8 16A6 6 0 0 1 13 13H17Z" />
</svg>
<div class="ttt-grid"> <div class="ttt-score ttt-score-x">0</div>
<div class="ttt-row">
<div class="ttt-cell"></div><div class="ttt-cell"></div><div class="ttt-cell"></div>
</div> </div>
<div class="ttt-row">
<div class="ttt-cell"></div><div class="ttt-cell"></div><div class="ttt-cell"></div> <div class="ttt-grid">
<div class="ttt-row">
<div class="ttt-cell"></div><div class="ttt-cell"></div><div class="ttt-cell"></div>
</div>
<div class="ttt-row">
<div class="ttt-cell"></div><div class="ttt-cell"></div><div class="ttt-cell"></div>
</div>
<div class="ttt-row">
<div class="ttt-cell"></div><div class="ttt-cell"></div><div class="ttt-cell"></div>
</div>
</div> </div>
<div class="ttt-row">
<div class="ttt-cell"></div><div class="ttt-cell"></div><div class="ttt-cell"></div> <div class="ttt-player">
<svg class="ttt-player-icon ttt-player-icon-o" viewBox="0 0 24 24">
<path fill="currentColor" d="M9,11.75A1.25,1.25 0 0,0 7.75,13A1.25,1.25 0 0,0 9,14.25A1.25,1.25 0 0,0 10.25,13A1.25,1.25 0 0,0 9,11.75M15,11.75A1.25,1.25 0 0,0 13.75,13A1.25,1.25 0 0,0 15,14.25A1.25,1.25 0 0,0 16.25,13A1.25,1.25 0 0,0 15,11.75M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,11.71 4,11.42 4.05,11.14C6.41,10.09 8.28,8.16 9.26,5.77C11.07,8.33 14.05,10 17.42,10C18.2,10 18.95,9.91 19.67,9.74C19.88,10.45 20,11.21 20,12C20,16.41 16.41,20 12,20Z" />
</svg>
<div class="ttt-score ttt-score-o">0</div>
</div> </div>
</div>
<div class="ttt-player"> <svg class="ttt-icon ttt-icon-x" viewBox="0 0 24 24">
<svg class="ttt-player-icon ttt-player-icon-o" viewBox="0 0 24 24"> <path fill="currentColor" d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" />
<path fill="currentColor" d="M9,11.75A1.25,1.25 0 0,0 7.75,13A1.25,1.25 0 0,0 9,14.25A1.25,1.25 0 0,0 10.25,13A1.25,1.25 0 0,0 9,11.75M15,11.75A1.25,1.25 0 0,0 13.75,13A1.25,1.25 0 0,0 15,14.25A1.25,1.25 0 0,0 16.25,13A1.25,1.25 0 0,0 15,11.75M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,11.71 4,11.42 4.05,11.14C6.41,10.09 8.28,8.16 9.26,5.77C11.07,8.33 14.05,10 17.42,10C18.2,10 18.95,9.91 19.67,9.74C19.88,10.45 20,11.21 20,12C20,16.41 16.41,20 12,20Z" />
</svg> </svg>
<div class="ttt-score ttt-score-o">0</div> <svg class="ttt-icon ttt-icon-o" viewBox="0 0 24 24">
<path fill="currentColor" d="M12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" />
</svg>
</div> </div>
<svg class="ttt-icon ttt-icon-x" viewBox="0 0 24 24">
<path fill="currentColor" d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" />
</svg>
<svg class="ttt-icon ttt-icon-o" viewBox="0 0 24 24">
<path fill="currentColor" d="M12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" />
</svg>
</div> </div>
</div>
<div class="detections"></div> <div class="detections"></div>
<div class="terms terms--hidden"> <div class="terms terms--hidden">
<div class="terms__content" data-i18n="termsContent"></div> <div class="terms__content" data-i18n="termsContent"></div>
<div class="terms__buttons">
<button class="terms__button terms__button--accept" data-i18n="termsAccept">&nbsp;</button>
<button class="terms__button terms__button--decline" data-i18n="termsDecline">&nbsp;</button>
</div>
<div class="terms__buttons"> <a class="terms__privacy" href="https://www.wappalyzer.com/privacy/?utm_source=popup&utm_medium=extension&utm_campaign=wappalyzer" data-i18n="privacyPolicy"></a>
<button class="terms__button terms__button--accept" data-i18n="termsAccept">&nbsp;</button>
<button class="terms__button terms__button--decline" data-i18n="termsDecline">&nbsp;</button>
</div> </div>
<a class="terms__privacy" href="https://www.wappalyzer.com/privacy/?utm_source=popup&utm_medium=extension&utm_campaign=wappalyzer" data-i18n="privacyPolicy"></a> <div data-template="category" class="category">
</div> <div class="category__heading">
<a class="category__link" href="#"></a>
<div data-template="category" class="category"> <svg class="category__pin category__pin--outline" viewBox="0 0 24 24">
<div class="category__heading"> <title data-i18n="categoryPin"></title>
<a class="category__link" href="#"></a> <path fill="currentColor" d="M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12M8.8,14L10,12.8V4H14V12.8L15.2,14H8.8Z" />
</svg>
<svg class="category__pin category__pin--outline" viewBox="0 0 24 24"> <svg class="category__pin" viewBox="0 0 24 24">
<title data-i18n="categoryPin"></title> <title data-i18n="categoryPin"></title>
<path fill="currentColor" d="M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12M8.8,14L10,12.8V4H14V12.8L15.2,14H8.8Z" /> <path fill="currentColor" d="M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12Z" />
</svg> </svg>
</div>
<svg class="category__pin" viewBox="0 0 24 24"> <div class="technologies"></div>
<title data-i18n="categoryPin"></title>
<path fill="currentColor" d="M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12Z" />
</svg>
</div> </div>
<div class="technologies"></div> <div data-template="technology" class="technology">
</div> <div class="technology__heading">
<a class="technology__link" href="#">
<div class="technology__icon">
<img alt="" src="../images/icons/default.svg" />
</div>
<div data-template="technology" class="technology"> <span class="technology__name">&nbsp;</span>
<div class="technology__heading">
<a class="technology__link" href="#">
<div class="technology__icon">
<img alt="" src="../images/icons/default.svg" />
</div>
<span class="technology__name">&nbsp;</span> <span>
<span class="technology__version">&nbsp;</span>
</span>
<span> <span class="technology__confidence">&nbsp;</span>
<span class="technology__version">&nbsp;</span> </a>
</span> </div>
<span class="technology__confidence">&nbsp;</span>
</a>
</div> </div>
</div> </div>
</div>
<div class="tab-item tab-item--hidden"> <div class="tab-item tab-item--hidden">
<div class="plus-download plus-download--hidden"> <div class="credits credits--hidden">
<div class="plus-download__button button"> <span data-i18n="creditBalance">&nbsp;</span>
<span class="button__link">
<svg class="button__icon button__icon--left" viewBox="0 0 24 24">
<path fill="currentColor" d="M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z" />
</svg>
<span class="button__text"> <span class="credits__remaining">&nbsp;</span>
Download CSV
</span>
</span>
</div> </div>
</div>
<div class="plus-error plus-error--hidden"> <div class="plus-error plus-error--hidden">
<div class="plus-error__message"> <div class="plus-error__message">
</div>
</div> </div>
</div>
<div class="loading"> <div class="loading">
<svg class="progress" viewBox="20 20 40 40"> <svg class="progress" viewBox="20 20 40 40">
<circle class="progress__circle"></circle> <circle class="progress__circle"></circle>
</svg> </svg>
</div> </div>
<div class="panels panels--hidden"> <div class="panels panels--hidden">
</div> </div>
<div class="plus-configure plus-configure--hidden"> <div class="plus-configure plus-configure--hidden">
<div class="message"> <div class="message">
<div class="message__heading"> <div class="message__heading">
<span data-i18n="plusMessageHeading">&nbsp;</span> <span data-i18n="plusMessageHeading">&nbsp;</span>
</div> </div>
<p> <p>
<span data-i18n="plusMessage">&nbsp;</span> <span data-i18n="plusMessage">&nbsp;</span>
</p> </p>
<div class="message__button button"> <div class="message__button button">
<a class="button__link" href="https://www.wappalyzer.com/plus/?utm_source=popup&utm_medium=extension&utm_campaign=wappalyzer"> <a class="button__link" href="https://www.wappalyzer.com/plus/?utm_source=popup&utm_medium=extension&utm_campaign=wappalyzer">
<span class="button__text" data-i18n="plusButton">&nbsp;</span> <span class="button__text" data-i18n="plusButton">&nbsp;</span>
<svg class="button__icon button__icon--right" 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" /> <path fill="currentColor" d="M4,11V13H16L10.5,18.5L11.92,19.92L19.84,12L11.92,4.08L10.5,5.5L16,11H4Z" />
</svg> </svg>
</a> </a>
</div>
</div> </div>
</div>
<form class="plus-configure__form"> <form class="plus-configure__form">
<div class="control"> <div class="control">
<span class="label"> <span class="label">
<span data-i18n="optionApiKey">&nbsp;</span> <span data-i18n="optionApiKey">&nbsp;</span>
<small class="label__description"> <small class="label__description">
(<a href="https://www.wappalyzer.com/apikey/?utm_source=popup&utm_medium=extension&utm_campaign=wappalyzer" data-i18n="optionApiKeyDescription"></a>) (<a href="https://www.wappalyzer.com/apikey/?utm_source=popup&utm_medium=extension&utm_campaign=wappalyzer" data-i18n="optionApiKeyDescription"></a>)
</small> </small>
</span> </span>
<input type="password" class="plus-configure__apikey input" /> <input type="password" class="plus-configure__apikey input" />
</div> </div>
<div class="message__button button"> <div class="message__button button">
<span class="plus-configure__save button__link"> <span class="plus-configure__save button__link">
<svg class="button__icon button__icon--left" viewBox="0 0 24 24"> <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" /> <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> </svg>
<span class="button__text" data-i18n="formSave">&nbsp;</span> <span class="button__text" data-i18n="formSave">&nbsp;</span>
</span> </span>
</div> </div>
</form> </form>
</div> </div>
<div class="plus-empty plus-empty--hidden" data-i18n="plusEmpty"></div> <div class="plus-empty plus-empty--hidden" data-i18n="plusEmpty"></div>
<div class="plus-crawl plus-crawl--hidden" data-i18n="plusCrawl"></div> <div class="plus-crawl plus-crawl--hidden" data-i18n="plusCrawl"></div>
</div>
</div> </div>
<div class="footer"> <div class="footer">

@ -8,9 +8,6 @@ const { agent, open, i18n, getOption, setOption, promisify, sendMessage } =
const baseUrl = 'https://www.wappalyzer.com' const baseUrl = 'https://www.wappalyzer.com'
const utm = '?utm_source=popup&utm_medium=extension&utm_campaign=wappalyzer' const utm = '?utm_source=popup&utm_medium=extension&utm_campaign=wappalyzer'
let csv = ''
let csvFilename = ''
const footers = [ const footers = [
{ {
heading: 'Generate sales leads', heading: 'Generate sales leads',
@ -107,6 +104,71 @@ function setDisabledDomain(enabled) {
} }
} }
function getCsv() {
let hostname = ''
let www = false
let https = false
try {
let protocol = ''
;({ hostname, protocol } = new URL(Popup.cache.url))
www = hostname.startsWith('www')
https = protocol === 'https:'
hostname = hostname.replace(/^www\./, '')
} catch (error) {
// Continue
}
const columns = [
'URL',
...Popup.cache.categories.map(({ id }) =>
chrome.i18n.getMessage(`categoryName${id}`)
),
...attributeKeys.map((key) =>
chrome.i18n.getMessage(
`attribute${
key.charAt(0).toUpperCase() + key.slice(1).replace('.', '_')
}`
)
),
]
const csv = [`"${columns.join('","')}"`]
const filename = `wappalyzer${
hostname ? `_${hostname.replace('.', '-')}` : ''
}.csv`
const row = [`http${https ? 's' : ''}://${www ? 'www.' : ''}${hostname}`]
row.push(
...Popup.cache.categories.reduce((categories, { id }) => {
categories.push(
Popup.cache.detections
.filter(({ categories }) =>
categories.some(({ id: _id }) => _id === id)
)
.map(({ name }) => name)
.join(' ; ')
)
return categories
}, [])
)
row.push(
...attributeKeys.map((key) => csvEscape(Popup.cache.attributeValues[key]))
)
csv.push(`"${row.join('","')}"`)
return { csv, filename }
}
function csvEscape(value = '') { function csvEscape(value = '') {
if (Array.isArray(value)) { if (Array.isArray(value)) {
value = value value = value
@ -136,6 +198,13 @@ const Popup = {
* Initialise popup * Initialise popup
*/ */
async init() { async init() {
Popup.cache = {
url: '',
categories: [],
detections: [],
attributeValues: {},
}
const el = { const el = {
body: document.body, body: document.body,
terms: document.querySelector('.terms'), terms: document.querySelector('.terms'),
@ -150,6 +219,7 @@ const Popup = {
headerSwitchDisabled: document.querySelector('.header__switch--disabled'), headerSwitchDisabled: document.querySelector('.header__switch--disabled'),
plusConfigureApiKey: document.querySelector('.plus-configure__apikey'), plusConfigureApiKey: document.querySelector('.plus-configure__apikey'),
plusConfigureSave: document.querySelector('.plus-configure__save'), plusConfigureSave: document.querySelector('.plus-configure__save'),
plusDownload: document.querySelector('.plus-download'),
plusDownloadLink: document.querySelector( plusDownloadLink: document.querySelector(
'.plus-download__button .button__link' '.plus-download__button .button__link'
), ),
@ -246,6 +316,8 @@ const Popup = {
;[{ url }] = tabs ;[{ url }] = tabs
if (url.startsWith('http')) { if (url.startsWith('http')) {
Popup.cache.url = url
const { hostname } = new URL(url) const { hostname } = new URL(url)
setDisabledDomain(disabledDomains.includes(hostname)) setDisabledDomain(disabledDomains.includes(hostname))
@ -322,6 +394,7 @@ const Popup = {
el.tabItems[index].classList.remove('tab-item--hidden') el.tabItems[index].classList.remove('tab-item--hidden')
el.credits.classList.add('credits--hidden') el.credits.classList.add('credits--hidden')
el.plusDownload.classList.remove('plus-download--hidden')
el.footer.classList.remove('footer--hidden') el.footer.classList.remove('footer--hidden')
if (tab.classList.contains('tab--plus')) { if (tab.classList.contains('tab--plus')) {
@ -331,7 +404,7 @@ const Popup = {
}) })
// Download // Download
el.plusDownloadLink.addEventListener('click', (event) => Popup.downloadCsv) el.plusDownloadLink.addEventListener('click', Popup.downloadCsv)
// Footer // Footer
const item = const item =
@ -381,6 +454,8 @@ const Popup = {
// Apply internationalization // Apply internationalization
i18n() i18n()
Popup.cache.categories = await Popup.driver('getCategories')
}, },
driver(func, args) { driver(func, args) {
@ -423,9 +498,12 @@ const Popup = {
* @param {Array} detections * @param {Array} detections
*/ */
async onGetDetections(detections = []) { async onGetDetections(detections = []) {
Popup.cache.detections = detections
const el = { const el = {
empty: document.querySelector('.empty'), empty: document.querySelector('.empty'),
detections: document.querySelector('.detections'), detections: document.querySelector('.detections'),
plusDownload: document.querySelector('.plus-download'),
} }
detections = (detections || []) detections = (detections || [])
@ -435,12 +513,14 @@ const Popup = {
if (!detections || !detections.length) { if (!detections || !detections.length) {
el.empty.classList.remove('empty--hidden') el.empty.classList.remove('empty--hidden')
el.detections.classList.add('detections--hidden') el.detections.classList.add('detections--hidden')
el.plusDownload.classList.add('plus-download--hidden')
return return
} }
el.empty.classList.add('empty--hidden') el.empty.classList.add('empty--hidden')
el.detections.classList.remove('detections--hidden') el.detections.classList.remove('detections--hidden')
el.plusDownload.classList.remove('plus-download--hidden')
while (el.detections.firstChild) { while (el.detections.firstChild) {
el.detections.removeChild(detections.firstChild) el.detections.removeChild(detections.firstChild)
@ -554,9 +634,6 @@ const Popup = {
crawl: document.querySelector('.plus-crawl'), crawl: document.querySelector('.plus-crawl'),
error: document.querySelector('.plus-error'), error: document.querySelector('.plus-error'),
download: document.querySelector('.plus-download'), download: document.querySelector('.plus-download'),
downloadLink: document.querySelector(
'.plus-download__button .button__link'
),
errorMessage: document.querySelector('.plus-error__message'), errorMessage: document.querySelector('.plus-error__message'),
configure: document.querySelector('.plus-configure'), configure: document.querySelector('.plus-configure'),
credits: document.querySelector('.credits'), credits: document.querySelector('.credits'),
@ -565,6 +642,7 @@ const Popup = {
} }
el.error.classList.add('plus-error--hidden') el.error.classList.add('plus-error--hidden')
el.download.classList.add('plus-download--hidden')
if (apiKey) { if (apiKey) {
el.loading.classList.remove('loading--hidden') el.loading.classList.remove('loading--hidden')
@ -579,7 +657,6 @@ const Popup = {
} }
el.panels.classList.add('panels--hidden') el.panels.classList.add('panels--hidden')
el.download.classList.add('plus-download--hidden')
el.empty.classList.add('plus-empty--hidden') el.empty.classList.add('plus-empty--hidden')
el.crawl.classList.add('plus-crawl--hidden') el.crawl.classList.add('plus-crawl--hidden')
el.error.classList.add('plus-error--hidden') el.error.classList.add('plus-error--hidden')
@ -588,24 +665,6 @@ const Popup = {
el.panels.removeChild(el.panels.lastElementChild) el.panels.removeChild(el.panels.lastElementChild)
} }
let hostname = ''
let www = false
let https = false
try {
let protocol = ''
;({ hostname, protocol } = new URL(url))
www = hostname.startsWith('www')
https = protocol === 'https:'
hostname = hostname.replace(/^www\./, '')
} catch (error) {
// Continue
}
try { try {
const response = await fetch( const response = await fetch(
`https://api.wappalyzer.com/v2/plus/${encodeURIComponent(url)}`, `https://api.wappalyzer.com/v2/plus/${encodeURIComponent(url)}`,
@ -635,9 +694,8 @@ const Popup = {
10 10
).toLocaleString() ).toLocaleString()
el.credits.classList.remove('credits--hidden')
el.loading.classList.add('loading--hidden') el.loading.classList.add('loading--hidden')
el.credits.classList.remove('credits--hidden')
if (crawl) { if (crawl) {
document document
@ -649,50 +707,11 @@ const Popup = {
if (!Object.keys(attributes).length) { if (!Object.keys(attributes).length) {
el.empty.classList.remove('plus-empty--hidden') el.empty.classList.remove('plus-empty--hidden')
el.download.classList.remove('plus-download--hidden')
return return
} }
const categories = await Popup.driver('getCategories')
const columns = [
'URL',
...categories.map(({ id }) =>
chrome.i18n.getMessage(`categoryName${id}`)
),
...attributeKeys.map((key) =>
chrome.i18n.getMessage(
`attribute${
key.charAt(0).toUpperCase() + key.slice(1).replace('.', '_')
}`
)
),
]
csv = [`"${columns.join('","')}"`]
csvFilename = `wappalyzer${
hostname ? `_${hostname.replace('.', '-')}` : ''
}.csv`
const row = [`http${https ? 's' : ''}://${www ? 'www.' : ''}${hostname}`]
const detections = await Popup.driver('getDetections')
row.push(
...categories.reduce((categories, { id }) => {
categories.push(
detections
.filter(({ categories }) =>
categories.some(({ id: _id }) => _id === id)
)
.map(({ name }) => name)
.join(' ; ')
)
return categories
}, [])
)
const attributeValues = {} const attributeValues = {}
Object.keys(attributes).forEach((set) => { Object.keys(attributes).forEach((set) => {
@ -800,26 +819,7 @@ const Popup = {
el.panels.appendChild(panel) el.panels.appendChild(panel)
}) })
row.push(...attributeKeys.map((key) => csvEscape(attributeValues[key]))) Popup.cache.attributeValues = attributeValues
csv.push(`"${row.join('","')}"`)
el.downloadLink.addEventListener('click', (event) => {
event.preventDefault()
const file = URL.createObjectURL(
new Blob([csv.join('\n')], { type: 'text/csv;charset=utf-8' })
)
chrome.downloads.download({
url: file,
filename: `wappalyzer${
hostname ? `_${hostname.replace('.', '-')}` : ''
}.csv`,
})
return false
})
el.panels.classList.remove('panels--hidden') el.panels.classList.remove('panels--hidden')
el.download.classList.remove('plus-download--hidden') el.download.classList.remove('plus-download--hidden')
@ -869,14 +869,20 @@ const Popup = {
i18n() i18n()
}, },
downloadCsv() { downloadCsv(event) {
console.log('x')
event.preventDefault() event.preventDefault()
const { csv, filename } = getCsv()
const file = URL.createObjectURL(
new Blob([csv.join('\n')], { type: 'text/csv;charset=utf-8' })
)
chrome.downloads.download({ chrome.downloads.download({
url: URL.createObjectURL( url: file,
new Blob([csv.join('\n')], { type: 'text/csv;charset=utf-8' }) filename,
),
filename: csvFilename,
}) })
return false return false