mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-27 04:29:05 +00:00
Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 32eafefc45 | |||
| 336999a34e | |||
| 1bf5f3b984 | |||
| c76d070237 | |||
| 8c7da1351f | |||
| 34588c6092 | |||
| 9f00bf8060 | |||
| f4a17b182f | |||
| 5c3b345fa6 | |||
| 46321fffd9 | |||
| 89ba17de84 | |||
| 115fba837c | |||
| 4df51546b2 | |||
| f68aaf7ff9 | |||
| e3d1746ba3 | |||
| a0d5646034 | |||
| 2f8af5f136 | |||
| 8b7abbd6f4 | |||
| f228f646eb | |||
| 45891d80c7 | |||
| ac900aa37f | |||
| 422cbed796 | |||
| 38cbbe46aa | |||
| c1b57a387b | |||
| aaa9ae8bb7 | |||
| 6340a64cff | |||
| 96bcfa4e20 | |||
| af0f56b26b | |||
| aed5befa48 | |||
| 8e37da672d | |||
| c0f68c45e2 | |||
| 47fdd73791 | |||
| 7c0b88cf2e | |||
| bf21633b6c | |||
| ad9b1fd6f7 | |||
| d245895085 | |||
| c7439d12f7 | |||
| 8d6923ce2c | |||
| b21fb24a82 | |||
| e7be132194 | |||
| 0e1717f4bb | |||
| 276dbe29c7 | |||
| 21fd5924b8 | |||
| 443def6cfd | |||
| 75464574d7 | |||
| 86f05c68c5 | |||
| 8dd407eaa8 | |||
| ea7ad10e8a | |||
| 62ec695dc8 | |||
| ca37ac73a9 | |||
| 96edd50c8f | |||
| 3993c1e35a | |||
| 7c65adc9b2 | |||
| 9b96ef9bae | |||
| e97d1941dc | |||
| 0b0ac46e34 | |||
| 622bbcb8ea |
+4
-1
@@ -24,4 +24,7 @@ build/Release
|
||||
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
|
||||
node_modules
|
||||
|
||||
theme/javascript/vendors
|
||||
theme/javascript/vendors
|
||||
|
||||
# vim swapfile
|
||||
*.swp
|
||||
|
||||
@@ -3,7 +3,7 @@ GitBook
|
||||
|
||||
[](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/). An [editor](https://github.com/GitbookIO/editor) is available for Windows, Mac and Linux. You can follow [@GitBookIO](https://twitter.com/GitBookIO) on Twitter.
|
||||
GitBook is a command line tool (and Node.js library) for building beautiful books and exercises using GitHub/Git and Markdown. You can see an example: [Learn Javascript](https://www.gitbook.io/book/GitBookIO/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. Complete documentation is available at [help.gitbook.io](http://help.gitbook.io/).
|
||||
|
||||

|
||||
|
||||
@@ -31,27 +31,27 @@ Options for commands `build` and `serve` are:
|
||||
|
||||
```
|
||||
-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, mobi, epub
|
||||
--config <config file> Configuration file to use, defaults to book.json
|
||||
```
|
||||
|
||||
GitBook load the default configuration from a `book.json` file in the repository if it exists.
|
||||
GitBook loads the default configuration from a `book.json` file in the repository if it exists.
|
||||
|
||||
Here are the options that can be stored in this file:
|
||||
|
||||
```
|
||||
{
|
||||
// Folders to use for output (caution: it override the value from the command line)
|
||||
// Folders to use for output (caution: it overrides the value from the command line)
|
||||
"output": null,
|
||||
|
||||
// Generator to use for building (caution: it override the value from the command line)
|
||||
// Generator to use for building (caution: it overrides the value from the command line)
|
||||
"generator": "site",
|
||||
|
||||
// Book title and description (defaults are extracted from the README)
|
||||
"title": null,
|
||||
"description": null,
|
||||
|
||||
// GitHub informations (defaults are extracted using git)
|
||||
// GitHub information (defaults are extracted using git)
|
||||
"github": null,
|
||||
"githubHost": "https://github.com/",
|
||||
|
||||
@@ -67,9 +67,9 @@ Here are the options that can be stored in this file:
|
||||
}
|
||||
},
|
||||
|
||||
// set another theme with your own layout
|
||||
// it's recommended to use plugins or add more options for default theme, though
|
||||
// see https://github.com/GitbookIO/gitbook/issues/209
|
||||
// Set another theme with your own layout
|
||||
// It's recommended to use plugins or add more options for default theme, though
|
||||
// See https://github.com/GitbookIO/gitbook/issues/209
|
||||
"theme": "./localtheme",
|
||||
|
||||
// Links in template (null: default, false: remove, string: new value)
|
||||
@@ -98,19 +98,18 @@ You can publish your books to our index by visiting [GitBook.io](http://www.gitb
|
||||
|
||||
GitBook can generate your book in the following formats:
|
||||
|
||||
* **Static Website**: This is the default format, it generates a complete interactive static website that can be for example hosted on GitHub Pages.
|
||||
* **PDF**: A complete PDF book with exercise solutions at the end of the book. Generate to this format using: ```gitbook pdf ./myrepo```, you need to have [gitbook-pdf](https://github.com/GitbookIO/gitbook-pdf) installed.
|
||||
* **eBook**: A complete eBook with exercise solutions at the end of the book. Generate to this format using: ```gitbook ebook ./myrepo```, you need to have [ebook-convert](http://manual.calibre-ebook.com/cli/ebook-convert.html) installed.
|
||||
* **Single Page**: The book will be stored in a single printable HTML page, this format is used for conversion to PDF or eBook. Generate to this format using: ```gitbook build ./myrepo -f page```.
|
||||
* **JSON**: This format is used for debugging or extracting metadata from a book. Generate to this format using: ```gitbook build ./myrepo -f json```.
|
||||
* **Static Website**: This is the default format. It generates a complete interactive static website that can be, for example, hosted on GitHub Pages.
|
||||
* **eBook**: A complete eBook with exercise solutions at the end of the book. Generate this format using: ```gitbook ebook ./myrepo```. You need to have [ebook-convert](http://manual.calibre-ebook.com/cli/ebook-convert.html) installed. The output format could be **PDF**, **ePub** or **MOBI**.
|
||||
* **Single Page**: The book will be stored in a single printable HTML page. This format is used for conversion to PDF or eBook. Generate this format using: ```gitbook build ./myrepo -f page```.
|
||||
* **JSON**: This format is used for debugging or extracting metadata from a book. Generate this format using: ```gitbook build ./myrepo -f json```.
|
||||
|
||||
## Book Format
|
||||
|
||||
A book is a GitHub repository containing at least 2 files: `README.md` and `SUMMARY.md`.
|
||||
A book is a Git repository containing at least 2 files: `README.md` and `SUMMARY.md`.
|
||||
|
||||
#### README.md
|
||||
|
||||
As usual, it should contains an introduction for your book. It will be automatically added to the final summary.
|
||||
Typically, this should be the introduction for your book. It will be automatically added to the final summary.
|
||||
|
||||
#### SUMMARY.md
|
||||
|
||||
@@ -130,11 +129,11 @@ This is the summary of my book.
|
||||
* [example 1](section2/example1.md)
|
||||
```
|
||||
|
||||
Files that are not included in the `SUMMARY.md` will not be processed by `gitbook`.
|
||||
Files that are not included in `SUMMARY.md` will not be processed by `gitbook`.
|
||||
|
||||
#### Exercises
|
||||
|
||||
A book can contain interactive exercises (currently only in Javascript but Python and Ruby are coming soon ;) ). An exercise is a code challenge provided to the reader, which is given a code editor to write a solution which is checked against the book author's validation code.
|
||||
A book can contain interactive exercises (currently only in Javascript but Python and Ruby are coming soon ;) ). An exercise is a code challenge provided to the reader, who is given a code editor to write a solution which is checked against the book author's validation code.
|
||||
|
||||
An exercise is defined by 4 simple parts:
|
||||
|
||||
@@ -143,7 +142,7 @@ An exercise is defined by 4 simple parts:
|
||||
* **Solution** code, being a correct solution to the exercise
|
||||
* **Validation** code that tests the correctness of the user's input
|
||||
|
||||
Exercises need to start and finish with a separation bar (```---``` or ```***```). It should contain 3 code elements (**base**, **solution** and **validation**). It can contain a 4th element that provides **context** code (functions, imports of libraries etc ... that shouldn't be displayed to the user).
|
||||
Exercises need to start and finish with a separation bar (```---``` or ```***```). It should contain 3 code elements (**base**, **solution** and **validation**). It can contain a 4th element that provides **context** code (functions, imports of libraries, etc which shouldn't be displayed to the user).
|
||||
|
||||
---
|
||||
|
||||
@@ -185,16 +184,22 @@ You can see a complete example with the [Learn Git](https://github.com/GitbookIO
|
||||
|
||||
#### 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`)
|
||||
|
||||
#### Cover
|
||||
|
||||
A cover image can be set by creating a file: **/cover.jpg** or **cover.png**.
|
||||
The best resolution is **1600x2400**. The generation of the cover can be done automatically using the plugin [autocover](https://github.com/GitbookIO/plugin-autocover).
|
||||
A cover image can be set by creating a file: **/cover.jpg**.
|
||||
The best resolution is **1800x2360**. The generation of the cover can be done automatically using the plugin [autocover](https://github.com/GitbookIO/plugin-autocover).
|
||||
|
||||
A small version of the cover can also be set by creating a file: **/cover_small.jpg**.
|
||||
|
||||
#### Publish your book
|
||||
|
||||
The platform [GitBook.io](https://www.gitbook.io/) is like an "Heroku for books": you can create a book on it (public, paid, or private) and update it using **git push**.
|
||||
|
||||
#### Plugins
|
||||
|
||||
Plugins can used to extend your book's functionality. Read [GitbookIO/plugin](https://github.com/GitbookIO/plugin) for more information about how to build a plugin for gitbook.
|
||||
Plugins can used to extend your book's functionality. Read [GitbookIO/plugin](https://github.com/GitbookIO/plugin) for more information about how to build a plugin for GitBook.
|
||||
|
||||
##### Default plugins:
|
||||
|
||||
@@ -211,3 +216,4 @@ Plugins can used to extend your book's functionality. Read [GitbookIO/plugin](ht
|
||||
* [Revealable sections](https://github.com/mrpotes/gitbook-plugin-reveal): Reveal sections of the page using buttons made from the first title in each section
|
||||
* [Markdown within HTML](https://github.com/mrpotes/gitbook-plugin-nestedmd): Process markdown within HTML blocks - allows custom layout options for individual pages
|
||||
* [Bootstrap JavaScript plugins](https://github.com/mrpotes/gitbook-plugin-bootstrapjs): Use the [Bootstrap JavaScript plugins](http://getbootstrap.com/javascript) in your online GitBook
|
||||
* [Piwik Open Analytics](https://github.com/emmanuel-keller/gitbook-plugin-piwik): Piwik Open Analytics tracking for your book
|
||||
|
||||
+7
-1
@@ -16,6 +16,11 @@ var buildCommand = function(command) {
|
||||
};
|
||||
|
||||
|
||||
var buildEbookCommand = function(command) {
|
||||
return buildCommand(command)
|
||||
.option('-c, --cover <path>', 'Cover image, default is cover.jpg if exists');
|
||||
};
|
||||
|
||||
var makeBuildFunc = function(converter) {
|
||||
return function(dir, options) {
|
||||
dir = dir || process.cwd();
|
||||
@@ -43,5 +48,6 @@ var makeBuildFunc = function(converter) {
|
||||
module.exports = {
|
||||
folder: makeBuildFunc(generate.folder),
|
||||
file: makeBuildFunc(generate.file),
|
||||
command: buildCommand
|
||||
command: buildCommand,
|
||||
commandEbook: buildEbookCommand
|
||||
};
|
||||
|
||||
+50
-14
@@ -4,6 +4,7 @@ var Q = require('q');
|
||||
var _ = require('lodash');
|
||||
var path = require('path');
|
||||
var prog = require('commander');
|
||||
var tinylr = require('tiny-lr-fork');
|
||||
|
||||
var pkg = require('../package.json');
|
||||
var generators = require("../lib/generate").generators;
|
||||
@@ -29,12 +30,24 @@ build.command(prog.command('serve [source_dir]'))
|
||||
.action(function(dir, options) {
|
||||
var server = new Server();
|
||||
|
||||
// init livereload server
|
||||
var lrOptions = {port: 35729};
|
||||
var lrServer = tinylr(lrOptions);
|
||||
var lrPath = undefined;
|
||||
lrServer.listen(lrOptions.port, function(err) {
|
||||
if (err) { return console.log(err); }
|
||||
console.log('Live reload server started on port: ' + lrOptions.port);
|
||||
});
|
||||
|
||||
var generate = function() {
|
||||
if (server.isRunning()) console.log("Stopping server");
|
||||
|
||||
server.stop()
|
||||
.then(function() {
|
||||
return build.folder(dir, options);
|
||||
return build.folder(dir, _.extend(options || {}, {
|
||||
cache: false,
|
||||
defaultsPlugins: ["livereload"]
|
||||
}));
|
||||
})
|
||||
.then(function(_options) {
|
||||
console.log();
|
||||
@@ -43,9 +56,16 @@ build.command(prog.command('serve [source_dir]'))
|
||||
.then(function() {
|
||||
console.log('Serving book on http://localhost:'+options.port);
|
||||
|
||||
if (lrPath) {
|
||||
// trigger livereload
|
||||
lrServer.changed({body:{files:[lrPath]}})
|
||||
}
|
||||
|
||||
if (!options.watch) return;
|
||||
return utils.watch(_options.input)
|
||||
.then(function(filepath) {
|
||||
// set livereload path
|
||||
lrPath = filepath;
|
||||
console.log("Restart after change in files");
|
||||
console.log('');
|
||||
return generate();
|
||||
@@ -60,19 +80,8 @@ build.command(prog.command('serve [source_dir]'))
|
||||
generate();
|
||||
});
|
||||
|
||||
build.command(prog.command('pdf [source_dir]'))
|
||||
.description('Build a gitbook as a PDF')
|
||||
.option('-pf, --paperformat <format>', 'PDF paper format (default is A4): "5in*7.5in", "10cm*20cm", "A4", "Letter"')
|
||||
.action(function(dir, options) {
|
||||
build.file(dir, _.extend(options, {
|
||||
extension: "pdf",
|
||||
format: "pdf"
|
||||
}));
|
||||
});
|
||||
|
||||
build.command(prog.command('ebook [source_dir]'))
|
||||
.description('Build a gitbook as a eBook')
|
||||
.option('-c, --cover <path>', 'Cover image, default is cover.png if exists')
|
||||
build.commandEbook(prog.command('ebook [source_dir]'))
|
||||
.description('Build a gitbook as a eBook (format detected according to the extension)')
|
||||
.action(function(dir, options) {
|
||||
var ext = options.output ? path.extname(options.output) : "epub";
|
||||
|
||||
@@ -82,6 +91,33 @@ build.command(prog.command('ebook [source_dir]'))
|
||||
}));
|
||||
});
|
||||
|
||||
build.commandEbook(prog.command('pdf [source_dir]'))
|
||||
.description('Build a gitbook as a PDF')
|
||||
.action(function(dir, options) {
|
||||
build.file(dir, _.extend(options, {
|
||||
extension: "pdf",
|
||||
format: "ebook"
|
||||
}));
|
||||
});
|
||||
|
||||
build.commandEbook(prog.command('epub [source_dir]'))
|
||||
.description('Build a gitbook as a ePub book')
|
||||
.action(function(dir, options) {
|
||||
build.file(dir, _.extend(options, {
|
||||
extension: "epub",
|
||||
format: "ebook"
|
||||
}));
|
||||
});
|
||||
|
||||
build.commandEbook(prog.command('mobi [source_dir]'))
|
||||
.description('Build a gitbook as a Mobi book')
|
||||
.action(function(dir, options) {
|
||||
build.file(dir, _.extend(options, {
|
||||
extension: "mobi",
|
||||
format: "ebook"
|
||||
}));
|
||||
});
|
||||
|
||||
prog
|
||||
.command('init [source_dir]')
|
||||
.description('Create files and folders based on contents of SUMMARY.md')
|
||||
|
||||
+26
-13
@@ -7,6 +7,7 @@ var exec = require('child_process').exec;
|
||||
var fs = require("fs");
|
||||
var parse = require("../../parse");
|
||||
var BaseGenerator = require("../page");
|
||||
var stringUtils = require("../../utils/string");
|
||||
|
||||
/*
|
||||
* This generator inherits from the single page generator
|
||||
@@ -19,10 +20,6 @@ var Generator = function() {
|
||||
this.options = _.defaults(this.options, {
|
||||
extension: "epub"
|
||||
});
|
||||
|
||||
if (!this.options.cover && fs.existsSync(path.join(this.options.input, "cover.png"))) {
|
||||
this.options.cover = path.join(this.options.input, "cover.png")
|
||||
}
|
||||
};
|
||||
util.inherits(Generator, BaseGenerator);
|
||||
|
||||
@@ -32,23 +29,39 @@ Generator.prototype.finish = function() {
|
||||
return BaseGenerator.prototype.finish.apply(this)
|
||||
.then(function() {
|
||||
var d = Q.defer();
|
||||
var format = that.options.extension || path.extname(that.options.output);
|
||||
|
||||
if (!that.options.cover && fs.existsSync(path.join(that.options.output, "cover.jpg"))) {
|
||||
that.options.cover = path.join(that.options.output, "cover.jpg");
|
||||
}
|
||||
|
||||
var _options = {
|
||||
"--cover": that.options.cover
|
||||
"--cover": that.options.cover,
|
||||
"--title": that.options.title,
|
||||
"--comments": that.options.description,
|
||||
"--authors": that.options.author,
|
||||
"--level1-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-1 ')]",
|
||||
"--level2-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-2 ')]",
|
||||
"--level3-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-3 ')]"
|
||||
};
|
||||
|
||||
if (format == "pdf") {
|
||||
_.extend(_options, {
|
||||
"--margin-left": "62",
|
||||
"--margin-right": "62",
|
||||
"--margin-top": "36",
|
||||
"--margin-bottom": "36",
|
||||
"--pdf-add-toc": true,
|
||||
"--pdf-default-font-size": "12",
|
||||
"--paper-size": "a4",
|
||||
});
|
||||
}
|
||||
|
||||
var command = [
|
||||
"ebook-convert",
|
||||
path.join(that.options.output, "index.html"),
|
||||
path.join(that.options.output, "index."+that.options.extension),
|
||||
_.chain(_options)
|
||||
.map(function(value, key) {
|
||||
if (value == null) return null;
|
||||
return key+"="+value;
|
||||
})
|
||||
.compact()
|
||||
.value()
|
||||
.join(" ")
|
||||
stringUtils.optionsToShellArgs(_options)
|
||||
].join(" ");
|
||||
|
||||
exec(command, function (error, stdout, stderr) {
|
||||
|
||||
@@ -9,6 +9,7 @@ var BaseGenerator = function(options) {
|
||||
this.options = options;
|
||||
|
||||
this.options.plugins = Plugin.normalizeNames(this.options.plugins);
|
||||
this.options.plugins = _.union(this.options.plugins, this.options.defaultsPlugins);
|
||||
this.plugins = [];
|
||||
};
|
||||
|
||||
|
||||
+50
-34
@@ -11,7 +11,6 @@ var Plugin = require("./plugin");
|
||||
var generators = {
|
||||
"site": require("./site"),
|
||||
"page": require("./page"),
|
||||
"pdf": require("./pdf"),
|
||||
"ebook": require("./ebook"),
|
||||
"json": require("./json")
|
||||
};
|
||||
@@ -64,6 +63,7 @@ var generate = function(options) {
|
||||
// Plugins
|
||||
"plugins": [],
|
||||
"pluginsConfig": {},
|
||||
"defaultsPlugins": [], // Default plugins that can't be set in book.json
|
||||
|
||||
// Links
|
||||
"links": {
|
||||
@@ -84,7 +84,39 @@ var generate = function(options) {
|
||||
}
|
||||
|
||||
// Check files to get folder type (book, multilanguage book or neither)
|
||||
return containsFiles(options.input, ['LANGS.md'])
|
||||
return Q()
|
||||
|
||||
// Read config file
|
||||
.then(function() {
|
||||
return fs.readFile(path.resolve(options.input, options.configFile))
|
||||
.then(function(_config) {
|
||||
// Extend current config
|
||||
_config = JSON.parse(_config);
|
||||
_config = _.omit(_config, 'input', 'configFile', 'defaultsPlugins');
|
||||
|
||||
_.extend(options, _config);
|
||||
}, function() {
|
||||
// No config file: not a big deal
|
||||
return Q();
|
||||
});
|
||||
})
|
||||
|
||||
// Read readme
|
||||
.then(function() {
|
||||
return fs.readFile(path.join(options.input, "README.md"), "utf-8")
|
||||
.then(function(_readme) {
|
||||
_readme = parse.readme(_readme);
|
||||
|
||||
options.title = options.title || _readme.title;
|
||||
options.description = options.description || _readme.description || defaultDescription;
|
||||
});
|
||||
})
|
||||
|
||||
// Detect multi-languages book
|
||||
.then(function() {
|
||||
return containsFiles(options.input, ['LANGS.md'])
|
||||
})
|
||||
|
||||
.then(function(isMultiLang) {
|
||||
// Multi language book
|
||||
if(isMultiLang) {
|
||||
@@ -98,24 +130,34 @@ var generate = function(options) {
|
||||
|
||||
|
||||
var generateMultiLang = function(options) {
|
||||
var langsSummary;
|
||||
options.output = options.output || path.join(options.input, "_book");
|
||||
|
||||
// Multi-languages book
|
||||
return fs.readFile(path.join(options.input, "LANGS.md"), "utf-8")
|
||||
|
||||
// Generate sub-books
|
||||
// Clean output folder
|
||||
.then(function(_langsSummary) {
|
||||
options.langsSummary = parse.langs(_langsSummary);
|
||||
langsSummary = _langsSummary;
|
||||
return fs.remove(options.output);
|
||||
})
|
||||
.then(function() {
|
||||
return fs.mkdirp(options.output);
|
||||
})
|
||||
|
||||
// Generate sub-books
|
||||
.then(function() {
|
||||
options.langsSummary = parse.langs(langsSummary);
|
||||
|
||||
// Generated a book for each valid entry
|
||||
return Q.all(
|
||||
_.map(options.langsSummary.list, function(entry) {
|
||||
return _.reduce(options.langsSummary.list, function(prev, entry) {
|
||||
return prev.then(function() {
|
||||
return generate(_.extend({}, options, {
|
||||
input: path.join(options.input, entry.path),
|
||||
output: path.join(options.output, entry.path)
|
||||
}));
|
||||
})
|
||||
);
|
||||
}, Q());
|
||||
})
|
||||
|
||||
.then(function() {
|
||||
@@ -150,21 +192,6 @@ var generateBook = function(options) {
|
||||
}
|
||||
})
|
||||
|
||||
// Read config file
|
||||
.then(function() {
|
||||
return fs.readFile(path.resolve(options.input, options.configFile))
|
||||
.then(function(_config) {
|
||||
// Extend current config
|
||||
_config = JSON.parse(_config);
|
||||
_config = _.omit(_config, 'input', 'configFile');
|
||||
|
||||
_.extend(options, _config);
|
||||
}, function() {
|
||||
// No config file: not a big deal
|
||||
return Q();
|
||||
});
|
||||
})
|
||||
|
||||
// Clean output folder
|
||||
.then(function() {
|
||||
return fs.remove(options.output);
|
||||
@@ -210,22 +237,11 @@ var generateBook = function(options) {
|
||||
return loadGenerator(options);
|
||||
})
|
||||
|
||||
// Detect multi-languages book
|
||||
// Convert files
|
||||
.then(function(generator) {
|
||||
// Generate the book
|
||||
return Q()
|
||||
|
||||
// Read readme
|
||||
.then(function() {
|
||||
return fs.readFile(path.join(options.input, "README.md"), "utf-8")
|
||||
.then(function(_readme) {
|
||||
_readme = parse.readme(_readme);
|
||||
|
||||
options.title = options.title || _readme.title;
|
||||
options.description = options.description || _readme.description || defaultDescription;
|
||||
});
|
||||
})
|
||||
|
||||
// Get summary
|
||||
.then(function() {
|
||||
return fs.readFile(path.join(options.input, "SUMMARY.md"), "utf-8")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
var util = require("util");
|
||||
var path = require("path");
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
|
||||
var fs = require("../fs");
|
||||
var parse = require("../../parse");
|
||||
@@ -40,6 +41,35 @@ Generator.prototype.convertFile = function(content, input) {
|
||||
});
|
||||
};
|
||||
|
||||
// Generate languages index
|
||||
// Contains the first languages readme and langs infos
|
||||
Generator.prototype.langsIndex = function(langs) {
|
||||
var that = this;
|
||||
|
||||
if (langs.list.length == 0) return Q.reject("Need at least one language");
|
||||
|
||||
var mainLang = _.first(langs.list).lang;
|
||||
console.log("Main language is", mainLang);
|
||||
|
||||
return Q()
|
||||
.then(function() {
|
||||
return fs.readFile(
|
||||
path.join(that.options.output, mainLang, "README.json")
|
||||
);
|
||||
})
|
||||
.then(function(content) {
|
||||
var json = JSON.parse(content);
|
||||
_.extend(json, {
|
||||
langs: langs.list
|
||||
});
|
||||
|
||||
return fs.writeFile(
|
||||
path.join(that.options.output, "README.json"),
|
||||
JSON.stringify(json, null, 4)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
Generator.prototype.finish = function() {
|
||||
// ignore
|
||||
};
|
||||
|
||||
@@ -18,6 +18,11 @@ swig.setFilter('code', function(code, lang) {
|
||||
}
|
||||
});
|
||||
|
||||
// Convert a level into a deep level
|
||||
swig.setFilter('lvl', function(lvl) {
|
||||
return lvl.split(".").length;
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* This generator will generate a simple index.html which can be converted as a PDF
|
||||
@@ -66,11 +71,14 @@ Generator.prototype.finish = function() {
|
||||
var basePath = ".";
|
||||
var output = path.join(this.options.output, "index.html");
|
||||
|
||||
var progress = parse.progress(this.options.navigation, "README.md");
|
||||
|
||||
return Q()
|
||||
// Generate html
|
||||
.then(function(pages) {
|
||||
return that._writeTemplate(that.template, {
|
||||
pages: that.pages,
|
||||
progress: progress,
|
||||
|
||||
basePath: basePath,
|
||||
staticBase: path.join(basePath, "gitbook"),
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
var util = require("util");
|
||||
var path = require("path");
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
var exec = require('child_process').exec;
|
||||
|
||||
var fs = require("../fs");
|
||||
var parse = require("../../parse");
|
||||
var BaseGenerator = require("../page");
|
||||
|
||||
/*
|
||||
* This generator inherits from the single page generator
|
||||
* and convert the page output to pdf using gitbook-pdf
|
||||
*/
|
||||
var Generator = function() {
|
||||
BaseGenerator.apply(this, arguments);
|
||||
|
||||
// Options for PDF generation
|
||||
this.options = _.defaults(this.options, {
|
||||
paperformat: "A4"
|
||||
});
|
||||
};
|
||||
util.inherits(Generator, BaseGenerator);
|
||||
|
||||
Generator.prototype.finish = function() {
|
||||
var that = this;
|
||||
|
||||
return BaseGenerator.prototype.finish.apply(this)
|
||||
.then(function() {
|
||||
var d = Q.defer();
|
||||
|
||||
var command = [
|
||||
"gitbook-pdf",
|
||||
"generate",
|
||||
path.join(that.options.output, "index.html"),
|
||||
path.join(that.options.output, "index.pdf"),
|
||||
"--format="+that.options.paperformat
|
||||
].join(" ");
|
||||
|
||||
exec(command, function (error, stdout, stderr) {
|
||||
if (error) {
|
||||
if (error.code == 127) {
|
||||
error.message = "Need to install gitbook-pdf using: npm install gitbook-pdf -g";
|
||||
} else {
|
||||
error.message = error.message + " "+stdout;
|
||||
}
|
||||
return d.reject(error);
|
||||
}
|
||||
d.resolve();
|
||||
});
|
||||
|
||||
return d.promise;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = Generator;
|
||||
+16
-1
@@ -160,6 +160,16 @@ Plugin.fromList = function(names, root) {
|
||||
})
|
||||
.object()
|
||||
.value();
|
||||
resources.html = {}
|
||||
|
||||
_.each(plugins, function(plugin) {
|
||||
var html = plugin.infos.book.html || {};
|
||||
_.each(html, function(code, key) {
|
||||
if (!_.isFunction(code)) code = _.constant(code);
|
||||
if (!resources.html[key]) resources.html[key] = [];
|
||||
resources.html[key].push(code);
|
||||
})
|
||||
});
|
||||
|
||||
return Q({
|
||||
'list': plugins,
|
||||
@@ -179,6 +189,11 @@ Plugin.fromList = function(names, root) {
|
||||
|
||||
if (!withTpl) return null;
|
||||
return withTpl.resolveFile(withTpl.infos.templates[name]);
|
||||
},
|
||||
'html': function(tag, context, options) {
|
||||
return _.map(resources.html[tag] || [], function(code) {
|
||||
return code.call(context, options);
|
||||
}).join("\n");
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -189,4 +204,4 @@ Plugin.defaults = [
|
||||
"mathjax"
|
||||
];
|
||||
|
||||
module.exports = Plugin;
|
||||
module.exports = Plugin;
|
||||
|
||||
@@ -63,7 +63,6 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate)
|
||||
var that = this;
|
||||
|
||||
interpolate = interpolate || _.identity;
|
||||
|
||||
return Q()
|
||||
.then(function(sections) {
|
||||
return tpl(_.extend({
|
||||
@@ -81,6 +80,7 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate)
|
||||
|
||||
plugins: that.plugins,
|
||||
pluginsConfig: JSON.stringify(that.options.pluginsConfig),
|
||||
htmlSnippet: _.partialRight(that.plugins.html, that, options),
|
||||
|
||||
options: that.options
|
||||
}, options));
|
||||
@@ -237,9 +237,13 @@ Generator.prototype.writeCacheManifest = function() {
|
||||
};
|
||||
|
||||
Generator.prototype.finish = function() {
|
||||
return this.copyAssets()
|
||||
.then(this.writeSearchIndex)
|
||||
.then(this.writeCacheManifest);
|
||||
var deferred = this.copyAssets().then(this.writeSearchIndex);
|
||||
|
||||
if (this.options.cache !== false) {
|
||||
deferred = deferred.then(this.writeCacheManifest);
|
||||
}
|
||||
|
||||
return deferred;
|
||||
};
|
||||
|
||||
module.exports = Generator;
|
||||
|
||||
@@ -8,7 +8,7 @@ function clean(obj) {
|
||||
|
||||
function flattenChapters(chapters) {
|
||||
return _.reduce(chapters, function(accu, chapter) {
|
||||
return accu.concat([clean(chapter)].concat(chapter.articles));
|
||||
return accu.concat([clean(chapter)].concat(flattenChapters(chapter.articles)));
|
||||
}, []);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ var calculProgress = function(navigation, current) {
|
||||
return nav;
|
||||
})
|
||||
.sortBy(function(nav) {
|
||||
return nav.level;
|
||||
return parseFloat(nav.level);
|
||||
})
|
||||
.map(function(nav, i) {
|
||||
// Calcul percent
|
||||
@@ -27,7 +27,7 @@ var calculProgress = function(navigation, current) {
|
||||
} else if (done) {
|
||||
prevPercent = nav.percent;
|
||||
}
|
||||
|
||||
|
||||
return nav;
|
||||
})
|
||||
.value();
|
||||
|
||||
+18
-5
@@ -1,5 +1,6 @@
|
||||
var _ = require('lodash');
|
||||
var marked = require('marked');
|
||||
var textRenderer = require('marked-text-renderer');
|
||||
|
||||
function extractFirstNode(nodes, nType) {
|
||||
return _.chain(nodes)
|
||||
@@ -14,16 +15,28 @@ function extractFirstNode(nodes, nType) {
|
||||
|
||||
function parseReadme(src) {
|
||||
var nodes, title, description;
|
||||
var renderer = textRenderer();
|
||||
|
||||
// Parse content
|
||||
nodes = marked.lexer(src);
|
||||
|
||||
var title = extractFirstNode(nodes, "heading");
|
||||
var description = extractFirstNode(nodes, "paragraph");
|
||||
|
||||
title = extractFirstNode(nodes, "heading") || '';
|
||||
description = extractFirstNode(nodes, "paragraph") || '';
|
||||
|
||||
var convert = _.compose(
|
||||
function(text) {
|
||||
return _.unescape(text.replace(/(\r\n|\n|\r)/gm, ""));
|
||||
},
|
||||
function(text) {
|
||||
return marked.parse(text, _.extend({}, marked.defaults, {
|
||||
renderer: renderer
|
||||
}));
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
title: title,
|
||||
description: description
|
||||
title: convert(title),
|
||||
description: convert(description)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -86,13 +86,11 @@ function parseTitle(src, nums) {
|
||||
};
|
||||
}
|
||||
|
||||
function parseArticle(chapterNum, nodes, idx) {
|
||||
return parseTitle(_.first(nodes).text, [chapterNum, idx+1]);
|
||||
}
|
||||
|
||||
function parseChapter(nodes, idx) {
|
||||
return _.extend(parseTitle(_.first(nodes).text, [idx+1]), {
|
||||
articles: _.map(listSplit(filterList(nodes), 'list_item_start', 'list_item_end'), parseArticle.bind(null, idx+1))
|
||||
function parseChapter(nodes, nums) {
|
||||
return _.extend(parseTitle(_.first(nodes).text, nums), {
|
||||
articles: _.map(listSplit(filterList(nodes), 'list_item_start', 'list_item_end'), function(nodes, i) {
|
||||
return parseChapter(nodes, nums.concat(i + 1));
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@@ -104,7 +102,9 @@ function parseSummary(src) {
|
||||
|
||||
// Split out chapter sections
|
||||
var chapters = _.chain(listSplit(chapterList, 'list_item_start', 'list_item_end'))
|
||||
.map(parseChapter)
|
||||
.map(function(nodes, i) {
|
||||
return parseChapter(nodes, [i + 1]);
|
||||
})
|
||||
.value();
|
||||
|
||||
return {
|
||||
|
||||
@@ -19,6 +19,10 @@ var toAbsolute = function(_href, dir, outdir) {
|
||||
// make it relative to output
|
||||
_href = path.relative(outdir, _href);
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
_href = _href.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
return _href;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
var _ = require("lodash");
|
||||
|
||||
function escapeShellArg(arg) {
|
||||
var ret = '';
|
||||
|
||||
ret = arg.replace(/"/g, '\\"');
|
||||
|
||||
return "\"" + ret + "\"";
|
||||
}
|
||||
|
||||
function optionsToShellArgs(options) {
|
||||
return _.chain(options)
|
||||
.map(function(value, key) {
|
||||
if (value == null) return null;
|
||||
if (value == true) return key;
|
||||
return key+"="+escapeShellArg(value);
|
||||
})
|
||||
.compact()
|
||||
.value()
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
escapeShellArg: escapeShellArg,
|
||||
optionsToShellArgs: optionsToShellArgs
|
||||
};
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gitbook",
|
||||
"version": "0.4.3",
|
||||
"version": "0.4.8",
|
||||
"homepage": "http://www.gitbook.io/",
|
||||
"description": "Library and cmd utility to generate GitBooks",
|
||||
"main": "lib/index.js",
|
||||
@@ -20,10 +20,11 @@
|
||||
"semver": "2.2.1",
|
||||
"gaze": "~0.5.1",
|
||||
"resolve": "0.6.3",
|
||||
|
||||
"tiny-lr-fork": "0.0.5",
|
||||
"gitbook-plugin": "0.0.2",
|
||||
"gitbook-plugin-mixpanel": "0.0.2",
|
||||
"gitbook-plugin-mathjax": "0.0.3"
|
||||
"gitbook-plugin-mathjax": "0.0.3",
|
||||
"gitbook-plugin-livereload": "0.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "1.18.2",
|
||||
|
||||
Vendored
+2
@@ -3,6 +3,8 @@
|
||||
* [Chapter 1](chapter-1/README.md)
|
||||
* [Article 1](chapter-1/ARTICLE1.md)
|
||||
* [Article 2](chapter-1/ARTICLE2.md)
|
||||
* [article 1.2.1](chapter-1/ARTICLE-1-2-1.md)
|
||||
* [article 1.2.2](chapter-1/ARTICLE-1-2-2.md)
|
||||
* [Chapter 2](chapter-2/README.md)
|
||||
* [Chapter 3](chapter-3/README.md)
|
||||
* [Chapter 4](chapter-4/README.md)
|
||||
|
||||
+12
-2
@@ -18,6 +18,8 @@ describe('Summary navigation', function() {
|
||||
'chapter-1/ARTICLE1.md',
|
||||
'chapter-1/ARTICLE2.md',
|
||||
'chapter-2/README.md',
|
||||
'chapter-1/ARTICLE-1-2-1.md',
|
||||
'chapter-1/ARTICLE-1-2-2.md'
|
||||
]);
|
||||
|
||||
// Make sure it found the files we gave it
|
||||
@@ -26,6 +28,8 @@ describe('Summary navigation', function() {
|
||||
assert(nav['chapter-1/ARTICLE1.md']);
|
||||
assert(nav['chapter-1/ARTICLE2.md']);
|
||||
assert(nav['chapter-2/README.md']);
|
||||
assert(nav['chapter-1/ARTICLE-1-2-1.md']);
|
||||
assert(nav['chapter-1/ARTICLE-1-2-2.md']);
|
||||
|
||||
|
||||
assert.equal(nav['README.md'].prev, null);
|
||||
@@ -38,9 +42,15 @@ describe('Summary navigation', function() {
|
||||
assert.equal(nav['chapter-1/ARTICLE1.md'].next.path, 'chapter-1/ARTICLE2.md');
|
||||
|
||||
assert.equal(nav['chapter-1/ARTICLE2.md'].prev.path, 'chapter-1/ARTICLE1.md');
|
||||
assert.equal(nav['chapter-1/ARTICLE2.md'].next.path, 'chapter-2/README.md');
|
||||
assert.equal(nav['chapter-1/ARTICLE2.md'].next.path, 'chapter-1/ARTICLE-1-2-1.md');
|
||||
|
||||
assert.equal(nav['chapter-2/README.md'].prev.path, 'chapter-1/ARTICLE2.md');
|
||||
assert.equal(nav['chapter-1/ARTICLE-1-2-1.md'].prev.path, 'chapter-1/ARTICLE2.md');
|
||||
assert.equal(nav['chapter-1/ARTICLE-1-2-1.md'].next.path, 'chapter-1/ARTICLE-1-2-2.md');
|
||||
|
||||
assert.equal(nav['chapter-1/ARTICLE-1-2-2.md'].prev.path, 'chapter-1/ARTICLE-1-2-1.md');
|
||||
assert.equal(nav['chapter-1/ARTICLE-1-2-2.md'].next.path, 'chapter-2/README.md');
|
||||
|
||||
assert.equal(nav['chapter-2/README.md'].prev.path, 'chapter-1/ARTICLE-1-2-2.md');
|
||||
assert.equal(nav['chapter-2/README.md'].next.path, 'chapter-3/README.md');
|
||||
});
|
||||
|
||||
|
||||
@@ -50,5 +50,6 @@ describe('Summary parsing', function () {
|
||||
|
||||
assert.equal(c[0].articles[0].level, '1.1');
|
||||
assert.equal(c[0].articles[1].level, '1.2');
|
||||
assert.equal(c[0].articles[1].articles[0].level, '1.2.1');
|
||||
});
|
||||
});
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -30,40 +30,23 @@ define([
|
||||
};
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
@@ -81,6 +64,22 @@ define([
|
||||
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;
|
||||
|
||||
@@ -98,15 +97,10 @@ define([
|
||||
});
|
||||
fontState.save = function(){
|
||||
storage.set("fontState",fontState);
|
||||
update();
|
||||
};
|
||||
|
||||
$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);
|
||||
update();
|
||||
|
||||
//Add event listeners
|
||||
$(document).on('click', "#enlarge-font-size", enlargeFontSize);
|
||||
@@ -121,6 +115,7 @@ define([
|
||||
};
|
||||
|
||||
return {
|
||||
init: init
|
||||
init: init,
|
||||
update: update
|
||||
}
|
||||
});
|
||||
@@ -2,11 +2,9 @@ define([
|
||||
"jQuery"
|
||||
], function($) {
|
||||
var showLoading = function(p) {
|
||||
var $book = $(".book");
|
||||
|
||||
$book.addClass("is-loading");
|
||||
$(".book").addClass("is-loading");
|
||||
p.always(function() {
|
||||
$book.removeClass("is-loading");
|
||||
$(".book").removeClass("is-loading");
|
||||
});
|
||||
|
||||
return p;
|
||||
|
||||
@@ -3,12 +3,11 @@ define([
|
||||
"utils/url",
|
||||
"core/events",
|
||||
"core/state",
|
||||
"core/search",
|
||||
"core/progress",
|
||||
"core/exercise",
|
||||
"core/quiz",
|
||||
"core/loading"
|
||||
], function($, URL, events, state, search, progress, exercises, quiz, loading) {
|
||||
], function($, URL, events, state, progress, exercises, quiz, loading) {
|
||||
var prev, next;
|
||||
var githubCountStars, githubCountWatch;
|
||||
|
||||
@@ -47,10 +46,12 @@ define([
|
||||
$("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
|
||||
@@ -94,9 +95,6 @@ define([
|
||||
// Focus on content
|
||||
$pageWrapper.focus();
|
||||
|
||||
// Prepare search bar
|
||||
search.prepare();
|
||||
|
||||
// Update GitHub count
|
||||
if (state.githubId) {
|
||||
if (githubCountStars) {
|
||||
|
||||
@@ -67,12 +67,10 @@ define([
|
||||
e.preventDefault();
|
||||
toggleSearch();
|
||||
});
|
||||
};
|
||||
|
||||
var prepare = function() {
|
||||
var $searchInput = $(".book-search input");
|
||||
|
||||
$searchInput.keyup(function(e) {
|
||||
// Type in search bar
|
||||
$(document).on("keyup", ".book-search input", function(e) {
|
||||
var key = (e.keyCode ? e.keyCode : e.which);
|
||||
var q = $(this).val();
|
||||
|
||||
@@ -89,13 +87,12 @@ define([
|
||||
_.pluck(results, "path")
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return {
|
||||
init: init,
|
||||
search: search,
|
||||
toggle: toggleSearch,
|
||||
prepare: prepare
|
||||
toggle: toggleSearch
|
||||
};
|
||||
});
|
||||
@@ -1,11 +1,10 @@
|
||||
define([
|
||||
"jQuery",
|
||||
"lodash",
|
||||
"utils/storage",
|
||||
"utils/platform",
|
||||
"core/state"
|
||||
], function(_, storage, platform, state) {
|
||||
var $summary = state.$book.find(".book-summary");
|
||||
|
||||
], function($, _, storage, platform, state) {
|
||||
// Toggle sidebar with or withour animation
|
||||
var toggleSidebar = function(_state, animation) {
|
||||
if (state != null && isOpen() == _state) return;
|
||||
@@ -38,6 +37,8 @@ define([
|
||||
|
||||
// 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);
|
||||
@@ -48,7 +49,6 @@ define([
|
||||
};
|
||||
|
||||
return {
|
||||
$el: $summary,
|
||||
init: init,
|
||||
toggle: toggleSidebar,
|
||||
filter: filterSummary
|
||||
|
||||
@@ -59,28 +59,6 @@
|
||||
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 {
|
||||
@@ -122,4 +100,37 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.font-size-0 {
|
||||
.book-body {
|
||||
font-size:@s-font-size;
|
||||
}
|
||||
}
|
||||
&.font-size-1 {
|
||||
.book-body {
|
||||
font-size:@m-font-size;
|
||||
}
|
||||
}
|
||||
&.font-size-2 {
|
||||
.book-body {
|
||||
font-size:@l-font-size;
|
||||
}
|
||||
}
|
||||
&.font-size-3 {
|
||||
.book-body {
|
||||
font-size:@xl-font-size;
|
||||
}
|
||||
}
|
||||
&.font-size-4 {
|
||||
.book-body {
|
||||
font-size:@xxl-font-size;
|
||||
}
|
||||
}
|
||||
|
||||
&.font-family-0{
|
||||
font-family: @font-family-serif;
|
||||
}
|
||||
&.font-family-1{
|
||||
font-family: @font-family-sans;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
.book .book-body .page-wrapper .page-inner {
|
||||
section.exercise {
|
||||
padding: 0px;
|
||||
margin: 20px 0px;
|
||||
margin: 20px 15px;
|
||||
border: 3px solid #2f8cde;
|
||||
|
||||
.header {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
padding-bottom: 25px;
|
||||
padding-top: 15px;
|
||||
color:@page-color;
|
||||
letter-spacing: 0.01rem;
|
||||
|
||||
& > *:first-child {
|
||||
margin-top: 0 !important; }
|
||||
|
||||
@@ -13,15 +13,15 @@
|
||||
flex-direction: column;
|
||||
|
||||
font-size: 40px;
|
||||
color: rgba(0,0,0,0.5);
|
||||
color: rgba(0,0,0,0.4);
|
||||
|
||||
text-align: center;
|
||||
|
||||
.transition(all 350ms ease);
|
||||
|
||||
&:hover {
|
||||
background-color: @body-pagination-background;
|
||||
text-decoration: none;
|
||||
color: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
&.navigation-next {
|
||||
@@ -50,11 +50,9 @@
|
||||
}
|
||||
.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;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
.book .book-body .page-wrapper .page-inner {
|
||||
section.quiz {
|
||||
padding: 0px;
|
||||
margin: 20px 0px;
|
||||
margin: 20px 15px;
|
||||
border: 3px solid #2f8cde;
|
||||
|
||||
.header {
|
||||
|
||||
@@ -90,7 +90,6 @@
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
span {
|
||||
|
||||
@@ -1,38 +1,19 @@
|
||||
@import "vendors/bootstrap/bootstrap.less";
|
||||
@import "vendors/fontawesome/font-awesome.less";
|
||||
|
||||
@import "mixins.less";
|
||||
@import "variables.less";
|
||||
@import "fonts.less";
|
||||
/*@import "variables.less";*/
|
||||
|
||||
@import "page/highlight.less";
|
||||
|
||||
|
||||
@font-size-base: 13px;
|
||||
|
||||
|
||||
* {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
-webkit-text-size-adjust: none;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
.book-chapter {
|
||||
display: none;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
text-rendering: optimizeLegibility;
|
||||
font-smoothing: antialiased;
|
||||
font-family: @font-family-base;
|
||||
}
|
||||
|
||||
|
||||
h1, h2, h3 {
|
||||
page-break-after: avoid;
|
||||
page-break-before: auto;
|
||||
article {
|
||||
page-break-after: always;
|
||||
}
|
||||
|
||||
h1 { font-size: floor(@font-size-base * 2.15); }
|
||||
@@ -52,64 +33,16 @@ img {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
section {
|
||||
page-break-after: always;
|
||||
.exercise {
|
||||
margin: 1cm 0cm;
|
||||
padding: 0.4cm;
|
||||
page-break-inside: avoid;
|
||||
|
||||
&#cover {
|
||||
padding: 3cm 0cm;
|
||||
text-align: center;
|
||||
border: 3px solid #ddd;
|
||||
|
||||
h1 {
|
||||
font-size: 1.5cm;
|
||||
}
|
||||
.exercise-header {
|
||||
margin-bottom: 0.4cm;
|
||||
padding-bottom: 0.2cm;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
&#summary {
|
||||
margin: 1.5cm;
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style: none;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
margin-left: 1cm;
|
||||
}
|
||||
|
||||
> ol {
|
||||
> li {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
article {
|
||||
margin-bottom: 1.5cm;
|
||||
|
||||
&.new-chapter {
|
||||
page-break-after: always;
|
||||
font-size: 0.6cm;
|
||||
text-align: center;
|
||||
padding: 3cm 0cm;
|
||||
|
||||
h1 {
|
||||
font-size: floor(@font-size-base * 2.7);
|
||||
}
|
||||
}
|
||||
|
||||
.exercise {
|
||||
margin: 1cm 0cm;
|
||||
padding: 0.4cm;
|
||||
page-break-inside: avoid;
|
||||
|
||||
border: 3px solid #ddd;
|
||||
|
||||
.exercise-header {
|
||||
margin-bottom: 0.4cm;
|
||||
padding-bottom: 0.2cm;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
// Body
|
||||
@body-background: white;
|
||||
@body-pagination-background: hsl(207, 15%, 95%);
|
||||
|
||||
// Sidebar
|
||||
@sidebar-width: 250px;
|
||||
@@ -21,7 +20,7 @@
|
||||
@sidebar-color: hsl(207, 15%, 80%);
|
||||
@sidebar-background: hsl(207, 15%, 95%);
|
||||
|
||||
@sidebar-nested-padding: 10px;
|
||||
@sidebar-nested-padding: 20px;
|
||||
|
||||
@sidebar-search-padding: 6px;
|
||||
@sidebar-search-background: transparent;
|
||||
@@ -91,12 +90,12 @@
|
||||
@xl-font-size: 2.2rem;
|
||||
@xxl-font-size: 4.0rem;
|
||||
|
||||
/*
|
||||
,--------.,--.
|
||||
'--. .--'| ,---. ,---. ,--,--,--. ,---. ,---. .--.
|
||||
| | | .-. || .-. :| || .-. :( .-' '--'
|
||||
| | | | | |\ --.| | | |\ --..-' `).--.
|
||||
`--' `--' `--' `----'`--`--`--' `----'`----' '--'
|
||||
/*
|
||||
,--------.,--.
|
||||
'--. .--'| ,---. ,---. ,--,--,--. ,---. ,---. .--.
|
||||
| | | .-. || .-. :| || .-. :( .-' '--'
|
||||
| | | | | |\ --.| | | |\ --..-' `).--.
|
||||
`--' `--' `--' `----'`--`--`--' `----'`----' '--'
|
||||
*/
|
||||
// Header
|
||||
@header-color-1: #AFA790;
|
||||
@@ -113,8 +112,6 @@
|
||||
// Body
|
||||
@body-background-1: #F3EACB;
|
||||
@body-background-2: #1d1f21;
|
||||
@body-pagination-background-1: #FFFAEA;
|
||||
@body-pagination-background-2: #33404D;
|
||||
|
||||
// Sidebar
|
||||
@sidebar-color-1: #AFA790;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
{% if options.links.home !== false and (options.links.home != null or githubId) %}
|
||||
<a href="{{ options.links.home|default(githubHost+githubId) }}" target="_blank" class="btn pull-left home-bookmark" aria-label="GitHub home"><i class="fa fa-bookmark-o"></i></a>
|
||||
{% endif %}
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
<a href="#" class="btn pull-left toggle-search" aria-label="Toggle search"><i class="fa fa-search"></i></a>
|
||||
<span id="font-settings-wrapper">
|
||||
<a href="#" class="btn pull-left toggle-font-settings" aria-label="Toggle font settings"><i class="fa fa-font"></i>
|
||||
|
||||
@@ -1,59 +1,57 @@
|
||||
{% macro articles(_articles) %}
|
||||
{% for item in _articles %}
|
||||
<li class="chapter {% if item._path == _input %}active{% endif %}" data-level="{{ item.level }}" {% if item.path %}data-path="{{ item.path|mdLink }}"{% endif %}>
|
||||
{% if item.path %}
|
||||
<a href="{{ basePath }}/{{ item.path|mdLink }}">
|
||||
<i class="fa fa-check"></i> <b>{{ item.level }}.</b> {{ item.title }}
|
||||
</a>
|
||||
{% else %}
|
||||
<span><b>{{ item.level }}.</b> {{ item.title }}</span>
|
||||
{% endif %}
|
||||
{% if item.articles.length > 0 %}
|
||||
<ul class="articles">
|
||||
{{ articles(item.articles) }}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
|
||||
<div class="book-summary">
|
||||
<div class="book-search">
|
||||
<input type="text" placeholder="Search" class="form-control" />
|
||||
</div>
|
||||
<ul class="summary">
|
||||
{% set _divider = false %}
|
||||
{% if options.links.about !== false && (options.links.about || githubId) %}
|
||||
{% set _divider = true %}
|
||||
<li>
|
||||
<a href="{{ options.links.about|default(githubHost+githubAuthor) }}" target="blank" class="author-link">About the author</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if options.links.issues !== false && (options.links.issues || githubId) %}
|
||||
{% set _divider = true %}
|
||||
<li>
|
||||
<a href="{{ options.links.issues|default(githubHost+githubId+"/issues") }}" target="blank"class="issues-link">Questions and Issues</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if options.links.contribute !== false && (options.links.contribute || githubId) %}
|
||||
{% set _divider = true %}
|
||||
<li>
|
||||
<a href="{{ options.links.contribute|default(githubHost+githubId+"/edit/master/"+_input) }}" target="blank" class="contribute-link">Edit and Contribute</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if options.links.contribute || options.links.issues || options.links.about %}
|
||||
{% if _divider %}
|
||||
<li class="divider"></li>
|
||||
{% endif %}
|
||||
|
||||
<li data-level="0" data-path="index.html">
|
||||
<a href="{{ basePath }}/"><i class="fa fa-check"></i> Introduction</a>
|
||||
</li>
|
||||
{% for item in summary.chapters %}
|
||||
<li class="chapter {% if item._path == _input %}active{% endif %}" data-level="{{ item.level }}" {% if item.path %}data-path="{{ item.path|mdLink }}"{% endif %}>
|
||||
{% if item.path %}
|
||||
<a href="{{ basePath }}/{{ item.path|mdLink }}">
|
||||
<i class="fa fa-check"></i> <b>{{ item.level }}.</b> {{ item.title }}
|
||||
</a>
|
||||
{% else %}
|
||||
<span><b>{{ item.level }}.</b> {{ item.title }}</span>
|
||||
{% endif %}
|
||||
{% if item.articles.length > 0 %}
|
||||
<ul class="articles">
|
||||
{% for article in item.articles %}
|
||||
<li {% if article._path == _input %}class="active"{% endif %} data-level="{{ article.level }}" {% if article.path %}data-path="{{ article.path|mdLink }}"{% endif %}>
|
||||
{% if article.path %}
|
||||
<a href="{{ basePath }}/{{ article.path|mdLink }}">
|
||||
<i class="fa fa-check"></i> <b>{{ article.level }}.</b> {{ article.title }}
|
||||
</a>
|
||||
{% else %}
|
||||
<span><b>{{ article.level }}.</b> {{ article.title }}</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{{ articles(summary.chapters) }}
|
||||
|
||||
{% if options.links.gitbook !== false %}
|
||||
<li class="divider"></li>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en-US" {% block htmlTag %}{% endblock %}>
|
||||
{{ htmlSnippet("html:start")|default("") }}
|
||||
<head prefix="og: http://ogp.me/ns# book: http://ogp.me/ns/book#">
|
||||
{{ htmlSnippet("head:start")|default("") }}
|
||||
{% block head %}
|
||||
<meta charset="UTF-8">
|
||||
<title>{% block title %} | {{ title }}{% endblock %}</title>
|
||||
@@ -33,8 +35,10 @@
|
||||
|
||||
<link rel="shortcut icon" href="{{ staticBase }}/images/favicon.ico" type="image/x-icon">
|
||||
{% endblock %}
|
||||
{{ htmlSnippet("head:end")|default("") }}
|
||||
</head>
|
||||
<body>
|
||||
{{ htmlSnippet("body:start")|default("") }}
|
||||
{% block style %}
|
||||
<link rel="stylesheet" href="{{ staticBase }}/style.css">
|
||||
{% endblock %}
|
||||
@@ -45,5 +49,7 @@
|
||||
<script src="{{ staticBase }}/jsrepl/jsrepl.js" id="jsrepl-script"></script>
|
||||
<script src="{{ staticBase }}/app.js"></script>
|
||||
{% endblock %}
|
||||
{{ htmlSnippet("body:end")|default("") }}
|
||||
</body>
|
||||
{{ htmlSnippet("html:end")|default("") }}
|
||||
</html>
|
||||
|
||||
+35
-94
@@ -20,10 +20,10 @@
|
||||
</div>
|
||||
{% elif section.type == "quiz" %}
|
||||
<div class="quiz">
|
||||
<div class="exercise-header">Exercise #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
{% autoescape false %}{{ section.quiz.base }}{% endautoescape %}
|
||||
{% set exercise = exercise + 1 %}
|
||||
<div class="exercise-header">Exercise #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
{% autoescape false %}{{ section.quiz.base }}{% endautoescape %}
|
||||
{% set exercise = exercise + 1 %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
@@ -31,104 +31,45 @@
|
||||
|
||||
{% set exercise = 1 %}
|
||||
{% block content %}
|
||||
{# Cover #}
|
||||
<section id="cover">
|
||||
<h1>{{ title }}</h1>
|
||||
{% if githubId %}
|
||||
<h2>By <a href="{{ githubHost }}{{ githubAuthor }}">@{{ githubAuthor }}</a></h2>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
{# Summary #}
|
||||
<section id="summary">
|
||||
<h1>Table of Contents</h1>
|
||||
<ol>
|
||||
<li>
|
||||
<a href="#README.md">
|
||||
<h2>Introduction</h2>
|
||||
</a>
|
||||
</li>
|
||||
{% for item in summary.chapters %}
|
||||
<li>
|
||||
<h2><span>{{ item.level }}.</span> <a href="#{{ item.path }}">{{ item.title }}</a></h2>
|
||||
{% if item.articles.length > 0 %}
|
||||
<ol>
|
||||
{% for article in item.articles %}
|
||||
<li>
|
||||
<span>{{ article.level }}</span> <a href="#{{ article.path }}">{{ article.title }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
{# Pages content #}
|
||||
<section>
|
||||
<article id="README.md">
|
||||
{{ articleContent(pages["README.md"].content) }}
|
||||
</article>
|
||||
</section>
|
||||
{% for item in summary.chapters %}
|
||||
<section>
|
||||
<article id="{{ item.path }}" class="new-chapter">
|
||||
<h1>{{ item.title }}</h1>
|
||||
</article>
|
||||
|
||||
{% if pages[item.path] %}
|
||||
<article>
|
||||
{{ articleContent(pages[item.path].content) }}
|
||||
</article>
|
||||
{% endif %}
|
||||
|
||||
{% if item.articles.length > 0 %}
|
||||
{% for article in item.articles %}
|
||||
{% if pages[article.path] %}
|
||||
<article id="{{ article.path }}">
|
||||
{{ articleContent(pages[article.path].content) }}
|
||||
</article>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</section>
|
||||
{% for item in progress.chapters %}
|
||||
<article id="{{ article.path }}">
|
||||
<h1 class="book-chapter book-chapter-{{ item.level|lvl }}">{{ item.title }}</h1>
|
||||
{% if pages[item.path] %}
|
||||
{{ articleContent(pages[item.path].content) }}
|
||||
{% endif %}
|
||||
</article>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{# Exercise solutions #}
|
||||
{% if exercise > 1 %}
|
||||
{% set exercise = 1 %}
|
||||
<section>
|
||||
<article class="new-chapter">
|
||||
<h1>Exercise Solutions</h1>
|
||||
</article>
|
||||
<article>
|
||||
{% for item in summary.chapters %}
|
||||
{% for article in item.articles %}
|
||||
{% if pages[article.path] %}
|
||||
{% for section in pages[article.path].content %}
|
||||
{% if section.type == "exercise" %}
|
||||
<div class="exercise">
|
||||
<div class="exercise-header">Exercise #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
<pre><code>{% autoescape false %}{{ section.code.solution|code }}{% endautoescape %}</code></pre>
|
||||
{% set exercise = exercise + 1 %}
|
||||
</div>
|
||||
{% elif section.type == "quiz" %}
|
||||
<div class="quiz">
|
||||
<div class="exercise-header">Exercise #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
{% autoescape false %}{{ section.quiz.solution }}{% endautoescape %}
|
||||
{% set exercise = exercise + 1 %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<article>
|
||||
<h1 class="book-chapter book-chapter-1">Exercise Solutions</h1>
|
||||
|
||||
{% for item in progress.chapters %}
|
||||
{% if pages[item.path] %}
|
||||
{% for section in pages[item.path].content %}
|
||||
{% if section.type == "exercise" %}
|
||||
<div class="exercise">
|
||||
<div class="exercise-header">Exercise #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
<pre><code>{% autoescape false %}{{ section.code.solution|code }}{% endautoescape %}</code></pre>
|
||||
{% set exercise = exercise + 1 %}
|
||||
</div>
|
||||
{% elif section.type == "quiz" %}
|
||||
<div class="quiz">
|
||||
<div class="exercise-header">Exercise #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
{% autoescape false %}{{ section.quiz.solution }}{% endautoescape %}
|
||||
{% set exercise = exercise + 1 %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</article>
|
||||
</section>
|
||||
</article>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "layout.html" %}
|
||||
|
||||
{% block htmlTag %}manifest="{{ basePath }}/manifest.appcache"{% endblock %}
|
||||
{% block htmlTag %}{% if options.cache !== false %}manifest="{{ basePath }}/manifest.appcache"{% endif %}{% endblock %}
|
||||
{% block title %}{{ progress.current.title }}{% parent %}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="book" {% if githubId %}data-github="{{ githubId }}"{% endif %} data-level="{{ progress.current.level }}" data-basepath="{{ basePath }}" data-revision="{{ revision }}">
|
||||
|
||||
Reference in New Issue
Block a user