A user script to show feed favicons in Feedly Title-Only View.
// ==UserScript==
// @name Feedly Faviconize List
// @namespace jmln.tw
// @version 0.2.7
// @description A user script to show feed favicons in Feedly Title-Only View.
// @author Jimmy Lin
// @license MIT
// @homepage https://github.com/jmlntw/feedly-faviconize-list
// @supportURL https://github.com/jmlntw/feedly-faviconize-list/issues
// @match https://*.feedly.com/*
// @compatible firefox
// @compatible chrome
// @compatible opera
// @run-at document-end
// @grant none
// ==/UserScript==
function addStyle (css) {
const style = document.createElement('style')
style.textContent = css
document.head.appendChild(style)
return style
}
addStyle(`
.gm-favicon {
width: 16px;
height: 16px;
margin: 0 6px 0 0;
padding: 0;
border-radius: 3px;
vertical-align: top;
}
`)
function awaitSelector (selector, root) {
return new Promise((resolve, reject) => {
try {
const rootElement = root ?
typeof root === 'string' ? document.querySelector(root) : root :
document
const findAndResolveElements = () => {
const allElements = document.querySelectorAll(selector)
const newElements = []
const resolvedAttr = 'data-awaitselector-resolved'
if (allElements.length > 0) {
Array.prototype.slice.call(allElements)
.filter(element => typeof element[resolvedAttr] === 'undefined')
.forEach(element => {
element[resolvedAttr] = true
newElements.push(element)
})
if (newElements.length > 0) {
observer.disconnect()
resolve(newElements)
}
}
}
const observer = new MutationObserver(mutations => {
const addedNodes = mutations.reduce((found, mutation) => {
return found || mutation.addedNodes && mutation.addedNodes.length > 0
})
if (addedNodes) {
findAndResolveElements()
}
})
observer.observe(rootElement, {
childList: true,
subtree: true
})
findAndResolveElements()
} catch (exception) {
reject(exception)
}
})
}
function waitAwaitSelector (selector, root, callback) {
(function awaiter () {
const continueWatching = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : true
if (continueWatching) {
awaitSelector(selector, root).then(callback).then(awaiter)
}
}())
}
function createFavicon (url) {
const domain = url.replace(/^https?:\/\/([^/:]+).*/i, '$1')
const favicon = document.createElement('img')
favicon.src = `https://www.google.com/s2/favicons?domain=${domain}&alt=feed`
favicon.classList.add('gm-favicon')
return favicon
}
awaitSelector('#feedlyPageFX', '#root').then(pages => {
waitAwaitSelector('a.EntryMetadataSource[href]', pages[0], sources => {
sources
.filter(source => source.querySelector('.gm-favicon') === null)
.forEach(source => {
source.insertAdjacentElement('afterbegin', createFavicon(source.href))
})
})
})