Compare commits

...

52 Commits

Author SHA1 Message Date
Aaron O'Mullan 67e7d77302 Bump version to 0.3.1 2014-04-24 10:45:55 -07:00
Aaron O'Mullan 8cbd8b2b48 Reorder plugin names
Improves #149 and improves GitBookIO/editor#12
2014-04-24 01:38:42 -07:00
Aaron O'Mullan 7c404dbf96 Fix plugin tests 2014-04-24 01:34:35 -07:00
Aaron O'Mullan c4a67b6f2f Require plugin names to contain gitbook-*
Fixes #149, fixes GitBookIO/editor#12
2014-04-24 01:28:04 -07:00
Samy Pessé 3b70347625 Fix #146: complete license 2014-04-24 09:00:15 +02:00
Aaron O'Mullan bb0a7935e9 Merge pull request #141 from frodsan/niceties
Niceties
2014-04-23 11:34:08 -07:00
Francesco Rodriguez 931e0c66c9 Update color themes for highlight.js link. 2014-04-23 12:30:05 -03:00
Francesco Rodriguez 0212084862 Fix typo. 2014-04-23 12:28:44 -03:00
Samy Pessé 9abbabbce3 Merge pull request #138 from Hermanya/patch-1
Add @Hermanya as a contributor
2014-04-22 23:47:13 +02:00
Hermanya ac610487e8 Add @Hermanya as a contributor 2014-04-22 17:45:41 -04:00
Aaron O'Mullan a9a0f89892 Bump version to 0.3.0 2014-04-22 14:05:52 -07:00
Samy Pessé 305f221077 Fix #136: replace links to .md by links to .html
Add more tests for links in page
2014-04-22 17:45:12 +02:00
Samy Pessé 53514f2602 Improve sepia theme sidebar 2014-04-22 14:27:48 +02:00
Samy Pessé 00327778cb Improve font-settings dropdown design 2014-04-22 14:12:53 +02:00
Samy Pessé 37fadec787 Merge branch 'master' of https://github.com/Hermanya/gitbook into Hermanya-master 2014-04-22 14:02:19 +02:00
Samy Pessé ada11d0b59 Add section in readme about plugins 2014-04-22 12:51:10 +02:00
Aaron O'Mullan 4c90fdb7b2 Merge pull request #123 from GitbookIO/feature/plugins
Fix #65: Plugins Architecture
2014-04-21 14:26:09 -07:00
Samy Pessé 98c133df3c Change plugins separator to "," 2014-04-21 23:23:53 +02:00
Samy Pessé f86c27b090 Add option --pluginsConfig 2014-04-21 22:58:51 +02:00
Samy Pessé b5b8ed72a5 Update default addons 2014-04-21 22:39:23 +02:00
Samy Pessé 5beff86548 Change js interface for plugins and add configuration 2014-04-21 22:20:41 +02:00
Samy Pessé dd395edae1 Use mathjax plugin version 0.0.2 2014-04-21 16:30:02 +02:00
Aaron O'Mullan d1c1d7b75b Bump version to 0.2.4 2014-04-20 16:41:09 -07:00
Samy Pessé 4a380e9279 Fix #36: add mathjax plugin as default 2014-04-20 15:20:21 +02:00
Samy Pessé 878c105def Use complete assets folder for plugins 2014-04-20 02:04:15 +02:00
Samy Pessé 74d628676b Add option to change templates from plugin 2014-04-20 00:10:46 +02:00
Samy Pessé 6fdd98ec3a Trigger event on document when ready 2014-04-19 20:39:58 +02:00
Samy Pessé 2ed1be3533 Add global js interface for plugins and remove mixpanel 2014-04-19 20:10:03 +02:00
Samy Pessé 0ac04e1584 Add call for hooks "init" and "finish" 2014-04-19 19:50:32 +02:00
Herman Starikov 2a38d33d8a Font settings, closes #102 2014-04-19 13:46:14 -04:00
Samy Pessé fb14caf5b3 Add resources to site build 2014-04-19 19:31:02 +02:00
Samy Pessé cf51772f54 Add command line option "plugins" 2014-04-19 18:00:24 +02:00
Samy Pessé cc8516df83 Load plugins during generation 2014-04-19 17:57:40 +02:00
Samy Pessé c6a624bc6b Add method to load a list of plugins from generator options 2014-04-19 17:54:15 +02:00
Samy Pessé 8be8b825ee Add base loading of default addons
Add mixpanel as default addon
2014-04-19 17:28:44 +02:00
Samy Pessé 56eb4a2897 Add base plugin object and test validation of plugin 2014-04-19 15:41:29 +02:00
Samy Pessé 7a461eaa61 Remove theme readme
Add semver dependency
2014-04-19 14:40:46 +02:00
Samy Pessé fd64c05809 Fix #118: fix single page generation when file from summary doesn't exists 2014-04-18 23:42:02 +02:00
Aaron O'Mullan 6bce93d893 Bump version to 0.2.3 2014-04-18 11:53:18 -07:00
Aaron O'Mullan c9db068227 Refactor navigation generation to be functional
This makes it a lot less error prone and far more robust. As well as a
lot easier to understand
2014-04-18 11:49:55 -07:00
Samy Pessé 8a6d36ac4d Improve stability of mixpanel tracking 2014-04-18 20:42:18 +02:00
Samy Pessé 64622c5945 Load css before content 2014-04-18 20:19:45 +02:00
Samy Pessé 837813edb0 Add warning about theme 2014-04-18 20:16:21 +02:00
Aaron O'Mullan 0d9d62f1a9 Merge pull request #117 from GitbookIO/fix/99
Fix #99
2014-04-18 10:17:20 -07:00
Samy Pessé f7e9abf984 Fix quiz interractivity 2014-04-18 19:10:13 +02:00
Samy Pessé dd0579b60d Update tests for relative images 2014-04-18 17:23:33 +02:00
Samy Pessé b5bb953e47 Fix image path calcul 2014-04-18 17:15:03 +02:00
Samy Pessé ef4a8ea1b6 Fix #116: update url before updating html content 2014-04-18 15:49:53 +02:00
Samy Pessé 49cde96ee7 Add contributors to package.json and change homepage 2014-04-18 15:00:40 +02:00
Samy Pessé 627be20e48 Add editor and twitter links in the readme 2014-04-18 14:48:03 +02:00
Samy Pessé e45bb84b02 Fix navigation on chrome for iOS: disable pushState 2014-04-18 13:09:16 +02:00
Aaron O'Mullan 4e8876c725 Update README.md 2014-04-17 16:39:06 -07:00
47 changed files with 1229 additions and 184 deletions
+1
View File
@@ -15,3 +15,4 @@ Contributors
------------ ------------
- Nijiko Yonskai - Nijiko Yonskai
- Herman Starikov
+1 -5
View File
@@ -24,7 +24,7 @@ module.exports = function (grunt) {
requirejs: { requirejs: {
compile: { compile: {
options: { options: {
name: "app", name: "gitbook",
baseUrl: "theme/javascript/", baseUrl: "theme/javascript/",
out: "theme/assets/app.js", out: "theme/assets/app.js",
preserveLicenseComments: false, preserveLicenseComments: false,
@@ -35,7 +35,6 @@ module.exports = function (grunt) {
"lodash": 'vendors/lodash', "lodash": 'vendors/lodash',
"requireLib": 'vendors/require', "requireLib": 'vendors/require',
"Mousetrap": 'vendors/mousetrap', "Mousetrap": 'vendors/mousetrap',
"mixpanel": 'vendors/mixpanel',
"lunr": path.join(__dirname, "node_modules/lunr/lunr") "lunr": path.join(__dirname, "node_modules/lunr/lunr")
}, },
shim: { shim: {
@@ -48,9 +47,6 @@ module.exports = function (grunt) {
'Mousetrap': { 'Mousetrap': {
exports: 'Mousetrap' exports: 'Mousetrap'
}, },
'mixpanel': {
exports: 'mixpanel'
},
'lunr': { 'lunr': {
exports: 'lunr' exports: 'lunr'
} }
+1 -1
View File
@@ -186,7 +186,7 @@ Apache License
same "printed page" as the copyright notice for easier same "printed page" as the copyright notice for easier
identification within third-party archives. identification within third-party archives.
Copyright {yyyy} {name of copyright owner} Copyright 2014 FriendCode Inc.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
+18 -2
View File
@@ -3,7 +3,7 @@ GitBook
[![Build Status](https://travis-ci.org/GitbookIO/gitbook.png?branch=master)](https://travis-ci.org/GitbookIO/gitbook) [![Build Status](https://travis-ci.org/GitbookIO/gitbook.png?branch=master)](https://travis-ci.org/GitbookIO/gitbook)
GitBook is a command line tool (and Node.js library) for building beautiful programming books and exercises using GitHub/Git and Markdown. You can see an example: [Learn Javascript](http://gitbookio.github.io/javascript/). GitBook is a command line tool (and Node.js library) for building beautiful programming books and exercises using GitHub/Git and Markdown. You can see an example: [Learn Javascript](http://gitbookio.github.io/javascript/). An [editor](https://github.com/GitbookIO/editor) is available for Windows, Mac and Linux. You can follow [@GitBookIO](https://twitter.com/GitBookIO) on Twitter.
![Image](https://raw.github.com/GitbookIO/gitbook/master/preview.png) ![Image](https://raw.github.com/GitbookIO/gitbook/master/preview.png)
@@ -36,9 +36,12 @@ Options for commands `build` and `serve` are:
-o, --output <directory> Path to output directory, defaults to ./_book -o, --output <directory> Path to output directory, defaults to ./_book
-f, --format <name> Change generation format, defaults to site, availables are: site, page, pdf, json -f, --format <name> Change generation format, defaults to site, availables are: site, page, pdf, json
--githubHost <url> The url of the github host (defaults to https://github.com/) --githubHost <url> The url of the github host (defaults to https://github.com/)
--theme <path> Path to theme directory --plugins <plugins> List of plugins to use separated by ","
--pluginsConfig <json file> JSON File containing plugins configuration
``` ```
You can publish your books to our index by visiting [GitBook.io](http://www.gitbook.io)
## Output Formats ## Output Formats
GitBook can generate your book in the following formats: GitBook can generate your book in the following formats:
@@ -131,3 +134,16 @@ You can see a complete example with the [Learn Git](https://github.com/GitbookIO
#### Ignoring files & folders #### Ignoring files & folders
GitBook will read the `.gitignore`, `.bookignore` and `.ignore` files to get a list of files and folders to skip. (The format inside those files, follows the same convention as `.gitignore`) GitBook will read the `.gitignore`, `.bookignore` and `.ignore` files to get a list of files and folders to skip. (The format inside those files, follows the same convention as `.gitignore`)
#### Plugins
Plugins can used to extend your book functionnalities. Read [GitbookIO/plugin](https://github.com/GitbookIO/plugin) for more informations about how to build a plugin for gitbook.
##### Default plugins:
* [mathjax](https://github.com/GitbookIO/plugin-mathjax): displays mathematical notation in the book.
* [mixpanel](https://github.com/GitbookIO/plugin-mixpanel): Mixpanel tracking for your book
##### Other plugins:
* [Google Analytics](https://github.com/GitbookIO/plugin-ga): Google Analytics tracking for your book
+27 -6
View File
@@ -1,18 +1,37 @@
var path = require('path'); var path = require('path');
var Q = require('q'); var Q = require('q');
var _ = require('lodash'); var _ = require('lodash');
var fs = require('fs');
var utils = require('./utils'); var utils = require('./utils');
var generate = require("../lib/generate"); var generate = require("../lib/generate");
var parse = require("../lib/parse"); var parse = require("../lib/parse");
var fs = require('../lib/generate/fs');
var generators = require("../lib/generate").generators; var generators = require("../lib/generate").generators;
var buildCommand = function(command) {
return command
.option('-o, --output <directory>', 'Path to output directory, defaults to ./_book')
.option('-f, --format <name>', 'Change generation format, defaults to site, availables are: '+_.keys(generators).join(", "))
.option('-t, --title <name>', 'Name of the book to generate, default is extracted from readme')
.option('-i, --intro <intro>', 'Description of the book to generate, default is extracted from readme')
.option('--plugins <plugins>', 'List of plugins to use separated by ","')
.option('--pluginsConfig <json file>', 'JSON File containing plugins configuration')
.option('-g, --github <repo_path>', 'ID of github repo like : username/repo')
.option('--githubHost <url>', 'The url of the github host (defaults to https://github.com/');
};
var makeBuildFunc = function(converter) { var makeBuildFunc = function(converter) {
return function(dir, options) { return function(dir, options) {
dir = dir || process.cwd(); dir = dir || process.cwd();
outputDir = options.output outputDir = options.output;
// Read plugins config
var pluginsConfig = {};
if (options.pluginsConfig) {
pluginsConfig = JSON.parse(fs.readFileSync(options.pluginsConfig))
}
console.log('Starting build ...'); console.log('Starting build ...');
// Get repo's URL // Get repo's URL
@@ -33,7 +52,8 @@ var makeBuildFunc = function(converter) {
github: options.github || repoID, github: options.github || repoID,
githubHost: options.githubHost, githubHost: options.githubHost,
generator: options.format, generator: options.format,
theme: options.theme plugins: options.plugins,
pluginsConfig: pluginsConfig
}) })
); );
}) })
@@ -42,9 +62,10 @@ var makeBuildFunc = function(converter) {
return output; return output;
}, utils.logError); }, utils.logError);
}; };
} };
module.exports = { module.exports = {
folder: makeBuildFunc(generate.folder), folder: makeBuildFunc(generate.folder),
file: makeBuildFunc(generate.file) file: makeBuildFunc(generate.file),
command: buildCommand
}; };
+4 -15
View File
@@ -17,22 +17,11 @@ var build = require('./build');
prog prog
.version(pkg.version); .version(pkg.version);
var buildCommand = function(command) { build.command(prog.command('build [source_dir]'))
return command
.option('-o, --output <directory>', 'Path to output directory, defaults to ./_book')
.option('-f, --format <name>', 'Change generation format, defaults to site, availables are: '+_.keys(generators).join(", "))
.option('-t, --title <name>', 'Name of the book to generate, default is extracted from readme')
.option('-i, --intro <intro>', 'Description of the book to generate, default is extracted from readme')
.option('-g, --github <repo_path>', 'ID of github repo like : username/repo')
.option('--githubHost <url>', 'The url of the github host (defaults to https://github.com/')
.option('--theme <path>', 'Path to theme directory');
};
buildCommand(prog.command('build [source_dir]'))
.description('Build a gitbook from a directory') .description('Build a gitbook from a directory')
.action(build.folder); .action(build.folder);
buildCommand(prog.command('serve [source_dir]')) build.command(prog.command('serve [source_dir]'))
.description('Build then serve a gitbook from a directory') .description('Build then serve a gitbook from a directory')
.option('-p, --port <port>', 'Port for server to listen on', 4000) .option('-p, --port <port>', 'Port for server to listen on', 4000)
.action(function(dir, options) { .action(function(dir, options) {
@@ -50,7 +39,7 @@ buildCommand(prog.command('serve [source_dir]'))
}); });
}); });
buildCommand(prog.command('pdf [source_dir]')) build.command(prog.command('pdf [source_dir]'))
.description('Build a gitbook as a PDF') .description('Build a gitbook as a PDF')
.option('-pf, --paperformat <format>', 'PDF paper format (default is A4): "5in*7.5in", "10cm*20cm", "A4", "Letter"') .option('-pf, --paperformat <format>', 'PDF paper format (default is A4): "5in*7.5in", "10cm*20cm", "A4", "Letter"')
.action(function(dir, options) { .action(function(dir, options) {
@@ -60,7 +49,7 @@ buildCommand(prog.command('pdf [source_dir]'))
})); }));
}); });
buildCommand(prog.command('ebook [source_dir]')) build.command(prog.command('ebook [source_dir]'))
.description('Build a gitbook as a eBook') .description('Build a gitbook as a eBook')
.option('-c, --cover <path>', 'Cover image, default is cover.png if exists') .option('-c, --cover <path>', 'Cover image, default is cover.png if exists')
.action(function(dir, options) { .action(function(dir, options) {
+21
View File
@@ -1,9 +1,30 @@
var _ = require("lodash");
var path = require("path"); var path = require("path");
var Q = require("q"); var Q = require("q");
var fs = require("./fs"); var fs = require("./fs");
var Plugin = require("./plugin");
var BaseGenerator = function(options) { var BaseGenerator = function(options) {
this.options = options; this.options = options;
this.options.plugins = Plugin.normalizeNames(this.options.plugins);
this.plugins = [];
};
BaseGenerator.prototype.callHook = function(name) {
return this.plugins.hook(name, this);
};
BaseGenerator.prototype.loadPlugins = function() {
var that = this;
return Plugin.fromList(this.options.plugins)
.then(function(_plugins) {
that.plugins = _plugins;
return that.callHook("init");
});
}; };
BaseGenerator.prototype.convertFile = function(content, input) { BaseGenerator.prototype.convertFile = function(content, input) {
+13 -3
View File
@@ -6,6 +6,7 @@ var tmp = require('tmp');
var fs = require("./fs"); var fs = require("./fs");
var parse = require("../parse"); var parse = require("../parse");
var Plugin = require("./plugin");
var generators = { var generators = {
"site": require("./site"), "site": require("./site"),
@@ -42,7 +43,11 @@ var generate = function(options) {
githubHost: 'https://github.com/', githubHost: 'https://github.com/',
// Theming // Theming
theme: path.resolve(__dirname, '../../theme') theme: path.resolve(__dirname, '../../theme'),
// Plugins
plugins: [],
pluginsConfig: {}
}); });
if (!options.input) { if (!options.input) {
@@ -70,9 +75,10 @@ var generate = function(options) {
}) })
}) })
// Create the generator // Create the generator and load plugins
.then(function() { .then(function() {
generator = new generators[options.generator](options); generator = new generators[options.generator](options);
return generator.loadPlugins();
}) })
// Detect multi-languages book // Detect multi-languages book
@@ -163,6 +169,9 @@ var generate = function(options) {
// Finish gneration // Finish gneration
.then(function() { .then(function() {
return generator.finish(); return generator.finish();
})
.then(function() {
return generator.callHook("finish");
}); });
} }
}) })
@@ -234,5 +243,6 @@ var generateFile = function(options) {
module.exports = { module.exports = {
generators: generators, generators: generators,
folder: generate, folder: generate,
file: generateFile file: generateFile,
Plugin: Plugin
}; };
+8 -3
View File
@@ -25,14 +25,18 @@ swig.setFilter('code', function(code, lang) {
var Generator = function() { var Generator = function() {
BaseGenerator.apply(this, arguments); BaseGenerator.apply(this, arguments);
// Load base template
this.template = swig.compileFile(path.resolve(this.options.theme, 'templates/page.html'));
// List of pages content // List of pages content
this.pages = {}; this.pages = {};
}; };
util.inherits(Generator, BaseGenerator); util.inherits(Generator, BaseGenerator);
// Load all templates
Generator.prototype.loadTemplates = function() {
this.template = swig.compileFile(
this.plugins.template("page") || path.resolve(this.options.theme, 'templates/page.html')
);
};
Generator.prototype.convertFile = function(content, input) { Generator.prototype.convertFile = function(content, input) {
var that = this; var that = this;
var json = { var json = {
@@ -46,6 +50,7 @@ Generator.prototype.convertFile = function(content, input) {
repo: that.options.githubId, repo: that.options.githubId,
dir: path.dirname(input) || '/', dir: path.dirname(input) || '/',
outdir: './', outdir: './',
singleFile: true
}); });
}) })
.then(function(sections) { .then(function(sections) {
+180
View File
@@ -0,0 +1,180 @@
var _ = require("lodash");
var Q = require("q");
var semver = require("semver");
var path = require("path");
var url = require("url");
var fs = require("./fs");
var pkg = require("../../package.json");
var RESOURCES = ["js", "css"];
var Plugin = function(name) {
this.name = name;
this.packageInfos = {};
this.infos = {};
_.each([
"gitbook-plugin-"+name,
"gitbook-theme-"+name,
"gitbook-"+name,
name,
], function(_name) {
if (this.load(_name)) return false;
}.bind(this));
};
// Load from a name
Plugin.prototype.load = function(name) {
try {
this.packageInfos = require(name+"/package.json");
this.infos = require(name);
this.name = name;
return true;
} catch (e) {
return false;
}
};
// Return resources
Plugin.prototype.getResources = function(resource) {
if (!this.infos.book || !this.infos.book[resource]) {
return [];
}
return _.chain(this.infos.book[resource])
.map(function(resource) {
var parsed = url.parse(resource);
if (parsed.protocol) return {"url": resource}
else return { "path": this.name+"/"+resource };
}.bind(this))
.value();
};
// Test if it's a valid plugin
Plugin.prototype.isValid = function() {
return (
this.packageInfos
&& this.packageInfos.name
&& this.packageInfos.engines
&& this.packageInfos.engines.gitbook
&& semver.satisfies(pkg.version, this.packageInfos.engines.gitbook)
);
};
// Resolve file path
Plugin.prototype.resolveFile = function(filename) {
return path.resolve(path.dirname(require.resolve(this.name)), filename);
};
// Resolve file path
Plugin.prototype.callHook = function(name, args) {
var hookFunc = this.infos.hooks? this.infos.hooks[name] : null;
args = _.isArray(args) ? args : [args];
if (!hookFunc) return Q();
return Q()
.then(function() {
return hookFunc.apply(null, args);
});
};
// Copy plugin assets fodler
Plugin.prototype.copyAssets = function(out) {
if (!this.infos.book || !this.infos.book.assets) return Q();
return fs.copy(
this.resolveFile(this.infos.book.assets),
out
);
};
// Normalize a list of plugin name to use
Plugin.normalizeNames = function(names) {
// Normalize list to an array
names = _.isString(names) ? names.split(":") : (names || []);
// List plugins to remove
var toremove = _.chain(names)
.filter(function(name) {
return name.length > 0 && name[0] == "-";
})
.map(function(name) {
return name.slice(1)
})
.value();
// Merge with defaults
names = _.chain(names)
.concat(Plugin.defaults)
.uniq()
.value();
// Remove plugins starting with
names = _.filter(names, function(name) {
return !_.contains(toremove, name) && !(name.length > 0 && name[0] == "-");
});
return names;
};
// Extract data from a list of plugin
Plugin.fromList = function(names) {
var failed = [];
// Load plugins
var plugins = _.map(names, function(name) {
var plugin = new Plugin(name);
if (!plugin.isValid()) failed.push(name);
return plugin;
});
if (_.size(failed) > 0) return Q.reject(new Error("Error loading plugins: "+failed.join(",")));
// Get all resources
var resources = _.chain(RESOURCES)
.map(function(resource) {
return [
resource,
_.chain(plugins)
.map(function(plugin) {
return plugin.getResources(resource);
})
.flatten()
.value()
];
})
.object()
.value();
return Q({
'list': plugins,
'resources': resources,
'hook': function(name, args) {
return _.reduce(plugins, function(prev, plugin) {
return prev.then(function() {
return plugin.callHook(name, args);
})
}, Q());
},
'template': function(name) {
var withTpl = _.find(plugins, function(plugin) {
return (plugin.infos.templates
&& plugin.infos.templates[name]);
});
if (!withTpl) return null;
return withTpl.resolveFile(withTpl.infos.templates[name]);
}
});
};
// Default plugins
Plugin.defaults = [
"mixpanel",
"mathjax"
];
module.exports = Plugin;
+32 -5
View File
@@ -30,13 +30,28 @@ var Generator = function() {
this.revision = Date.now(); this.revision = Date.now();
this.indexer = indexer(); this.indexer = indexer();
// Load base template
this.template = swig.compileFile(path.resolve(this.options.theme, 'templates/site.html'));
this.langsTemplate = swig.compileFile(path.resolve(this.options.theme, 'templates/langs.html'));
}; };
util.inherits(Generator, BaseGenerator); util.inherits(Generator, BaseGenerator);
// Load all templates
Generator.prototype.loadTemplates = function() {
this.template = swig.compileFile(
this.plugins.template("site") || path.resolve(this.options.theme, 'templates/site.html')
);
this.langsTemplate = swig.compileFile(
this.plugins.template("langs") || path.resolve(this.options.theme, 'templates/langs.html')
);
};
// Load plugins
Generator.prototype.loadPlugins = function() {
var that = this;
return BaseGenerator.prototype.loadPlugins.apply(this)
.then(function() {
return that.loadTemplates();
});
};
// Generate a template // Generate a template
Generator.prototype._writeTemplate = function(tpl, options, output) { Generator.prototype._writeTemplate = function(tpl, options, output) {
@@ -54,7 +69,10 @@ Generator.prototype._writeTemplate = function(tpl, options, output) {
githubHost: that.options.githubHost, githubHost: that.options.githubHost,
summary: that.options.summary, summary: that.options.summary,
allNavigation: that.options.navigation allNavigation: that.options.navigation,
plugins: that.plugins,
pluginsConfig: JSON.stringify(that.options.pluginsConfig)
}, options)); }, options));
}) })
.then(function(html) { .then(function(html) {
@@ -134,10 +152,19 @@ Generator.prototype.langsIndex = function(langs) {
Generator.prototype.copyAssets = function() { Generator.prototype.copyAssets = function() {
var that = this; var that = this;
// Copy gitbook assets
return fs.copy( return fs.copy(
path.join(that.options.theme, "assets"), path.join(that.options.theme, "assets"),
path.join(that.options.output, "gitbook") path.join(that.options.output, "gitbook")
)
// Copy plugins assets
.then(function() {
return Q.all(
_.map(that.plugins.list, function(plugin) {
return plugin.copyAssets(path.join(that.options.output, "gitbook/plugins/", plugin.name))
})
); );
})
}; };
// Dump search index to disk // Dump search index to disk
+30 -49
View File
@@ -6,6 +6,12 @@ function clean(obj) {
return obj && _.omit(obj, ['articles']); return obj && _.omit(obj, ['articles']);
} }
function flattenChapters(chapters) {
return _.reduce(chapters, function(accu, chapter) {
return accu.concat([clean(chapter)].concat(chapter.articles));
}, []);
}
// Returns from a summary a map of // Returns from a summary a map of
/* /*
{ {
@@ -20,62 +26,37 @@ function navigation(summary, files) {
// Support single files as well as list // Support single files as well as list
files = _.isArray(files) ? files : (_.isString(files) ? [files] : null); files = _.isArray(files) ? files : (_.isString(files) ? [files] : null);
// Mapping of prev/next for a give path
var mapping = {};
// Special README nav // Special README nav
var README_NAV = { var README_NAV = {
path: 'README.md', path: 'README.md',
title: 'Introduction', title: 'Introduction',
};
// Walk the chapter/article tree and create navigation entires
_.each(summary.chapters, function(chapter, idx, chapters) {
// Skip if no path
if(!chapter.path) return;
var currentChapter = clean(chapter);
var prevChapter = (idx-1 < 0) ? README_NAV : chapters[idx-1];
var nextChapter = (idx+1 >= chapters.length) ? null : chapters[idx+1];
var prev = (!prevChapter || _.isEmpty(prevChapter.articles)) ?
prevChapter : _.last(prevChapter.articles);
var next = (!chapter || _.isEmpty(chapter.articles)) ?
nextChapter : _.first(chapter.articles);
// Add chapter mapping
mapping[chapter.path] = {
title: chapter.title,
prev: clean(prev),
next: clean(next),
level: (idx+1).toString(),
};
// Check a chapter's articles
_.each(chapter.articles, function(article, _idx, articles) {
// Skip if no path
if(!article.path) return;
var prev = (_idx-1 < 0) ? currentChapter : clean(articles[_idx-1]);
var next = (_idx+1 >= articles.length) ? nextChapter : clean(articles[_idx+1]);
mapping[article.path] = {
title: article.title,
prev: clean(prev),
next: clean(next),
level: [idx+1, _idx+1].join('.'),
};
});
});
// Hack for README.html
mapping['README.md'] = {
title: README_NAV.title,
prev: null,
next: clean(summary.chapters[0]),
level: '0', level: '0',
}; };
// List of all navNodes
var navNodes = [README_NAV].concat(flattenChapters(summary.chapters));
var prevNodes = [null].concat(navNodes.slice(0, -1));
var nextNodes = navNodes.slice(1).concat([null]);
// Mapping of prev/next for a give path
var mapping = _.chain(_.zip(navNodes, prevNodes, nextNodes))
.map(function(nodes) {
var current = nodes[0], prev = nodes[1], next = nodes[2];
// Skip if no path
if(!current.path) return null;
return [current.path, {
title: current.title,
prev: prev,
next: next,
level: current.level,
}];
})
.filter()
.object()
.value();
// Filter for only files we want // Filter for only files we want
if(files) { if(files) {
return _.pick(mapping, files); return _.pick(mapping, files);
+26 -7
View File
@@ -1,5 +1,7 @@
var url = require('url'); var url = require('url');
var inherits = require('util').inherits; var inherits = require('util').inherits;
var links = require('../utils').links;
var path = require('path'); var path = require('path');
@@ -38,23 +40,38 @@ GitBookRenderer.prototype._unsanitized = function(href) {
}; };
GitBookRenderer.prototype.link = function(href, title, text) { GitBookRenderer.prototype.link = function(href, title, text) {
// Our "fixed" href
var _href = href;
// Don't build if it looks malicious // Don't build if it looks malicious
if (this.options.sanitize && this._unsanitized(href)) { if (this.options.sanitize && this._unsanitized(href)) {
return ''; return text;
} }
// Parsed version of the url // Parsed version of the url
var parsed = url.parse(href); var parsed = url.parse(href);
var o = this._extra_options; var o = this._extra_options;
// Relative link, rewrite it to point to github repo // Relative link, rewrite it to point to github repo
if(!parsed.protocol && parsed.path && parsed.path[0] != '/' && o && o.repo && o.dir) { if(links.isRelative(_href)) {
href = url.resolve('https://github.com/' + o.repo + '/blob/', [o.dir, href].join("/")); if (path.extname(_href) == ".md") {
_href = links.toAbsolute(_href, o.dir || "./", o.outdir || "./");
if (o.singleFile) {
_href = "#"+_href;
} else {
_href = _href.replace(".md", ".html");
}
} else if (o && o.repo && o.dir) {
href = url.resolve('https://github.com/' + o.repo + '/blob/', [o.dir, _href].join("/"));
parsed = url.parse(href); parsed = url.parse(href);
_href = parsed.href;
}
} }
// Generate HTML for link // Generate HTML for link
var out = '<a href="' + parsed.href + '"'; var out = '<a href="' + _href + '"';
// Title if no null // Title if no null
if (title) { if (title) {
out += ' title="' + title + '"'; out += ' title="' + title + '"';
@@ -78,9 +95,11 @@ GitBookRenderer.prototype.image = function(href, title, text) {
var o = this._extra_options; var o = this._extra_options;
// Relative image, rewrite it depending output // Relative image, rewrite it depending output
if(!parsed.protocol && parsed.path && parsed.path[0] != '/' && o && o.dir && o.outdir) { if(links.isRelative(href) && o && o.dir && o.outdir) {
var outdir = o.outdir.charAt(o.outdir.length - 1) === '/' ? o.outdir : o.outdir + '/'; // o.dir: directory parent of the file currently in rendering process
_href = url.resolve(outdir, [o.dir, href].join('/')); // o.outdir: directory parent from the html output
_href = links.toAbsolute(_href, o.dir, o.outdir);
} }
return GitBookRenderer.super_.prototype.image.call(this, _href, title, text); return GitBookRenderer.super_.prototype.image.call(this, _href, title, text);
+1
View File
@@ -1,3 +1,4 @@
module.exports = { module.exports = {
lang: require('./lang'), lang: require('./lang'),
links: require('./links')
}; };
+29
View File
@@ -0,0 +1,29 @@
var url = require('url');
var path = require('path');
// Return true if the link is relative
var isRelative = function(href) {
var parsed = url.parse(href);
return !parsed.protocol && parsed.path && parsed.path[0] != '/';
};
// Relative to absolute path
// dir: directory parent of the file currently in rendering process
// outdir: directory parent from the html output
var toAbsolute = function(_href, dir, outdir) {
// Absolute file in source
_href = path.join(dir, _href);
// make it relative to output
_href = path.relative(outdir, _href);
return _href;
};
module.exports = {
isRelative: isRelative,
toAbsolute: toAbsolute
};
+19 -3
View File
@@ -1,6 +1,7 @@
{ {
"name": "gitbook", "name": "gitbook",
"version": "0.2.2", "version": "0.3.1",
"homepage": "http://www.gitbook.io/",
"description": "Library and cmd utility to generate GitBooks", "description": "Library and cmd utility to generate GitBooks",
"main": "lib/index.js", "main": "lib/index.js",
"dependencies": { "dependencies": {
@@ -15,7 +16,12 @@
"commander": "2.2.0", "commander": "2.2.0",
"fs-extra": "0.8.1", "fs-extra": "0.8.1",
"highlight.js": "8.0.0", "highlight.js": "8.0.0",
"tmp": "0.0.23" "tmp": "0.0.23",
"semver": "2.2.1",
"gitbook-plugin": "0.0.2",
"gitbook-plugin-mixpanel": "0.0.2",
"gitbook-plugin-mathjax": "0.0.3"
}, },
"devDependencies": { "devDependencies": {
"mocha": "1.18.2", "mocha": "1.18.2",
@@ -39,9 +45,19 @@
"book", "book",
"gitbook" "gitbook"
], ],
"author": "Aaron O'Mullan <aaron.omullan@friendco.de>", "author": "FriendCode <contact@friendco.de>",
"license": "Apache 2", "license": "Apache 2",
"bugs": { "bugs": {
"url": "https://github.com/GitbookIO/gitbook/issues" "url": "https://github.com/GitbookIO/gitbook/issues"
},
"contributors": [
{
"name": "Aaron O'Mullan",
"email": "aaron.omullan@gmail.com"
},
{
"name": "Samy Pessé",
"email": "samypesse@gmail.com"
} }
]
} }
+12
View File
@@ -0,0 +1,12 @@
# Images Test
This is a test for images path calculation. It supposed this fiel is in a syntax/ folder
### #
[![Screen](./preview.png)](./preview.png)
### 2
[![Screen](../preview2.png)](./preview2.png)
+5
View File
@@ -0,0 +1,5 @@
# Nice course
Check out this other chapter [Test](test.md)
Check out this other chapter [Test](../before.md)
+61 -5
View File
@@ -36,10 +36,6 @@ describe('Page parsing', function() {
assert(LEXED[2].content); assert(LEXED[2].content);
}); });
it('should make image URLs relative', function() {
assert(LEXED[2].content.indexOf('_book/assets/my-pretty-picture.png') !== -1);
});
it('should gen code and content for exercise sections', function() { it('should gen code and content for exercise sections', function() {
assert(LEXED[1].content); assert(LEXED[1].content);
assert(LEXED[1].code); assert(LEXED[1].code);
@@ -88,9 +84,69 @@ describe('Relative links', function() {
repo: 'GitBookIO/javascript', repo: 'GitBookIO/javascript',
// Imaginary folder of markdown file // Imaginary folder of markdown file
dir: 'course' dir: 'course',
outdir: './'
}); });
assert(LEXED[0].content.indexOf('https://github.com/GitBookIO/javascript/blob/src/something.cpp') !== -1); assert(LEXED[0].content.indexOf('https://github.com/GitBookIO/javascript/blob/src/something.cpp') !== -1);
}); });
it('should replace link to .md by link to .html', function() {
var LEXED = loadPage('MARKDOWN_LINKS', {
// GitHub repo ID
repo: 'GitBookIO/javascript',
// Imaginary folder of markdown file
dir: 'course',
outdir: 'course'
});
assert(LEXED[0].content.indexOf('test.html') !== -1);
assert(LEXED[0].content.indexOf('../before.html') !== -1);
});
it('should replace link to .md by link to page in format single page', function() {
var LEXED = loadPage('MARKDOWN_LINKS', {
// GitHub repo ID
repo: 'GitBookIO/javascript',
// Imaginary folder of markdown file
dir: 'course',
outdir: './',
singleFile: true
});
assert(LEXED[0].content.indexOf('#course/test.md') !== -1);
assert(LEXED[0].content.indexOf('#before.md') !== -1);
});
});
describe('Relative images', function() {
it('should keep image relative with considering output directory in site format', function() {
var LEXED = loadPage('IMAGES', {
// GitHub repo ID
repo: 'GitBookIO/javascript',
// Imaginary folder of markdown file
dir: 'syntax',
outdir: 'syntax'
});
assert(LEXED[0].content.indexOf('"preview.png"') !== -1);
assert(LEXED[0].content.indexOf('"../preview2.png"') !== -1);
});
it('should keep image relative with considering output directory in page format', function() {
var LEXED = loadPage('IMAGES', {
// GitHub repo ID
repo: 'GitBookIO/javascript',
// Imaginary folder of markdown file
dir: 'syntax',
outdir: './'
});
assert(LEXED[0].content.indexOf('"syntax/preview.png"') !== -1);
assert(LEXED[0].content.indexOf('"preview2.png"') !== -1);
});
}); });
+49
View File
@@ -0,0 +1,49 @@
var _ = require('lodash');
var path = require('path');
var assert = require('assert');
var Plugin = require('../').generate.Plugin;
describe('Plugin validation', function () {
var plugin = new Plugin("plugin");
it('should be valid', function() {
assert(plugin.isValid());
});
});
describe('Plugin list of names', function () {
var firstDefault = _.first(Plugin.defaults);
it('should convert string to array', function() {
var _name = "test";
assert(_.contains(Plugin.normalizeNames(_name), _name));
});
it('should contains default plugins', function() {
assert(_.contains(Plugin.normalizeNames([]), firstDefault));
});
it('should remove name starting with -', function() {
assert(!_.contains(Plugin.normalizeNames(["-"+firstDefault]), firstDefault));
});
});
describe('Plugin defaults loading', function () {
var ret = true;
beforeEach(function(done){
Plugin.fromList(Plugin.defaults)
.then(function(_r) {
ret = _r;
}, function(err) {
ret = null;
})
.fin(done);
});
it('should load defaults addons', function() {
assert(ret != null);
});
});
-5
View File
@@ -1,5 +0,0 @@
# GitBook Theme
For creating a theme for GitBook, just copy this folder. The theme folder should at least contains two folders: `assets` and `templates`.
The theme can be changed using the option ```--theme=<path>```
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+7
View File
@@ -0,0 +1,7 @@
define([
"jQuery"
], function($) {
var events = $({});
return events;
});
+3 -3
View File
@@ -1,9 +1,9 @@
define([ define([
"jQuery", "jQuery",
"utils/execute", "utils/execute",
"utils/analytic", "core/events",
"core/state" "core/state"
], function($, execute, analytic, state){ ], function($, execute, events, state){
// Bind an exercise // Bind an exercise
var prepareExercise = function($exercise) { var prepareExercise = function($exercise) {
var codeSolution = $exercise.find(".code-solution").text(); var codeSolution = $exercise.find(".code-solution").text();
@@ -19,7 +19,7 @@ define([
$exercise.find(".action-submit").click(function(e) { $exercise.find(".action-submit").click(function(e) {
e.preventDefault(); e.preventDefault();
analytic.track("exercise.submit", {type: "code"}); events.trigger("exercise.submit", {type: "code"});
execute("javascript", editor.getValue(), codeValidation, codeContext, function(err, result) { execute("javascript", editor.getValue(), codeValidation, codeContext, function(err, result) {
$exercise.toggleClass("return-error", err != null); $exercise.toggleClass("return-error", err != null);
+115
View File
@@ -0,0 +1,115 @@
define([
"jQuery",
"utils/storage"
], function($, storage) {
var fontState;
var togglePopover = function(e) {
var $dropdown = $("#font-settings-wrapper .dropdown-menu");
$dropdown.toggleClass("open");
e.stopPropagation();
e.preventDefault();
};
var closePopover = function(e) {
var $dropdown = $("#font-settings-wrapper .dropdown-menu");
$dropdown.removeClass("open");
};
var enlargeFontSize = function(e){
var $bookBody = $(".book-body");
if (fontState.size < 4){
$bookBody.toggleClass("font-size-"+fontState.size, false);
fontState.size++;
$bookBody.toggleClass("font-size-"+fontState.size, true);
fontState.save();
}
};
var reduceFontSize = function(e){
var $bookBody = $(".book-body");
if (fontState.size > 0){
$bookBody.toggleClass("font-size-"+fontState.size);
fontState.size--;
$bookBody.toggleClass("font-size-"+fontState.size);
fontState.save();
}
};
var changeFontFamily = function(){
var $bookBody = $(".book-body");
var index = $(this).data("font");
$bookBody.toggleClass("font-family-"+fontState.family);
$(".font-settings .font-family-list li:nth-child("+(fontState.family+1)+")")
.removeClass("active");
fontState.family = index;
$bookBody.toggleClass("font-family-"+fontState.family);
$(this).addClass("active");
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 init = function() {
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:1,
family:0,
theme:0
});
fontState.save = function(){
storage.set("fontState",fontState);
};
$bookBody.addClass("font-size-"+fontState.size);
$bookBody.addClass("font-family-"+fontState.family);
$(".font-settings .font-family-list li:nth-child("+(fontState.family+1)+")").addClass("active");
if(fontState.theme !== 0)
$book.addClass("color-theme-"+fontState.theme);
//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 li", changeFontFamily);
$(document).on('click', "#font-settings-wrapper .color-theme-list button", changeColorTheme);
$(document).on('click', ".book-header .toggle-font-settings", togglePopover);
$(document).on('click', "#font-settings-wrapper .dropdown-menu", function(e){ e.stopPropagation(); });
$(document).on("click", closePopover);
};
return {
init: init
}
});
+8 -6
View File
@@ -1,21 +1,23 @@
define([ define([
"jQuery", "jQuery",
"utils/analytic", "core/events",
"core/state", "core/state",
"core/search", "core/search",
"core/progress", "core/progress",
"core/exercise", "core/exercise",
"core/quiz" "core/quiz"
], function($, analytic, state, search, progress, exercises, quiz) { ], function($, events, state, search, progress, exercises, quiz) {
var prev, next; var prev, next;
var githubCountStars, githubCountWatch; var githubCountStars, githubCountWatch;
var usePushState = !navigator.userAgent.match('CriOS') && (typeof history.pushState !== "undefined");
var updateHistory = function(url, title) { var updateHistory = function(url, title) {
history.pushState({ path: url }, title, url); history.pushState({ path: url }, title, url);
}; };
var handleNavigation = function(url, push) { var handleNavigation = function(url, push) {
if (typeof history.pushState === "undefined") { if (!usePushState) {
// Refresh the page to the new URL if pushState not supported // Refresh the page to the new URL if pushState not supported
location.href = url; location.href = url;
return return
@@ -23,6 +25,8 @@ define([
return $.get(url) return $.get(url)
.done(function (html) { .done(function (html) {
if (push) updateHistory(url, null);
html = html.replace( /<(\/?)(html|head|body)([^>]*)>/ig, function(a,b,c,d){ html = html.replace( /<(\/?)(html|head|body)([^>]*)>/ig, function(a,b,c,d){
return '<' + b + 'div' + ( b ? '' : ' data-element="' + c + '"' ) + d + '>'; return '<' + b + 'div' + ( b ? '' : ' data-element="' + c + '"' ) + d + '>';
}); });
@@ -49,8 +53,6 @@ define([
// Update state // Update state
state.update($("html")); state.update($("html"));
if (push) updateHistory(url, null);
preparePage(); preparePage();
}) })
.fail(function () { .fail(function () {
@@ -108,7 +110,7 @@ define([
} }
// Send to mixpanel // Send to mixpanel
analytic.track("page.view"); events.trigger("page.change");
}; };
var handlePagination = function (e) { var handlePagination = function (e) {
+8 -5
View File
@@ -1,9 +1,9 @@
define([ define([
"jQuery", "jQuery",
"utils/execute", "utils/execute",
"utils/analytic", "core/events",
"core/state" "core/state"
], function($, execute, analytic, state){ ], function($, execute, events, state){
// Bind an exercise // Bind an exercise
var prepareQuiz = function($quiz) { var prepareQuiz = function($quiz) {
@@ -14,14 +14,17 @@ define([
// Submit: test code // Submit: test code
$quiz.find(".action-submit").click(function(e) { $quiz.find(".action-submit").click(function(e) {
e.preventDefault(); e.preventDefault();
analytic.track("exercise.submit", {type: "quiz"}); events.trigger("exercise.submit", {type: "quiz"});
$quiz.find("tr.alert-danger,li.alert-danger").removeClass("alert-danger"); $quiz.find("tr.alert-danger,li.alert-danger").removeClass("alert-danger");
$quiz.find(".alert-success,.alert-danger").addClass("hidden"); $quiz.find(".alert-success,.alert-danger").addClass("hidden");
$quiz.find(".question").each(function(q) { $quiz.find(".question").each(function(q) {
var result = true; var result = true;
var $answers = $quiz.find(".question-answers").slice(q).find("input[type=radio], input[type=checkbox]");
$(this).find("input[type=radio],input[type=checkbox]").each(function(i) { var $questions = $(this).find(".question-content").find("input[type=radio], input[type=checkbox]");
var $answers = $(this).find(".question-answers").find("input[type=radio], input[type=checkbox]");
$questions.each(function(i) {
var correct = $(this).is(":checked") === $answers.slice(i).first().is(":checked"); var correct = $(this).is(":checked") === $answers.slice(i).first().is(":checked");
result = result && correct; result = result && correct;
if (!correct) { if (!correct) {
@@ -1,17 +1,18 @@
require([ define([
"jQuery", "jQuery",
"utils/storage", "utils/storage",
"utils/analytic",
"utils/sharing", "utils/sharing",
"core/events",
"core/font-settings",
"core/state", "core/state",
"core/keyboard", "core/keyboard",
"core/navigation", "core/navigation",
"core/progress", "core/progress",
"core/sidebar", "core/sidebar",
"core/search" "core/search"
], function($, storage, analytic, sharing, state, keyboard, navigation, progress, sidebar, search){ ], function($, storage, sharing, events, fontSettings, state, keyboard, navigation, progress, sidebar, search){
$(document).ready(function() { var start = function(config) {
var $book; var $book;
$book = state.$book; $book = state.$book;
@@ -34,5 +35,15 @@ require([
// Init navigation // Init navigation
navigation.init(); navigation.init();
});
//Init font settings
fontSettings.init();
events.trigger("start", config);
}
return {
start: start,
events: events
};
}); });
-33
View File
@@ -1,33 +0,0 @@
define([
"lodash",
"mixpanel"
], function(_, mixpanel) {
mixpanel.init("01eb2b950ae09a5fdb15a98dcc5ff20e", {
loaded: function() {
track("page.start");
}
});
var isAvailable = function() {
return (
typeof mixpanel !== "undefined" &&
typeof mixpanel.track === "function"
);
};
var track = function(e, data) {
if (!isAvailable()) {
console.warn("tracking not available!");
return;
}
console.log("track", e);
mixpanel.track(e, _.extend(data || {}, {
'domain': window.location.host
}));
};
return {
isAvailable: isAvailable,
track: track
};
});
-2
View File
@@ -1,2 +0,0 @@
(function(e,b){if(!b.__SV){var a,f,i,g;window.mixpanel=b;b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!==typeof d?c=b[d]=[]:d="mixpanel";c.people=c.people||[];c.toString=function(b){var a="mixpanel";"mixpanel"!==d&&(a+="."+d);b||(a+=" (stub)");return a};c.people.toString=function(){return c.toString(1)+".people (stub)"};i="disable track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.track_charge people.clear_charges people.delete_user".split(" ");
for(g=0;g<i.length;g++)f(c,i[g]);b._i.push([a,e,d])};b.__SV=1.2;a=e.createElement("script");a.type="text/javascript";a.async=!0;a.src=("https:"===e.location.protocol?"https:":"http:")+'//cdn.mxpnl.com/libs/mixpanel-2.2.min.js';f=e.getElementsByTagName("script")[0];f.parentNode.insertBefore(a,f)}})(document,window.mixpanel||[]);
+49 -3
View File
@@ -6,6 +6,7 @@
left: 0px; left: 0px;
bottom: 0px; bottom: 0px;
color: @page-color;
background: @body-background; background: @body-background;
.transition(left 0.5s ease); .transition(left 0.5s ease);
@@ -23,7 +24,6 @@
outline: none; outline: none;
.page-inner { .page-inner {
font-family: @font-family-serif;
max-width: 800px; max-width: 800px;
margin: 0px auto; margin: 0px auto;
@@ -33,8 +33,6 @@
background: @page-background; background: @page-background;
border-radius: 2px; border-radius: 2px;
font-size: 16px;
line-height: 1.5em; line-height: 1.5em;
} }
@@ -57,6 +55,28 @@
min-height: calc(~"100% - 57px") min-height: calc(~"100% - 57px")
} }
} }
&.font-size-0{
font-size:@s-font-size;
}
&.font-size-1{
font-size:@m-font-size;
}
&.font-size-2{
font-size:@l-font-size;
}
&.font-size-3{
font-size:@xl-font-size;
}
&.font-size-4{
font-size:@xxl-font-size;
}
&.font-family-0{
font-family: @font-family-serif;
}
&.font-family-1{
font-family: @font-family-sans;
}
} }
&.with-summary { &.with-summary {
@@ -72,4 +92,30 @@
.transition(none) !important; .transition(none) !important;
} }
} }
&.color-theme-1{
.book-body {
color: @page-color-1;
background: @body-background-1;
.page-wrapper {
.page-inner {
section{
background: @page-background-1;
}
}
}
}
}
&.color-theme-2{
.book-body {
color: @page-color-2;
background: @body-background-2;
.page-wrapper {
.page-inner {
section{
background: @page-background-2;
}
}
}
}
}
} }
+159
View File
@@ -0,0 +1,159 @@
.book-header{
#font-settings-wrapper{
position:relative;
.dropdown-menu{
background-color:@header-background;
border-color:@sidebar-divider-color;
padding: 0px;
.dropdown-caret{
position: absolute;
top: 14px;
left: -8px;
width: 10px;
height: 18px;
float: left;
overflow: hidden;
.caret-outer{
position: absolute;
border-bottom: 9px solid transparent;
border-top: 9px solid transparent;
border-right: 9px solid rgba(0,0,0,0.1);
height: auto;
left: 0;
top: 0;
width: auto;
display: inline-block;
margin-left: -1px;
}
.caret-inner{
position: absolute;
display: inline-block;
margin-left: -1px;
top: 0;
left: 1px;
border-bottom: 9px solid transparent;
border-top: 9px solid transparent;
border-right: 9px solid @header-background;
}
}
button{
border: 0;
background-color:transparent;
color:@header-button-color;
&:hover{
color:@header-button-hover-color;
background-color:@header-button-hover-background;
}
}
#enlarge-font-size{
width: 50%;
font-size:1.4em;
}
#reduce-font-size{
width: 50.5%;
font-size:1em;
}
.btn-group-xs{
.btn{
width: 33.7%;
padding: initial;
}
}
.list-group{
margin: 0px 0;
.list-group-item{
cursor:pointer;
background-color:transparent;
border-color: @sidebar-divider-color;
border-width: 1px 0 !important;
&:hover{
color:@header-button-hover-color;
background-color:@header-button-hover-background !important;
}
&.active{
color:@header-button-hover-color;
background-color:@header-button-hover-background !important;
}
}
}
&.open{
display:block;
}
}
}
}
/*
* Theme 1
*/
.color-theme-1{
#font-settings-wrapper{
.dropdown-menu{
background-color:@sidebar-background-1;
border-color:@sidebar-divider-color-1;
.dropdown-caret .caret-inner{
border-right: 9px solid @sidebar-background-1;
}
button{
color:@header-button-color-1;
&:hover{
color:@header-button-hover-color-1;
background-color:@header-button-hover-background-1;
}
}
.list-group{
.list-group-item{
border-color:@sidebar-divider-color-1;
&:hover{
color:@header-button-hover-color-1;
background-color:@header-button-hover-background-1 !important;
}
&.active{
color:@header-button-hover-color-1;
background-color:@header-button-hover-background-1 !important;
}
}
}
}
}
}
/*
* Theme 2
*/
.color-theme-2{
#font-settings-wrapper{
.dropdown-menu{
background-color:@sidebar-background-2;
border-color:@sidebar-divider-color-2;
.dropdown-caret .caret-inner{
border-right: 9px solid @sidebar-background-2;
}
button{
color:@header-button-color-2;
&:hover{
color:@header-button-hover-color-2;
background-color:@header-button-hover-background-2;
}
}
.list-group{
.list-group-item{
border-color:@sidebar-divider-color-2;
&:hover{
color:@header-button-hover-color-2;
background-color:@header-button-hover-background-2 !important;
}
&.active{
color:@header-button-hover-color-2;
background-color:@header-button-hover-background-2 !important;
}
}
}
}
}
}
+28 -1
View File
@@ -3,7 +3,7 @@
font-family: @font-family-sans; font-family: @font-family-sans;
position: fixed; position: fixed;
overflow: hidden; overflow: visible;
top: 0px; top: 0px;
right: 0px; right: 0px;
left: 0px; left: 0px;
@@ -24,6 +24,7 @@
text-transform: uppercase; text-transform: uppercase;
line-height: @header-height; line-height: @header-height;
.box-shadow(none) !important; .box-shadow(none) !important;
position:relative;
&:hover { &:hover {
position: relative; position: relative;
@@ -64,4 +65,30 @@
.transition(none) !important; .transition(none) !important;
} }
} }
&.color-theme-1{
.book-header{
color: @header-color-1;
background: @header-background-1;
.btn {
color: @header-button-color-1;
&:hover {
color: @header-button-hover-color-1;
background: @header-button-hover-background-1;
}
}
}
}
&.color-theme-2{
.book-header{
color: @header-color-2;
background: @header-background-2;
.btn {
color: @header-button-color-2;
&:hover {
color: @header-button-hover-color-2;
background: @header-button-hover-background-2;
}
}
}
}
} }
+1 -1
View File
@@ -1,4 +1,4 @@
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ /* http://jmblog.github.io/color-themes-for-highlightjs */
/* Tomorrow Comment */ /* Tomorrow Comment */
.hljs-comment { .hljs-comment {
+9 -5
View File
@@ -1,8 +1,13 @@
.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal {
color:@page-color-1;
}
.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal {
color:@page-color-2;
}
.book .book-body .page-wrapper .page-inner section.normal { .book .book-body .page-wrapper .page-inner section.normal {
padding: 25px; padding: 25px;
padding-top: 15px; padding-top: 15px;
background-color: white; color:@page-color;
& > *:first-child { & > *:first-child {
margin-top: 0 !important; } margin-top: 0 !important; }
@@ -52,12 +57,12 @@
h1 { h1 {
font-size: 28px; font-size: 28px;
color: black; } }
h2 { h2 {
font-size: 24px; font-size: 24px;
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
color: black; } }
h3 { h3 {
font-size: 18px; } font-size: 18px; }
@@ -69,7 +74,6 @@
font-size: 14px; } font-size: 14px; }
h6 { h6 {
color: #777777;
font-size: 14px; } font-size: 14px; }
p, blockquote, ul, ol, dl, table, pre { p, blockquote, ul, ol, dl, table, pre {
+10
View File
@@ -43,3 +43,13 @@
} }
} }
} }
.book.color-theme-1 .book-body {
.navigation:hover{
background-color: @body-pagination-background-1;
}
}
.book.color-theme-2 .book-body {
.navigation:hover{
background-color: @body-pagination-background-2;
}
}
+32 -1
View File
@@ -17,7 +17,6 @@
position: relative; position: relative;
background: #fff; background: #fff;
margin-bottom: 20px; margin-bottom: 20px;
z-index: 10;
.bar { .bar {
height: @bar-height; height: @bar-height;
@@ -82,3 +81,35 @@
} }
} }
} }
.book.color-theme-1 .book-body {
.book-progress {
.bar {
background: @bar-background-1;
.inner {
background: @bar-progress-background-1;
}
}
}
.chapters .chapter{
background: @bar-background-1;
&.done{
background: @bar-progress-background-1;
}
}
}
.book.color-theme-2 .book-body {
.book-progress {
.bar {
background: @bar-background-2;
.inner {
background: @bar-progress-background-2;
}
}
}
.chapters .chapter{
background: @bar-background-2;
&.done{
background: @bar-progress-background-2;
}
}
}
+88
View File
@@ -141,3 +141,91 @@
} }
} }
} }
/*
* Theme 1
*/
.book.color-theme-1 {
.book-summary {
color: @sidebar-color-1;
background: @sidebar-background-1;
.book-search {
background: @sidebar-search-background-1;
input, input:focus {
border: 1px solid @sidebar-search-input-border-color-1;
}
}
ul.summary {
li {
&.divider {
background: @sidebar-divider-color-1;
}
i.fa-check {
color: @sidebar-icon-color-1;
}
&.done > a {
color: @sidebar-link-completed-1;
}
a, span {
color: @sidebar-link-color-1;
background: @sidebar-link-background-1;
}
&.active > a, a:hover {
color: @sidebar-link-hover-color-1;
background: @sidebar-link-hover-background-1;
}
}
}
}
}
/*
* Theme 2
*/
.book.color-theme-2{
.book-summary {
color: @sidebar-color-2;
background: @sidebar-background-2;
.book-search {
background: @sidebar-search-background-2;
input, input:focus {
border: 1px solid @sidebar-search-input-border-color-2;
}
}
ul.summary {
li {
&.divider {
background: @sidebar-divider-color-2;
}
i.fa-check {
color: @sidebar-icon-color-2;
}
&.done > a {
color: @sidebar-link-completed-2;
}
a, span {
color: @sidebar-link-color-2;
background: @sidebar-link-background-2;
}
&.active > a, a:hover {
color: @sidebar-link-hover-color-2;
background: @sidebar-link-hover-background-2;
}
}
}
}
}
+1
View File
@@ -9,6 +9,7 @@
@import "book/languages.less"; @import "book/languages.less";
@import "book/header.less"; @import "book/header.less";
@import "book/summary.less"; @import "book/summary.less";
@import "book/font-settings.less";
@import "book/body.less"; @import "book/body.less";
@import "book/exercise.less"; @import "book/exercise.less";
@import "book/quiz.less"; @import "book/quiz.less";
+1 -1
View File
@@ -1,4 +1,4 @@
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ /* http://jmblog.github.io/color-themes-for-highlightjs */
/* Tomorrow Comment */ /* Tomorrow Comment */
.hljs-comment, .hljs-comment,
+90
View File
@@ -38,6 +38,7 @@
@sidebar-link-completed-weight: normal; @sidebar-link-completed-weight: normal;
// Page // Page
@page-color: black;
@page-background: white; @page-background: white;
// Progress Bar // Progress Bar
@@ -79,5 +80,94 @@
@font-family-sans: "Open Sans", "Clear Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; @font-family-sans: "Open Sans", "Clear Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
@font-family-base: @font-family-serif; @font-family-base: @font-family-serif;
@FontPath: '@{staticPath}/fonts'; @FontPath: '@{staticPath}/fonts';
@fa-font-path: "@{FontPath}/fontawesome"; @fa-font-path: "@{FontPath}/fontawesome";
@s-font-size: 1.2rem;
@m-font-size: 1.4rem;
@l-font-size: 1.6rem;
@xl-font-size: 2.2rem;
@xxl-font-size: 4.0rem;
/*
,--------.,--.
'--. .--'| ,---. ,---. ,--,--,--. ,---. ,---. .--.
| | | .-. || .-. :| || .-. :( .-' '--'
| | | | | |\ --.| | | |\ --..-' `).--.
`--' `--' `--' `----'`--`--`--' `----'`----' '--'
*/
// Header
@header-color-1: #AFA790;
@header-color-2: #7e888b;
@header-background-1: #ECE3C4;
@header-background-2: #1d1f21;
@header-button-color-1: #AFA790;
@header-button-color-2: #7e888b;
@header-button-hover-color-1: #73553C;
@header-button-hover-color-2: #C9C9C9;
@header-button-hover-background-1: #E2DABE;
@header-button-hover-background-2: #0B0D0E;
// Body
@body-background-1: #F3EACB;
@body-background-2: #1d1f21;
@body-pagination-background-1: #FFFAEA;
@body-pagination-background-2: #33404D;
// Sidebar
@sidebar-color-1: #AFA790;
@sidebar-color-2: hsl(207, 15%, 80%);
@sidebar-background-1: #F3EACB;
@sidebar-background-2: #111111;
@sidebar-search-background-1: transparent;
@sidebar-search-background-2: transparent;
@sidebar-search-input-border-color-1: #D6CFBA;
@sidebar-search-input-border-color-2: #c4cdd4;
@sidebar-divider-color-1: hsl(207, 15%, 85%);
@sidebar-divider-color-2: #1d1f21;
@sidebar-link-color-1: #877F6A;
@sidebar-link-color-2: hsl(207, 15%, 50%);
@sidebar-link-background-1: transparent;
@sidebar-link-background-2: transparent;
@sidebar-link-hover-color-1: #704214;
@sidebar-link-hover-color-2: hsl(207, 100%, 50%);
@sidebar-link-hover-background-1: transparent;
@sidebar-link-hover-background-2: transparent;
@sidebar-icon-color-1: @bar-progress-background;
@sidebar-icon-color-2: @bar-progress-background;
@sidebar-link-completed-1: hsl(207, 15%, 25%);
@sidebar-link-completed-2: hsl(207, 15%, 25%);
@sidebar-link-completed-weight-1: normal;
@sidebar-link-completed-weight-2: normal;
// Page
@page-color-1: #704214;
@page-color-2: #a4b1b1;
@page-background-1: #F3EACB;
@page-background-2: #1d1f21;
@bar-background-1: #F3EACB;
@bar-background-2: #1d1f21;
@bar-progress-background-1: #704214;
@bar-progress-background-2: hsl(120, 60%, 50%);
// Basics of a navbar
@navbar-default-border-1: #d5d5d5;
@navbar-default-border-2: #d5d5d5;
// Navbar brand label
@navbar-default-color-1: #333;
@navbar-default-color-2: #333;
@navbar-default-link-color-1: #333;
@navbar-default-link-color-2: #333;
@navbar-default-brand-color-1: @navbar-default-link-color;
@navbar-default-brand-color-2: @navbar-default-link-color;
@navbar-default-brand-hover-color-1: @navbar-default-link-color;
@navbar-default-brand-hover-color-2: @navbar-default-link-color;
@navbar-default-brand-hover-bg-1: transparent;
@navbar-default-brand-hover-bg-2: transparent;
@@ -0,0 +1,22 @@
<div class="dropdown-menu font-settings">
<div class="dropdown-caret">
<span class="caret-outer"></span>
<span class="caret-inner"></span>
</div>
<div class="btn-group btn-block">
<button id="reduce-font-size" class="btn btn-default">A</button>
<button id="enlarge-font-size" class="btn btn-default">A</button>
</div>
<ul class="list-group font-family-list">
<li class="list-group-item" data-font="0">Serif</li>
<li class="list-group-item" data-font="1">Sans</li>
</ul>
<div class="btn-group btn-group-xs btn-block color-theme-list">
<button type="button" class="btn btn-default" id="color-theme-preview-0" data-theme="0">White</button>
<button type="button" class="btn btn-default" id="color-theme-preview-1" data-theme="1">Sepia</button>
<button type="button" class="btn btn-default" id="color-theme-preview-2" data-theme="2">Night</button>
</div>
</div>
@@ -5,6 +5,11 @@
{% endif %} {% endif %}
<a href="#" class="btn pull-left toggle-summary"><i class="fa fa-align-justify"></i></a> <a href="#" class="btn pull-left toggle-summary"><i class="fa fa-align-justify"></i></a>
<a href="#" class="btn pull-left toggle-search"><i class="fa fa-search"></i></a> <a href="#" class="btn pull-left toggle-search"><i class="fa fa-search"></i></a>
<span id="font-settings-wrapper">
<a href="#" class="btn pull-left toggle-font-settings"><i class="fa fa-font"></i>
</a>
{% include "./font-settings.html" %}
</span>
<!-- Actions Right --> <!-- Actions Right -->
<a href="#" target="_blank" class="btn pull-right" data-sharing="google-plus"><i class="fa fa-google-plus"></i></a> <a href="#" target="_blank" class="btn pull-right" data-sharing="google-plus"><i class="fa fa-google-plus"></i></a>
+1 -1
View File
@@ -35,10 +35,10 @@
{% endblock %} {% endblock %}
</head> </head>
<body> <body>
{% block content %}{% endblock %}
{% block style %} {% block style %}
<link rel="stylesheet" href="{{ staticBase }}/style.css"> <link rel="stylesheet" href="{{ staticBase }}/style.css">
{% endblock %} {% endblock %}
{% block content %}{% endblock %}
{% block javascript %} {% block javascript %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/ace.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/ace.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/mode-javascript.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/mode-javascript.js"></script>
+2
View File
@@ -77,9 +77,11 @@
<h1>{{ item.title }}</h1> <h1>{{ item.title }}</h1>
</article> </article>
{% if pages[item.path] %}
<article> <article>
{{ articleContent(pages[item.path].content) }} {{ articleContent(pages[item.path].content) }}
</article> </article>
{% endif %}
{% if item.articles.length > 0 %} {% if item.articles.length > 0 %}
{% for article in item.articles %} {% for article in item.articles %}
+28
View File
@@ -35,3 +35,31 @@
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block javascript %}
{% parent %}
{% for resource in plugins.resources.js %}
{% if resource.url %}
<script src="{{ resource.url }}"></script>
{% else %}
<script src="{{ staticBase }}/plugins/{{ resource.path }}"></script>
{% endif %}
{% endfor %}
<script>
require(["gitbook"], function(gitbook) {
var config = {% autoescape false %}{{ pluginsConfig }}{% endautoescape %};
gitbook.start(config);
});
</script>
{% endblock %}
{% block style %}
{% parent %}
{% for resource in plugins.resources.css %}
{% if resource.url %}
<link rel="stylesheet" href="{{ resource.url }}">
{% else %}
<link rel="stylesheet" href="{{ staticBase }}/plugins/{{ resource.path }}">
{% endif %}
{% endfor %}
{% endblock %}