summaryrefslogtreecommitdiffstats
path: root/theme/javascript/core
diff options
context:
space:
mode:
authorSamy Pessé <samypesse@gmail.com>2015-01-20 11:29:26 +0100
committerSamy Pessé <samypesse@gmail.com>2015-01-20 11:29:26 +0100
commit238cd9f7f09f11ecdb1717a5d07fc33642d29043 (patch)
tree72eac94839bda4f32cb41441b2b88532eed52603 /theme/javascript/core
parentbac25cdf297a7fa54f21977aa17d455e439cdf51 (diff)
downloadgitbook-238cd9f7f09f11ecdb1717a5d07fc33642d29043.zip
gitbook-238cd9f7f09f11ecdb1717a5d07fc33642d29043.tar.gz
gitbook-238cd9f7f09f11ecdb1717a5d07fc33642d29043.tar.bz2
Add back theme
Diffstat (limited to 'theme/javascript/core')
-rw-r--r--theme/javascript/core/events.js7
-rw-r--r--theme/javascript/core/font-settings.js103
-rw-r--r--theme/javascript/core/glossary.js117
-rwxr-xr-xtheme/javascript/core/keyboard.js38
-rw-r--r--theme/javascript/core/loading.js16
-rwxr-xr-xtheme/javascript/core/navigation.js151
-rwxr-xr-xtheme/javascript/core/progress.js73
-rwxr-xr-xtheme/javascript/core/search.js114
-rwxr-xr-xtheme/javascript/core/sidebar.js56
-rwxr-xr-xtheme/javascript/core/state.js18
10 files changed, 693 insertions, 0 deletions
diff --git a/theme/javascript/core/events.js b/theme/javascript/core/events.js
new file mode 100644
index 0000000..855755f
--- /dev/null
+++ b/theme/javascript/core/events.js
@@ -0,0 +1,7 @@
+define([
+ "jQuery"
+], function($) {
+ var events = $({});
+
+ return events;
+}); \ No newline at end of file
diff --git a/theme/javascript/core/font-settings.js b/theme/javascript/core/font-settings.js
new file mode 100644
index 0000000..63177ee
--- /dev/null
+++ b/theme/javascript/core/font-settings.js
@@ -0,0 +1,103 @@
+define([
+ "jQuery",
+ "utils/storage"
+], function($, storage) {
+ var fontState;
+
+ var THEMES = {
+ "white": 0,
+ "sepia": 1,
+ "night": 2
+ };
+
+ var FAMILY = {
+ "serif": 0,
+ "sans": 1
+ };
+
+ var enlargeFontSize = function(e){
+ if (fontState.size < 4){
+ fontState.size++;
+ fontState.save();
+ }
+ };
+
+ var reduceFontSize = function(e){
+ if (fontState.size > 0){
+ fontState.size--;
+ fontState.save();
+ }
+ };
+
+ var changeFontFamily = function(){
+ var index = $(this).data("font");
+
+ fontState.family = index;
+ fontState.save();
+ };
+
+ var changeColorTheme = function(){
+ var $book = $(".book");
+ var index = $(this).data("theme");
+
+ if (fontState.theme !== 0)
+ $book.removeClass("color-theme-"+fontState.theme);
+
+ fontState.theme = index;
+ if (fontState.theme !== 0)
+ $book.addClass("color-theme-"+fontState.theme);
+
+ fontState.save();
+ };
+
+ var update = function() {
+ var $book = $(".book");
+
+ $(".font-settings .font-family-list li").removeClass("active");
+ $(".font-settings .font-family-list li:nth-child("+(fontState.family+1)+")").addClass("active");
+
+ $book[0].className = $book[0].className.replace(/\bfont-\S+/g, '');
+ $book.addClass("font-size-"+fontState.size);
+ $book.addClass("font-family-"+fontState.family);
+
+ if(fontState.theme !== 0) {
+ $book[0].className = $book[0].className.replace(/\bcolor-theme-\S+/g, '');
+ $book.addClass("color-theme-"+fontState.theme);
+ }
+ };
+
+ var init = function(config) {
+ var $toggle, $bookBody, $dropdown, $book;
+
+ //Find DOM elements.
+ $book = $(".book");
+ $toggle = $(".book-header .toggle-font-settings");
+ $dropdown = $("#font-settings-wrapper .dropdown-menu");
+ $bookBody = $(".book-body");
+
+ // Instantiate font state object
+ fontState = storage.get("fontState", {
+ size: config.size || 2,
+ family: FAMILY[config.family || "sans"],
+ theme: THEMES[config.theme || "white"]
+ });
+ fontState.save = function(){
+ storage.set("fontState",fontState);
+ update();
+ };
+
+ update();
+
+ //Add event listeners
+ $(document).on('click', "#enlarge-font-size", enlargeFontSize);
+ $(document).on('click', "#reduce-font-size", reduceFontSize);
+
+ $(document).on('click', "#font-settings-wrapper .font-family-list .button", changeFontFamily);
+ $(document).on('click', "#font-settings-wrapper .color-theme-list .button", changeColorTheme);
+ };
+
+ return {
+ init: init,
+ update: update
+ }
+}); \ No newline at end of file
diff --git a/theme/javascript/core/glossary.js b/theme/javascript/core/glossary.js
new file mode 100644
index 0000000..44ebab1
--- /dev/null
+++ b/theme/javascript/core/glossary.js
@@ -0,0 +1,117 @@
+define([
+ "jQuery",
+ "lodash",
+ "core/state"
+], function($, _, state) {
+ var index = null;
+
+ // Use a specific idnex
+ var useIndex = function(data) {
+ index = data;
+ };
+
+ // Load complete index
+ var loadIndex = function() {
+ return $.getJSON(state.basePath+"/glossary_index.json")
+ .then(useIndex);
+ };
+
+ // Get index and return a promise
+ var getIndex = function() {
+ var d = $.Deferred();
+
+ if (index) {
+ d.resolve(index);
+ } else {
+ loadIndex().done(function(){
+ d.resolve(index);
+ }).fail(d.reject);
+ }
+
+ return d.promise();
+ }
+
+ $.fn.replaceText = function( search, replace, text_only ) {
+ return this.each(function(){
+ var node = this.firstChild,
+ val,
+ new_val,
+
+ // Elements to be removed at the end.
+ remove = [];
+
+ // Only continue if firstChild exists.
+ if ( node ) {
+
+ // Loop over all childNodes.
+ do {
+
+ // Only process text nodes.
+ if ( node.nodeType === 3 ) {
+
+ // The original node value.
+ val = node.nodeValue;
+
+ // The new value.
+ new_val = val.replace( search, replace );
+
+ // Only replace text if the new value is actually different!
+ if ( new_val !== val ) {
+
+ if ( !text_only && /</.test( new_val ) ) {
+ // The new value contains HTML, set it in a slower but far more
+ // robust way.
+ $(node).before( new_val );
+
+ // Don't remove the node yet, or the loop will lose its place.
+ remove.push( node );
+ } else {
+ // The new value contains no HTML, so it can be set in this
+ // very fast, simple way.
+ node.nodeValue = new_val;
+ }
+ }
+ }
+
+ } while ( node = node.nextSibling );
+ }
+
+ // Time to remove those elements!
+ remove.length && $(remove).remove();
+ });
+ };
+
+ var pregQuote = function( str ) {
+ return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1");
+ };
+
+ var init = function() {
+ // Bind click on glossary item
+ $(document).on("click", ".book-body .page-wrapper .page-inner .glossary-term", function(e) {
+ e.preventDefault();
+
+ location.href = state.basePath+"/GLOSSARY.html#"+$(e.currentTarget).data("glossary-term");
+ });
+ };
+
+ var replaceTerm = function($el, term) {
+ var r = new RegExp( "\\b(" + pregQuote(term.name.toLowerCase()) + ")\\b" , 'gi' );
+
+ $el.find("*").replaceText(r, function(match) {
+ return "<span class='glossary-term' data-glossary-term='"+term.id+"' title='"+term.description+"'>"+match+"</span>";
+ });
+
+ };
+
+ var prepare = function() {
+ getIndex()
+ .done(function() {
+ _.each(index, _.partial(replaceTerm, $(".book-body .page-wrapper .page-inner")));
+ });
+ };
+
+ return {
+ init: init,
+ prepare: prepare
+ };
+}); \ No newline at end of file
diff --git a/theme/javascript/core/keyboard.js b/theme/javascript/core/keyboard.js
new file mode 100755
index 0000000..22fe953
--- /dev/null
+++ b/theme/javascript/core/keyboard.js
@@ -0,0 +1,38 @@
+define([
+ "jQuery",
+ "Mousetrap",
+ "core/navigation",
+ "core/sidebar",
+ "core/search"
+], function($, Mousetrap, navigation, sidebar, search){
+ // Bind keyboard shortcuts
+ var init = function() {
+ // Next
+ Mousetrap.bind(['right'], function(e) {
+ navigation.goNext();
+ return false;
+ });
+
+ // Prev
+ Mousetrap.bind(['left'], function(e) {
+ navigation.goPrev();
+ return false;
+ });
+
+ // Toggle Summary
+ Mousetrap.bind(['s'], function(e) {
+ sidebar.toggle();
+ return false;
+ });
+
+ // Toggle Search
+ Mousetrap.bind(['f'], function(e) {
+ search.toggle();
+ return false;
+ });
+ };
+
+ return {
+ init: init
+ };
+}); \ No newline at end of file
diff --git a/theme/javascript/core/loading.js b/theme/javascript/core/loading.js
new file mode 100644
index 0000000..1ddb213
--- /dev/null
+++ b/theme/javascript/core/loading.js
@@ -0,0 +1,16 @@
+define([
+ "jQuery"
+], function($) {
+ var showLoading = function(p) {
+ $(".book").addClass("is-loading");
+ p.always(function() {
+ $(".book").removeClass("is-loading");
+ });
+
+ return p;
+ };
+
+ return {
+ show: showLoading
+ };
+}); \ No newline at end of file
diff --git a/theme/javascript/core/navigation.js b/theme/javascript/core/navigation.js
new file mode 100755
index 0000000..f5d03df
--- /dev/null
+++ b/theme/javascript/core/navigation.js
@@ -0,0 +1,151 @@
+define([
+ "jQuery",
+ "utils/url",
+ "core/events",
+ "core/state",
+ "core/progress",
+ "core/loading",
+ "core/search",
+ "core/glossary"
+], function($, URL, events, state, progress, loading, search, glossary) {
+ var prev, next;
+
+ var usePushState = (typeof history.pushState !== "undefined");
+
+ var handleNavigation = function(relativeUrl, push) {
+ var url = URL.join(window.location.pathname, relativeUrl);
+ console.log("navigate to ", url, "baseurl="+relativeUrl, "current="+window.location.pathname);
+
+ if (!usePushState) {
+ // Refresh the page to the new URL if pushState not supported
+ location.href = relativeUrl;
+ return
+ }
+
+ return loading.show($.get(url)
+ .done(function (html) {
+ // Push url to history
+ if (push) history.pushState({ path: url }, null, url);
+
+ // Replace html content
+ html = html.replace( /<(\/?)(html|head|body)([^>]*)>/ig, function(a,b,c,d){
+ return '<' + b + 'div' + ( b ? '' : ' data-element="' + c + '"' ) + d + '>';
+ });
+
+ var $page = $(html);
+ var $pageHead = $page.find("[data-element=head]");
+ var $pageBody = $page.find('.book');
+
+ // Merge heads
+ var headContent = $pageHead.html()
+
+ $("head style").each(function() {
+ headContent = headContent + this.outerHTML
+ });
+ $("head").html(headContent);
+
+ // Merge body
+ var bodyClass = $(".book").attr("class");
+ var scrollPosition = $('.book-summary .summary').scrollTop();
+ $pageBody.toggleClass("with-summary", $(".book").hasClass("with-summary"))
+
+ $(".book").replaceWith($pageBody);
+ $(".book").attr("class", bodyClass);
+ $('.book-summary .summary').scrollTop(scrollPosition);
+
+ // Update state
+ state.update($("html"));
+ // recover search keyword
+ search.recover();
+ preparePage();
+ })
+ .fail(function (e) {
+ location.href = relativeUrl;
+ }));
+ };
+
+ var updateNavigationPosition = function() {
+ var bodyInnerWidth, pageWrapperWidth;
+
+ bodyInnerWidth = parseInt($('.body-inner').css('width'), 10);
+ pageWrapperWidth = parseInt($('.page-wrapper').css('width'), 10);
+ $('.navigation-next').css('margin-right', (bodyInnerWidth - pageWrapperWidth) + 'px');
+ };
+
+ var preparePage = function() {
+ var $pageWrapper = $(".book-body .page-wrapper");
+
+ // Show progress
+ progress.show();
+
+ // Update navigation position
+ updateNavigationPosition();
+
+ // Set glossary items
+ glossary.prepare();
+
+ // Reset scroll
+ $pageWrapper.scrollTop(0);
+
+ // Focus on content
+ $pageWrapper.focus();
+
+ // Notify
+ events.trigger("page.change");
+ };
+
+ var handlePagination = function (e) {
+ e.stopPropagation();
+ e.preventDefault();
+
+ var url = $(this).attr('href');
+ if (url) handleNavigation(url, true);
+ };
+
+ var goNext = function() {
+ var url = $(".navigation-next").attr("href");
+ if (url) handleNavigation(url, true);
+ };
+
+ var goPrev = function() {
+ var url = $(".navigation-prev").attr("href");
+ if (url) handleNavigation(url, true);
+ };
+
+
+
+ var init = function() {
+ // Prevent cache so that using the back button works
+ // See: http://stackoverflow.com/a/15805399/983070
+ $.ajaxSetup({
+ cache: false
+ });
+
+ // Recreate first page when the page loads.
+ history.replaceState({ path: window.location.href }, '');
+
+ // Back Button Hijacking :(
+ window.onpopstate = function (event) {
+ if (event.state === null) {
+ return;
+ }
+ return handleNavigation(event.state.path, false);
+ };
+
+ $(document).on('click', ".navigation-prev", handlePagination);
+ $(document).on('click', ".navigation-next", handlePagination);
+ $(document).on('click', ".summary [data-path] a", handlePagination);
+
+ $(window).resize(updateNavigationPosition);
+
+ // Prepare current page
+ preparePage();
+ };
+
+ return {
+ init: init,
+ goNext: goNext,
+ goPrev: goPrev
+ };
+});
+
diff --git a/theme/javascript/core/progress.js b/theme/javascript/core/progress.js
new file mode 100755
index 0000000..a409a11
--- /dev/null
+++ b/theme/javascript/core/progress.js
@@ -0,0 +1,73 @@
+define([
+ "lodash",
+ "jQuery",
+ "utils/storage",
+ "core/state"
+], function(_, $, storage, state) {
+ // Get current level
+ var getCurrentLevel = function() {
+ return state.level;
+ };
+
+ // Return all levels
+ var getLevels = function () {
+ var levels = $(".book-summary li[data-level]");
+
+ return _.map(levels, function(level) {
+ return $(level).data("level").toString();
+ });
+ };
+
+ // Return a map chapter -> number (timestamp)
+ var getProgress = function () {
+ // Current level
+ var progress = storage.get("progress", {});
+
+ // Levels
+ var levels = getLevels();
+
+ _.each(levels, function(level) {
+ progress[level] = progress[level] || 0;
+ });
+
+ return progress;
+ };
+
+ // Change value of progress for a level
+ var markProgress = function (level, state) {
+ var progress = getProgress();
+
+ if (state == null) {
+ state = true;
+ }
+
+ progress[level] = state
+ ? Date.now()
+ : 0;
+
+ storage.set("progress", progress);
+ };
+
+ // Show progress
+ var showProgress = function () {
+ var progress = getProgress();
+ var $summary = $(".book-summary");
+
+ _.each(progress, function (value, level) {
+ $summary.find("li[data-level='"+level+"']").toggleClass("done", value > 0);
+ });
+
+ // Mark current progress if we have not already
+ if (!progress[getCurrentLevel()]) {
+ markProgress(getCurrentLevel(), true);
+ }
+ };
+
+ return {
+ 'current': getCurrentLevel,
+ 'levels': getLevels,
+ 'get': getProgress,
+ 'mark': markProgress,
+ 'show': showProgress
+ };
+}); \ No newline at end of file
diff --git a/theme/javascript/core/search.js b/theme/javascript/core/search.js
new file mode 100755
index 0000000..4cbc3ed
--- /dev/null
+++ b/theme/javascript/core/search.js
@@ -0,0 +1,114 @@
+define([
+ "jQuery",
+ "lodash",
+ "lunr",
+ "utils/storage",
+ "core/state",
+ "core/sidebar"
+], function($, _, lunr, storage, state, sidebar) {
+ var index = null;
+
+ // Use a specific idnex
+ var useIndex = function(data) {
+ index = lunr.Index.load(data);
+ };
+
+ // Load complete index
+ var loadIndex = function() {
+ $.getJSON(state.basePath+"/search_index.json")
+ .then(useIndex);
+ };
+
+ // Search for a term
+ var search = function(q) {
+ if (!index) return;
+ var results = _.chain(index.search(q))
+ .map(function(result) {
+ var parts = result.ref.split("#")
+ return {
+ path: parts[0],
+ hash: parts[1]
+ }
+ })
+ .value();
+
+ return results;
+ };
+
+ // Toggle search bar
+ var toggleSearch = function(_state) {
+ if (state != null && isSearchOpen() == _state) return;
+
+ var $searchInput = $(".book-search input");
+ state.$book.toggleClass("with-search", _state);
+
+ // If search bar is open: focus input
+ if (isSearchOpen()) {
+ sidebar.toggle(true);
+ $searchInput.focus();
+ } else {
+ $searchInput.blur();
+ $searchInput.val("");
+ sidebar.filter(null);
+ }
+ };
+
+ // Return true if search bar is open
+ var isSearchOpen = function() {
+ return state.$book.hasClass("with-search");
+ };
+
+
+ var init = function() {
+ loadIndex();
+
+ // Toggle search
+ $(document).on("click", ".book-header .toggle-search", function(e) {
+ e.preventDefault();
+ toggleSearch();
+ });
+
+
+ // Type in search bar
+ $(document).on("keyup", ".book-search input", function(e) {
+ var key = (e.keyCode ? e.keyCode : e.which);
+ var q = $(this).val();
+
+ if (key == 27) {
+ e.preventDefault();
+ toggleSearch(false);
+ return;
+ }
+ if (q.length == 0) {
+ sidebar.filter(null);
+ storage.remove("keyword");
+ } else {
+ var results = search(q);
+ sidebar.filter(
+ _.pluck(results, "path")
+ );
+ storage.set("keyword", q);
+ }
+ })
+
+ };
+
+ // filter sidebar menu with current search keyword
+ var recoverSearch = function() {
+ var keyword = storage.get("keyword", "");
+ if(keyword.length > 0) {
+ if(!isSearchOpen()){
+ toggleSearch();
+ }
+ sidebar.filter(_.pluck(search(keyword), "path"));
+ }
+ $(".book-search input").val(keyword);
+ };
+
+ return {
+ init: init,
+ search: search,
+ toggle: toggleSearch,
+ recover:recoverSearch
+ };
+});
diff --git a/theme/javascript/core/sidebar.js b/theme/javascript/core/sidebar.js
new file mode 100755
index 0000000..828dc73
--- /dev/null
+++ b/theme/javascript/core/sidebar.js
@@ -0,0 +1,56 @@
+define([
+ "jQuery",
+ "lodash",
+ "utils/storage",
+ "utils/platform",
+ "core/state"
+], function($, _, storage, platform, state) {
+ // Toggle sidebar with or withour animation
+ var toggleSidebar = function(_state, animation) {
+ if (state != null && isOpen() == _state) return;
+ if (animation == null) animation = true;
+
+ state.$book.toggleClass("without-animation", !animation);
+ state.$book.toggleClass("with-summary", _state);
+
+ storage.set("sidebar", isOpen());
+ };
+
+ // Return true if sidebar is open
+ var isOpen = function() {
+ return state.$book.hasClass("with-summary");
+ };
+
+ // Prepare sidebar: state and toggle button
+ var init = function() {
+ // Toggle summary
+ $(document).on("click", ".book-header .toggle-summary", function(e) {
+ e.preventDefault();
+ toggleSidebar();
+ });
+
+ // Init last state if not mobile
+ if (!platform.isMobile) {
+ toggleSidebar(storage.get("sidebar", true), false);
+ }
+ };
+
+ // Filter summary with a list of path
+ var filterSummary = function(paths) {
+ var $summary = $(".book-summary");
+
+ $summary.find("li").each(function() {
+ var path = $(this).data("path");
+ var st = paths == null || _.contains(paths, path);
+
+ $(this).toggle(st);
+ if (st) $(this).parents("li").show();
+ });
+ };
+
+ return {
+ init: init,
+ toggle: toggleSidebar,
+ filter: filterSummary
+ }
+}); \ No newline at end of file
diff --git a/theme/javascript/core/state.js b/theme/javascript/core/state.js
new file mode 100755
index 0000000..de5c65c
--- /dev/null
+++ b/theme/javascript/core/state.js
@@ -0,0 +1,18 @@
+define([
+ "jQuery"
+], function() {
+ var state = {};
+
+ state.update = function(dom) {
+ var $book = $(dom.find(".book"));
+
+ state.$book = $book;
+ state.level = $book.data("level");
+ state.basePath = $book.data("basepath");
+ state.revision = $book.data("revision");
+ };
+
+ state.update($);
+
+ return state;
+}); \ No newline at end of file