-
Notifications
You must be signed in to change notification settings - Fork 310
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
✨ ENH: Support version and locale switcher #360
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<ul class="navbar-nav"> | ||
<li class="nav-item"> | ||
<span id="current-switcher"> | ||
{{version}} | ||
</span> | ||
</li> | ||
</ul> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<script type="text/javascript"> | ||
(function () { | ||
window.versionSwitcher = { | ||
pageName: "{{pagename}}.html", | ||
versionJsonUrl: "{{theme_version_switch_json_url}}", | ||
enableLocaleSupport: "{{theme_version_switch_enable_locale}}" === "True", | ||
// TODO read from "{{theme_version_switch_locales}}" | ||
allLocales: [ | ||
{ | ||
"locale": "zh", | ||
"display": "中文" | ||
}, | ||
{ | ||
"locale": "en", | ||
"display": "EN" | ||
} | ||
] | ||
} | ||
})(); | ||
</script> | ||
|
||
<ul class="navbar-nav"> | ||
<li class="nav-item dropdown"> | ||
<button id="version-dropdown" class="btn btn-secondary btn-sm dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> | ||
<!-- placeholder for javascript filling above --> | ||
</button> | ||
<div id="version-menu" class="dropdown-menu" style="min-width: 6rem;"> | ||
<!-- placeholder for javascript filling above --> | ||
</div> | ||
</li> | ||
<li class="nav-item"> | ||
<span id="locale-switcher"> | ||
<!-- placeholder for locale switcher --> | ||
</span> | ||
</li> | ||
</ul> |
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -72,8 +72,208 @@ function scrollToActive() { | |
}); | ||
} | ||
|
||
|
||
$(document).ready(() => { | ||
scrollToActive(); | ||
addTOCInteractivity(); | ||
}); | ||
|
||
function setupVersionSwitcher() { | ||
// Setup Version Switcher | ||
|
||
// Only enable version switcher when window.versionSwitcher is filled by sphinx | ||
if (!window.versionSwitcher) { | ||
return; | ||
} | ||
let pageName = window.versionSwitcher.pageName; | ||
let versionJsonUrl = window.versionSwitcher.versionJsonUrl; | ||
let enableLocaleSupport = window.versionSwitcher.enableLocaleSupport; | ||
let allLocales = window.versionSwitcher.allLocales; | ||
|
||
// Remote version should like this. | ||
// .name and .alias must be unique in each locale. | ||
// It's not necessary to have same versions in all locales. | ||
// When locale is enabled, there must be only one .default=true item in each locale to indicate which one should be redirect if target version doesn't exist in target locale. | ||
|
||
/* | ||
let allVersions = { | ||
"en": [ | ||
{ | ||
"name": "v1.2.0", | ||
"url": "v1.2.0", | ||
"alias": ["latest"], | ||
"default": true | ||
}, | ||
{ | ||
"name": "v1.1.0", | ||
"url": "v1.1.0", | ||
"alias": [] | ||
}, | ||
], | ||
"zh":[ | ||
{ | ||
"name": "v1.0.0", | ||
"url": "v1.0.0", | ||
"alias": [] | ||
"default": true | ||
}, | ||
], | ||
}; | ||
*/ | ||
function parseCurrentURL() { | ||
// parseCurrentURL look up current pathname, generate all information about current version and locale. | ||
|
||
let pathname = window.location.pathname; | ||
|
||
// add "index.html" back when browser omit it. | ||
if (pageName.endsWith("index.html") && pathname.endsWith("/")) { | ||
pathname += "index.html"; | ||
} | ||
if (pathname.slice(-pageName.length) !== pageName) { | ||
// Sphinx generated pages should have exactly same suffix | ||
throw 'page suffix do not match requirements'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to |
||
} | ||
|
||
// Get base URL by Removing '/' and pageName | ||
let baseURL = pathname.slice(0, -(pageName.length + 1)); | ||
let parts = baseURL.split('/'); | ||
|
||
let currentVersion = ''; | ||
let currentLocale = ''; | ||
|
||
if (enableLocaleSupport) { | ||
if (parts.length < 1) { | ||
throw 'page base URL do not have any locale information'; | ||
} | ||
currentLocale = parts.pop(); | ||
} | ||
if (parts.length < 1) { | ||
throw 'page base URL do not have any locale information'; | ||
} | ||
currentVersion = parts.pop(); | ||
// This is base URL without any version or locate. | ||
let globalBaseURL = parts.join('/') | ||
|
||
return {pageName, baseURL, currentVersion, currentLocale, globalBaseURL}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When I was testing this locally, I was getting an empty string for |
||
} | ||
|
||
// validate Check currentLocale and currentVersion is valid. | ||
// Return canonicalVersion: indicate current version's real name | ||
function validate(allVersions, info) { | ||
let locale = "default"; // Use default as key when locale feature is disabled | ||
if (enableLocaleSupport) { | ||
locale = info.currentLocale; | ||
} | ||
let version_list = allVersions[locale]; | ||
|
||
if (version_list === undefined) { | ||
throw `locale '${locale}'doesn't exist in remote version mapping`; | ||
} | ||
|
||
let canonicalVersion = function() { // Match currentVersion in version_list, try to find canonical version name by matching name and alias | ||
for (const v of version_list) { | ||
if (info.currentVersion === v.name) { | ||
return v.name; | ||
} | ||
for (const alias_name of v.alias) { | ||
if (info.currentVersion === alias_name) { | ||
return v.name; | ||
} | ||
} | ||
} | ||
throw `version '${info.currentVersion}' doesn't exist in remove version maaping` | ||
}() | ||
|
||
return canonicalVersion; | ||
} | ||
|
||
// Set locale or version to null to indicate unchanged property. | ||
function constructUrl(info, targetLocale, targetVersion) { | ||
let segments = [info.globalBaseURL]; | ||
|
||
if (targetLocale == null) { | ||
targetLocale = info.currentLocale; | ||
} | ||
if (targetVersion == null) { | ||
targetVersion = info.currentVersion; | ||
} | ||
segments.push(targetVersion); | ||
if (enableLocaleSupport) { | ||
segments.push(targetLocale); | ||
} | ||
segments.push(info.pageName); | ||
return segments.join('/') + window.location.hash; | ||
} | ||
|
||
function render(allVersions, info) { | ||
function onSwitchVersion(evt) { | ||
evt.preventDefault() | ||
let selected = evt.currentTarget.getAttribute('key'); | ||
|
||
// process with alias problem, e.g. do not jump if target is just an alias of current one. | ||
if (selected == info.canonicalVersion) { | ||
// Current page is already the target version, ignore | ||
return; | ||
} | ||
|
||
let new_url = constructUrl(info, null, selected); | ||
window.location.assign(new_url); | ||
} | ||
|
||
function onSwitchLocale(evt) { | ||
evt.preventDefault() | ||
let selected = evt.currentTarget.getAttribute('key'); | ||
|
||
let new_url = constructUrl(info, selected, null); | ||
window.location.assign(new_url); | ||
} | ||
|
||
// Fill the current version in the dropdown, always show real name instead of alias | ||
document.getElementById("version-dropdown").innerText = info.canonicalVersion; | ||
|
||
const menuHTML = (function() { | ||
return allVersions[info.currentLocale].map((version) => { | ||
let text = version.name; | ||
if (version.alias.length > 0) { | ||
text = `${version.name} (${version.alias.join(' ')})` | ||
} | ||
|
||
return `<button class="dropdown-item" key="${version.name}">${text}</button>` | ||
}) | ||
})().join('') | ||
// fill the version menu | ||
document.getElementById("version-menu").innerHTML = menuHTML; | ||
|
||
// bind the changes to this menu to trigger the switching function | ||
$('#version-menu button').on('click', onSwitchVersion) | ||
|
||
// Adding locale switcher | ||
const localeHTML = (function() { | ||
return allLocales.map((l) => { | ||
if (l.locale === info.currentLocale) { | ||
return `<a class="locale-btn locale-current" key="${l.locale}">${l.display}</a>` | ||
} else { | ||
return `<a class="locale-btn locale-option "key="${l.locale}">${l.display}</a>` | ||
} | ||
}) | ||
})().join('/') | ||
document.getElementById("locale-switcher").innerHTML = localeHTML; | ||
|
||
$('#locale-switcher .locale-option').on('click', onSwitchLocale) | ||
} | ||
|
||
// Trigger fetch as earlier as possible to speedup page loading. | ||
let p = fetch(versionJsonUrl).then((resp) => { | ||
return resp.json() | ||
}); | ||
|
||
let info = parseCurrentURL(); | ||
|
||
p.then((allVersions) => { | ||
let canonicalVersion = validate(allVersions, info); | ||
info.canonicalVersion = canonicalVersion; | ||
|
||
render(allVersions, info); | ||
}) | ||
} | ||
|
||
$(document).ready(setupVersionSwitcher); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this (and enable_locale) should probably default to False start as an opt-in in case other people are already rolling their own version switcher?