')
// Remove cookie consent
html = html.replace(/'
// Scrollbar colors
headhtml += '\n'
// Highlight annotated lines on hover
headhtml += `
`
// Add to
const parts = html.split('')
html = parts[0] + '\n' + headhtml + '\n' + parts.slice(1).join('')
return cb(html)
}
},
spotify: {
name: 'Spotify',
scripts: function themeSpotifyScripts () {
const onload = []
// Define globals
var annotations1234
// Hide cookies box function
// var iv458
// function hideCookieBox458 () {if(document.querySelector(".optanon-allow-all")){document.querySelector(".optanon-allow-all").click(); clearInterval(iv458)}}
// onload.push(function() { iv458 = window.setInterval(hideCookieBox458, 500) })
// Hide footer
function hideFooter895 () { const f = document.querySelectorAll('.footer div'); if (f.length) { removeIfExists(f[0]); removeIfExists(f[1]) } }
function hideSecondaryFooter895 () { if (document.querySelector('.footer.footer--secondary')) { document.querySelector('.footer.footer--secondary').parentNode.removeChild(document.querySelector('.footer.footer--secondary')) } }
onload.push(hideFooter895)
onload.push(hideSecondaryFooter895)
// Hide other stuff
function hideStuff235 () {
const grayBox = document.querySelector('.column_layout-column_span-initial_content>.dfp_unit.u-x_large_bottom_margin.dfp_unit--in_read')
removeIfExists(grayBox)
removeIfExists(document.querySelector('.header .header-expand_nav_menu'))
}
onload.push(hideStuff235)
// Show annotations function
function showAnnotation1234 (ev) {
ev.preventDefault()
const id = this.dataset.annotationid
document.querySelectorAll('.song_body-lyrics .referent--yellow.referent--highlighted').forEach(function (e) {
e.className = e.className.replace(/\breferent--yellow\b/, '').replace(/\breferent--highlighted\b/, '')
})
this.className += ' referent--yellow referent--highlighted'
if (typeof annotations1234 === 'undefined') {
if (document.getElementById('annotationsdata1234')) {
annotations1234 = JSON.parse(document.getElementById('annotationsdata1234').innerHTML)
} else {
annotations1234 = {}
console.log('No annotation data found #annotationsdata1234')
}
}
if (id in annotations1234) {
const annotation = annotations1234[id][0]
const main = document.querySelector('.annotationbox')
main.innerHTML = ''
main.style.display = 'block'
const bodyRect = document.body.getBoundingClientRect()
const elemRect = this.getBoundingClientRect()
const top = elemRect.top - bodyRect.top + elemRect.height
main.style.top = top + 'px'
main.style.left = '5px'
const div0 = document.createElement('div')
div0.className = 'annotationcontent'
main.appendChild(div0)
let html = '
$author
$body
'
html = html.replace(/\$body/g, decodeHTML(annotation.body.html)).replace(/\$author/g, decodeHTML(annotation.created_by.name))
div0.innerHTML = html
targetBlankLinks145() // Change link target to _blank
window.setTimeout(function () { document.body.addEventListener('click', hideAnnotationOnClick1234) }, 100) // hide on click
}
}
function hideAnnotationOnClick1234 (ev) {
let target = ev.target
while (target) {
if (target.id === 'annotationbox') {
return
}
if (target.className && target.className.indexOf('referent') !== -1) {
const id = parseInt(target.dataset.id)
return showAnnotation1234.call(target, ev, id)
}
target = target.parentNode
}
document.body.removeEventListener('click', hideAnnotationOnClick1234)
const main = document.querySelector('.annotationbox')
main.style.display = 'none'
}
onload.push(function () {
if (document.getElementById('annotationsdata1234')) {
annotations1234 = JSON.parse(document.getElementById('annotationsdata1234').innerHTML)
}
})
// Make song title clickable
function clickableTitle037 () {
if (!document.querySelector('.header_with_cover_art-primary_info-title')) {
return
}
const url = document.querySelector('meta[property="og:url"]').content
const h1 = document.querySelector('.header_with_cover_art-primary_info-title')
h1.innerHTML = '
' + h1.innerHTML + ''
// Featuring and album name
const h2 = document.querySelector('.header_with_cover_art-primary_info-primary_artist').parentNode
document.querySelectorAll('.metadata_unit-label').forEach(function (el) {
if (el.innerText.toLowerCase().indexOf('feat') !== -1) { h1.innerHTML += ' ' + el.parentNode.innerText.trim() } else if (el.innerText.toLowerCase().indexOf('album') !== -1) { h2.innerHTML = h2.innerHTML + ' \u2022 ' + el.parentNode.querySelector('a').parentNode.innerHTML.trim() }
})
// Remove other meta like Producer
while (document.querySelector('h3')) {
document.querySelector('h3').remove()
}
}
onload.push(clickableTitle037)
// Change links to target=_blank
function targetBlankLinks145 () {
const as = document.querySelectorAll('body a:not([href|="#"]):not([target=_blank])')
as.forEach(function (a) {
a.target = '_blank'
})
}
onload.push(() => window.setTimeout(targetBlankLinks145, 1000))
if (!annotationsEnabled) {
// Remove all annotations
onload.push(function removeAnnotations135 () {
document.querySelectorAll('.song_body-lyrics .referent,.song_body-lyrics a[class*=referent]').forEach(function (a) {
while (a.firstChild) {
a.parentNode.insertBefore(a.firstChild, a)
}
a.remove()
})
})
} else {
// Add click handler to annotations
document.querySelectorAll('*[data-annotationid]').forEach((a) => a.addEventListener('click', showAnnotation1234))
}
// Open real page if not in frame
onload.push(function () {
if (window.top === window) {
document.location.href = document.querySelector('meta[property="og:url"]').content
}
})
return onload
},
combine: function themeSpotifyXombineGeniusResources (song, html, annotations, onCombine) {
let headhtml = ''
if (html.indexOf('class="lyrics">') === -1) {
const doc = new window.DOMParser().parseFromString(html, 'text/html')
const originalUrl = doc.querySelector('meta[property="og:url"]').content
if (html.indexOf('__PRELOADED_STATE__ = JSON.parse(\'') !== -1) {
const jsonStr = html.split('__PRELOADED_STATE__ = JSON.parse(\'')[1].split('\');\n')[0].replace(/\\([^\\])/g, '$1').replace(/\\\\/g, '\\')
const jData = JSON.parse(jsonStr)
const root = parsePreloadedStateData(jData.songPage.lyricsData.body, document.createElement('div'))
// Annotations
root.querySelectorAll('a[data-id]').forEach(function (a) {
a.dataset.annotationid = a.dataset.id
a.classList.add('referent--yellow')
})
const lyricshtml = root.innerHTML
const h1 = doc.querySelector('div[class^=SongHeader__Column] h1')
const titleNode = h1.firstChild
const titleA = h1.appendChild(document.createElement('a'))
titleA.href = originalUrl
titleA.target = '_blank'
titleA.appendChild(titleNode)
h1.classList.add('mytitle')
const titlehtml = ''
headhtml = ``
// Add annotation data
headhtml += '\n'
return onCombine(`
${headhtml}
${titlehtml}
${lyricshtml}
`)
}
return onCombine(`
😱 Oops!
Sorry, these lyrics seem to use new genius page design.
They cannot be shown with the "Spotify theme" (yet)
Could you inform the author of this program about the problem and provide the following information:
Error: Unknown genius page design
Genius: ${originalUrl}
You can simply post the information on github:
https://github.com/cvzi/Spotify-Genius-Lyrics-userscript/issues/4
or via email:
cuzi@openmail.cc
Thanks for your help!
`)
}
// Make annotations clickable
const regex = /annotation-fragment="(\d+)"/g
html = html.replace(regex, '$0 data-annotationid="$1"')
// Remove cookie consent
html = html.replace(/'
// CSS
headhtml += '\n\n'
// Add to
parts = html.split('')
html = parts[0] + '\n' + headhtml + '\n' + parts.slice(1).join('')
return onCombine(html)
}
}
}
themeKey = Object.keys(themes)[0]
theme = themes[themeKey]
function combineGeniusResources (song, html, annotations, cb) {
if (html.indexOf('__PRELOADED_STATE__ = JSON.parse') !== -1) {
if (!themeKey.endsWith('React') && (themeKey + 'React') in themes) {
themeKey += 'React'
theme = themes[themeKey]
console.log(`Temporarily activated React theme: ${theme.name}`)
}
} else {
if (themeKey.endsWith('React') && themeKey.substring(0, themeKey.length - 5) in themes) {
themeKey = themeKey.substring(0, themeKey.length - 5)
theme = themes[themeKey]
console.log(`Temporarily deactivated React theme: ${theme.name}`)
}
}
return theme.combine(song, html, annotations, cb)
}
function onResize () {
const iframe = document.getElementById('lyricsiframe')
if (iframe) {
iframe.style.width = document.getElementById('lyricscontainer').clientWidth - 1 + 'px'
iframe.style.height = (document.querySelector('.Root__nav-bar .navBar').clientHeight + document.querySelector('.now-playing-bar ').clientHeight - document.querySelector('.lyricsnavbar').clientHeight) + 'px'
}
}
function initResize () {
window.addEventListener('mousemove', onMouseMoveResize)
window.addEventListener('mouseup', stopResize)
window.removeEventListener('resize', onResize)
}
function onMouseMoveResize (e) {
optionCurrentSize = 100 - (e.clientX / document.body.clientWidth * 100)
resizeLeftContainer.style.width = (100 - optionCurrentSize) + '%'
resizeContainer.style.width = optionCurrentSize + '%'
}
function stopResize () {
window.removeEventListener('mousemove', onMouseMoveResize)
window.removeEventListener('mouseup', stopResize)
window.addEventListener('resize', onResize)
onResize()
GM.setValue('optioncurrentsize', optionCurrentSize)
}
function getCleanLyricsContainer () {
document.querySelectorAll('.loadingspinner').forEach((spinner) => spinner.remove())
const topContainer = document.querySelector('.Root__top-container')
if (!document.getElementById('lyricscontainer')) {
topContainer.style.width = (100 - optionCurrentSize) + '%'
topContainer.style.float = 'left'
resizeContainer = document.createElement('div')
resizeContainer.id = 'lyricscontainer'
resizeContainer.style = 'min-height: 100%; width: ' + optionCurrentSize + '%; position: relative; z-index: 1; float:left'
topContainer.parentNode.insertBefore(resizeContainer, topContainer.nextSibling)
} else {
resizeContainer = document.getElementById('lyricscontainer')
resizeContainer.innerHTML = ''
topContainer.parentNode.insertBefore(resizeContainer, topContainer.nextSibling)
}
resizeLeftContainer = topContainer
return document.getElementById('lyricscontainer')
}
function hideLyrics () {
document.querySelectorAll('.loadingspinner').forEach((spinner) => spinner.remove())
if (document.getElementById('lyricscontainer')) {
document.getElementById('lyricscontainer').parentNode.removeChild(document.getElementById('lyricscontainer'))
const topContainer = document.querySelector('.Root__top-container')
topContainer.style.width = '100%'
topContainer.style.removeProperty('float')
}
addLyricsButton()
}
function showLyrics (song, searchresultsLengths) {
const container = getCleanLyricsContainer()
if ('info' in GM && 'scriptHandler' in GM.info && GM.info.scriptHandler === 'Greasemonkey') {
container.innerHTML = '
This script only works in Tampermonkey
Greasemonkey is no longer supported because of this
bug greasemonkey/issues/2574 in Greasemonkey.'
return
}
const separator = document.createElement('span')
separator.setAttribute('class', 'second-line-separator')
separator.setAttribute('style', 'padding:0px 3px')
separator.appendChild(document.createTextNode('•'))
const bar = document.createElement('div')
bar.setAttribute('class', 'lyricsnavbar')
bar.style.fontSize = '0.7em'
bar.style.userSelect = 'none'
container.appendChild(bar)
// Resize button
const resizeButton = document.createElement('span')
resizeButton.style.fontSize = '1.8em'
resizeButton.style.cursor = 'ew-resize'
resizeButton.appendChild(document.createTextNode('⇹'))
resizeButton.addEventListener('mousedown', initResize)
bar.appendChild(resizeButton)
bar.appendChild(separator.cloneNode(true))
// Hide button
const hideButton = document.createElement('a')
hideButton.href = '#'
hideButton.appendChild(document.createTextNode('Hide'))
hideButton.addEventListener('click', function hideButtonClick (ev) {
ev.preventDefault()
optionAutoShow = false // Temporarily disable showing lyrics automatically on song change
clearInterval(mainIv)
hideLyrics()
})
bar.appendChild(hideButton)
bar.appendChild(separator.cloneNode(true))
// Config button
const configButton = document.createElement('a')
configButton.href = '#'
configButton.appendChild(document.createTextNode('Options'))
configButton.addEventListener('click', function configButtonClick (ev) {
ev.preventDefault()
config()
})
bar.appendChild(configButton)
bar.appendChild(separator.cloneNode(true))
// Wrong lyrics
const wrongLyricsButton = document.createElement('a')
wrongLyricsButton.href = '#'
wrongLyricsButton.appendChild(document.createTextNode('Wrong lyrics'))
wrongLyricsButton.addEventListener('click', function wrongLyricsButtonClick (ev) {
ev.preventDefault()
document.querySelectorAll('.loadingspinner').forEach((spinner) => spinner.remove())
forgetLyricsSelection(currentTitle, currentArtists, this.dataset.hit)
showSearchField(currentArtists + ' ' + currentTitle)
})
bar.appendChild(wrongLyricsButton)
// Back button
if (searchresultsLengths) {
bar.appendChild(separator.cloneNode(true))
const backbutton = document.createElement('a')
backbutton.href = '#'
if (searchresultsLengths === true) {
backbutton.appendChild(document.createTextNode('Back to search results'))
} else {
backbutton.appendChild(document.createTextNode('Back to search (' + (searchresultsLengths - 1) + ' other result' + (searchresultsLengths === 2 ? '' : 's') + ')'))
}
backbutton.addEventListener('click', function backbuttonClick (ev) {
ev.preventDefault()
addLyrics(true)
})
bar.appendChild(backbutton)
}
const iframe = document.createElement('iframe')
iframe.id = 'lyricsiframe'
container.appendChild(iframe)
iframe.style.opacity = 0.1
iframe.src = emptySpotifyURL + '?405#html,' + encodeURIComponent('Loading...')
iframe.style.width = container.clientWidth - 1 + 'px'
iframe.style.height = (document.querySelector('.Root__nav-bar .navBar').clientHeight + document.querySelector('.now-playing-bar ').clientHeight - bar.clientHeight) + 'px'
const spinnerHolder = document.body.appendChild(document.createElement('div'))
spinnerHolder.classList.add('loadingspinnerholder')
spinnerHolder.style.left = (iframe.getClientRects()[0].left + container.clientWidth / 2) + 'px'
spinnerHolder.style.top = '100px'
spinnerHolder.title = 'Downloading lyrics...'
const spinner = spinnerHolder.appendChild(document.createElement('div'))
spinner.classList.add('loadingspinner')
spinner.innerHTML = '5'
loadGeniusSong(song, function loadGeniusSongCb (html) {
spinner.innerHTML = '4'
spinnerHolder.title = 'Downloading annotations...'
loadGeniusAnnotations(song, html, annotationsEnabled, function loadGeniusAnnotationsCb (song, html, annotations) {
spinner.innerHTML = '3'
spinnerHolder.title = 'Composing page...'
combineGeniusResources(song, html, annotations, function combineGeniusResourcesCb (html) {
spinner.innerHTML = '3'
spinnerHolder.title = 'Loading page...'
iframe.src = emptySpotifyURL + '#html:post'
const iv = window.setInterval(function () {
spinner.innerHTML = '2'
spinnerHolder.title = 'Rendering...'
iframe.contentWindow.postMessage({ iAm: scriptName, type: 'writehtml', html: html, themeKey: themeKey }, '*')
}, 1500)
const clear = function () {
window.clearInterval(iv)
window.setTimeout(function () {
iframe.style.opacity = 1.0
spinnerHolder.remove()
}, 1000)
}
addOneMessageListener('htmlwritten', function () {
window.clearInterval(iv)
spinner.innerHTML = '1'
spinnerHolder.title = 'Calculating...'
})
addOneMessageListener('pageready', clear)
window.setTimeout(clear, 20000)
iframe.style.position = 'fixed'
})
})
})
}
function listSongs (hits, container, query) {
if (!container) {
container = getCleanLyricsContainer()
}
// Back to search button
const backToSearchButton = document.createElement('a')
backToSearchButton.href = '#'
backToSearchButton.appendChild(document.createTextNode('Back to search'))
backToSearchButton.addEventListener('click', function backToSearchButtonClick (ev) {
ev.preventDefault()
if (query) {
showSearchField(query)
} else if (currentArtists) {
showSearchField(currentArtists + ' ' + currentTitle)
} else {
showSearchField()
}
})
const separator = document.createElement('span')
separator.setAttribute('class', 'second-line-separator')
separator.setAttribute('style', 'padding:0px 3px')
separator.appendChild(document.createTextNode('•'))
// Hide button
const hideButton = document.createElement('a')
hideButton.href = '#'
hideButton.appendChild(document.createTextNode('Hide'))
hideButton.addEventListener('click', function hideButtonClick (ev) {
ev.preventDefault()
hideLyrics()
})
// List search results
const trackhtml = '
'
container.innerHTML = '
'
container.insertBefore(hideButton, container.firstChild)
container.insertBefore(separator, container.firstChild)
container.insertBefore(backToSearchButton, container.firstChild)
const ol = container.querySelector('ol.tracklist')
const searchresultsLengths = hits.length
const title = currentTitle
const artists = currentArtists
const onclick = function onclick () {
rememberLyricsSelection(title, artists, this.dataset.hit)
showLyrics(JSON.parse(this.dataset.hit), searchresultsLengths)
}
hits.forEach(function forEachHit (hit) {
const li = document.createElement('li')
li.setAttribute('class', 'tracklist-row')
li.setAttribute('role', 'button')
li.innerHTML = trackhtml.replace(/\$title/g, hit.result.title_with_featured).replace(/\$artist/g, hit.result.primary_artist.name).replace(/\$lyrics_state/g, hit.result.lyrics_state).replace(/\$stats\.pageviews/g, 'pageviews' in hit.result.stats ? metricPrefix(hit.result.stats.pageviews, 1) : ' - ')
li.dataset.hit = JSON.stringify(hit)
li.addEventListener('click', onclick)
ol.appendChild(li)
})
}
function addLyrics (force, beLessSpecific) {
let songTitle = document.querySelector('a[data-testid="nowplaying-track-link"]').innerText
const feat = songTitle.indexOf(' (feat')
if (feat !== -1) {
songTitle = songTitle.substring(0, feat).trim()
}
const musicIsPlaying = document.querySelector('.now-playing-bar .player-controls__buttons .control-button.control-button--circled').className.toLowerCase().indexOf('pause') !== -1
const songArtistsArr = []
document.querySelectorAll('.Root__now-playing-bar .now-playing .ellipsis-one-line a[href^="/artist/"]').forEach((e) => songArtistsArr.push(e.innerText))
let songArtists = songArtistsArr.join(' ')
if (force || (!document.hidden && musicIsPlaying && (currentTitle !== songTitle || currentArtists !== songArtists))) {
currentTitle = songTitle
currentArtists = songArtists
const firstArtist = songArtistsArr[0]
const simpleTitle = songTitle = songTitle.replace(/\s*-\s*.+?$/, '') // Remove anything following the last dash
if (beLessSpecific) {
songArtists = firstArtist
songTitle = simpleTitle
}
const hitFromCache = getLyricsSelection(songTitle, songArtists)
if (!force && hitFromCache) {
showLyrics(hitFromCache, true)
} else {
geniusSearch(songTitle + ' ' + songArtists, function geniusSearchCb (r) {
const hits = r.response.sections[0].hits
if (hits.length === 0) {
hideLyrics()
if (!beLessSpecific && (firstArtist !== songArtists || simpleTitle !== songTitle)) {
// Try again with only the first artist or the simple title
addLyrics(!!force, true)
} else if (force) {
showSearchField()
}
} else if (hits.length === 1) {
showLyrics(hits[0])
} else {
listSongs(hits)
}
})
}
}
}
function searchByQuery (query, container) {
geniusSearch(query, function geniusSearchCb (r) {
const hits = r.response.sections[0].hits
if (hits.length === 0) {
window.alert(scriptName + '\n\nNo search results')
} else {
listSongs(hits, container, query)
}
})
}
function showSearchField (query) {
const b = getCleanLyricsContainer()
const div = b.appendChild(document.createElement('div'))
div.style = 'padding:5px'
div.appendChild(document.createTextNode('Search genius.com: '))
div.appendChild(document.createElement('br'))
div.style.paddingRight = '15px'
const input = div.appendChild(document.createElement('input'))
input.style = 'width:92%;border:0;border-radius:500px;padding:8px 5px 8px 25px;text-overflow:ellipsis'
input.placeholder = 'Search genius.com...'
if (query) {
input.value = query
} else if (currentArtists) {
input.value = currentArtists
}
input.addEventListener('change', function onSearchLyricsButtonClick () {
this.style.color = 'black'
if (input.value) {
searchByQuery(input.value, b)
}
})
input.addEventListener('keyup', function onSearchLyricsKeyUp (ev) {
this.style.color = 'black'
if (ev.keyCode === 13) {
ev.preventDefault()
if (input.value) {
searchByQuery(input.value, b)
}
}
})
input.focus()
const mag = div.appendChild(document.createElement('div'))
mag.style.marginTop = '-27px'
mag.style.marginLeft = '3px'
mag.appendChild(document.createTextNode('🔎'))
}
function addLyricsButton () {
if (document.getElementById('showlyricsbutton')) {
return
}
const b = document.createElement('div')
b.setAttribute('id', 'showlyricsbutton')
b.setAttribute('style', 'position:absolute; top: 0px; right:0px; color:#ffff64; cursor:pointer')
b.setAttribute('title', 'Load lyrics from genius.com')
b.appendChild(document.createTextNode('🅖'))
b.addEventListener('click', function onShowLyricsButtonClick () {
optionAutoShow = true // Temporarily enable showing lyrics automatically on song change
mainIv = window.setInterval(main, 2000)
addLyrics(true)
})
document.body.appendChild(b)
}
function config () {
loadCache()
// Blur background
if (document.querySelector('.Root__top-container')) {
document.querySelector('.Root__top-container').style.filter = 'blur(4px)'
}
if (document.getElementById('lyricscontainer')) {
document.getElementById('lyricscontainer').style.filter = 'blur(1px)'
}
const win = document.createElement('div')
win.setAttribute('id', 'myconfigwin39457845')
win.setAttribute('style', 'position:absolute; top: 10px; right:10px; padding:15px; background:white; border-radius:10%; border:2px solid black; color:black; z-index:10')
win.appendChild(document.createElement('style')).innerHTML = '#myconfigwin39457845 div {margin:2px 0; padding:5px;border-radius: 5px;background-color: #EFEFEF;}'
document.body.appendChild(win)
const h1 = document.createElement('h1')
win.appendChild(h1).appendChild(document.createTextNode('Options'))
const a = document.createElement('a')
a.href = 'https://github.com/cvzi/Spotify-Genius-Lyrics-userscript/issues'
a.style = 'color:blue'
win.appendChild(a).appendChild(document.createTextNode('Report problem: github.com/cvzi/Spotify-Genius-Lyrics-userscript'))
// Switch: Show automatically
let div = win.appendChild(document.createElement('div'))
const checkAutoShow = div.appendChild(document.createElement('input'))
checkAutoShow.type = 'checkbox'
checkAutoShow.id = 'checkAutoShow748'
checkAutoShow.checked = optionAutoShow === true
const onAutoShow = function onAutoShowListener () {
GM.setValue('optionautoshow', checkAutoShow.checked === true)
}
checkAutoShow.addEventListener('click', onAutoShow)
checkAutoShow.addEventListener('change', onAutoShow)
let label = div.appendChild(document.createElement('label'))
label.setAttribute('for', 'checkAutoShow748')
label.appendChild(document.createTextNode(' Automatically show lyrics when new song starts'))
div.appendChild(document.createElement('br'))
div.appendChild(document.createTextNode('(if you disable this, a small button will appear in the top right corner to show the lyrics)'))
// Select: Theme
div = win.appendChild(document.createElement('div'))
div.appendChild(document.createTextNode('Theme: '))
const selectTheme = div.appendChild(document.createElement('select'))
if (themeKey.endsWith('React')) {
themeKey = themeKey.substring(0, themeKey.length - 5)
}
for (const key in themes) {
if (key.endsWith('React')) {
continue
}
const option = selectTheme.appendChild(document.createElement('option'))
option.value = key
if (themeKey === key) {
option.selected = true
}
option.appendChild(document.createTextNode(themes[key].name))
}
const onSelectTheme = function onSelectThemeListener () {
if (themeKey !== selectTheme.selectedOptions[0].value) {
theme = themes[selectTheme.selectedOptions[0].value]
addLyrics(true)
}
themeKey = selectTheme.selectedOptions[0].value
GM.setValue('theme', themeKey)
}
selectTheme.addEventListener('change', onSelectTheme)
// Switch: Show annotations
div = win.appendChild(document.createElement('div'))
const checkAnnotationsEnabled = div.appendChild(document.createElement('input'))
checkAnnotationsEnabled.type = 'checkbox'
checkAnnotationsEnabled.id = 'checkAnnotationsEnabled748'
checkAnnotationsEnabled.checked = annotationsEnabled === true
const onAnnotationsEnabled = function onAnnotationsEnabledListener () {
if (checkAnnotationsEnabled.checked !== annotationsEnabled) {
annotationsEnabled = checkAnnotationsEnabled.checked === true
addLyrics(true)
GM.setValue('annotationsenabled', annotationsEnabled)
}
}
checkAnnotationsEnabled.addEventListener('click', onAnnotationsEnabled)
checkAnnotationsEnabled.addEventListener('change', onAnnotationsEnabled)
label = div.appendChild(document.createElement('label'))
label.setAttribute('for', 'checkAnnotationsEnabled748')
label.appendChild(document.createTextNode(' Show annotations'))
// Buttons
div = win.appendChild(document.createElement('div'))
const closeButton = div.appendChild(document.createElement('button'))
closeButton.appendChild(document.createTextNode('Close'))
closeButton.style.color = 'black'
closeButton.addEventListener('click', function onCloseButtonClick () {
win.parentNode.removeChild(win)
// Un-blur background
if (document.querySelector('.Root__top-container')) {
document.querySelector('.Root__top-container').style.filter = ''
}
if (document.getElementById('lyricscontainer')) {
document.getElementById('lyricscontainer').style.filter = ''
}
})
const bytes = metricPrefix(JSON.stringify(selectionCache).length + JSON.stringify(requestCache).length, 2, 1024) + 'Bytes'
const clearCacheButton = div.appendChild(document.createElement('button'))
clearCacheButton.appendChild(document.createTextNode('Clear cache (' + bytes + ')'))
clearCacheButton.style.color = 'black'
clearCacheButton.addEventListener('click', function onClearCacheButtonClick () {
Promise.all([GM.setValue('selectioncache', '{}'), GM.setValue('requestcache', '{}')]).then(function () {
clearCacheButton.innerHTML = 'Cleared'
selectionCache = {}
requestCache = {}
})
})
}
function addOneMessageListener (type, cb) {
onMessage.push([type, cb])
}
function listenToMessages () {
window.addEventListener('message', function (e) {
if (!onMessage || typeof e.data !== 'object' || !('iAm' in e.data) || e.data.iAm !== scriptName) {
return
}
for (let i = 0; i < onMessage.length; i++) {
if (onMessage[i][0] === e.data.type) {
onMessage[i][1](e)
onMessage.splice(i, 1)
i--
}
}
})
}
function addCss () {
document.head.appendChild(document.createElement('style')).innerHTML = `
.lyricsiframe {
opacity:0.1;
transition:opacity 2s;
}
.loadingspinnerholder {
position:absolute;
top:100px;
left:100px;
cursor:progress
}
.loadingspinner {
color:rgb(255, 255, 100);
text-align:center;
pointer-events: none;
width: 2.5em; height: 2.5em;
border: 0.4em solid transparent;
border-color: rgb(255, 255, 100) #181818 #181818 #181818;
border-radius: 50%;
animation: loadingspin 2s ease infinite
}
@keyframes loadingspin {
25% {
transform: rotate(90deg)
}
50% {
transform: rotate(180deg)
}
75% {
transform: rotate(270deg)
}
100% {
transform: rotate(360deg)
}
}
.lyricsnavbar span,.lyricsnavbar a:link,.lyricsnavbar a:visited {
color: rgb(179, 179, 179);
text-decoration:none;
transition:color 400ms;
}
.lyricsnavbar a:hover,.lyricsnavbar span:hover {
color:white;
text-decoration:none;
}`
}
function main () {
if (document.querySelector('.now-playing')) {
if (optionAutoShow) {
addLyrics()
} else {
addLyricsButton()
}
}
}
(function () {
Promise.all([
GM.getValue('theme', themeKey),
GM.getValue('annotationsenabled', annotationsEnabled)
]).then(function (values) {
if (Object.prototype.hasOwnProperty.call(themes, values[0])) {
themeKey = values[0]
} else {
console.log('Invalid value for theme key: GM.getValue("theme") = ' + values[0])
themeKey = Reflect.ownKeys(themes)[0]
}
theme = themes[themeKey]
annotationsEnabled = !!values[1]
if (document.location.href.startsWith(emptySpotifyURL + '#html:post')) {
let received = false
window.addEventListener('message', function (e) {
if (received || typeof e.data !== 'object' || !('iAm' in e.data) || e.data.iAm !== scriptName || e.data.type !== 'writehtml') {
return
}
if ('themeKey' in e.data && Object.prototype.hasOwnProperty.call(themes, e.data.themeKey)) {
themeKey = e.data.themeKey
theme = themes[themeKey]
console.log(`Theme activated in iframe: ${theme.name}`)
}
received = true
document.write(e.data.html)
e.source.postMessage({ iAm: scriptName, type: 'htmlwritten' }, '*')
window.setTimeout(function () {
const onload = theme.scripts()
onload.forEach((func) => func())
e.source.postMessage({ iAm: scriptName, type: 'pageready' }, '*')
}, 500)
})
} else if (document.location.href.startsWith(emptySpotifyURL + '?405#html,')) {
document.write(decodeURIComponent(document.location.hash.split('#html,')[1]))
} else {
listenToMessages()
loadCache()
addCss()
mainIv = window.setInterval(main, 2000)
window.addEventListener('resize', onResize)
}
})
})()