diff --git a/doc/python_api/static/css/version_switch.css b/doc/python_api/static/css/version_switch.css
index 80d5c2a52b0..b001a1a2ce7 100644
--- a/doc/python_api/static/css/version_switch.css
+++ b/doc/python_api/static/css/version_switch.css
@@ -31,23 +31,30 @@
cursor: pointer;
z-index: 400;
}
+
.version-btn-open::after {
color: gray;
}
-.version-btn:hover, .version-btn:focus {
+
+.version-btn:hover,
+.version-btn:focus {
border-color: #525252;
}
+
.version-btn-open {
color: gray;
border: solid 1px var(--color-sidebar-background-border);
}
+
.version-btn.wait {
cursor: wait;
}
+
.version-btn.disabled {
cursor: not-allowed;
color: dimgray;
}
+
.version-dialog {
display: none;
position: absolute;
@@ -63,6 +70,7 @@
overflow-y: auto;
cursor: default;
}
+
.version-title {
padding: 5px;
color: var(--color-content-foreground);
@@ -72,6 +80,7 @@
background-color: var(--color-brand-primary);
border-bottom: solid 1.5px var(--color-sidebar-background-border);
}
+
.version-list {
padding-left: 0;
margin-top: 0;
@@ -80,7 +89,10 @@
border: solid 1px var(--color-sidebar-background-border);
border-radius: 0px 0px 3px 3px;
}
-.version-list a, .version-list span, .version-list li {
+
+.version-list a,
+.version-list span,
+.version-list li {
position: relative;
display: block;
font-size: 98%;
@@ -90,6 +102,7 @@
padding: 4px 0px;
color: var(--color-sidebar-link-text);
}
+
.version-list li {
background-color: var(--color-sidebar-background);
color: var(--color-sidebar-link-text);
diff --git a/doc/python_api/static/js/version_switch.js b/doc/python_api/static/js/version_switch.js
index b2d25069fbe..c00253bfe73 100644
--- a/doc/python_api/static/js/version_switch.js
+++ b/doc/python_api/static/js/version_switch.js
@@ -1,63 +1,60 @@
-(function() { // switch: v1.2
+(function() { // switch: v1.4
"use strict";
var versionsFileUrl = "https://docs.blender.org/PROD/versions.json"
var all_versions;
-var Popover = function() {
- function Popover(id)
+class Popover {
+ constructor(id)
{
this.isOpen = false;
this.type = (id === "version-popover");
- this.$btn = $('#' + id);
- this.$dialog = this.$btn.next();
- this.$list = this.$dialog.children("ul");
+ this.btn = document.querySelector('#' + id);
+ this.dialog = this.btn.nextElementSibling;
+ this.list = this.dialog.querySelector("ul");
this.sel = null;
- this.beforeInit();
- }
-
- Popover.prototype = {
- beforeInit : function() {
- var that = this;
- this.$btn.on("click", function(e) {
+ const that = this;
+ this.btnClickHandler = function(e) {
+ that.init();
+ e.preventDefault();
+ e.stopPropagation();
+ };
+ this.btnKeyHandler = function(e) {
+ if (that.btnKeyFilter(e)) {
that.init();
e.preventDefault();
e.stopPropagation();
- });
- this.$btn.on("keydown", function(e) {
- if (that.btnKeyFilter(e)) {
- that.init();
- e.preventDefault();
- e.stopPropagation();
- }
- });
- },
- init : function() {
- this.$btn.off("click");
- this.$btn.off("keydown");
+ }
+ };
+ this.btn.addEventListener("click", this.btnClickHandler);
+ this.btn.addEventListener("keydown", this.btnKeyHandler);
+ }
+ init()
+ {
+ this.btn.removeEventListener("click", this.btnClickHandler);
+ this.btn.removeEventListener("keydown", this.btnKeyHandler);
+
+ new Promise((resolve, reject) => {
if (all_versions === undefined) {
- this.$btn.addClass("wait");
- this.loadVL(this);
+ this.btn.classList.add("wait");
+ fetch(versionsFileUrl)
+ .then((response) => response.json())
+ .then((data) => {
+ all_versions = data;
+ resolve();
+ })
+ .catch(() => {
+ console.error("Version Switch Error: versions.json could not be loaded.");
+ this.btn.classList.remove("disabled");
+ });
}
else {
- this.afterLoad();
+ resolve();
}
- },
- loadVL : function(that) {
- $.getJSON(versionsFileUrl, function(data) {
- all_versions = data;
- that.afterLoad();
- return true;
- }).fail(function() {
- console.log("Version Switch Error: versions.json could not be loaded.");
- that.$btn.addClass("disabled");
- return false;
- });
- },
- afterLoad : function() {
- var release = DOCUMENTATION_OPTIONS.VERSION;
+ }).then(() => {
+ let release = DOCUMENTATION_OPTIONS.VERSION;
const m = release.match(/\d\.\d+/g);
if (m) {
release = m[0];
@@ -65,259 +62,274 @@ var Popover = function() {
this.warnOld(release, all_versions);
- var version = this.getNamed(release);
- var list = this.buildList(version);
+ const version = this.getNamed(release);
+ this.buildList(version);
- this.$list.children(":first-child").remove();
- this.$list.append(list);
- var that = this;
- this.$list.on("keydown", function(e) {
+ this.list.firstElementChild.remove();
+ const that = this;
+ this.list.addEventListener("keydown", function(e) {
that.keyMove(e);
});
- this.$btn.removeClass("wait");
+ this.btn.classList.remove("wait");
this.btnOpenHandler();
- this.$btn.on("mousedown", function(e) {
+ this.btn.addEventListener("mousedown", function(e) {
that.btnOpenHandler();
e.preventDefault()
});
- this.$btn.on("keydown", function(e) {
+ this.btn.addEventListener("keydown", function(e) {
if (that.btnKeyFilter(e)) {
that.btnOpenHandler();
}
});
- },
- warnOld : function(release, all_versions) {
- // Note this is effectively disabled now, two issues must fixed:
- // * versions.js does not contain a current entry, because that leads to
- // duplicate version numbers in the menu. These need to be deduplicated.
- // * It only shows the warning after opening the menu to switch version
- // when versions.js is loaded. This is too late to be useful.
- var current = all_versions.current
- if (!current)
- {
- // console.log("Version Switch Error: no 'current' in version.json.");
- return;
- }
- const m = current.match(/\d\.\d+/g);
- if (m) {
- current = parseFloat(m[0]);
- }
- if (release < current) {
- var currentURL = window.location.pathname.replace(release, current);
- var warning = $('
' +
- '
Note
' +
- '
' +
- 'You are not using the most up to date version of the documentation. ' +
- ' is the newest version.' +
- '
' +
- '
');
-
- warning.find('a').attr('href', currentURL).text(current);
-
- var body = $("div.body");
- if (!body.length) {
- body = $("div.document");
- }
- body.prepend(warning);
- }
- },
- buildList : function(v) {
- var url = new URL(window.location.href);
- let pathSplit = [ "", "api", v ];
- if (url.pathname.startsWith("/api/")) {
- pathSplit.push(url.pathname.split('/').slice(3).join('/'));
- }
- else {
- pathSplit.push(url.pathname.substring(1));
- }
- if (this.type) {
- var dyn = all_versions;
- var cur = v;
- }
- var buf = [];
- var that = this;
- $.each(dyn, function(ix, title) {
- buf.push("' +
- title + '');
- }
- else {
- pathSplit[2 + that.type] = ix;
- var href = new URL(url);
- href.pathname = pathSplit.join('/');
- buf.push(' tabindex="-1" role="presentation">' +
- title + '');
- }
- });
- return buf.join('');
- },
- getNamed : function(v) {
- $.each(all_versions, function(ix, title) {
- if (ix === "master" || ix === "main" || ix === "latest") {
- var m = title.match(/\d\.\d[\w\d\.]*/)[0];
- if (parseFloat(m) == v) {
- v = ix;
- return false;
- }
- }
- });
- return v;
- },
- dialogToggle : function(speed) {
- var wasClose = !this.isOpen;
- var that = this;
- if (!this.isOpen) {
- this.$btn.addClass("version-btn-open");
- this.$btn.attr("aria-pressed", true);
- this.$dialog.attr("aria-hidden", false);
- this.$dialog.fadeIn(speed, function() {
- that.$btn.parent().on("focusout", function(e) {
- that.focusoutHandler();
- e.stopImmediatePropagation();
- })
- that.$btn.parent().on("mouseleave", function(e) {
- that.mouseoutHandler();
- e.stopImmediatePropagation();
- });
- });
- this.isOpen = true;
- }
- else {
- this.$btn.removeClass("version-btn-open");
- this.$btn.attr("aria-pressed", false);
- this.$dialog.attr("aria-hidden", true);
- this.$btn.parent().off("focusout");
- this.$btn.parent().off("mouseleave");
- this.$dialog.fadeOut(speed, function() {
- if (this.$sel) {
- this.$sel.attr("tabindex", -1);
- }
- that.$btn.attr("tabindex", 0);
- if (document.activeElement !== null && document.activeElement !== document &&
- document.activeElement !== document.body) {
- that.$btn.focus();
- }
- });
- this.isOpen = false;
- }
-
- if (wasClose) {
- if (this.$sel) {
- this.$sel.attr("tabindex", -1);
- }
- if (document.activeElement !== null && document.activeElement !== document &&
- document.activeElement !== document.body) {
- var $nw = this.listEnter();
- $nw.attr("tabindex", 0);
- $nw.focus();
- this.$sel = $nw;
- }
- }
- },
- btnOpenHandler : function() {
- this.dialogToggle(300);
- },
- focusoutHandler : function() {
- var list = this.$list;
- var that = this;
- setTimeout(function() {
- if (list.find(":focus").length === 0) {
- that.dialogToggle(200);
- }
- }, 200);
- },
- mouseoutHandler : function() {
- this.dialogToggle(200);
- },
- btnKeyFilter : function(e) {
- if (e.ctrlKey || e.shiftKey) {
- return false;
- }
- if (e.key === " " || e.key === "Enter" || (e.key === "ArrowDown" && e.altKey) ||
- e.key === "ArrowDown" || e.key === "ArrowUp") {
- return true;
- }
- return false;
- },
- keyMove : function(e) {
- if (e.ctrlKey || e.shiftKey) {
- return true;
- }
- var p = true;
- var $nw = $(e.target);
- switch (e.key) {
- case "ArrowUp":
- $nw = this.listPrev($nw);
- break;
- case "ArrowDown":
- $nw = this.listNext($nw);
- break;
- case "Home":
- $nw = this.listFirst();
- break;
- case "End":
- $nw = this.listLast();
- break;
- case "Escape":
- $nw = this.listExit();
- break;
- case "ArrowLeft":
- $nw = this.listExit();
- break;
- case "ArrowRight":
- $nw = this.listExit();
- break;
- default:
- p = false;
- }
- if (p) {
- $nw.attr("tabindex", 0);
- $nw.focus();
- if (this.$sel) {
- this.$sel.attr("tabindex", -1);
- }
- this.$sel = $nw;
- e.preventDefault();
- e.stopPropagation();
- }
- },
- listPrev : function($nw) {
- if ($nw.parent().prev().length !== 0) {
- return $nw.parent().prev().children(":first-child");
- }
- else {
- return this.listLast();
- }
- },
- listNext : function($nw) {
- if ($nw.parent().next().length !== 0) {
- return $nw.parent().next().children(":first-child");
- }
- else {
- return this.listFirst();
- }
- },
- listFirst : function() {
- return this.$list.children(":first-child").children(":first-child");
- },
- listLast : function() {
- return this.$list.children(":last-child").children(":first-child");
- },
- listExit : function() {
- this.mouseoutHandler();
- return this.$btn;
- },
- listEnter : function() {
- return this.$list.children(":first-child").children(":first-child");
+ });
+ }
+ warnOld(release, all_versions)
+ {
+ // Note this is effectively disabled now, two issues must fixed:
+ // * versions.js does not contain a current entry, because that leads to
+ // duplicate version numbers in the menu. These need to be deduplicated.
+ // * It only shows the warning after opening the menu to switch version
+ // when versions.js is loaded. This is too late to be useful.
+ let current = all_versions.current
+ if (!current) {
+ // console.log("Version Switch Error: no 'current' in version.json.");
+ return;
}
- };
- return Popover
-}();
+ const m = current.match(/\d\.\d+/g);
+ if (m) {
+ current = parseFloat(m[0]);
+ }
+ if (release < current) {
+ const currentURL = window.location.pathname.replace(release, current);
+ const warning =
+ document.querySelector("template#version-warning").firstElementChild.cloneNode(true);
+ const link = warning.querySelector('a');
+ link.setAttribute('href', currentURL);
+ link.textContent = current;
-$(document).ready(function() {
- var lng_popover = new Popover("version-popover");
-});
+ let body = document.querySelector("div.body");
+ if (!body.length) {
+ body = document.querySelector("div.document");
+ }
+ body.prepend(warning);
+ }
+ }
+ buildList(v)
+ {
+ const url = new URL(window.location.href);
+ let pathSplit = [ "", "api", v ];
+ if (url.pathname.startsWith("/api/")) {
+ pathSplit.push(url.pathname.split('/').slice(4).join('/'));
+ }
+ else {
+ pathSplit.push(url.pathname.substring(1));
+ }
+ let dyn, cur;
+ if (this.type) {
+ dyn = all_versions;
+ cur = v;
+ }
+ const that = this;
+ const template = document.querySelector("template#version-entry").content;
+ for (let [ix, title] of Object.entries(dyn)) {
+ let clone;
+ if (ix === cur) {
+ clone = template.querySelector("li.selected").cloneNode(true);
+ clone.querySelector("span").innerHTML = title;
+ }
+ else {
+ pathSplit[1 + that.type] = ix;
+ let href = new URL(url);
+ href.pathname = pathSplit.join('/');
+ clone = template.firstElementChild.cloneNode(true);
+ const link = clone.querySelector("a");
+ link.href = href;
+ link.innerHTML = title;
+ }
+ that.list.append(clone);
+ };
+ return this.list;
+ }
+ getNamed(v)
+ {
+ for (let [ix, title] of Object.entries(all_versions)) {
+ if (ix === "master" || ix === "main" || ix === "latest") {
+ const m = title.match(/\d\.\d[\w\d\.]*/)[0];
+ if (parseFloat(m) == v) {
+ v = ix;
+ return false;
+ }
+ }
+ };
+ return v;
+ }
+ dialogToggle(speed)
+ {
+ const wasClose = !this.isOpen;
+ const that = this;
+ if (!this.isOpen) {
+ this.btn.classList.add("version-btn-open");
+ this.btn.setAttribute("aria-pressed", true);
+ this.dialog.setAttribute("aria-hidden", false);
+ this.dialog.style.display = "block";
+ this.dialog.animate({opacity : [ 0, 1 ], easing : [ 'ease-in', 'ease-out' ]}, speed)
+ .finished.then(() => {
+ this.focusoutHandlerPrime = function(e) {
+ that.focusoutHandler();
+ e.stopImmediatePropagation();
+ };
+ this.mouseoutHandlerPrime = function(e) {
+ that.mouseoutHandler();
+ e.stopImmediatePropagation();
+ };
+ this.btn.parentNode.addEventListener("focusout", this.focusoutHandlerPrime);
+ this.btn.parentNode.addEventListener("mouseleave", this.mouseoutHandlerPrime);
+ });
+ this.isOpen = true;
+ }
+ else {
+ this.btn.classList.remove("version-btn-open");
+ this.btn.setAttribute("aria-pressed", false);
+ this.dialog.setAttribute("aria-hidden", true);
+ this.btn.parentNode.removeEventListener("focusout", this.focusoutHandlerPrime);
+ this.btn.parentNode.removeEventListener("mouseleave", this.mouseoutHandlerPrime);
+ this.dialog.animate({opacity : [ 1, 0 ], easing : [ 'ease-in', 'ease-out' ]}, speed)
+ .finished.then(() => {
+ this.dialog.style.display = "none";
+ if (this.sel) {
+ this.sel.setAttribute("tabindex", -1);
+ }
+ this.btn.setAttribute("tabindex", 0);
+ if (document.activeElement !== null && document.activeElement !== document &&
+ document.activeElement !== document.body)
+ {
+ this.btn.focus();
+ }
+ });
+ this.isOpen = false;
+ }
+
+ if (wasClose) {
+ if (this.sel) {
+ this.sel.setAttribute("tabindex", -1);
+ }
+ if (document.activeElement !== null && document.activeElement !== document &&
+ document.activeElement !== document.body)
+ {
+ const nw = this.listEnter();
+ nw.setAttribute("tabindex", 0);
+ nw.focus();
+ this.sel = nw;
+ }
+ }
+ }
+ btnOpenHandler()
+ {
+ this.dialogToggle(300);
+ }
+ focusoutHandler()
+ {
+ const list = this.list;
+ const that = this;
+ setTimeout(function() {
+ if (!list.querySelector(":focus")) {
+ that.dialogToggle(200);
+ }
+ }, 200);
+ }
+ mouseoutHandler()
+ {
+ this.dialogToggle(200);
+ }
+ btnKeyFilter(e)
+ {
+ if (e.ctrlKey || e.shiftKey) {
+ return false;
+ }
+ if (e.key === " " || e.key === "Enter" || (e.key === "ArrowDown" && e.altKey) ||
+ e.key === "ArrowDown" || e.key === "ArrowUp")
+ {
+ return true;
+ }
+ return false;
+ }
+ keyMove(e)
+ {
+ if (e.ctrlKey || e.shiftKey) {
+ return true;
+ }
+ let nw = e.target;
+ switch (e.key) {
+ case "ArrowUp":
+ nw = this.listPrev(nw);
+ break;
+ case "ArrowDown":
+ nw = this.listNext(nw);
+ break;
+ case "Home":
+ nw = this.listFirst();
+ break;
+ case "End":
+ nw = this.listLast();
+ break;
+ case "Escape":
+ nw = this.listExit();
+ break;
+ case "ArrowLeft":
+ nw = this.listExit();
+ break;
+ case "ArrowRight":
+ nw = this.listExit();
+ break;
+ default:
+ return false;
+ }
+ nw.setAttribute("tabindex", 0);
+ nw.focus();
+ if (this.sel) {
+ this.sel.setAttribute("tabindex", -1);
+ }
+ this.sel = nw;
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ listPrev(nw)
+ {
+ if (nw.parentNode.previousElementSibling.length !== 0) {
+ return nw.parentNode.previousElementSibling.firstElementChild;
+ }
+ else {
+ return this.listLast();
+ }
+ }
+ listNext(nw)
+ {
+ if (nw.parentNode.nextElementSibling.length !== 0) {
+ return nw.parentNode.nextElementSibling.firstElementChild;
+ }
+ else {
+ return this.listFirst();
+ }
+ }
+ listFirst()
+ {
+ return this.list.firstElementChild.firstElementChild;
+ }
+ listLast()
+ {
+ return this.list.lastElementChild.firstElementChild;
+ }
+ listExit()
+ {
+ this.mouseoutHandler();
+ return this.btn;
+ }
+ listEnter()
+ {
+ return this.list.firstElementChild.firstElementChild;
+ }
+}
+
+document.addEventListener('DOMContentLoaded', () => { new Popover("version-popover"); });
})();
diff --git a/doc/python_api/templates/sidebar/variant-selector.html b/doc/python_api/templates/sidebar/variant-selector.html
index 4eb56915f18..3bac5f95306 100644
--- a/doc/python_api/templates/sidebar/variant-selector.html
+++ b/doc/python_api/templates/sidebar/variant-selector.html
@@ -1,19 +1,21 @@
-
-
+
{{ release }}
+
+
+
+