dataforge-core/scripts/platform-content-handler.js

347 lines
12 KiB
JavaScript

filteringContext = {
dependencies: {},
restrictedDependencies: [],
activeFilters: []
}
let highlightedAnchor;
let topNavbarOffset;
window.addEventListener('load', () => {
document.querySelectorAll("div[data-platform-hinted]")
.forEach(elem => elem.addEventListener('click', (event) => togglePlatformDependent(event,elem)))
document.querySelectorAll("div[tabs-section]")
.forEach(elem => elem.addEventListener('click', (event) => toggleSectionsEventHandler(event)))
const filterSection = document.getElementById('filter-section')
if (filterSection) {
filterSection.addEventListener('click', (event) => filterButtonHandler(event))
initializeFiltering()
}
initTabs()
handleAnchor()
initHidingLeftNavigation()
document.getElementById('main').addEventListener("scroll", (e) => {
document.getElementsByClassName("navigation-wrapper")[0].classList.toggle("sticky-navigation", e.target.scrollTop > 0)
})
topNavbarOffset = document.getElementById('navigation-wrapper')
})
const darkModeSwitch = () => {
const localStorageKey = "dokka-dark-mode"
const storage = localStorage.getItem(localStorageKey)
const savedDarkMode = storage ? JSON.parse(storage) : false
const element = document.getElementById("theme-toggle-button")
initPlayground(savedDarkMode ? samplesDarkThemeName : samplesLightThemeName)
element.addEventListener('click', () => {
const enabledClasses = document.getElementsByTagName("html")[0].classList
enabledClasses.toggle("theme-dark")
//if previously we had saved dark theme then we set it to light as this is what we save in local storage
const darkModeEnabled = enabledClasses.contains("theme-dark")
if (darkModeEnabled) {
initPlayground(samplesDarkThemeName)
}
else {
initPlayground(samplesLightThemeName)
}
localStorage.setItem(localStorageKey, JSON.stringify(darkModeEnabled))
})
}
const initPlayground = (theme) => {
if(!samplesAreEnabled()) return
instances.forEach(instance => instance.destroy())
instances = []
// Manually tag code fragments as not processed by playground since we also manually destroy all of its instances
document.querySelectorAll('code.runnablesample').forEach(node => {
node.removeAttribute("data-kotlin-playground-initialized");
})
KotlinPlayground('code.runnablesample', {
getInstance: playgroundInstance => {
instances.push(playgroundInstance)
},
theme: theme
});
}
// We check if type is accessible from the current scope to determine if samples script is present
// As an alternative we could extract this samples-specific script to new js file but then we would handle dark mode in 2 separate files which is not ideal
const samplesAreEnabled = () => {
try {
KotlinPlayground
return true
} catch (e) {
return false
}
}
const initHidingLeftNavigation = () => {
document.getElementById("leftToggler").onclick = function(event) {
//Events need to be prevented from bubbling since they will trigger next handler
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
document.getElementById("leftColumn").classList.toggle("open");
}
document.getElementById("main").onclick = () => {
document.getElementById("leftColumn").classList.remove("open");
}
}
// Hash change is needed in order to allow for linking inside the same page with anchors
// If this is not present user is forced to refresh the site in order to use an anchor
window.onhashchange = handleAnchor
function scrollToElementInContent(element){
const scrollToElement = () => document.getElementById('main').scrollTo({ top: element.offsetTop - topNavbarOffset.offsetHeight, behavior: "smooth"})
const waitAndScroll = () => {
setTimeout(() => {
if(topNavbarOffset){
scrollToElement()
} else {
waitForScroll()
}
}, 50)
}
if(topNavbarOffset){
scrollToElement()
} else {
waitAndScroll()
}
}
function handleAnchor() {
if(highlightedAnchor){
highlightedAnchor.classList.remove('anchor-highlight')
highlightedAnchor = null;
}
let searchForTab = function(element) {
if(element && element.hasAttribute) {
if(element.hasAttribute("data-togglable")) return element;
else return searchForTab(element.parentNode)
} else return null
}
let anchor = window.location.hash
if (anchor != "") {
anchor = anchor.substring(1)
let element = document.querySelector('a[data-name="' + anchor+'"]')
if (element) {
let tab = searchForTab(element)
if (tab) {
toggleSections(tab)
}
const content = element.nextElementSibling
if(content){
content.classList.add('anchor-highlight')
highlightedAnchor = content
}
scrollToElementInContent(element)
}
}
}
function initTabs(){
document.querySelectorAll("div[tabs-section]")
.forEach(element => {
showCorrespondingTabBody(element)
element.addEventListener('click', (event) => toggleSectionsEventHandler(event))
})
let cached = localStorage.getItem("active-tab")
if (cached) {
let parsed = JSON.parse(cached)
let tab = document.querySelector('div[tabs-section] > button[data-togglable="' + parsed + '"]')
if(tab) {
toggleSections(tab)
}
}
}
function showCorrespondingTabBody(element) {
const buttonWithKey = element.querySelector("button[data-active]")
if (buttonWithKey) {
const key = buttonWithKey.getAttribute("data-togglable")
document.querySelector(".tabs-section-body")
.querySelector("div[data-togglable='" + key + "']")
.setAttribute("data-active", "")
}
}
function filterButtonHandler(event) {
if(event.target.tagName == "BUTTON" && event.target.hasAttribute("data-filter")) {
let sourceset = event.target.getAttribute("data-filter")
if(filteringContext.activeFilters.indexOf(sourceset) != -1) {
filterSourceset(sourceset)
} else {
unfilterSourceset(sourceset)
}
}
}
function initializeFiltering() {
filteringContext.dependencies = JSON.parse(sourceset_dependencies)
document.querySelectorAll("#filter-section > button")
.forEach(p => filteringContext.restrictedDependencies.push(p.getAttribute("data-filter")))
Object.keys(filteringContext.dependencies).forEach(p => {
filteringContext.dependencies[p] = filteringContext.dependencies[p]
.filter(q => -1 !== filteringContext.restrictedDependencies.indexOf(q))
})
let cached = window.localStorage.getItem('inactive-filters')
if (cached) {
let parsed = JSON.parse(cached)
//Events are used by react to get values in 'on this page'
const event = new CustomEvent('sourceset-filter-change', { detail: parsed });
window.dispatchEvent(event);
filteringContext.activeFilters = filteringContext.restrictedDependencies
.filter(q => parsed.indexOf(q) == -1 )
} else {
filteringContext.activeFilters = filteringContext.restrictedDependencies
}
refreshFiltering()
}
function filterSourceset(sourceset) {
filteringContext.activeFilters = filteringContext.activeFilters.filter(p => p != sourceset)
refreshFiltering()
addSourcesetFilterToCache(sourceset)
}
function unfilterSourceset(sourceset) {
if(filteringContext.activeFilters.length == 0) {
filteringContext.activeFilters = filteringContext.dependencies[sourceset].concat([sourceset])
refreshFiltering()
filteringContext.dependencies[sourceset].concat([sourceset]).forEach(p => removeSourcesetFilterFromCache(p))
} else {
filteringContext.activeFilters.push(sourceset)
refreshFiltering()
removeSourcesetFilterFromCache(sourceset)
}
}
function addSourcesetFilterToCache(sourceset) {
let cached = localStorage.getItem('inactive-filters')
if (cached) {
let parsed = JSON.parse(cached)
localStorage.setItem('inactive-filters', JSON.stringify(parsed.concat([sourceset])))
} else {
localStorage.setItem('inactive-filters', JSON.stringify([sourceset]))
}
}
function removeSourcesetFilterFromCache(sourceset) {
let cached = localStorage.getItem('inactive-filters')
if (cached) {
let parsed = JSON.parse(cached)
localStorage.setItem('inactive-filters', JSON.stringify(parsed.filter(p => p != sourceset)))
}
}
function toggleSections(target) {
localStorage.setItem('active-tab', JSON.stringify(target.getAttribute("data-togglable")))
const activateTabs = (containerClass) => {
for(const element of document.getElementsByClassName(containerClass)){
for(const child of element.children){
if(child.getAttribute("data-togglable") === target.getAttribute("data-togglable")){
child.setAttribute("data-active", "")
} else {
child.removeAttribute("data-active")
}
}
}
}
activateTabs("tabs-section")
activateTabs("tabs-section-body")
}
function toggleSectionsEventHandler(evt){
if(!evt.target.getAttribute("data-togglable")) return
toggleSections(evt.target)
}
function togglePlatformDependent(e, container) {
let target = e.target
if (target.tagName != 'BUTTON') return;
let index = target.getAttribute('data-toggle')
for(let child of container.children){
if(child.hasAttribute('data-toggle-list')){
for(let bm of child.children){
if(bm == target){
bm.setAttribute('data-active',"")
} else if(bm != target) {
bm.removeAttribute('data-active')
}
}
}
else if(child.getAttribute('data-togglable') == index) {
child.setAttribute('data-active',"")
}
else {
child.removeAttribute('data-active')
}
}
}
function refreshFiltering() {
let sourcesetList = filteringContext.activeFilters
document.querySelectorAll("[data-filterable-set]")
.forEach(
elem => {
let platformList = elem.getAttribute("data-filterable-set").split(' ').filter(v => -1 !== sourcesetList.indexOf(v))
elem.setAttribute("data-filterable-current", platformList.join(' '))
}
)
const event = new CustomEvent('sourceset-filter-change', { detail: sourcesetList });
window.dispatchEvent(event);
refreshFilterButtons()
refreshPlatformTabs()
}
function refreshPlatformTabs() {
document.querySelectorAll(".platform-hinted > .platform-bookmarks-row").forEach(
p => {
let active = false;
let firstAvailable = null
p.childNodes.forEach(
element => {
if(element.getAttribute("data-filterable-current") != ''){
if( firstAvailable == null) {
firstAvailable = element
}
if(element.hasAttribute("data-active")) {
active = true;
}
}
}
)
if( active == false && firstAvailable) {
firstAvailable.click()
}
}
)
}
function refreshFilterButtons() {
document.querySelectorAll("#filter-section > button")
.forEach(f => {
if(filteringContext.activeFilters.indexOf(f.getAttribute("data-filter")) != -1){
f.setAttribute("data-active","")
} else {
f.removeAttribute("data-active")
}
})
}