diff options
author | Samy Pessé <samypesse@gmail.com> | 2015-01-20 11:29:26 +0100 |
---|---|---|
committer | Samy Pessé <samypesse@gmail.com> | 2015-01-20 11:29:26 +0100 |
commit | 238cd9f7f09f11ecdb1717a5d07fc33642d29043 (patch) | |
tree | 72eac94839bda4f32cb41441b2b88532eed52603 /theme/javascript/core | |
parent | bac25cdf297a7fa54f21977aa17d455e439cdf51 (diff) | |
download | gitbook-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.js | 7 | ||||
-rw-r--r-- | theme/javascript/core/font-settings.js | 103 | ||||
-rw-r--r-- | theme/javascript/core/glossary.js | 117 | ||||
-rwxr-xr-x | theme/javascript/core/keyboard.js | 38 | ||||
-rw-r--r-- | theme/javascript/core/loading.js | 16 | ||||
-rwxr-xr-x | theme/javascript/core/navigation.js | 151 | ||||
-rwxr-xr-x | theme/javascript/core/progress.js | 73 | ||||
-rwxr-xr-x | theme/javascript/core/search.js | 114 | ||||
-rwxr-xr-x | theme/javascript/core/sidebar.js | 56 | ||||
-rwxr-xr-x | theme/javascript/core/state.js | 18 |
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 |