Last active
November 6, 2017 14:28
-
-
Save jnv/729ab13d84d7c60266c471ac557a55ec to your computer and use it in GitHub Desktop.
[Facebook Audience Insights: Copy Page Likes] #sb #userscripts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @id [email protected] | |
// @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