Last active
February 21, 2024 12:08
-
-
Save eriwen/423b2e7301b95c6a0e2f28bf0883ddd0 to your computer and use it in GitHub Desktop.
Multi-language code samples on the web
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="exampleblock testable-sample multi-language-sample"> | |
<div class="title">build.gradle</div> | |
<div class="content"> | |
<pre class="prettyprint highlight"><code class="language-groovy" data-lang="groovy">logging.captureStandardOutput LogLevel.INFO | |
println 'A message which is logged at INFO level'</code></pre> | |
</div> | |
</div> | |
<div class="exampleblock testable-sample multi-language-sample"> | |
<div class="title">build.gradle.kts</div> | |
<div class="content"> | |
<pre class="prettyprint highlight"><code class="language-kotlin" data-lang="kotlin">logging.captureStandardOutput(LogLevel.INFO) | |
println("A message which is logged at INFO level")</code></pre> | |
</div> | |
</div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.multi-language-selector { | |
display: block; | |
} | |
.multi-language-selector .language-option { | |
background-color: white; | |
border: 1px solid #f7f7f8; | |
border-radius: 4px 4px 0 0; | |
cursor: pointer; | |
display: inline-block; | |
font-weight: normal; | |
font-family: 'Lato', Arial, sans-serif; | |
margin: 0; | |
padding: 4px 20px; | |
min-width: 130px; | |
max-width: 320px; | |
text-align: center; | |
filter: grayscale(1); | |
-webkit-filter: grayscale(1); | |
opacity: 0.7; | |
} | |
.multi-language-selector .language-option.selected { | |
background-color: #f7f7f8; | |
color: #02303a; | |
filter: none; | |
-webkit-filter: none; | |
opacity: 1; | |
} | |
.multi-language-text.hidden, | |
.multi-language-selector ~ .multi-language-sample.hidden { | |
display: none; | |
} | |
.multi-language-sample { | |
border-radius: 0 0 4px 4px; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function postProcessCodeBlocks() { | |
// Assumptions: | |
// 1) All siblings that are marked with class="multi-language-sample" should be grouped | |
// 2) Only one language can be selected per domain (to allow selection to persist across all docs pages) | |
// 3) There is exactly 1 small set of languages to choose from. This does not allow for multiple language preferences. For example, users cannot prefer both Kotlin and ZSH. | |
// 4) Only 1 sample of each language can exist in the same collection. | |
var LANGUAGE_OPTIONS = ["markdown", "asciidoc"]; | |
var preferredBuildScriptLanguage = initPreferredSampleLanguage(); | |
/** | |
* Get preferred sample language from local storage. Default to first of all options. | |
*/ | |
function initPreferredSampleLanguage() { | |
var lang = window.localStorage.getItem("preferred-sample-language"); | |
if (LANGUAGE_OPTIONS.indexOf(lang) === -1) { | |
window.localStorage.setItem("preferred-sample-language", LANGUAGE_OPTIONS[0]); | |
lang = LANGUAGE_OPTIONS[0]; | |
} | |
return lang; | |
} | |
/** | |
* Given a non-null String, return it with first letter capitalized. | |
*/ | |
function capitalizeFirstLetter(string) { | |
return string.charAt(0).toUpperCase() + string.slice(1); | |
} | |
function processSampleEl(sampleEl, preferredLanguage) { | |
var codeEl = sampleEl.querySelector("code[data-lang]"); | |
if (codeEl != null) { | |
sampleEl.setAttribute("data-lang", codeEl.getAttribute("data-lang")); | |
if (codeEl.getAttribute("data-lang") !== preferredLanguage) { | |
sampleEl.classList.add("hidden"); | |
} else { | |
sampleEl.classList.remove("hidden"); | |
} | |
} | |
} | |
function switchSampleLanguage(languageId) { | |
var multiLanguageSampleElements = [].slice.call(document.querySelectorAll(".multi-language-sample")); | |
// Array of Arrays, each top-level array representing a single collection of samples | |
var multiLanguageSets = []; | |
for (var i = 0; i < multiLanguageSampleElements.length; i++) { | |
var currentCollection = [multiLanguageSampleElements[i]]; | |
var currentSampleElement = multiLanguageSampleElements[i]; | |
processSampleEl(currentSampleElement, languageId); | |
while (currentSampleElement.nextElementSibling != null && currentSampleElement.nextElementSibling.classList.contains("multi-language-sample")) { | |
currentCollection.push(currentSampleElement.nextElementSibling); | |
currentSampleElement = currentSampleElement.nextElementSibling; | |
processSampleEl(currentSampleElement, languageId); | |
i++; | |
} | |
multiLanguageSets.push(currentCollection); | |
} | |
multiLanguageSets.forEach(function (sampleCollection) { | |
// Create selector element if not existing | |
if (sampleCollection.length > 1 && | |
(sampleCollection[0].previousElementSibling == null || | |
!sampleCollection[0].previousElementSibling.classList.contains("multi-language-selector"))) { | |
var languageSelectorFragment = document.createDocumentFragment(); | |
var multiLanguageSelectorElement = document.createElement("div"); | |
multiLanguageSelectorElement.classList.add("multi-language-selector"); | |
languageSelectorFragment.appendChild(multiLanguageSelectorElement); | |
sampleCollection.forEach(function (sampleEl) { | |
var optionEl = document.createElement("code"); | |
var sampleLanguage = sampleEl.getAttribute("data-lang"); | |
if (sampleLanguage == null) { | |
return; | |
} | |
optionEl.setAttribute("data-lang", sampleLanguage); | |
optionEl.setAttribute("role", "button"); | |
optionEl.classList.add("language-option"); | |
optionEl.innerText = capitalizeFirstLetter(sampleLanguage); | |
optionEl.addEventListener("click", function updatePreferredLanguage(evt) { | |
var preferredLanguageId = optionEl.getAttribute("data-lang"); | |
window.localStorage.setItem("preferred-sample-language", preferredLanguageId); | |
// Record how far down the page the clicked element is before switching all samples | |
var beforeOffset = evt.target.offsetTop; | |
switchSampleLanguage(preferredLanguageId); | |
// Scroll the window to account for content height differences between different sample languages | |
window.scrollBy(0, evt.target.offsetTop - beforeOffset); | |
}); | |
multiLanguageSelectorElement.appendChild(optionEl); | |
}); | |
sampleCollection[0].parentNode.insertBefore(languageSelectorFragment, sampleCollection[0]); | |
} | |
}); | |
[].slice.call(document.querySelectorAll(".multi-language-selector .language-option")).forEach(function (optionEl) { | |
if (optionEl.getAttribute("data-lang") === languageId) { | |
optionEl.classList.add("selected"); | |
} else { | |
optionEl.classList.remove("selected"); | |
} | |
}); | |
// Also hide/show .multi-language-text blocks associated with a language | |
[].slice.call(document.querySelectorAll(".multi-language-text")).forEach(function (el) { | |
if (!el.classList.contains("lang-" + languageId)) { | |
el.classList.add("hidden"); | |
} else { | |
el.classList.remove("hidden"); | |
} | |
}); | |
} | |
switchSampleLanguage(preferredBuildScriptLanguage); | |
} | |
document.addEventListener("DOMContentLoaded", function () { | |
postProcessCodeBlocks(); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment