summaryrefslogtreecommitdiffstats
path: root/src/theme.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/theme.js266
1 files changed, 266 insertions, 0 deletions
diff --git a/src/theme.js b/src/theme.js
new file mode 100644
index 0000000..db546b4
--- /dev/null
+++ b/src/theme.js
@@ -0,0 +1,266 @@
+// jQuery is included via the sphinxcontrib-jquery extension that we depend on
+var jQuery = (typeof(window) != 'undefined') ? window.jQuery : require('jquery');
+
+// Sphinx theme nav state
+function ThemeNav () {
+
+ var nav = {
+ navBar: null,
+ win: null,
+ winScroll: false,
+ winResize: false,
+ linkScroll: false,
+ winPosition: 0,
+ winHeight: null,
+ docHeight: null,
+ isRunning: false
+ };
+
+ nav.enable = function (withStickyNav) {
+ var self = this;
+
+ // TODO this can likely be removed once the theme javascript is broken
+ // out from the RTD assets. This just ensures old projects that are
+ // calling `enable()` get the sticky menu on by default. All other cals
+ // to `enable` should include an argument for enabling the sticky menu.
+ if (typeof(withStickyNav) == 'undefined') {
+ withStickyNav = true;
+ }
+
+ if (self.isRunning) {
+ // Only allow enabling nav logic once
+ return;
+ }
+
+ self.isRunning = true;
+ jQuery(function ($) {
+ self.init($);
+
+ self.reset();
+ self.win.on('hashchange', self.reset);
+
+ if (withStickyNav) {
+ // Set scroll monitor
+ self.win.on('scroll', function () {
+ if (!self.linkScroll) {
+ if (!self.winScroll) {
+ self.winScroll = true;
+ requestAnimationFrame(function() { self.onScroll(); });
+ }
+ }
+ });
+ }
+
+ // Set resize monitor
+ self.win.on('resize', function () {
+ if (!self.winResize) {
+ self.winResize = true;
+ requestAnimationFrame(function() { self.onResize(); });
+ }
+ });
+
+ self.onResize();
+ });
+
+ };
+
+ // TODO remove this with a split in theme and Read the Docs JS logic as
+ // well, it's only here to support 0.3.0 installs of our theme.
+ nav.enableSticky = function() {
+ this.enable(true);
+ };
+
+ nav.init = function ($) {
+ var doc = $(document),
+ self = this;
+
+ this.navBar = $('div.wy-side-scroll:first');
+ this.win = $(window);
+
+ // Set up javascript UX bits
+ $(document)
+ // Shift nav in mobile when clicking the menu.
+ .on('click', "[data-toggle='wy-nav-top']", function() {
+ $("[data-toggle='wy-nav-shift']").toggleClass("shift");
+ $("[data-toggle='rst-versions']").toggleClass("shift");
+ })
+
+ // Nav menu link click operations
+ .on('click', ".wy-menu-vertical .current ul li a", function() {
+ var target = $(this);
+ // Close menu when you click a link.
+ $("[data-toggle='wy-nav-shift']").removeClass("shift");
+ $("[data-toggle='rst-versions']").toggleClass("shift");
+ // Handle dynamic display of l3 and l4 nav lists
+ self.toggleCurrent(target);
+ self.hashChange();
+ })
+ .on('click', "[data-toggle='rst-current-version']", function() {
+ $("[data-toggle='rst-versions']").toggleClass("shift-up");
+ })
+
+ // Make tables responsive
+ $("table.docutils:not(.field-list,.footnote,.citation)")
+ .wrap("<div class='wy-table-responsive'></div>");
+
+ // Add extra class to responsive tables that contain
+ // footnotes or citations so that we can target them for styling
+ $("table.docutils.footnote")
+ .wrap("<div class='wy-table-responsive footnote'></div>");
+ $("table.docutils.citation")
+ .wrap("<div class='wy-table-responsive citation'></div>");
+
+ // Add expand links to all parents of nested ul
+ $('.wy-menu-vertical ul').not('.simple').siblings('a').each(function () {
+ var link = $(this);
+ expand =
+ $('<button class="toctree-expand" title="Open/close menu"></button>');
+ expand.on('click', function (ev) {
+ self.toggleCurrent(link);
+ ev.stopPropagation();
+ return false;
+ });
+ link.prepend(expand);
+ });
+ };
+
+ nav.reset = function () {
+ // Get anchor from URL and open up nested nav
+ var anchor = encodeURI(window.location.hash) || '#';
+
+ try {
+ var vmenu = $('.wy-menu-vertical');
+ var link = vmenu.find('[href="' + anchor + '"]');
+ if (link.length === 0) {
+ // this link was not found in the sidebar.
+ // Find associated id element, then its closest section
+ // in the document and try with that one.
+ var id_elt = $('.document [id="' + anchor.substring(1) + '"]');
+ var closest_section = id_elt.closest('div.section');
+ link = vmenu.find('[href="#' + closest_section.attr("id") + '"]');
+ if (link.length === 0) {
+ // still not found in the sidebar. fall back to main section
+ link = vmenu.find('[href="#"]');
+ }
+ }
+ // If we found a matching link then reset current and re-apply
+ // otherwise retain the existing match
+ if (link.length > 0) {
+ $('.wy-menu-vertical .current')
+ .removeClass('current')
+ .attr('aria-expanded','false');
+ link.addClass('current')
+ .attr('aria-expanded','true');
+ link.closest('li.toctree-l1')
+ .parent()
+ .addClass('current')
+ .attr('aria-expanded','true');
+ for (let i = 1; i <= 10; i++) {
+ link.closest('li.toctree-l' + i)
+ .addClass('current')
+ .attr('aria-expanded','true');
+ }
+ link[0].scrollIntoView();
+ }
+ }
+ catch (err) {
+ console.log("Error expanding nav for anchor", err);
+ }
+
+ };
+
+ nav.onScroll = function () {
+ this.winScroll = false;
+ var newWinPosition = this.win.scrollTop(),
+ winBottom = newWinPosition + this.winHeight,
+ navPosition = this.navBar.scrollTop(),
+ newNavPosition = navPosition + (newWinPosition - this.winPosition);
+ if (newWinPosition < 0 || winBottom > this.docHeight) {
+ return;
+ }
+ this.navBar.scrollTop(newNavPosition);
+ this.winPosition = newWinPosition;
+ };
+
+ nav.onResize = function () {
+ this.winResize = false;
+ this.winHeight = this.win.height();
+ this.docHeight = $(document).height();
+ };
+
+ nav.hashChange = function () {
+ this.linkScroll = true;
+ this.win.one('hashchange', function () {
+ this.linkScroll = false;
+ });
+ };
+
+ nav.toggleCurrent = function (elem) {
+ var parent_li = elem.closest('li');
+ parent_li
+ .siblings('li.current')
+ .removeClass('current')
+ .attr('aria-expanded','false');
+ parent_li
+ .siblings()
+ .find('li.current')
+ .removeClass('current')
+ .attr('aria-expanded','false');
+ var children = parent_li.find('> ul li');
+ // Don't toggle terminal elements.
+ if (children.length) {
+ children
+ .removeClass('current')
+ .attr('aria-expanded','false');
+ parent_li
+ .toggleClass('current')
+ .attr('aria-expanded', function(i, old) {
+ return old == 'true' ? 'false' : 'true';
+ });
+ }
+ }
+
+ return nav;
+};
+
+module.exports.ThemeNav = ThemeNav();
+
+if (typeof(window) != 'undefined') {
+ window.SphinxRtdTheme = {
+ Navigation: module.exports.ThemeNav,
+ // TODO remove this once static assets are split up between the theme
+ // and Read the Docs. For now, this patches 0.3.0 to be backwards
+ // compatible with a pre-0.3.0 layout.html
+ StickyNav: module.exports.ThemeNav,
+ };
+}
+
+
+// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
+// https://gist.github.com/paulirish/1579671
+// MIT license
+
+(function() {
+ var lastTime = 0;
+ var vendors = ['ms', 'moz', 'webkit', 'o'];
+ for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
+ window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
+ window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
+ || window[vendors[x]+'CancelRequestAnimationFrame'];
+ }
+
+ if (!window.requestAnimationFrame)
+ window.requestAnimationFrame = function(callback, element) {
+ var currTime = new Date().getTime();
+ var timeToCall = Math.max(0, 16 - (currTime - lastTime));
+ var id = window.setTimeout(function() { callback(currTime + timeToCall); },
+ timeToCall);
+ lastTime = currTime + timeToCall;
+ return id;
+ };
+
+ if (!window.cancelAnimationFrame)
+ window.cancelAnimationFrame = function(id) {
+ clearTimeout(id);
+ };
+}());