theme.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. var jQuery = (typeof(window) != 'undefined') ? window.jQuery : require('jquery');
  2. // Sphinx theme nav state
  3. function ThemeNav () {
  4. var nav = {
  5. navBar: null,
  6. win: null,
  7. winScroll: false,
  8. winResize: false,
  9. linkScroll: false,
  10. winPosition: 0,
  11. winHeight: null,
  12. docHeight: null,
  13. isRunning: false
  14. };
  15. nav.enable = function (withStickyNav) {
  16. var self = this;
  17. // TODO this can likely be removed once the theme javascript is broken
  18. // out from the RTD assets. This just ensures old projects that are
  19. // calling `enable()` get the sticky menu on by default. All other cals
  20. // to `enable` should include an argument for enabling the sticky menu.
  21. if (typeof(withStickyNav) == 'undefined') {
  22. withStickyNav = true;
  23. }
  24. if (self.isRunning) {
  25. // Only allow enabling nav logic once
  26. return;
  27. }
  28. self.isRunning = true;
  29. jQuery(function ($) {
  30. self.init($);
  31. self.reset();
  32. self.win.on('hashchange', self.reset);
  33. if (withStickyNav) {
  34. // Set scroll monitor
  35. self.win.on('scroll', function () {
  36. if (!self.linkScroll) {
  37. if (!self.winScroll) {
  38. self.winScroll = true;
  39. requestAnimationFrame(function() { self.onScroll(); });
  40. }
  41. }
  42. });
  43. }
  44. // Set resize monitor
  45. self.win.on('resize', function () {
  46. if (!self.winResize) {
  47. self.winResize = true;
  48. requestAnimationFrame(function() { self.onResize(); });
  49. }
  50. });
  51. self.onResize();
  52. });
  53. };
  54. // TODO remove this with a split in theme and Read the Docs JS logic as
  55. // well, it's only here to support 0.3.0 installs of our theme.
  56. nav.enableSticky = function() {
  57. this.enable(true);
  58. };
  59. nav.init = function ($) {
  60. var doc = $(document),
  61. self = this;
  62. this.navBar = $('div.wy-side-scroll:first');
  63. this.win = $(window);
  64. // Set up javascript UX bits
  65. $(document)
  66. // Shift nav in mobile when clicking the menu.
  67. .on('click', "[data-toggle='wy-nav-top']", function() {
  68. $("[data-toggle='wy-nav-shift']").toggleClass("shift");
  69. $("[data-toggle='rst-versions']").toggleClass("shift");
  70. })
  71. // Nav menu link click operations
  72. .on('click', ".wy-menu-vertical .current ul li a", function() {
  73. var target = $(this);
  74. // Close menu when you click a link.
  75. $("[data-toggle='wy-nav-shift']").removeClass("shift");
  76. $("[data-toggle='rst-versions']").toggleClass("shift");
  77. // Handle dynamic display of l3 and l4 nav lists
  78. self.toggleCurrent(target);
  79. self.hashChange();
  80. })
  81. .on('click', "[data-toggle='rst-current-version']", function() {
  82. $("[data-toggle='rst-versions']").toggleClass("shift-up");
  83. })
  84. // Make tables responsive
  85. $("table.docutils:not(.field-list,.footnote,.citation)")
  86. .wrap("<div class='wy-table-responsive'></div>");
  87. // Add extra class to responsive tables that contain
  88. // footnotes or citations so that we can target them for styling
  89. $("table.docutils.footnote")
  90. .wrap("<div class='wy-table-responsive footnote'></div>");
  91. $("table.docutils.citation")
  92. .wrap("<div class='wy-table-responsive citation'></div>");
  93. // Add expand links to all parents of nested ul
  94. $('.wy-menu-vertical ul').not('.simple').siblings('a').each(function () {
  95. var link = $(this);
  96. expand = $('<span class="toctree-expand"></span>');
  97. expand.on('click', function (ev) {
  98. self.toggleCurrent(link);
  99. ev.stopPropagation();
  100. return false;
  101. });
  102. link.prepend(expand);
  103. });
  104. };
  105. nav.reset = function () {
  106. // Get anchor from URL and open up nested nav
  107. var anchor = encodeURI(window.location.hash) || '#';
  108. try {
  109. var vmenu = $('.wy-menu-vertical');
  110. var link = vmenu.find('[href="' + anchor + '"]');
  111. if (link.length === 0) {
  112. // this link was not found in the sidebar.
  113. // Find associated id element, then its closest section
  114. // in the document and try with that one.
  115. var id_elt = $('.document [id="' + anchor.substring(1) + '"]');
  116. var closest_section = id_elt.closest('div.section');
  117. link = vmenu.find('[href="#' + closest_section.attr("id") + '"]');
  118. if (link.length === 0) {
  119. // still not found in the sidebar. fall back to main section
  120. link = vmenu.find('[href="#"]');
  121. }
  122. }
  123. // If we found a matching link then reset current and re-apply
  124. // otherwise retain the existing match
  125. if (link.length > 0) {
  126. $('.wy-menu-vertical .current').removeClass('current');
  127. link.addClass('current');
  128. link.closest('li.toctree-l1').addClass('current');
  129. link.closest('li.toctree-l1').parent().addClass('current');
  130. link.closest('li.toctree-l1').addClass('current');
  131. link.closest('li.toctree-l2').addClass('current');
  132. link.closest('li.toctree-l3').addClass('current');
  133. link.closest('li.toctree-l4').addClass('current');
  134. link.closest('li.toctree-l5').addClass('current');
  135. link[0].scrollIntoView();
  136. }
  137. }
  138. catch (err) {
  139. console.log("Error expanding nav for anchor", err);
  140. }
  141. };
  142. nav.onScroll = function () {
  143. this.winScroll = false;
  144. var newWinPosition = this.win.scrollTop(),
  145. winBottom = newWinPosition + this.winHeight,
  146. navPosition = this.navBar.scrollTop(),
  147. newNavPosition = navPosition + (newWinPosition - this.winPosition);
  148. if (newWinPosition < 0 || winBottom > this.docHeight) {
  149. return;
  150. }
  151. this.navBar.scrollTop(newNavPosition);
  152. this.winPosition = newWinPosition;
  153. };
  154. nav.onResize = function () {
  155. this.winResize = false;
  156. this.winHeight = this.win.height();
  157. this.docHeight = $(document).height();
  158. };
  159. nav.hashChange = function () {
  160. this.linkScroll = true;
  161. this.win.one('hashchange', function () {
  162. this.linkScroll = false;
  163. });
  164. };
  165. nav.toggleCurrent = function (elem) {
  166. var parent_li = elem.closest('li');
  167. parent_li.siblings('li.current').removeClass('current');
  168. parent_li.siblings().find('li.current').removeClass('current');
  169. parent_li.find('> ul li.current').removeClass('current');
  170. parent_li.toggleClass('current');
  171. }
  172. return nav;
  173. };
  174. _ThemeNav = ThemeNav();
  175. if (typeof(window) != 'undefined') {
  176. window.SphinxRtdTheme = {
  177. Navigation: _ThemeNav,
  178. // TODO remove this once static assets are split up between the theme
  179. // and Read the Docs. For now, this patches 0.3.0 to be backwards
  180. // compatible with a pre-0.3.0 layout.html
  181. StickyNav: _ThemeNav,
  182. };
  183. }
  184. // requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
  185. // https://gist.github.com/paulirish/1579671
  186. // MIT license
  187. (function() {
  188. var lastTime = 0;
  189. var vendors = ['ms', 'moz', 'webkit', 'o'];
  190. for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
  191. window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
  192. window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
  193. || window[vendors[x]+'CancelRequestAnimationFrame'];
  194. }
  195. if (!window.requestAnimationFrame)
  196. window.requestAnimationFrame = function(callback, element) {
  197. var currTime = new Date().getTime();
  198. var timeToCall = Math.max(0, 16 - (currTime - lastTime));
  199. var id = window.setTimeout(function() { callback(currTime + timeToCall); },
  200. timeToCall);
  201. lastTime = currTime + timeToCall;
  202. return id;
  203. };
  204. if (!window.cancelAnimationFrame)
  205. window.cancelAnimationFrame = function(id) {
  206. clearTimeout(id);
  207. };
  208. }());