Add back theme

This commit is contained in:
Samy Pessé
2015-01-20 11:29:26 +01:00
parent bac25cdf29
commit 238cd9f7f0
87 changed files with 5115 additions and 2 deletions
+7
View File
@@ -0,0 +1,7 @@
define([
"jQuery"
], function($) {
var events = $({});
return events;
});
+103
View File
@@ -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
}
});
+117
View File
@@ -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
};
});
+38
View File
@@ -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
};
});
+16
View File
@@ -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
};
});
+151
View File
@@ -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
};
});
+73
View File
@@ -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
};
});
+114
View File
@@ -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
};
});
+56
View File
@@ -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
}
});
+18
View File
@@ -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;
});
+53
View File
@@ -0,0 +1,53 @@
define([
"jQuery",
"utils/storage",
"utils/sharing",
"utils/dropdown",
"core/events",
"core/font-settings",
"core/state",
"core/keyboard",
"core/navigation",
"core/progress",
"core/sidebar",
"core/search",
"core/glossary"
], function($, storage, sharing, dropdown, events, fontSettings, state, keyboard, navigation, progress, sidebar, search, glossary){
var start = function(config) {
var $book;
$book = state.$book;
// Init sidebar
sidebar.init();
// Load search
search.init();
// Load glossary
glossary.init();
// Init keyboard
keyboard.init();
// Bind sharing button
sharing.init();
// Bind dropdown
dropdown.init();
// Init navigation
navigation.init();
//Init font settings
fontSettings.init(config.fontSettings || {});
events.trigger("start", config);
}
return {
start: start,
events: events,
state: state
};
});
+27
View File
@@ -0,0 +1,27 @@
define([
"jQuery"
], function($) {
var toggleDropdown = function(e) {
var $dropdown = $(e.currentTarget).parent().find(".dropdown-menu");
$dropdown.toggleClass("open");
e.stopPropagation();
e.preventDefault();
};
var closeDropdown = function(e) {
$(".dropdown-menu").removeClass("open");
};
// Bind all dropdown
var init = function() {
$(document).on('click', ".toggle-dropdown", toggleDropdown);
$(document).on('click', ".dropdown-menu", function(e){ e.stopPropagation(); });
$(document).on("click", closeDropdown);
};
return {
init: init
};
});
+5
View File
@@ -0,0 +1,5 @@
define([], function() {
return {
isMobile: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
};
});
+36
View File
@@ -0,0 +1,36 @@
define([
"jQuery"
], function($) {
var types = {
"twitter": function($el) {
window.open("http://twitter.com/home?status="+encodeURIComponent($("title").text()+" "+location.href))
},
"facebook": function($el) {
window.open("http://www.facebook.com/sharer/sharer.php?s=100&p[url]="+encodeURIComponent(location.href))
},
"google-plus": function($el) {
window.open("https://plus.google.com/share?url="+encodeURIComponent(location.href))
},
"weibo": function($el) {
window.open("http://service.weibo.com/share/share.php?content=utf-8&url="+encodeURIComponent(location.href)+"&title="+encodeURIComponent($("title").text()))
},
"instapaper": function($el) {
window.open("http://www.instapaper.com/text?u="+encodeURIComponent(location.href));
}
};
// Bind all sharing button
var init = function() {
$(document).on("click", "a[data-sharing],button[data-sharing]", function(e) {
if (e) e.preventDefault();
var type = $(this).data("sharing");
types[type]($(this));
})
};
return {
init: init
};
});
+31
View File
@@ -0,0 +1,31 @@
define(function(){
var baseKey = "";
/*
* Simple module for storing data in the browser's local storage
*/
return {
setBaseKey: function(key) {
baseKey = key;
},
set: function(key, value) {
key = baseKey+":"+key;
localStorage[key] = JSON.stringify(value);
},
get: function(key, def) {
key = baseKey+":"+key;
if (localStorage[key] === undefined) return def;
try {
var v = JSON.parse(localStorage[key]);
return v == null ? def : v;;
} catch(err) {
console.error(err);
return localStorage[key] || def;
}
},
remove: function(key) {
key = baseKey+":"+key;
localStorage.removeItem(key);
}
};
});
+33
View File
@@ -0,0 +1,33 @@
define([
"URIjs/URI"
], function(URI) {
// Joins path segments. Preserves initial "/" and resolves ".." and "."
// Does not support using ".." to go above/outside the root.
// This means that join("foo", "../../bar") will not resolve to "../bar"
function join(baseUrl, url) {
var theUrl = new URI(url);
if (theUrl.is("relative")) {
theUrl = theUrl.absoluteTo(baseUrl);
}
return theUrl.toString();
}
// A simple function to get the dirname of a path
// Trailing slashes are ignored. Leading slash is preserved.
function dirname(path) {
return join(path, "..");
}
// test if a path or url is absolute
function isAbsolute(path) {
if (!path) return false;
return (path[0] == "/" || path.indexOf("http://") == 0 || path.indexOf("https://") == 0);
}
return {
dirname: dirname,
join: join,
isAbsolute: isAbsolute
};
})