From e9f5da45c0fcfec5f7c150229301904d7915e090 Mon Sep 17 00:00:00 2001 From: Marcin Aman Date: Wed, 7 Oct 2020 13:58:46 +0200 Subject: Make logo replaceable #1339 (#1488) --- plugins/base/src/main/kotlin/DokkaBase.kt | 12 +- .../base/src/main/kotlin/DokkaBaseConfiguration.kt | 14 ++ .../src/main/kotlin/renderers/DefaultRenderer.kt | 8 +- .../base/src/main/kotlin/renderers/FileWriter.kt | 13 +- .../main/kotlin/renderers/contentTypeChecking.kt | 16 ++ .../src/main/kotlin/renderers/html/HtmlRenderer.kt | 2 +- .../kotlin/renderers/html/htmlPreprocessors.kt | 103 +++++---- .../src/main/resources/dokka/images/logo-text.svg | 6 - .../resources/dokka/scripts/navigation-loader.js | 54 +++++ .../resources/dokka/scripts/navigationLoader.js | 54 ----- .../dokka/scripts/platform-content-handler.js | 256 +++++++++++++++++++++ .../dokka/scripts/platformContentHandler.js | 256 --------------------- .../src/main/resources/dokka/scripts/search.js | 7 - .../main/resources/dokka/styles/logo-styles.css | 3 + .../base/src/main/resources/dokka/styles/style.css | 1 - 15 files changed, 416 insertions(+), 389 deletions(-) create mode 100644 plugins/base/src/main/kotlin/DokkaBaseConfiguration.kt create mode 100644 plugins/base/src/main/kotlin/renderers/contentTypeChecking.kt delete mode 100755 plugins/base/src/main/resources/dokka/images/logo-text.svg create mode 100644 plugins/base/src/main/resources/dokka/scripts/navigation-loader.js delete mode 100644 plugins/base/src/main/resources/dokka/scripts/navigationLoader.js create mode 100644 plugins/base/src/main/resources/dokka/scripts/platform-content-handler.js delete mode 100644 plugins/base/src/main/resources/dokka/scripts/platformContentHandler.js delete mode 100644 plugins/base/src/main/resources/dokka/scripts/search.js create mode 100644 plugins/base/src/main/resources/dokka/styles/logo-styles.css (limited to 'plugins/base/src') diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt index be3c09b9..e709b48b 100644 --- a/plugins/base/src/main/kotlin/DokkaBase.kt +++ b/plugins/base/src/main/kotlin/DokkaBase.kt @@ -198,10 +198,6 @@ class DokkaBase : DokkaPlugin() { htmlPreprocessors with NavigationPageInstaller order { after(rootCreator) } } - val searchPageInstaller by extending { - htmlPreprocessors with SearchPageInstaller order { after(rootCreator) } - } - val scriptsInstaller by extending { htmlPreprocessors with ScriptsInstaller order { after(rootCreator) } } @@ -214,6 +210,14 @@ class DokkaBase : DokkaPlugin() { htmlPreprocessors with AssetsInstaller order { after(rootCreator) } } + val customResourceInstaller by extending { + htmlPreprocessors providing { ctx -> CustomResourceInstaller(ctx) } order { + after(stylesInstaller) + after(scriptsInstaller) + after(assetsInstaller) + } + } + val packageListCreator by extending { htmlPreprocessors providing { PackageListCreator(it, RecognizedLinkFormat.DokkaHtml) diff --git a/plugins/base/src/main/kotlin/DokkaBaseConfiguration.kt b/plugins/base/src/main/kotlin/DokkaBaseConfiguration.kt new file mode 100644 index 00000000..c18b3446 --- /dev/null +++ b/plugins/base/src/main/kotlin/DokkaBaseConfiguration.kt @@ -0,0 +1,14 @@ +package org.jetbrains.dokka.base + +import org.jetbrains.dokka.plugability.ConfigurableBlock +import java.io.File + +data class DokkaBaseConfiguration( + var customStyleSheets: List = defaultCustomStyleSheets, + var customAssets: List = defaultCustomAssets +): ConfigurableBlock { + companion object { + val defaultCustomStyleSheets: List = emptyList() + val defaultCustomAssets: List = emptyList() + } +} \ No newline at end of file diff --git a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt index 5c0f54d8..f393c0fa 100644 --- a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt +++ b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt @@ -15,7 +15,6 @@ import org.jetbrains.dokka.plugability.plugin import org.jetbrains.dokka.plugability.querySingle import org.jetbrains.dokka.renderers.Renderer import org.jetbrains.dokka.transformers.pages.PageTransformer -import java.io.File abstract class DefaultRenderer( protected val context: DokkaContext @@ -223,9 +222,4 @@ abstract class DefaultRenderer( internal typealias SerializedBeforeAndAfter = Pair internal typealias InstanceWithSource = Pair -fun ContentPage.sourceSets() = this.content.sourceSets - -fun ContentEmbeddedResource.isImage(): Boolean { - val imageExtensions = setOf("png", "jpg", "jpeg", "gif", "bmp", "tif", "webp", "svg") - return File(address).extension.toLowerCase() in imageExtensions -} +fun ContentPage.sourceSets() = this.content.sourceSets \ No newline at end of file diff --git a/plugins/base/src/main/kotlin/renderers/FileWriter.kt b/plugins/base/src/main/kotlin/renderers/FileWriter.kt index cd38f1b9..f8c0e882 100644 --- a/plugins/base/src/main/kotlin/renderers/FileWriter.kt +++ b/plugins/base/src/main/kotlin/renderers/FileWriter.kt @@ -33,7 +33,7 @@ class FileWriter(val context: DokkaContext): OutputWriter { } override suspend fun writeResources(pathFrom: String, pathTo: String) = - if (javaClass.getResource(pathFrom).toURI().toString().startsWith(jarUriPrefix)) { + if (javaClass.getResource(pathFrom)?.toURI()?.toString()?.startsWith(jarUriPrefix) == true) { copyFromJar(pathFrom, pathTo) } else { copyFromDirectory(pathFrom, pathTo) @@ -42,9 +42,10 @@ class FileWriter(val context: DokkaContext): OutputWriter { private suspend fun copyFromDirectory(pathFrom: String, pathTo: String) { val dest = Paths.get(root.path, pathTo).toFile() - val uri = javaClass.getResource(pathFrom).toURI() + val uri = javaClass.getResource(pathFrom)?.toURI() + val file = uri?.let { File(it) } ?: File(pathFrom) withContext(Dispatchers.IO) { - File(uri).copyRecursively(dest, true) + file.copyRecursively(dest, true) } } @@ -52,7 +53,11 @@ class FileWriter(val context: DokkaContext): OutputWriter { val rebase = fun(path: String) = "$pathTo/${path.removePrefix(pathFrom)}" val dest = Paths.get(root.path, pathTo).toFile() - dest.mkdirsOrFail() + if(dest.isDirectory){ + dest.mkdirsOrFail() + } else { + dest.parentFile.mkdirsOrFail() + } val uri = javaClass.getResource(pathFrom).toURI() val fs = getFileSystemForURI(uri) val path = fs.getPath(pathFrom) diff --git a/plugins/base/src/main/kotlin/renderers/contentTypeChecking.kt b/plugins/base/src/main/kotlin/renderers/contentTypeChecking.kt new file mode 100644 index 00000000..4619bc53 --- /dev/null +++ b/plugins/base/src/main/kotlin/renderers/contentTypeChecking.kt @@ -0,0 +1,16 @@ +package org.jetbrains.dokka.base.renderers + +import org.jetbrains.dokka.base.renderers.HtmlFileExtensions.imageExtensions +import org.jetbrains.dokka.pages.ContentEmbeddedResource +import java.io.File + +fun ContentEmbeddedResource.isImage(): Boolean { + return File(address).extension.toLowerCase() in imageExtensions +} + +fun String.isImage(): Boolean = + substringBefore('?').substringAfterLast('.') in imageExtensions + +object HtmlFileExtensions { + val imageExtensions = setOf("png", "jpg", "jpeg", "gif", "bmp", "tif", "webp", "svg") +} \ No newline at end of file diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt index 6bdeae62..7a047d3c 100644 --- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt +++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt @@ -679,10 +679,10 @@ open class HtmlRenderer( ) { async = true } + it.isImage() -> link(href = page.root(it)) else -> unsafe { +it } } } - link(rel = LinkRel.stylesheet, href = page.root("styles/main.css")) {} script { unsafe { +"""var pathToRoot = "${locationProvider.pathToRoot(page)}";""" } } } body { diff --git a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt index a5fca92f..a87254ce 100644 --- a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt +++ b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt @@ -7,6 +7,9 @@ import kotlinx.html.h1 import kotlinx.html.id import kotlinx.html.table import kotlinx.html.tbody +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.DokkaBaseConfiguration import org.jetbrains.dokka.base.renderers.sourceSets import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.DEnum @@ -14,30 +17,9 @@ import org.jetbrains.dokka.model.DEnumEntry import org.jetbrains.dokka.model.withDescendants import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.configuration import org.jetbrains.dokka.transformers.pages.PageTransformer - -object SearchPageInstaller : PageTransformer { - override fun invoke(input: RootPageNode) = input.modified(children = input.children + searchPage) - - private val searchPage = RendererSpecificResourcePage( - name = "Search", - children = emptyList(), - strategy = RenderingStrategy { - buildHtml(it, listOf("styles/style.css", "scripts/pages.js", "scripts/search.js")) { - h1 { - id = "searchTitle" - text("Search results for ") - } - table { - tbody { - id = "searchTable" - } - } - } - }) -} - object NavigationPageInstaller : PageTransformer { private val mapper = jacksonObjectMapper() @@ -89,56 +71,79 @@ object NavigationPageInstaller : PageTransformer { }.sortedBy { it.name.toLowerCase() } } +class CustomResourceInstaller(val dokkaContext: DokkaContext) : PageTransformer { + private val configuration = configuration(dokkaContext) + + private val customAssets = configuration?.customAssets?.map { + RendererSpecificResourcePage("images/${it.name}", emptyList(), RenderingStrategy.Copy(it.absolutePath)) + }.orEmpty() + + private val customStylesheets = configuration?.customStyleSheets?.map { + RendererSpecificResourcePage("styles/${it.name}", emptyList(), RenderingStrategy.Copy(it.absolutePath)) + }.orEmpty() + + override fun invoke(input: RootPageNode): RootPageNode { + val customResourcesPaths = (customAssets + customStylesheets).map { it.name }.toSet() + val withEmbeddedResources = input.transformContentPagesTree { it.modified(embeddedResources = it.embeddedResources + customResourcesPaths) } + val (currentResources, otherPages) = withEmbeddedResources.children.partition { it is RendererSpecificResourcePage } + return input.modified(children = otherPages + currentResources.filterNot { it.name in customResourcesPaths } + customAssets + customStylesheets) + } +} + object ScriptsInstaller : PageTransformer { + private val scriptsPages = listOf( + "scripts/clipboard.js", + "scripts/navigation-loader.js", + "scripts/platform-content-handler.js", + "scripts/main.js" + ) + override fun invoke(input: RootPageNode): RootPageNode { return input.modified( - children = input.children + RendererSpecificResourcePage( - "scripts", - emptyList(), - RenderingStrategy.Copy("/dokka/scripts") - ) + children = input.children + scriptsPages.toRenderSpecificResourcePage() ).transformContentPagesTree { it.modified( - embeddedResources = it.embeddedResources + listOf( - "scripts/navigationLoader.js", - "scripts/platformContentHandler.js", - "scripts/sourceset_dependencies.js", - "scripts/clipboard.js", - ) + embeddedResources = it.embeddedResources + scriptsPages ) } } } object StylesInstaller : PageTransformer { + private val stylesPages = listOf( + "styles/style.css", + "styles/logo-styles.css", + "styles/jetbrains-mono.css", + "styles/main.css" + ) + override fun invoke(input: RootPageNode): RootPageNode = input.modified( - children = input.children + RendererSpecificResourcePage( - "styles", - emptyList(), - RenderingStrategy.Copy("/dokka/styles") - ) + children = input.children + stylesPages.toRenderSpecificResourcePage() ).transformContentPagesTree { it.modified( - embeddedResources = it.embeddedResources + listOf( - "styles/style.css", - "styles/jetbrains-mono.css" - ) + embeddedResources = it.embeddedResources + stylesPages ) } } object AssetsInstaller : PageTransformer { + private val imagesPages = listOf( + "images/arrow_down.svg", + "images/docs_logo.svg", + "images/logo-icon.svg" + ) + override fun invoke(input: RootPageNode) = input.modified( - children = input.children + RendererSpecificResourcePage( - "images", - emptyList(), - RenderingStrategy.Copy("/dokka/images") - ) + children = input.children + imagesPages.toRenderSpecificResourcePage() ) } +private fun List.toRenderSpecificResourcePage(): List = + map { RendererSpecificResourcePage(it, emptyList(), RenderingStrategy.Copy("/dokka/$it")) } + class SourcesetDependencyAppender(val context: DokkaContext) : PageTransformer { + private val name = "scripts/sourceset_dependencies.js" override fun invoke(input: RootPageNode): RootPageNode { val dependenciesMap = context.configuration.sourceSets.map { it.sourceSetID to it.dependentSourceSets @@ -155,14 +160,14 @@ class SourcesetDependencyAppender(val context: DokkaContext) : PageTransformer { }}'" val deps = RendererSpecificResourcePage( - name = "scripts/sourceset_dependencies.js", + name = name, children = emptyList(), strategy = RenderingStrategy.Write(createDependenciesJson()) ) return input.modified( children = input.children + deps - ) + ).transformContentPagesTree { it.modified(embeddedResources = it.embeddedResources + name) } } } diff --git a/plugins/base/src/main/resources/dokka/images/logo-text.svg b/plugins/base/src/main/resources/dokka/images/logo-text.svg deleted file mode 100755 index 7bf3e6c5..00000000 --- a/plugins/base/src/main/resources/dokka/images/logo-text.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/plugins/base/src/main/resources/dokka/scripts/navigation-loader.js b/plugins/base/src/main/resources/dokka/scripts/navigation-loader.js new file mode 100644 index 00000000..c2f60ec5 --- /dev/null +++ b/plugins/base/src/main/resources/dokka/scripts/navigation-loader.js @@ -0,0 +1,54 @@ +window.addEventListener('load', () => { + fetch(pathToRoot + "navigation.html") + .then(response => response.text()) + .then(data => { + document.getElementById("sideMenu").innerHTML = data; + }).then(() => { + document.querySelectorAll(".overview > a").forEach(link => { + link.setAttribute("href", pathToRoot + link.getAttribute("href")); + }) + }).then(() => { + document.querySelectorAll(".sideMenuPart").forEach(nav => { + if (!nav.classList.contains("hidden")) nav.classList.add("hidden") + }) + }).then(() => { + revealNavigationForCurrentPage() + }) + + /* Smooth scrolling support for going to the top of the page */ + document.querySelectorAll('a[href^="#"]').forEach(anchor => { + anchor.addEventListener('click', function (e) { + e.preventDefault(); + + document.querySelector(this.getAttribute('href')).scrollIntoView({ + behavior: 'smooth' + }); + }); + }); +}) + +revealNavigationForCurrentPage = () => { + let pageId = document.getElementById("content").attributes["pageIds"].value.toString(); + let parts = document.querySelectorAll(".sideMenuPart"); + let found = 0; + do { + parts.forEach(part => { + if (part.attributes['pageId'].value.indexOf(pageId) !== -1 && found === 0) { + found = 1; + if (part.classList.contains("hidden")){ + part.classList.remove("hidden"); + part.setAttribute('data-active',""); + } + revealParents(part) + } + }); + pageId = pageId.substring(0, pageId.lastIndexOf("/")) + } while (pageId.indexOf("/") !== -1 && found === 0) +}; + +revealParents = (part) => { + if (part.classList.contains("sideMenuPart")) { + if (part.classList.contains("hidden")) part.classList.remove("hidden"); + revealParents(part.parentNode) + } +}; \ No newline at end of file diff --git a/plugins/base/src/main/resources/dokka/scripts/navigationLoader.js b/plugins/base/src/main/resources/dokka/scripts/navigationLoader.js deleted file mode 100644 index c2f60ec5..00000000 --- a/plugins/base/src/main/resources/dokka/scripts/navigationLoader.js +++ /dev/null @@ -1,54 +0,0 @@ -window.addEventListener('load', () => { - fetch(pathToRoot + "navigation.html") - .then(response => response.text()) - .then(data => { - document.getElementById("sideMenu").innerHTML = data; - }).then(() => { - document.querySelectorAll(".overview > a").forEach(link => { - link.setAttribute("href", pathToRoot + link.getAttribute("href")); - }) - }).then(() => { - document.querySelectorAll(".sideMenuPart").forEach(nav => { - if (!nav.classList.contains("hidden")) nav.classList.add("hidden") - }) - }).then(() => { - revealNavigationForCurrentPage() - }) - - /* Smooth scrolling support for going to the top of the page */ - document.querySelectorAll('a[href^="#"]').forEach(anchor => { - anchor.addEventListener('click', function (e) { - e.preventDefault(); - - document.querySelector(this.getAttribute('href')).scrollIntoView({ - behavior: 'smooth' - }); - }); - }); -}) - -revealNavigationForCurrentPage = () => { - let pageId = document.getElementById("content").attributes["pageIds"].value.toString(); - let parts = document.querySelectorAll(".sideMenuPart"); - let found = 0; - do { - parts.forEach(part => { - if (part.attributes['pageId'].value.indexOf(pageId) !== -1 && found === 0) { - found = 1; - if (part.classList.contains("hidden")){ - part.classList.remove("hidden"); - part.setAttribute('data-active',""); - } - revealParents(part) - } - }); - pageId = pageId.substring(0, pageId.lastIndexOf("/")) - } while (pageId.indexOf("/") !== -1 && found === 0) -}; - -revealParents = (part) => { - if (part.classList.contains("sideMenuPart")) { - if (part.classList.contains("hidden")) part.classList.remove("hidden"); - revealParents(part.parentNode) - } -}; \ No newline at end of file diff --git a/plugins/base/src/main/resources/dokka/scripts/platform-content-handler.js b/plugins/base/src/main/resources/dokka/scripts/platform-content-handler.js new file mode 100644 index 00000000..022aca4f --- /dev/null +++ b/plugins/base/src/main/resources/dokka/scripts/platform-content-handler.js @@ -0,0 +1,256 @@ +filteringContext = { + dependencies: {}, + restrictedDependencies: [], + activeFilters: [] +} +let highlightedAnchor; + +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() +}) + +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 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 + } + element.scrollIntoView({behavior: "smooth"}) + } + } +} + +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 key = element.querySelector("button[data-active]").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) + 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(' ')) + } + ) + 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") + } + }) +} + diff --git a/plugins/base/src/main/resources/dokka/scripts/platformContentHandler.js b/plugins/base/src/main/resources/dokka/scripts/platformContentHandler.js deleted file mode 100644 index 022aca4f..00000000 --- a/plugins/base/src/main/resources/dokka/scripts/platformContentHandler.js +++ /dev/null @@ -1,256 +0,0 @@ -filteringContext = { - dependencies: {}, - restrictedDependencies: [], - activeFilters: [] -} -let highlightedAnchor; - -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() -}) - -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 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 - } - element.scrollIntoView({behavior: "smooth"}) - } - } -} - -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 key = element.querySelector("button[data-active]").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) - 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(' ')) - } - ) - 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") - } - }) -} - diff --git a/plugins/base/src/main/resources/dokka/scripts/search.js b/plugins/base/src/main/resources/dokka/scripts/search.js deleted file mode 100644 index 04d88ab5..00000000 --- a/plugins/base/src/main/resources/dokka/scripts/search.js +++ /dev/null @@ -1,7 +0,0 @@ -let query = new URLSearchParams(window.location.search).get("query"); -document.getElementById("searchTitle").innerHTML += '"' + query + '":'; -document.getElementById("searchTable").innerHTML = pages - .filter(el => el.name.toLowerCase().startsWith(query.toLowerCase())) - .reduce((acc, element) => { - return acc + '' + element.name + '' - }, ""); \ No newline at end of file diff --git a/plugins/base/src/main/resources/dokka/styles/logo-styles.css b/plugins/base/src/main/resources/dokka/styles/logo-styles.css new file mode 100644 index 00000000..a3a07d75 --- /dev/null +++ b/plugins/base/src/main/resources/dokka/styles/logo-styles.css @@ -0,0 +1,3 @@ +#logo { + background-image: url(../images/docs_logo.svg); +} \ No newline at end of file diff --git a/plugins/base/src/main/resources/dokka/styles/style.css b/plugins/base/src/main/resources/dokka/styles/style.css index 24fd8fa3..7c1b2803 100644 --- a/plugins/base/src/main/resources/dokka/styles/style.css +++ b/plugins/base/src/main/resources/dokka/styles/style.css @@ -191,7 +191,6 @@ background-size: 125px 26px; border-bottom: 1px solid #DADFE6; background-repeat: no-repeat; - background-image: url(../images/docs_logo.svg); background-origin: content-box; padding-left: 24px; padding-top: 24px; -- cgit