dobo-redis
Version:
Redis driver for Dobo
726 lines (583 loc) • 17.5 kB
JavaScript
/* global document */
const accordionLocalStorageKey = 'accordion-id'
const themeLocalStorageKey = 'theme'
const fontSizeLocalStorageKey = 'font-size'
const html = document.querySelector('html')
const MAX_FONT_SIZE = 30
const MIN_FONT_SIZE = 10
// eslint-disable-next-line no-undef
const localStorage = window.localStorage
function getTheme () {
let theme = localStorage.getItem(themeLocalStorageKey)
if (theme) return theme
theme = document.body.getAttribute('data-theme')
switch (theme) {
case 'dark':
case 'light':
return theme
case 'fallback-dark':
if (
// eslint-disable-next-line no-undef
window.matchMedia('(prefers-color-scheme)').matches &&
// eslint-disable-next-line no-undef
window.matchMedia('(prefers-color-scheme: light)').matches
) {
return 'light'
}
return 'dark'
case 'fallback-light':
if (
// eslint-disable-next-line no-undef
window.matchMedia('(prefers-color-scheme)').matches &&
// eslint-disable-next-line no-undef
window.matchMedia('(prefers-color-scheme: dark)').matches
) {
return 'dark'
}
return 'light'
default:
return 'dark'
}
}
function localUpdateTheme (theme) {
const body = document.body
const svgUse = document.querySelectorAll('.theme-svg-use')
const iconID = theme === 'dark' ? '#light-theme-icon' : '#dark-theme-icon'
body.setAttribute('data-theme', theme)
body.classList.remove('dark', 'light')
body.classList.add(theme)
svgUse.forEach(function (svg) {
svg.setAttribute('xlink:href', iconID)
})
}
function updateTheme (theme) {
localUpdateTheme(theme)
localStorage.setItem(themeLocalStorageKey, theme)
}
function toggleTheme () {
const body = document.body
const theme = body.getAttribute('data-theme')
const newTheme = theme === 'dark' ? 'light' : 'dark'
updateTheme(newTheme)
}
(function () {
const theme = getTheme()
updateTheme(theme)
})()
/**
* Function to set accordion id to localStorage.
* @param {string} id Accordion id
*/
function setAccordionIdToLocalStorage (id) {
/**
* @type {object}
*/
const ids = JSON.parse(localStorage.getItem(accordionLocalStorageKey))
ids[id] = id
localStorage.setItem(accordionLocalStorageKey, JSON.stringify(ids))
}
/**
* Function to remove accordion id from localStorage.
* @param {string} id Accordion id
*/
function removeAccordionIdFromLocalStorage (id) {
/**
* @type {object}
*/
const ids = JSON.parse(localStorage.getItem(accordionLocalStorageKey))
delete ids[id]
localStorage.setItem(accordionLocalStorageKey, JSON.stringify(ids))
}
/**
* Function to get all accordion ids from localStorage.
*
* @returns {object}
*/
function getAccordionIdsFromLocalStorage () {
/**
* @type {object}
*/
const ids = JSON.parse(localStorage.getItem(accordionLocalStorageKey))
return ids || {}
}
function toggleAccordion (element) {
const currentNode = element
const isCollapsed = currentNode.getAttribute('data-isopen') === 'false'
if (isCollapsed) {
currentNode.setAttribute('data-isopen', 'true')
setAccordionIdToLocalStorage(currentNode.id)
} else {
currentNode.setAttribute('data-isopen', 'false')
removeAccordionIdFromLocalStorage(currentNode.id)
}
}
function initAccordion () {
if (
localStorage.getItem(accordionLocalStorageKey) === undefined ||
localStorage.getItem(accordionLocalStorageKey) === null
) {
localStorage.setItem(accordionLocalStorageKey, '{}')
}
const allAccordion = document.querySelectorAll('.sidebar-section-title')
const ids = getAccordionIdsFromLocalStorage()
allAccordion.forEach(function (item) {
item.addEventListener('click', function () {
toggleAccordion(item)
})
if (item.id in ids) {
toggleAccordion(item)
}
})
}
function isSourcePage () {
return Boolean(document.querySelector('#source-page'))
}
function bringElementIntoView (element, updateHistory = true) {
// If element is null then we are not going further
if (!element) {
return
}
/**
* tocbotInstance is defined in layout.tmpl
* It is defined when we are initializing tocbot.
*
*/
// eslint-disable-next-line no-undef
if (tocbotInstance) {
setTimeout(
// eslint-disable-next-line no-undef
() => tocbotInstance.updateTocListActiveElement(element),
60
)
}
const navbar = document.querySelector('.navbar-container')
const body = document.querySelector('.main-content')
const elementTop = element.getBoundingClientRect().top
let offset = 16
if (navbar) {
offset += navbar.scrollHeight
}
if (body) {
body.scrollBy(0, elementTop - offset)
}
if (updateHistory) {
// eslint-disable-next-line no-undef
history.pushState(null, null, '#' + element.id)
}
}
// eslint-disable-next-line no-unused-vars
function bringLinkToView (event) {
event.preventDefault()
event.stopPropagation()
const id = event.currentTarget.getAttribute('href')
if (!id) {
return
}
const element = document.getElementById(id.slice(1))
if (element) {
bringElementIntoView(element)
}
}
function bringIdToViewOnMount () {
if (isSourcePage()) {
return
}
// eslint-disable-next-line no-undef
let id = window.location.hash
if (id === '') {
return
}
let element = document.getElementById(id.slice(1))
if (!element) {
id = decodeURI(id)
element = document.getElementById(id.slice(1))
}
if (element) {
bringElementIntoView(element, false)
}
}
function createAnchorElement (id) {
const anchor = document.createElement('a')
anchor.textContent = '#'
anchor.href = '#' + id
anchor.classList.add('link-anchor')
anchor.onclick = bringLinkToView
return anchor
}
function addAnchor () {
const main = document.querySelector('.main-content').querySelector('section')
const h1 = main.querySelectorAll('h1')
const h2 = main.querySelectorAll('h2')
const h3 = main.querySelectorAll('h3')
const h4 = main.querySelectorAll('h4')
const targets = [h1, h2, h3, h4]
targets.forEach(function (target) {
target.forEach(function (heading) {
const anchor = createAnchorElement(heading.id)
heading.classList.add('has-anchor')
heading.append(anchor)
})
})
}
/**
*
* @param {string} value
*/
function copy (value) {
const el = document.createElement('textarea')
el.value = value
document.body.appendChild(el)
el.select()
document.execCommand('copy')
document.body.removeChild(el)
}
function showTooltip (id) {
const tooltip = document.getElementById(id)
tooltip.classList.add('show-tooltip')
setTimeout(function () {
tooltip.classList.remove('show-tooltip')
}, 3000)
}
/* eslint-disable-next-line */
function copyFunction(id) {
// selecting the pre element
const code = document.getElementById(id)
// selecting the ol.linenums
let element = code.querySelector('.linenums')
if (!element) {
// selecting the code block
element = code.querySelector('code')
}
// copy
copy(element.innerText.trim().replace(/(^\t)/gm, ''))
// show tooltip
showTooltip('tooltip-' + id)
}
function hideTocOnSourcePage () {
if (isSourcePage()) {
document.querySelector('.toc-container').style.display = 'none'
}
}
function getPreTopBar (id, lang = '') {
// tooltip
const tooltip = '<div class="tooltip" id="tooltip-' + id + '">Copied!</div>'
// template of copy to clipboard icon container
const copyToClipboard =
'<button aria-label="copy code" class="icon-button copy-code" onclick="copyFunction(\'' +
id +
'\')"><svg class="sm-icon" alt="click to copy"><use xlink:href="#copy-icon"></use></svg>' +
tooltip +
'</button>'
const langNameDiv =
'<div class="code-lang-name-container"><div class="code-lang-name">' +
lang.toLocaleUpperCase() +
'</div></div>'
const topBar =
'<div class="pre-top-bar-container">' +
langNameDiv +
copyToClipboard +
'</div>'
return topBar
}
function getPreDiv () {
const divElement = document.createElement('div')
divElement.classList.add('pre-div')
return divElement
}
function processAllPre () {
const targets = document.querySelectorAll('pre')
const footer = document.querySelector('#PeOAagUepe')
const navbar = document.querySelector('#VuAckcnZhf')
let navbarHeight = 0
let footerHeight = 0
if (footer) {
footerHeight = footer.getBoundingClientRect().height
}
if (navbar) {
navbarHeight = navbar.getBoundingClientRect().height
}
// eslint-disable-next-line no-undef
const preMaxHeight = window.innerHeight - navbarHeight - footerHeight - 250
targets.forEach(function (pre, idx) {
const parent = pre.parentNode
if (parent && parent.getAttribute('data-skip-pre-process') === 'true') {
return
}
const div = getPreDiv()
const id = 'ScDloZOMdL' + idx
const lang = pre.getAttribute('data-lang') || 'code'
const topBar = getPreTopBar(id, lang)
div.innerHTML = topBar
pre.style.maxHeight = preMaxHeight + 'px'
pre.id = id
pre.classList.add('prettyprint')
pre.parentNode.insertBefore(div, pre)
div.appendChild(pre)
})
}
function highlightAndBringLineIntoView () {
// eslint-disable-next-line no-undef
const lineNumber = window.location.hash.replace('#line', '')
try {
const selector = '[data-line-number="' + lineNumber + '"'
const element = document.querySelector(selector)
element.scrollIntoView()
element.parentNode.classList.add('selected')
} catch (error) {
console.error(error)
}
}
function getFontSize () {
let currentFontSize = 16
try {
currentFontSize = Number.parseInt(
html.style.fontSize.split('px')[0],
10
)
} catch (error) {
console.log(error)
}
return currentFontSize
}
function localUpdateFontSize (fontSize) {
html.style.fontSize = fontSize + 'px'
const fontSizeText = document.querySelector(
'#b77a68a492f343baabea06fad81f651e'
)
if (fontSizeText) {
fontSizeText.innerHTML = fontSize
}
}
function updateFontSize (fontSize) {
localUpdateFontSize(fontSize)
localStorage.setItem(fontSizeLocalStorageKey, fontSize)
}
(function () {
const fontSize = getFontSize()
const fontSizeInLocalStorage = localStorage.getItem(fontSizeLocalStorageKey)
if (fontSizeInLocalStorage) {
const n = Number.parseInt(fontSizeInLocalStorage, 10)
if (n === fontSize) {
return
}
updateFontSize(n)
} else {
updateFontSize(fontSize)
}
})()
// eslint-disable-next-line no-unused-vars
function incrementFont (event) {
const n = getFontSize()
if (n < MAX_FONT_SIZE) {
updateFontSize(n + 1)
}
}
// eslint-disable-next-line no-unused-vars
function decrementFont (event) {
const n = getFontSize()
if (n > MIN_FONT_SIZE) {
updateFontSize(n - 1)
}
}
function fontSizeTooltip () {
const fontSize = getFontSize()
return `
<div class="font-size-tooltip">
<button aria-label="decrease-font-size" class="icon-button ${
fontSize >= MAX_FONT_SIZE ? 'disabled' : ''
}" onclick="decrementFont(event)">
<svg>
<use xlink:href="#minus-icon"></use>
</svg>
</button>
<div class="font-size-text" id="b77a68a492f343baabea06fad81f651e">
${fontSize}
</div>
<button aria-label="increase-font-size" class="icon-button ${
fontSize <= MIN_FONT_SIZE ? 'disabled' : ''
}" onclick="incrementFont(event)">
<svg>
<use xlink:href="#add-icon"></use>
</svg>
</button>
<button class="icon-button" onclick="updateFontSize(16)">
<svg>
<use xlink:href="#reset-icon"></use>
</svg>
</button>
</div>
`
}
function initTooltip () {
// add tooltip to navbar item
// eslint-disable-next-line no-undef
tippy('.theme-toggle', {
content: 'Toggle Theme',
delay: 500
})
// eslint-disable-next-line no-undef
tippy('.search-button', {
content: 'Search',
delay: 500
})
// eslint-disable-next-line no-undef
tippy('.font-size', {
content: 'Change font size',
delay: 500
})
// eslint-disable-next-line no-undef
tippy('.codepen-button', {
content: 'Open code in CodePen',
placement: 'left'
})
// eslint-disable-next-line no-undef
tippy('.copy-code', {
content: 'Copy this code',
placement: 'left'
})
// eslint-disable-next-line no-undef
tippy('.font-size', {
content: fontSizeTooltip(),
trigger: 'click',
interactive: true,
allowHTML: true,
placement: 'left'
})
}
function fixTable () {
const tables = document.querySelectorAll('table')
for (const table of tables) {
if (table.classList.contains('hljs-ln')) {
// don't want to wrap code blocks.
return
}
const div = document.createElement('div')
div.classList.add('table-div')
table.parentNode.insertBefore(div, table)
div.appendChild(table)
}
}
function hideMobileMenu () {
const mobileMenuContainer = document.querySelector('#mobile-sidebar')
const target = document.querySelector('#mobile-menu')
const svgUse = target.querySelector('use')
if (mobileMenuContainer) {
mobileMenuContainer.classList.remove('show')
}
if (target) {
target.setAttribute('data-isopen', 'false')
}
if (svgUse) {
svgUse.setAttribute('xlink:href', '#menu-icon')
}
}
function showMobileMenu () {
const mobileMenuContainer = document.querySelector('#mobile-sidebar')
const target = document.querySelector('#mobile-menu')
const svgUse = target.querySelector('use')
if (mobileMenuContainer) {
mobileMenuContainer.classList.add('show')
}
if (target) {
target.setAttribute('data-isopen', 'true')
}
if (svgUse) {
svgUse.setAttribute('xlink:href', '#close-icon')
}
}
function onMobileMenuClick () {
const target = document.querySelector('#mobile-menu')
const isOpen = target.getAttribute('data-isopen') === 'true'
if (isOpen) {
hideMobileMenu()
} else {
showMobileMenu()
}
}
function initMobileMenu () {
const menu = document.querySelector('#mobile-menu')
if (menu) {
menu.addEventListener('click', onMobileMenuClick)
}
}
function addHrefToSidebarTitle () {
const titles = document.querySelectorAll('.sidebar-title-anchor')
titles.forEach(function (title) {
// eslint-disable-next-line no-undef
title.setAttribute('href', baseURL)
})
}
function highlightActiveLinkInSidebar () {
const list = document.location.href.split('/')
const targetURL = decodeURI(list[list.length - 1])
let element = document.querySelector(`.sidebar a[href*='${targetURL}']`)
if (!element) {
try {
element = document.querySelector(
`.sidebar a[href*='${targetURL.split('#')[0]}']`
)
} catch (e) {
console.error(e)
return
}
}
if (!element) return
element.parentElement.classList.add('active')
element.scrollIntoView()
}
function onDomContentLoaded () {
const themeButton = document.querySelectorAll('.theme-toggle')
initMobileMenu()
if (themeButton) {
themeButton.forEach(function (button) {
button.addEventListener('click', toggleTheme)
})
}
// Highlighting code
// eslint-disable-next-line no-undef
hljs.addPlugin({
'after:highlightElement': function (obj) {
// Replace 'code' with result.language when
// we are able to cross-check the correctness of
// result.
obj.el.parentNode.setAttribute('data-lang', 'code')
}
})
// eslint-disable-next-line no-undef
hljs.highlightAll()
// eslint-disable-next-line no-undef
hljs.initLineNumbersOnLoad({
singleLine: true
})
// Highlight complete
initAccordion()
addAnchor()
processAllPre()
hideTocOnSourcePage()
setTimeout(function () {
bringIdToViewOnMount()
if (isSourcePage()) {
highlightAndBringLineIntoView()
}
}, 1000)
initTooltip()
fixTable()
addHrefToSidebarTitle()
highlightActiveLinkInSidebar()
}
// eslint-disable-next-line no-undef
window.addEventListener('DOMContentLoaded', onDomContentLoaded)
// eslint-disable-next-line no-undef
window.addEventListener('hashchange', (event) => {
const url = new URL(event.newURL)
if (url.hash !== '') {
bringIdToViewOnMount(url.hash)
}
})
// eslint-disable-next-line no-undef
window.addEventListener('storage', (event) => {
if (event.newValue === 'undefined') return
initTooltip()
if (event.key === themeLocalStorageKey) localUpdateTheme(event.newValue)
if (event.key === fontSizeLocalStorageKey) { localUpdateFontSize(event.newValue) }
})