From f0863fa0eb7520263222c0f7797f2d266e04fc23 Mon Sep 17 00:00:00 2001 From: Anthony Johnson Date: Wed, 9 Sep 2015 00:17:10 -0700 Subject: [PATCH] Refactor js to exportable module --- Gruntfile.js | 33 +++- bower.json | 2 +- js/theme.js | 150 +++++++++++++++ package.json | 10 +- sphinx_rtd_theme/static/js/theme.js | 280 +++++++++++++++------------- 5 files changed, 339 insertions(+), 136 deletions(-) create mode 100644 js/theme.js diff --git a/Gruntfile.js b/Gruntfile.js index 8b2a2071..50092d0b 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -57,6 +57,29 @@ module.exports = function(grunt) { } }, + browserify: { + dev: { + options: { + external: ['jquery'], + alias: { + 'sphinx-rtd-theme': './js/theme.js' + } + }, + src: ['js/*.js'], + dest: 'sphinx_rtd_theme/static/js/theme.js' + }, + build: { + options: { + external: ['jquery'], + alias: { + 'sphinx-rtd-theme': './js/theme.js' + } + }, + src: ['js/*.js'], + dest: 'sphinx_rtd_theme/static/js/theme.js' + } + }, + exec: { bower_update: { cmd: 'bower update' @@ -81,6 +104,11 @@ module.exports = function(grunt) { files: ['sphinx_rtd_theme/**/*', 'demo_docs/**/*.rst', 'demo_docs/**/*.py'], tasks: ['clean:build','exec:build_sphinx'] }, + /* JavaScript */ + browserify: { + files: ['js/*.js'], + tasks: ['browserify:dev'] + }, /* live-reload the demo_docs if sphinx re-builds */ livereload: { files: ['demo_docs/build/**/*'], @@ -97,9 +125,10 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-open'); + grunt.loadNpmTasks('grunt-browserify'); grunt.registerTask('fonts', ['clean:fonts','copy:fonts']); - grunt.registerTask('default', ['exec:bower_update','clean:build','sass:dev','exec:build_sphinx','connect','open','watch']); - grunt.registerTask('build', ['exec:bower_update','clean:build','sass:build','exec:build_sphinx']); + grunt.registerTask('default', ['exec:bower_update','clean:build','sass:dev','browserify:dev','exec:build_sphinx','connect','open','watch']); + grunt.registerTask('build', ['exec:bower_update','clean:build','sass:build','browserify:build','exec:build_sphinx']); } diff --git a/bower.json b/bower.json index 1286d485..18b957d8 100644 --- a/bower.json +++ b/bower.json @@ -8,7 +8,7 @@ "description": "Sphinx theme for readthedocs.org.", "license": "MIT", "main": [ - "dist/**" + "js/theme.js" ], "ignore": [ "docs", diff --git a/js/theme.js b/js/theme.js new file mode 100644 index 00000000..b1f0c1b9 --- /dev/null +++ b/js/theme.js @@ -0,0 +1,150 @@ +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: null + }; + + nav.enable = function () { + var self = this; + + jQuery(function ($) { + self.init($); + + self.reset(); + self.win.on('hashchange', self.reset); + + // Set scroll monitor + self.win.on('scroll', function () { + if (!self.linkScroll) { + self.winScroll = true; + } + }); + setInterval(function () { if (self.winScroll) self.onScroll(); }, 25); + + // Set resize monitor + self.win.on('resize', function () { + self.winResize = true; + }); + setInterval(function () { if (self.winResize) self.onResize(); }, 25); + self.onResize(); + }); + }; + + 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)") + .wrap("
"); + + // Add expand links to all parents of nested ul + $('.wy-menu-vertical ul').not('.simple').siblings('a').each(function () { + var link = $(this); + expand = $(''); + 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); + if (anchor) { + try { + var link = $('.wy-menu-vertical') + .find('[href="' + anchor + '"]'); + $('.wy-menu-vertical li.toctree-l1 li.current') + .removeClass('current'); + link.closest('li.toctree-l2').addClass('current'); + link.closest('li.toctree-l3').addClass('current'); + link.closest('li.toctree-l4').addClass('current'); + } + 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'); + parent_li.siblings().find('li.current').removeClass('current'); + parent_li.find('> ul li.current').removeClass('current'); + parent_li.toggleClass('current'); + } + + return nav; +}; + +module.exports.ThemeNav = ThemeNav(); + +if (typeof(window) != 'undefined') { + window.SphinxRtdTheme = { StickyNav: module.exports.ThemeNav }; +} diff --git a/package.json b/package.json index d5bbf203..c765d1e9 100644 --- a/package.json +++ b/package.json @@ -4,13 +4,15 @@ "private": true, "dependencies": {}, "devDependencies": { + "browserify": "^11.0.0", + "connect-livereload": "~0.3.0", "grunt": "~0.4.1", + "grunt-browserify": "^3.8.0", + "grunt-contrib-clean": "0.5.0", + "grunt-contrib-connect": "0.5.0", + "grunt-contrib-copy": "0.5.0", "grunt-contrib-sass": "~0.7.2", "grunt-contrib-watch": "~0.4.3", - "grunt-contrib-connect": "0.5.0", - "grunt-contrib-clean": "0.5.0", - "grunt-contrib-copy": "0.5.0", - "connect-livereload": "~0.3.0", "grunt-exec": "~0.4.2", "grunt-open": "0.2.2", "matchdep": "~0.1.2" diff --git a/sphinx_rtd_theme/static/js/theme.js b/sphinx_rtd_theme/static/js/theme.js index ac49f457..48a9f06b 100644 --- a/sphinx_rtd_theme/static/js/theme.js +++ b/sphinx_rtd_theme/static/js/theme.js @@ -1,131 +1,153 @@ -function toggleCurrent (elem) { - var parent_li = elem.closest('li'); - parent_li.siblings('li.current').removeClass('current'); - parent_li.siblings().find('li.current').removeClass('current'); - parent_li.find('> ul li.current').removeClass('current'); - parent_li.toggleClass('current'); +require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o"); + + // Add expand links to all parents of nested ul + $('.wy-menu-vertical ul').not('.simple').siblings('a').each(function () { + var link = $(this); + expand = $(''); + 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); + if (anchor) { + try { + var link = $('.wy-menu-vertical') + .find('[href="' + anchor + '"]'); + $('.wy-menu-vertical li.toctree-l1 li.current') + .removeClass('current'); + link.closest('li.toctree-l2').addClass('current'); + link.closest('li.toctree-l3').addClass('current'); + link.closest('li.toctree-l4').addClass('current'); + } + 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'); + parent_li.siblings().find('li.current').removeClass('current'); + parent_li.find('> ul li.current').removeClass('current'); + parent_li.toggleClass('current'); + } + + return nav; +}; + +module.exports.ThemeNav = ThemeNav(); + +if (typeof(window) != 'undefined') { + window.SphinxRtdTheme = { StickyNav: module.exports.ThemeNav }; } -$(document).ready(function() { - // Shift nav in mobile when clicking the menu. - $(document).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 - $(document).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 - toggleCurrent(target); - if (typeof(window.SphinxRtdTheme) != 'undefined') { - window.SphinxRtdTheme.StickyNav.hashChange(); - } - }); - $(document).on('click', "[data-toggle='rst-current-version']", function() { - $("[data-toggle='rst-versions']").toggleClass("shift-up"); - }); - // Make tables responsive - $("table.docutils:not(.field-list)").wrap("
"); - - // Add expand links to all parents of nested ul - $('.wy-menu-vertical ul').not('.simple').siblings('a').each(function () { - var link = $(this); - expand = $(''); - expand.on('click', function (ev) { - toggleCurrent(link); - ev.stopPropagation(); - return false; - }); - link.prepend(expand); - }); -}); - -// Sphinx theme state -window.SphinxRtdTheme = (function (jquery) { - var stickyNav = (function () { - var navBar, - win, - winScroll = false, - winResize = false, - linkScroll = false, - winPosition = 0, - winHeight, - docHeight, - enable = function () { - init(); - reset(); - win.on('hashchange', reset); - - // Set scroll monitor - win.on('scroll', function () { - if (!linkScroll) { - winScroll = true; - } - }); - setInterval(function () { if (winScroll) scroll(); }, 25); - - // Set resize monitor - win.on('resize', function () { - winResize = true; - }); - setInterval(function () { if (winResize) resize(); }, 25); - resize(); - }, - init = function () { - navBar = jquery('div.wy-side-scroll:first'); - win = jquery(window); - }, - reset = function () { - // Get anchor from URL and open up nested nav - var anchor = encodeURI(window.location.hash); - if (anchor) { - try { - var link = $('.wy-menu-vertical') - .find('[href="' + anchor + '"]'); - $('.wy-menu-vertical li.toctree-l1 li.current') - .removeClass('current'); - link.closest('li.toctree-l2').addClass('current'); - link.closest('li.toctree-l3').addClass('current'); - link.closest('li.toctree-l4').addClass('current'); - } - catch (err) { - console.log("Error expanding nav for anchor", err); - } - } - }, - scroll = function () { - winScroll = false; - var newWinPosition = win.scrollTop(), - winBottom = newWinPosition + winHeight, - navPosition = navBar.scrollTop(), - newNavPosition = navPosition + (newWinPosition - winPosition); - if (newWinPosition < 0 || winBottom > docHeight) { - return; - } - navBar.scrollTop(newNavPosition); - winPosition = newWinPosition; - }, - resize = function () { - winResize = false; - winHeight = win.height(); - docHeight = $(document).height(); - }, - hashChange = function () { - linkScroll = true; - win.one('hashchange', function () { - linkScroll = false; - }); - }; - jquery(init); - return { - enable: enable, - hashChange: hashChange - }; - }()); - return { - StickyNav: stickyNav - }; -}($)); +},{"jquery":"jquery"}]},{},["sphinx-rtd-theme"]);