Skip to content

Instantly share code, notes, and snippets.

@eriwen
Last active February 21, 2024 12:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eriwen/423b2e7301b95c6a0e2f28bf0883ddd0 to your computer and use it in GitHub Desktop.
Save eriwen/423b2e7301b95c6a0e2f28bf0883ddd0 to your computer and use it in GitHub Desktop.
Multi-language code samples on the web
<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>
.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;
}
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