Skip to content

Instantly share code, notes, and snippets.

@jnv
Last active November 6, 2017 14:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jnv/729ab13d84d7c60266c471ac557a55ec to your computer and use it in GitHub Desktop.
Save jnv/729ab13d84d7c60266c471ac557a55ec to your computer and use it in GitHub Desktop.
[Facebook Audience Insights: Copy Page Likes] #sb #userscripts
// ==UserScript==
// @id fbaicopylikes@jnv.github.io
// @name Facebook Audience Insights: Copy Page Likes
// @namespace https://jnv.github.io
// @version 2017.10.11
// @description Copy Page Likes table in Facebook Audience Insights
// @author Jan Vlnas
// @match https://www.facebook.com/ads/audience-insights/*
// @grant GM_setClipboard
// @run-at document-idle
// @license CC0 1.0; https://creativecommons.org/publicdomain/zero/1.0/
// ==/UserScript==
const SEL = {
root: '._5p02:nth-last-of-type(1)',
table: 'table',
buttonHolder: '._-0c',
more: 'button[type="submit"]',
rows: 'tbody tr',
};
const INJECT_SEL = `${SEL.root} ${SEL.table}`;
const COPY_BTN_ID = 'js-tamper-copybtn';
const ROWS_LIMIT = 30;
const PATH_MATCH = /\/interests/;
function createCopyButton () {
const btn = document.createElement('button');
btn.type = 'button';
btn.textContent = '🦄 Copy';
btn.style = 'cursor: pointer;';
btn.id = COPY_BTN_ID;
return btn;
}
function textContent (element) {
return element.textContent;
}
function firstTextNodeContent (node) {
'use strict';
const iter = document.createNodeIterator(node, NodeFilter.SHOW_TEXT);
const first = iter.nextNode();
return first.textContent;
}
function parseRow (rowNode, cols) {
const cells = Array.from(rowNode.querySelectorAll('td'));
const rowObj = {};
for (let i = 0; i < cells.length; i++) {
const node = cells[i];
const col = cols[i];
rowObj[col] = node.textContent;
const link = node.querySelector('a');
if (link) {
rowObj.URL = link.href;
}
}
return rowObj;
}
function parseTable (table) {
const th = Array.from(table.querySelectorAll('thead th'));
const rows = Array.from(table.querySelectorAll('tbody tr'));
const cols = th.map(firstTextNodeContent);
const rowsData = rows.map(row => parseRow(row, cols));
return rowsData;
}
function formatRow (row) {
return row.join('\t');
}
function formatTableOutput (data) {
const resultCols = ['Page', 'URL', 'Relevance', 'Audience', 'Facebook', 'Affinity'];
const rows = data.map(row => {
return formatRow(resultCols.map(col => row[col]));
});
// Add header
// rows.unshift(formatRow(resultCols));
return rows.join('\n');
}
function copyTable (table) {
const data = parseTable(table);
const count = data.length;
const output = formatTableOutput(data);
GM_setClipboard(output);
return count;
}
function unrollTable (root, table) {
const rowsCount = () => table.querySelectorAll(SEL.rows).length;
const moreBtn = () => root.querySelector(SEL.more);
const isTop10Btn = () => /10/.test(moreBtn().textContent);
const shouldContinue = () => rowsCount() < ROWS_LIMIT && !isTop10Btn();
while (shouldContinue()) {
moreBtn().click();
}
}
function inject () {
'use strict';
const root = document.querySelector(SEL.root);
if (!root) {
return false;
}
const table = root.querySelector(SEL.table);
if (!table) {
return false;
}
const btnHolder = root.querySelector(SEL.buttonHolder);
const copyBtn = createCopyButton();
copyBtn.onclick = () => {
unrollTable(root, table);
copyTable(table);
};
btnHolder.appendChild(copyBtn);
return true;
}
function canInject () {
return PATH_MATCH.test(window.location.pathname) &&
!document.getElementById(COPY_BTN_ID) &&
document.querySelector(INJECT_SEL);
}
function tryInject () {
'use strict';
if (canInject()) {
inject();
window.setTimeout(tryInject, 5000);
} else {
window.setTimeout(tryInject, 1000);
}
}
tryInject();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment