Adds a download button to Adobe Fonts.
// ==UserScript== // @name Adobe Fonts Downloader // @description Adds a download button to Adobe Fonts. // @icon https://badnoise.net/TypeRip/favicon.png // @version 1.3 // @author afkarxyz // @namespace https://github.com/afkarxyz/misc-scripts/ // @supportURL https://github.com/afkarxyz/misc-scripts/issues // @license MIT // @match https://fonts.adobe.com/* // @match https://badnoise.net/TypeRip/* // @grant GM_setValue // @grant GM_getValue // @grant window.close // @run-at document-end // ==/UserScript== (function() { 'use strict'; if (window.location.hostname === 'badnoise.net') { const adobeUrl = GM_getValue('adobeFontUrl', ''); if (adobeUrl) { GM_setValue('adobeFontUrl', ''); const waitForElement = (selector) => { return new Promise(resolve => { if (document.querySelector(selector)) { return resolve(document.querySelector(selector)); } const observer = new MutationObserver(mutations => { if (document.querySelector(selector)) { observer.disconnect(); resolve(document.querySelector(selector)); } }); observer.observe(document.body, { childList: true, subtree: true }); }); }; Promise.all([ waitForElement('#url_input'), waitForElement('#url_submit_button') ]).then(([urlInput, submitButton]) => { urlInput.value = adobeUrl; urlInput.dispatchEvent(new Event('input', { bubbles: true })); submitButton.click(); }); } return; } function getFullFontUrl(href) { if (!href) return ''; if (href.startsWith('http')) return href; return `https://fonts.adobe.com${href}`; } function createDownloadButton(originalButton) { const newButton = originalButton.cloneNode(true); originalButton.parentNode.replaceChild(newButton, originalButton); const labelSpan = newButton.querySelector('.spectrum-Button-label, .add-family-label'); if (labelSpan) { labelSpan.textContent = 'Download'; } newButton.removeAttribute('ng-click'); newButton.removeAttribute('ng-show'); newButton.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); let fontUrl = ''; const cardLink = newButton.closest('.adobe-fonts-family-card')?.querySelector('.adobe-fonts-family-card--link'); if (cardLink) { fontUrl = getFullFontUrl(cardLink.getAttribute('href')); } else { fontUrl = window.location.href; } GM_setValue('adobeFontUrl', fontUrl); window.open('https://badnoise.net/TypeRip/', '_blank'); }); return newButton; } function modifyButtons() { const buttons = document.querySelectorAll([ '.adobe-fonts-family__top-actions-add-family button', 'button[ng-click*="useModelSyncFontpack"]', '.collection-show__font-pack-actions-bottom button.add-family-button', '.add-family-button' ].join(',')); buttons.forEach(button => { if (button.hasAttribute('data-modified')) return; const newButton = createDownloadButton(button); newButton.setAttribute('data-modified', 'true'); }); } function changeTextToDownload(node) { if (node.nodeType === Node.ELEMENT_NODE) { if (node.classList.contains('add-family-button') && !node.hasAttribute('data-modified')) { const newButton = createDownloadButton(node); newButton.setAttribute('data-modified', 'true'); } if (node.shadowRoot) { changeTextToDownload(node.shadowRoot); } node.childNodes.forEach(child => changeTextToDownload(child)); } } function init() { modifyButtons(); changeTextToDownload(document.body); } init(); const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { const currentUrl = location.href; if (window.lastUrl !== currentUrl) { window.lastUrl = currentUrl; init(); continue; } const addedNodes = Array.from(mutation.addedNodes); const hasNewButton = addedNodes.some(node => node.querySelector && ( node.querySelector('.adobe-fonts-family__top-actions-add-family') || node.querySelector('button[ng-click*="useModelSyncFontpack"]') || node.querySelector('.collection-show__font-pack-actions-bottom') || node.querySelector('.add-family-button') ) ); if (hasNewButton) { init(); break; } addedNodes.forEach(changeTextToDownload); } }); window.lastUrl = location.href; observer.observe(document.body, { childList: true, subtree: true }); })();