|
// ==UserScript== |
|
// @name IAFD |
|
// @author peolic |
|
// @version 4.98 |
|
// @description IAFD helper |
|
// @icon https://www.iafd.com/favicon-196x196.png |
|
// @namespace https://github.com/peolic |
|
// @match https://www.iafd.com/* |
|
// @grant GM.setClipboard |
|
// @grant GM.addStyle |
|
// @homepageURL https://gist.github.com/peolic/9e2981a8a14a49b9626cb277f878b157 |
|
// @downloadURL https://gist.github.com/peolic/9e2981a8a14a49b9626cb277f878b157/raw/iafd.user.js |
|
// @updateURL https://gist.github.com/peolic/9e2981a8a14a49b9626cb277f878b157/raw/iafd.user.js |
|
// ==/UserScript== |
|
|
|
function main() { |
|
makeInternalLinksHTTPS(); |
|
fixIAFDLinks(); |
|
|
|
const { pathname } = window.location; |
|
|
|
if (/^\/title\.(asp|rme\/)/.test(pathname)) |
|
return titlePage(); |
|
if (/^\/person\.(asp|rme\/)/.test(pathname)) |
|
return personPage(); |
|
if (/^\/(([a-z]{4}_)?studio|distrib)\.(asp|rme\/)$/.test(pathname)) |
|
return studioDistribSelectPage(); |
|
if (/^\/(studio|distrib)\.(asp\?|rme\/)(studio|distrib)=\d+/.test(pathname)) |
|
return studioDistribPage(); |
|
if (/^\/(new|updated)(perfs|headshots)\.asp/.test(pathname)) |
|
return personUpdatePage(); |
|
if (/^\/lookuptat\.asp/.test(pathname)) |
|
return tattooLookupPage(); |
|
if (/^\/results\.asp/.test(pathname)) |
|
return setSearchField(); |
|
} |
|
|
|
const getBioDataElement = (headingText) => { |
|
return Array.from(document.querySelectorAll('p.bioheading')) |
|
.find(e => e.innerText.localeCompare(headingText, 'en', { sensitivity: 'accent' }) === 0) |
|
?.nextElementSibling; |
|
}; |
|
|
|
const makeBioEntry = (heading, ...data) => { |
|
return [['heading', heading], ...data.map((d) => ['data', d])].map(([type, text]) => { |
|
const p = document.createElement('p'); |
|
p.classList.add(`bio${type}`); |
|
if (text instanceof Node) p.append(text); |
|
else p.innerText = text; |
|
return p; |
|
}); |
|
}; |
|
|
|
const makeQuickSelect = (text) => { |
|
const b = document.createElement('b'); |
|
b.style.userSelect = 'all'; |
|
b.innerText = text; |
|
return b; |
|
}; |
|
|
|
const makeISODateElement = (date) => { |
|
const isoDate = new Date(`${date} 0:00 UTC`).toISOString().slice(0, 10); |
|
return makeQuickSelect(isoDate); |
|
}; |
|
|
|
|
|
const slug = (text, char='-') => { |
|
return encodeURI(text.replace(/[^a-z0-9_.,:'-]+/gi, '___')) |
|
.toLowerCase() |
|
.replace(/___/g, char); |
|
}; |
|
|
|
const makeLegacyTitleLink = (title, year) => { |
|
const encodedTitle = slug(title, '+').replace(/^\+|\++$|^(a|an|the)\+/g, ''); |
|
return `https://www.iafd.com/title.rme/title=${encodedTitle}/year=${year}/${encodedTitle.replace(/\+/g, '-')}.htm`; |
|
}; |
|
|
|
const makeLegacyPerformerLink = (perfId, gender, name=undefined) => { |
|
const nameSlug = name ? `/${slug(name, '-')}.htm` : ''; |
|
return `https://www.iafd.com/person.rme/perfid=${perfId}/gender=${gender}${nameSlug}`; |
|
}; |
|
|
|
function makeInternalLinksHTTPS() { |
|
/** @type {NodeListOf<HTMLAnchorElement>} */ |
|
(document.querySelectorAll('a[href^="http://www.iafd.com/"]')).forEach((el) => { |
|
el.href = el.href.replace(/^http:/, 'https:'); |
|
}); |
|
}; |
|
|
|
function fixIAFDLinks() { |
|
const replacer = (_, offset, string) => { |
|
if (offset > string.indexOf('/year=')) return '-'; |
|
else return '+'; |
|
}; |
|
/** @type {NodeListOf<HTMLAnchorElement>} */ |
|
(document.querySelectorAll('a[href*="//www.iafd.com/"]')).forEach((el) => { |
|
el.href = el.href.replace(/%2f/g, replacer); |
|
}); |
|
}; |
|
|
|
function titlePage() { |
|
const canonical = document.querySelector('.panel:last-of-type .padded-panel') |
|
?.innerText?.match(/should be linked to:\n(.+)/)?.[1]?.replace(/^http:/, 'https:'); |
|
if (canonical) |
|
history.replaceState(null, '', canonical); |
|
|
|
const titleHeading = document.querySelector('h1'); |
|
// remove trailing space |
|
titleHeading.textContent = titleHeading.textContent.replace(/[ \n]$/, ''); |
|
|
|
const correct = document.querySelector('#correct'); |
|
|
|
if (correct) { |
|
const filmIdBio = makeBioEntry('Film ID', correct.querySelector('input[name="FilmID"]')?.value); |
|
filmIdBio[0].style.marginTop = '4em'; |
|
correct.before(...filmIdBio); |
|
|
|
const links = []; |
|
if (canonical) { |
|
const link = document.createElement('a'); |
|
link.href = canonical; |
|
link.title = link.href; |
|
link.innerText = 'Title Page Link'; |
|
Object.assign(link.style, { |
|
color: '#337ab7', |
|
margin: '1em 0', |
|
}); |
|
links.push(link); |
|
} |
|
|
|
// Legacy link |
|
const titleAndYear = titleHeading.innerText.match(/^(.+) \((\d+)\)$/)?.slice(1) ?? []; |
|
const legacyLink = document.createElement('a'); |
|
legacyLink.href = makeLegacyTitleLink(...titleAndYear); |
|
legacyLink.title = legacyLink.href; |
|
legacyLink.innerText = 'Legacy Title Page Link'; |
|
Object.assign(legacyLink.style, { |
|
color: '#337ab7', |
|
margin: '1em 0', |
|
}); |
|
links.push(legacyLink); |
|
|
|
correct.before(...makeBioEntry('🔗 Links', ...links)); |
|
} |
|
|
|
const titleSeq = titleHeading.innerText.match(/\((X{0,3}(IX|IV|V?I{0,3}))\)/); |
|
if (titleSeq) { |
|
// Break up the text node |
|
for (let i=0; i < titleHeading.childNodes.length; i++) { |
|
if (i >= 10) break; |
|
const node = titleHeading.childNodes[i]; |
|
const open = node.textContent.indexOf('(', 1), |
|
close = node.textContent.indexOf(')'); |
|
if (!(open || close)) continue; |
|
if (close && close+1 >= node.length) break; |
|
node.splitText(open ?? close+1); |
|
} |
|
const seqNode = Array.from(titleHeading.childNodes).find((node) => node.textContent.includes(titleSeq?.[1])); |
|
const seq = document.createElement('small'); |
|
seq.style.color = 'lightgray'; |
|
seq.textContent = seqNode.textContent; |
|
titleHeading.replaceChild(seq, seqNode); |
|
} |
|
|
|
const releaseDateElement = getBioDataElement('RELEASE DATE'); |
|
const releaseDateNodes = Array.from(releaseDateElement.childNodes).filter((node) => node.nodeType === node.TEXT_NODE); |
|
releaseDateNodes.forEach((node) => { |
|
const text = node.textContent.trim(); |
|
const releaseDate = text.replace(/ \(.+\)$/, ''); |
|
if (!releaseDate || releaseDate === 'No Data') |
|
return; |
|
releaseDateElement.insertBefore(makeISODateElement(releaseDate), node); |
|
releaseDateElement.insertBefore(document.createElement('br'), node); |
|
}); |
|
|
|
setupScenePerformers(); |
|
} |
|
|
|
const setupScenePerformers = () => { |
|
const sceneInfo = document.querySelector('#sceneinfo'); |
|
if (!sceneInfo) return; |
|
const scenes = Array.from(sceneInfo.querySelectorAll('table tr')); |
|
if (scenes.length === 0) return; |
|
|
|
const castboxes = Array.from(document.querySelectorAll('.castbox')).map((cb) => ({ |
|
name: cb.querySelector('a').lastChild.textContent, |
|
cb, |
|
})); |
|
|
|
GM.addStyle(` |
|
.ext__scene-performers-toggle { white-space: nowrap; } |
|
.ext__scene-performers { display: flex; flex-wrap: wrap; column-gap: 1em; } |
|
.ext__scene-performers .multiple-matches { background: #bf0000cc; color: white; flex-basis: 100%; font-weight: 600; margin: 0.5em 1em 0 0; padding: 0.25em 0.6em; } |
|
.ext__scene-performers .castbox { max-width: 180px; min-height: unset; float: unset; margin-left: 0; } |
|
.ext__scene-performers .castbox img.headshot { margin-left: -14px; } |
|
`); |
|
|
|
const toggleStates = new Array(scenes.length).fill(false); |
|
const sceneToggles = scenes.map((sceneRow, sceneIndex) => { |
|
const sceneLabel = sceneRow.querySelector(':scope > td:first-of-type'); |
|
const sceneLabelText = sceneLabel.innerText.toLowerCase(); |
|
const scenePerformers = sceneLabel.nextElementSibling; |
|
const toggle = (newState = undefined) => { |
|
let sceneContainer = sceneRow.querySelector('.ext__scene-performers'); |
|
if (sceneContainer) { |
|
sceneContainer.style.display = sceneContainer.style.display === 'none' || newState === true ? '' : 'none'; |
|
toggleStates[sceneIndex] = sceneContainer.style.display !== 'none'; |
|
return; |
|
} else if (newState === false) { |
|
return; |
|
} |
|
const performers = scenePerformers.innerText |
|
.match(/^.+$/m)[0] // grab first row |
|
.replace(/ \[[a-z0-9 ]+\]/gi, '') // remove tags |
|
.split(/, /g); // split names |
|
sceneContainer = document.createElement('div'); |
|
sceneContainer.className = 'ext__scene-performers'; |
|
performers.forEach((performer) => { |
|
const matches = castboxes.filter(({ name }) => name === performer); |
|
if (matches.length === 0) { |
|
const [count, text] = performer.match(/^(\d+) (.+?)s?$/)?.slice(1, 3) ?? [1, performer]; |
|
for (let i = 1; i <= count; i++) { |
|
const node = document.createElement('div'); |
|
node.className = 'castbox'; |
|
const p = document.createElement('p'); |
|
const placeholder = document.createElement('div'); |
|
Object.assign(placeholder.style, { width: '170px', height: '200px', padding: '1em' }); |
|
const na = document.createElement('abbr'); |
|
na.title = `Performer "${performer}" was not found above`; |
|
na.innerText = '[N/A]'; |
|
p.append(placeholder, `${count == 1 ? performer : text} `, na); |
|
node.append(p); |
|
sceneContainer.append(node); |
|
} |
|
} else { |
|
if (matches.length > 1) { |
|
const warning = document.createElement('div'); |
|
warning.innerText = `multiple matches for "${performer}"`; |
|
warning.className = 'multiple-matches'; |
|
sceneContainer.prepend(warning); |
|
} |
|
matches.forEach(({ cb: performerCB }) => { |
|
const node = performerCB.cloneNode(true); |
|
// remove empty elements and extra line breaks from the inner paragraph element |
|
for (const cn of Array.from(node.firstElementChild.childNodes).reverse()) { |
|
if (cn.textContent.trim()) break; |
|
cn.remove(); |
|
} |
|
sceneContainer.append(node); |
|
}); |
|
} |
|
}); |
|
scenePerformers.append(sceneContainer); |
|
toggleStates[sceneIndex] = true; |
|
}; |
|
|
|
const sceneToggle = document.createElement('div'); |
|
sceneLabel.append(sceneToggle); |
|
sceneToggle.append(sceneLabel.firstChild); |
|
Object.assign(sceneToggle.style, { cursor: 'pointer', textDecoration: 'underline' }); |
|
sceneToggle.title = `View performers for ${sceneLabelText}`; |
|
sceneToggle.className = 'ext__scene-performers-toggle'; |
|
sceneToggle.addEventListener('click', () => { |
|
if (window.getSelection().type === 'Range') return; // Prevent unwanted action when selecting text |
|
toggle(); |
|
}); |
|
|
|
return toggle; |
|
}); |
|
|
|
// Toggle all scenes |
|
const sceneInfoHeading = sceneInfo.querySelector('.panel-heading h3'); |
|
Object.assign(sceneInfoHeading.style, { cursor: 'pointer', textDecoration: 'double underline' }); |
|
sceneInfoHeading.title = `View performers for all ${scenes.length} scenes\n [hold Alt to reset]`; |
|
sceneInfoHeading.addEventListener('click', (ev) => { |
|
if (window.getSelection().type === 'Range') return; // Prevent unwanted action when selecting text |
|
if (ev.altKey) { |
|
sceneInfo.querySelectorAll('.ext__scene-performers').forEach((el) => el.remove()); |
|
return toggleStates.fill(false); |
|
} |
|
const newState = toggleStates.filter(Boolean).length < scenes.length; |
|
sceneToggles.forEach((toggle) => toggle(newState)); |
|
}); |
|
}; |
|
|
|
function personPage() { |
|
makeExportButton(); |
|
|
|
const canonical = document.querySelector('#perfwith a[href^="/person.rme/"]')?.href; |
|
if (canonical) |
|
history.replaceState(null, '', canonical); |
|
|
|
const nameHeading = document.querySelector('h1'); |
|
// remove trailing space |
|
nameHeading.textContent = nameHeading.textContent.replace(/[ \n]$/, ''); |
|
// Director page |
|
if (/\/gender=d\//.test(window.location.pathname)) { |
|
const directorPage = document.createElement('b'); |
|
directorPage.innerText = 'Director-only page:'; |
|
const [maleLink, femaleLink] = ['male', 'female'].map((gender) => { |
|
const a = document.createElement('a'); |
|
a.href = (canonical ?? window.location.href).replace('/gender=d/', `/gender=${gender.charAt(0)}/`); |
|
a.innerText = gender; |
|
return a; |
|
}); |
|
const directorHelp = document.createElement('div'); |
|
directorHelp.append(directorPage, ' try the ', maleLink, ' or ', femaleLink, ' performer pages'); |
|
nameHeading.after(directorHelp); |
|
} |
|
|
|
const corrections = document.querySelector('#corrections'); |
|
|
|
if (corrections) { |
|
const perfIdCorrectionInput = corrections.querySelector('input[name="PerfID"]'); |
|
const gender = corrections.querySelector('input[name="Gender"]')?.value; |
|
const perfIdScenePair = document.querySelector('#scenepairings')?.dataset.src.match(/\/perfid=(.+)$/)[1]; |
|
const perfId = perfIdCorrectionInput?.value ?? perfIdScenePair; |
|
|
|
const perfIdBio = makeBioEntry('Performer ID', `${perfId} [${gender.toUpperCase()}]`); |
|
perfIdBio[0].style.marginTop = '4em'; |
|
corrections.before(...perfIdBio); |
|
|
|
if (!perfIdCorrectionInput.getAttribute('value')) { |
|
corrections.querySelector('input[type="submit"]').style.color = 'red'; |
|
perfIdCorrectionInput.setAttribute('value', perfId); |
|
} |
|
|
|
const links = []; |
|
if (canonical) { |
|
const link = document.createElement('a'); |
|
link.href = canonical; |
|
link.title = link.href; |
|
link.innerText = 'Performer Page Link'; |
|
Object.assign(link.style, { |
|
color: '#337ab7', |
|
margin: '1em 0', |
|
}); |
|
links.push(link); |
|
} |
|
|
|
// Legacy link |
|
const legacyLink = document.createElement('a'); |
|
legacyLink.href = makeLegacyPerformerLink(perfId, gender, nameHeading.innerText); |
|
legacyLink.title = legacyLink.href; |
|
legacyLink.innerText = 'Legacy Performer Page Link'; |
|
Object.assign(legacyLink.style, { |
|
color: '#337ab7', |
|
margin: '1em 0', |
|
}); |
|
links.push(legacyLink); |
|
|
|
corrections.before(...makeBioEntry('🔗 Links', ...links)); |
|
} |
|
|
|
const birthDateElement = getBioDataElement('BIRTHDAY'); |
|
const birthdayText = birthDateElement.innerText; |
|
const birthDate = birthdayText.trim().match(/([A-Z][a-z]+ \d{1,2}, \d{4})\b/); |
|
if (birthDate) { |
|
birthDateElement.prepend(makeISODateElement(birthDate[1]), document.createElement('br')); |
|
} else { |
|
const partialDate = birthdayText.trim().match(/(?<month>\?\?|\d{1,2})\/(?<day>\?\?|\d{1,2})\/(?<year>\d{2}[\d?]{2})/); |
|
if (partialDate) { |
|
const { year, month, day } = partialDate.groups; |
|
const exactYear = /^\d{4}$/.test(year); |
|
if (exactYear) { |
|
const dateParts = [year, month, day].join('-'); |
|
const firstQM = dateParts.indexOf('?', 4)-1; |
|
const [isoDate, remainder] = [dateParts.slice(0, firstQM), dateParts.slice(firstQM)]; |
|
birthDateElement.prepend(makeQuickSelect(isoDate), remainder, document.createElement('br')); |
|
} |
|
|
|
if (month !== '??') { |
|
const partialDateStr = (new Date(birthdayText.replace(/\?\?/g, '01') + ' 12:00')) |
|
.toLocaleString('en-us', { |
|
month: month === '??' ? undefined : 'long', |
|
day: day === '??' ? undefined : 'numeric', |
|
year: exactYear ? 'numeric' : undefined, |
|
}) + (exactYear ? '' : `, ${year}`); |
|
birthDateElement.lastChild.before(document.createTextNode(partialDateStr), document.createElement('br')); |
|
} |
|
} |
|
} |
|
|
|
const heightElement = getBioDataElement('HEIGHT'); |
|
const height = heightElement?.innerText.trim().match(/\((\d+) cm\)/); |
|
if (height && heightElement) { |
|
heightElement.prepend(makeQuickSelect(height[1]), document.createElement('br')); |
|
} |
|
|
|
// example: Lee Stone |
|
const akasDirectorElement = getBioDataElement('DIRECTOR AKA'); |
|
if (akasDirectorElement && akasDirectorElement.innerText.match(/^None No known aliases$/)) { |
|
akasDirectorElement.innerText = akasDirectorElement.innerText.replace(/^None /, ''); |
|
} |
|
|
|
const akasElement = getBioDataElement('AKA') ?? getBioDataElement('PERFORMER AKA'); |
|
const akas = akasElement?.innerText.trim(); |
|
if (akas && akas !== 'No known aliases') { |
|
const copyButtonDefaultText = '[copy names]'; |
|
|
|
const akasCopy = document.createElement('a'); |
|
akasCopy.innerText = copyButtonDefaultText; |
|
akasCopy.title = 'Copy only the names used (removes site names)' |
|
akasCopy.id = 'copy-akas'; |
|
Object.assign(akasCopy.style, { float: 'right', cursor: 'pointer', lineHeight: 1 }); |
|
|
|
akasCopy.addEventListener('click', async (ev) => { |
|
ev.stopPropagation(); |
|
ev.preventDefault(); |
|
|
|
// https://regex101.com/r/7Ad3U1/2 |
|
const names = akas.replace(/ \(.+?\)/g, '').split(/[,;] ?/g).map((s) => s.trim()).filter(Boolean); |
|
if (!names || names.length === 0) { |
|
akasCopy.innerText = '❌ Failed!'; |
|
akasCopy.style.color = 'red'; |
|
return; |
|
} |
|
|
|
const result = names.join(', '); |
|
GM.setClipboard(result); |
|
akasCopy.innerText = '✔ Copied to clipboard'; |
|
akasCopy.style.color = 'green'; |
|
akasElement.innerText = result; |
|
akasElement.style.backgroundColor = 'yellow'; |
|
|
|
await wait(1500); |
|
akasCopy.innerText = copyButtonDefaultText; |
|
akasCopy.style.color = null; |
|
akasElement.innerText = akas; |
|
akasElement.style.backgroundColor = ''; |
|
}) |
|
|
|
akasElement.previousElementSibling.append(akasCopy); |
|
} |
|
} |
|
|
|
function studioDistribSelectPage() { |
|
const select = document.querySelector('select[name="Studio"], select[name="Distrib"]'); |
|
const pageType = select.closest('form').getAttribute('action').replace(/^\/|\.rme\/$/g, '').toLowerCase(); |
|
const selectType = select.name.toLowerCase(); |
|
const fullType = selectType === 'distrib' ? 'distributor' : selectType; |
|
const listId = `${selectType}-list`; |
|
const submit = document.querySelector('form input[type="submit"]'); |
|
|
|
const input = document.createElement('input'); |
|
input.type = 'text'; |
|
input.placeholder = `Lookup ${fullType}...`; |
|
input.setAttribute('list', listId); |
|
Object.assign(input.style, { |
|
display: 'block', |
|
width: window.getComputedStyle(select).width, |
|
marginBottom: '.5rem', |
|
}); |
|
select.before(input); |
|
input.focus(); |
|
|
|
const datalist = document.createElement('datalist'); |
|
datalist.id = listId; |
|
for (const option of select.children) { |
|
const cloned = option.cloneNode(true); |
|
cloned.removeAttribute('value'); |
|
datalist.append(cloned); |
|
} |
|
select.before(datalist); |
|
|
|
const escapeRegex = (string) => { |
|
return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&'); |
|
}; |
|
|
|
input.addEventListener('input', () => { |
|
const value = input.value.trim(); |
|
if (!value) return; |
|
const search = new RegExp(`^${escapeRegex(value)}`, 'i'); |
|
const found = Array.from(select.children).find((o) => search.test(o.innerText)); |
|
if (!found) return; |
|
select.value = found.value; |
|
}); |
|
|
|
const handleClick = (/** @type {Event} */ ev) => { |
|
if (!select.value) |
|
return; |
|
|
|
ev.preventDefault(); |
|
ev.stopPropagation(); |
|
|
|
const displayName = select.selectedOptions[0].textContent.replace(/[^a-z0-9.-]/ig, '-'); |
|
window.location = `https://www.iafd.com/${pageType}.rme/${selectType}=${select.value}/${displayName}.htm`; |
|
}; |
|
|
|
select.addEventListener('dblclick', (ev) => { |
|
if (ev.target instanceof HTMLOptionElement) |
|
return handleClick(ev); |
|
}); |
|
select.addEventListener('keyup', (/** @type {KeyboardEvent} */ ev) => { |
|
if (ev.key === 'Enter') |
|
return handleClick(ev); |
|
}); |
|
|
|
submit.addEventListener('click', handleClick); |
|
} |
|
|
|
function studioDistribPage() { |
|
makeExportButton(); |
|
} |
|
|
|
async function makeExportButton() { |
|
const filter = await elementReady('div[id$="_filter"]'); |
|
const type = filter.id.split('_')[0]; |
|
|
|
const exportButtonDefaultText = 'Export CSV'; |
|
const exportTimestamp = (new Date()).toISOString(); |
|
|
|
const tools = document.createElement('div'); |
|
Object.assign(tools.style, { |
|
marginRight: '.5em', |
|
display: 'inline-block', |
|
}); |
|
filter.prepend(tools); |
|
|
|
const button = document.createElement('button'); |
|
button.type = 'button'; |
|
button.innerText = exportButtonDefaultText; |
|
button.style.marginRight = '.5em'; |
|
tools.prepend(button); |
|
|
|
(async () => { |
|
const info = await Promise.race([ |
|
elementReady(`div#${type}_info`).then((el) => /** @type {HTMLDivElement} */ (el).innerText), |
|
wait(5000).then(() => null), |
|
]); |
|
|
|
if (!info) return; |
|
|
|
/** @param {string} s */ |
|
const toNumber = (s) => Number(s?.replace(/,/, '')); |
|
|
|
const { start, end, total } = info |
|
.match(/Showing (?<start>[\d,]+) to (?<end>[\d,]+) of (?<total>[\d,]+) entries/i) |
|
?.groups || {}; |
|
const count = toNumber(end) - Math.max(toNumber(start) - 1, 0); |
|
const totalCount = toNumber(total); |
|
let countLabel = `Count: ${count}`; |
|
if (count !== totalCount) |
|
countLabel += ` out of ${total}`; |
|
button.title = countLabel; |
|
})(); |
|
|
|
button.addEventListener('click', async () => { |
|
const output = makeOutput(type); |
|
if (!output) { |
|
button.innerText = '❌ Failed!'; |
|
button.style.backgroundColor = 'red'; |
|
return; |
|
} |
|
|
|
GM.setClipboard(output); |
|
button.innerText = '✔ Copied to clipboard'; |
|
button.style.backgroundColor = 'yellow'; |
|
|
|
await wait(1500); |
|
button.innerText = exportButtonDefaultText; |
|
button.style.backgroundColor = ''; |
|
}); |
|
|
|
/** @param {string} type */ |
|
const makeOutput = (type) => { |
|
const dataRows = Array.from(document.querySelectorAll(`div#${type}_wrapper .dataTable tbody > tr`)); |
|
let columns; |
|
let csv; |
|
|
|
if (type === 'studio' || type === 'distable') { |
|
const data = dataRows.map((tr) => ({ |
|
url: tr.children[0].querySelector('a').href, |
|
title: tr.children[0].innerText, |
|
studio: tr.children[1].innerText, |
|
year: Number(tr.children[2].innerText), |
|
})); |
|
|
|
columns = ['Title', 'Studio', 'Year', 'URL']; |
|
csv = data.map((d) => columns.map((c) => d[c.toLowerCase()]).join('\t')); |
|
} else if (type === 'personal') { |
|
const data = dataRows.map((tr) => ({ |
|
url: tr.children[0].querySelector('a').href, |
|
title: tr.children[0].innerText, |
|
year: Number(tr.children[1].innerText), |
|
distributor: tr.children[2].innerText, |
|
notes: tr.children[3].innerText, |
|
})); |
|
|
|
columns = ['Title', 'Distributor', 'Year', /*'Notes', */'URL']; |
|
csv = data.map((d) => columns.map((c) => d[c.toLowerCase()]).join('\t')); |
|
} else { |
|
return null; |
|
} |
|
|
|
return columns.join('\t') +`\t${exportTimestamp}` + '\n' + csv.join('\n') + '\n'; |
|
}; |
|
} |
|
|
|
function personUpdatePage() { |
|
} |
|
|
|
async function tattooLookupPage() { |
|
const info = await Promise.race([ |
|
elementReady(`div[id^="tat"][id$="_info"]`).then((el) => /** @type {HTMLDivElement} */ (el).innerText), |
|
wait(5000).then(() => null), |
|
]); |
|
|
|
if (!info) return; |
|
} |
|
|
|
function setSearchField() { |
|
const field = document.querySelector('#the-basics > input[name="searchstring"]'); |
|
const params = new URLSearchParams(window.location.search); |
|
field.value = params.get('searchstring'); |
|
} |
|
|
|
const wait = (/** @type {number} */ ms) => new Promise((resolve) => setTimeout(resolve, ms)); |
|
|
|
/** |
|
* Waits for an element satisfying selector to exist, then resolves promise with the element. |
|
* Useful for resolving race conditions. |
|
* |
|
* @param {string} selector |
|
* @param {HTMLElement} [parentEl] |
|
* @returns {Promise<Element>} |
|
*/ |
|
function elementReady(selector, parentEl) { |
|
return new Promise((resolve, reject) => { |
|
let el = (parentEl || document).querySelector(selector); |
|
if (el) {resolve(el);} |
|
new MutationObserver((mutationRecords, observer) => { |
|
// Query for elements matching the specified selector |
|
Array.from((parentEl || document).querySelectorAll(selector)).forEach((element) => { |
|
resolve(element); |
|
//Once we have resolved we don't need the observer anymore. |
|
observer.disconnect(); |
|
}); |
|
}) |
|
.observe(parentEl || document.documentElement, { |
|
childList: true, |
|
subtree: true |
|
}); |
|
}); |
|
} |
|
|
|
main(); |
the button is on the same line as
![image](https://private-user-images.githubusercontent.com/66393006/337182658-66e27dd0-a51d-45fa-b641-d91fff8f70a4.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MTk3NTM0NTgsIm5iZiI6MTcxOTc1MzE1OCwicGF0aCI6Ii82NjM5MzAwNi8zMzcxODI2NTgtNjZlMjdkZDAtYTUxZC00NWZhLWI2NDEtZDkxZmZmOGY3MGE0LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA2MzAlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwNjMwVDEzMTIzOFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTI3NjljYjcyYTg0YzZiOGI0MWY5ZDc1MGRmYmM3YzBlYzI2ZjRiN2Q3ZjZkMGRjYWQxZTQ4MDc2MGExNzQzNjUmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.bPcETmyMWczNScaLDE_xu2iCNDn1PDp05iMPphiitsI)
Performer AKA
, and the parentheses are removed when it's copied to the clipboard (not on the page).