mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-26 20:27:00 +00:00
Compare commits
137 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b9360de3e3 | |||
| 9f241457bb | |||
| 60a7c28e66 | |||
| 5dff9b795e | |||
| 0134677e0e | |||
| 39ef4fa199 | |||
| b507a30301 | |||
| 539c2d0c57 | |||
| aa6f3530f9 | |||
| 98057af219 | |||
| bb48c8233c | |||
| 8f14db64df | |||
| 37d40dc66c | |||
| 6c96680cca | |||
| 4e475857d5 | |||
| f8ff0453dd | |||
| 3cc5c47535 | |||
| a2046814b4 | |||
| 82f814fdbd | |||
| cd382dc8fc | |||
| 3842e5bf4a | |||
| f05b138fd6 | |||
| 1b87cec893 | |||
| 4a894b1e18 | |||
| c7c98009e7 | |||
| 356322e0ed | |||
| 7a47049367 | |||
| a9f473260c | |||
| d16577e8bf | |||
| 15b4053d6b | |||
| 925a9b1388 | |||
| fc942fdeca | |||
| 75a6ecbf52 | |||
| 520045441b | |||
| 00c3e966d3 | |||
| 8b6ebe86b8 | |||
| d5bd3453e1 | |||
| ddacbbe73b | |||
| f7d52f0eff | |||
| 39c50640ea | |||
| 16128c7e60 | |||
| 8b6a80cffb | |||
| 86f8a9d8fb | |||
| 790dfd601e | |||
| 3426428142 | |||
| 6c5c8e6eb7 | |||
| 3aefcaea73 | |||
| 04c025774d | |||
| 80aac7f581 | |||
| 266f7ca73d | |||
| 6bbefad24e | |||
| d6eb8e4c60 | |||
| c87edb155a | |||
| ef0468ad5d | |||
| f03153eb0c | |||
| 3d8b6e550a | |||
| 4c3dbca5ca | |||
| a258feea37 | |||
| 3a3332d0b9 | |||
| 4259283f32 | |||
| 243d977cd6 | |||
| 94bbbecedf | |||
| f49eab42a9 | |||
| ce6b2bc47d | |||
| 3245d7e430 | |||
| 6ee17c32ae | |||
| bf7ffe6d4c | |||
| ec7bc6b347 | |||
| b425db0979 | |||
| 30c6d1c9de | |||
| cee1d90fc6 | |||
| 522342667c | |||
| d1c1277bb9 | |||
| 9a66f56c79 | |||
| 087ca28ca2 | |||
| 3d5281ff3a | |||
| f62cf340c5 | |||
| 7e12bd3588 | |||
| 4dbdb95ebe | |||
| 2c5ddbcd8f | |||
| be4fd74f90 | |||
| 57491e3a8b | |||
| f7a442d297 | |||
| cfebdc6f6b | |||
| 65319e8fd6 | |||
| 55db44a3b4 | |||
| 13b6045300 | |||
| b7ff879c31 | |||
| 57ade87fe1 | |||
| 907e9ea5a7 | |||
| 215488dc45 | |||
| a195b3eac2 | |||
| 77c6f0e967 | |||
| f0e18afec1 | |||
| b6d11d88b7 | |||
| c803270af8 | |||
| 5d51263a58 | |||
| 7e30720b2c | |||
| 771c5e8b68 | |||
| 8f53afab6b | |||
| 0ad0403d41 | |||
| 2515c1dd73 | |||
| 7dfcd102dd | |||
| a91245d575 | |||
| 5f207f5e14 | |||
| 718692de3b | |||
| 9856a51255 | |||
| 58eaaf5514 | |||
| c0ae0138f8 | |||
| b1b9dc5b0a | |||
| f85c5a6498 | |||
| 32eafefc45 | |||
| 336999a34e | |||
| 1bf5f3b984 | |||
| c76d070237 | |||
| 8c7da1351f | |||
| 34588c6092 | |||
| 9f00bf8060 | |||
| f4a17b182f | |||
| 5c3b345fa6 | |||
| 46321fffd9 | |||
| 1f4cf33dd0 | |||
| 7274aa6940 | |||
| 125e804cd5 | |||
| 89ba17de84 | |||
| 115fba837c | |||
| 4df51546b2 | |||
| f68aaf7ff9 | |||
| e3d1746ba3 | |||
| a0d5646034 | |||
| 2f8af5f136 | |||
| 8b7abbd6f4 | |||
| f228f646eb | |||
| 45891d80c7 | |||
| ac900aa37f | |||
| 422cbed796 | |||
| 38cbbe46aa |
@@ -2,4 +2,3 @@ language: node_js
|
||||
node_js:
|
||||
- "0.11"
|
||||
- "0.10"
|
||||
- "0.8"
|
||||
|
||||
@@ -1,2 +1,7 @@
|
||||
# Release notes
|
||||
|
||||
## 0.6.0
|
||||
- Generate header id the same as github
|
||||
- Custom links can be added at top of sidebar
|
||||
- Summary can now be transformed by plugins
|
||||
- Support importing code snippets
|
||||
|
||||
+4
-3
@@ -24,8 +24,8 @@ module.exports = function (grunt) {
|
||||
optimization: 2
|
||||
},
|
||||
files: {
|
||||
"theme/assets/style.css": "theme/stylesheets/main.less",
|
||||
"theme/assets/print.css": "theme/stylesheets/print.less"
|
||||
"theme/assets/style.css": "theme/stylesheets/website.less",
|
||||
"theme/assets/print.css": "theme/stylesheets/ebook.less"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -44,7 +44,8 @@ module.exports = function (grunt) {
|
||||
"requireLib": 'vendors/requirejs/require',
|
||||
"Mousetrap": 'vendors/mousetrap/mousetrap',
|
||||
"lunr": 'vendors/lunr.js/lunr',
|
||||
"URI": 'vendors/URIjs/src/URI'
|
||||
"URI": 'vendors/URIjs/src/URI',
|
||||
"ace": 'vendors/ace-builds/src-noconflict/'
|
||||
},
|
||||
shim: {
|
||||
'jQuery': {
|
||||
|
||||
@@ -3,7 +3,7 @@ GitBook
|
||||
|
||||
[](https://travis-ci.org/GitbookIO/gitbook)
|
||||
|
||||
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. A complete documentation is available at [help.gitbook.io](http://help.gitbook.io/).
|
||||
GitBook is a command line tool (and Node.js library) for building beautiful books and exercises using GitHub/Git and Markdown. Here is an example: [Learn Javascript](https://www.gitbook.io/book/GitBookIO/javascript). You can publish book easily online using [gitbook.io](https://www.gitbook.io) and 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,37 @@ 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
|
||||
--config <config file> Configuration file to use, defaults to book.json
|
||||
-f, --format <name> Change generation format, defaults to site, availables are: site, page, ebook, json
|
||||
--config <config file> Configuration file to use, defaults to book.js or 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:
|
||||
|
||||
```
|
||||
```js
|
||||
{
|
||||
// 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
|
||||
// It's not advised this option in the book.json
|
||||
"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
|
||||
// It's not advised this option in the book.json
|
||||
"generator": "site",
|
||||
|
||||
// Book title and description (defaults are extracted from the README)
|
||||
"title": null,
|
||||
"description": null,
|
||||
|
||||
// GitHub informations (defaults are extracted using git)
|
||||
// For ebook format, the extension to use for generation (default is detected from output extension)
|
||||
// "epub", "pdf", "mobi"
|
||||
// Caution: it overrides the value from the command line
|
||||
// It's not advised this option in the book.json
|
||||
"extension": null,
|
||||
|
||||
// GitHub information (defaults are extracted using git)
|
||||
"github": null,
|
||||
"githubHost": "https://github.com/",
|
||||
|
||||
@@ -67,9 +77,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)
|
||||
@@ -82,12 +92,43 @@ Here are the options that can be stored in this file:
|
||||
"issues": null,
|
||||
"contribute": null,
|
||||
|
||||
// Custom links at top of sidebar
|
||||
"custom": {
|
||||
"Custom link name": "https://customlink.com"
|
||||
},
|
||||
|
||||
// Sharing links
|
||||
"sharing": {
|
||||
"google": null,
|
||||
"facebook": null,
|
||||
"twitter": null
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// Options for PDF generation
|
||||
"pdf": {
|
||||
// Add toc at the end of the file
|
||||
"toc": true,
|
||||
|
||||
// Add page numbers to the bottom of every page
|
||||
"pageNumbers": false,
|
||||
|
||||
// Font size for the fiel content
|
||||
"fontSize": 12,
|
||||
|
||||
// Paper size for the pdf
|
||||
// Choices are [u’a0’, u’a1’, u’a2’, u’a3’, u’a4’, u’a5’, u’a6’, u’b0’, u’b1’, u’b2’, u’b3’, u’b4’, u’b5’, u’b6’, u’legal’, u’letter’]
|
||||
"paperSize": "a4",
|
||||
|
||||
// Margin (in pts)
|
||||
// Note: 72 pts equals 1 inch
|
||||
"margin": {
|
||||
"right": 62,
|
||||
"left": 62,
|
||||
"top": 36,
|
||||
"bottom": 36
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -98,11 +139,10 @@ 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
|
||||
|
||||
@@ -134,7 +174,7 @@ 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 +183,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,7 +225,7 @@ 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
|
||||
|
||||
@@ -196,11 +236,11 @@ A small version of the cover can also be set by creating a file: **/cover_small.
|
||||
|
||||
#### 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**.
|
||||
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:
|
||||
|
||||
@@ -217,3 +257,15 @@ 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
|
||||
* [Heading Anchors](https://github.com/rlmv/gitbook-plugin-anchors): Add linkable Github-style anchors to headings
|
||||
* [JSBin](https://github.com/jcouyang/gitbook-plugin-jsbin): Ebedded jsbin frame into your book
|
||||
|
||||
#### Debugging
|
||||
|
||||
You can use the environment variable `DEBUG=true` to get better error messages (with stack trace). For example:
|
||||
|
||||
```
|
||||
$ export DEBUG=true
|
||||
$ gitbook build ./
|
||||
```
|
||||
|
||||
+21
-5
@@ -10,17 +10,28 @@ var generators = require("../lib/generate").generators;
|
||||
|
||||
var buildCommand = function(command) {
|
||||
return command
|
||||
.option('-v, --verbose', 'Activate verbose mode, useful for debugging errors')
|
||||
.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('--config <config file>', 'Configuration file to use, defualt to book.json')
|
||||
.option('--config <config file>', 'Configuration file to use, defaults to book.js or book.json');
|
||||
};
|
||||
|
||||
|
||||
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();
|
||||
outputDir = options.output;
|
||||
|
||||
// Set debugging
|
||||
if(options.verbose) {
|
||||
process.env.DEBUG = "true";
|
||||
}
|
||||
|
||||
console.log('Starting build ...');
|
||||
return converter(
|
||||
_.extend({}, options || {}, {
|
||||
@@ -31,10 +42,14 @@ var makeBuildFunc = function(converter) {
|
||||
})
|
||||
)
|
||||
.then(function(output) {
|
||||
console.log("Successfuly built !");
|
||||
console.log("Successfully built!");
|
||||
return output;
|
||||
}, utils.logError)
|
||||
.fail(function() {
|
||||
})
|
||||
.fail(function(err) {
|
||||
// Log error
|
||||
utils.logError(err);
|
||||
|
||||
// Exit process with failure code
|
||||
process.exit(-1);
|
||||
});
|
||||
};
|
||||
@@ -43,5 +58,6 @@ var makeBuildFunc = function(converter) {
|
||||
module.exports = {
|
||||
folder: makeBuildFunc(generate.folder),
|
||||
file: makeBuildFunc(generate.file),
|
||||
command: buildCommand
|
||||
command: buildCommand,
|
||||
commandEbook: buildEbookCommand
|
||||
};
|
||||
|
||||
+31
-9
@@ -14,6 +14,7 @@ var fs = require('../lib/generate/fs');
|
||||
var utils = require('./utils');
|
||||
var build = require('./build');
|
||||
var Server = require('./server');
|
||||
var platform = require("./platform");
|
||||
|
||||
// General options
|
||||
prog
|
||||
@@ -80,24 +81,29 @@ build.command(prog.command('serve [source_dir]'))
|
||||
generate();
|
||||
});
|
||||
|
||||
build.command(prog.command('pdf [source_dir]'))
|
||||
build.commandEbook(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"
|
||||
format: "ebook"
|
||||
}));
|
||||
});
|
||||
|
||||
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('epub [source_dir]'))
|
||||
.description('Build a gitbook as a ePub book')
|
||||
.action(function(dir, options) {
|
||||
var ext = options.output ? path.extname(options.output) : "epub";
|
||||
|
||||
build.file(dir, _.extend(options, {
|
||||
extension: ext,
|
||||
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"
|
||||
}));
|
||||
});
|
||||
@@ -110,6 +116,22 @@ prog
|
||||
return initDir(dir);
|
||||
});
|
||||
|
||||
prog
|
||||
.command('publish [source_dir]')
|
||||
.description('Publish content to the associated gitbook.io book')
|
||||
.action(function(dir) {
|
||||
dir = dir || process.cwd();
|
||||
return platform.publish(dir);
|
||||
});
|
||||
|
||||
prog
|
||||
.command('git:remote [source_dir] [book_id]')
|
||||
.description('Adds a git remote to a book repository')
|
||||
.action(function(dir, bookId) {
|
||||
dir = dir || process.cwd();
|
||||
return platform.remote(dir, bookId);
|
||||
});
|
||||
|
||||
// Parse and fallback to help if no args
|
||||
if(_.isEmpty(prog.parse(process.argv).args) && process.argv.length === 2) {
|
||||
prog.help();
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
var Q = require("q");
|
||||
var utils = require("./utils");
|
||||
|
||||
var publish = function(folder) {
|
||||
if (!folder) {
|
||||
console.log("Need a repository folder");
|
||||
return process.exit(-1);
|
||||
}
|
||||
|
||||
utils.gitCmd("push", ["gitbook", "master"])
|
||||
.then(function(out) {
|
||||
console.log(out.stdout);
|
||||
}, function(err) {
|
||||
if (err.code == 128) {
|
||||
console.log("No book on gitbook.io is configured with this git repository.");
|
||||
console.log("Run 'gitbook git:remote username/book' to intialize this repository.");
|
||||
} else {
|
||||
console.log(err.message);
|
||||
}
|
||||
process.exit(-1);
|
||||
});
|
||||
};
|
||||
|
||||
var remote = function(folder, bookId) {
|
||||
if (!folder || !bookId) {
|
||||
console.log("Need a repository folder and a book id");
|
||||
return process.exit(-1);
|
||||
}
|
||||
|
||||
var url = "https://push.gitbook.io/"+bookId+".git";
|
||||
var addRemote = function() {
|
||||
return utils.gitCmd("remote", ["add", "gitbook", url]);
|
||||
}
|
||||
|
||||
addRemote()
|
||||
.fail(function(err) {
|
||||
if (err.code == 128) {
|
||||
return utils.gitCmd("remote", ["rm", "gitbook"]).then(addRemote);
|
||||
}
|
||||
return Q.reject(err);
|
||||
})
|
||||
.then(function(out) {
|
||||
console.log("Book remote '"+url+"' added to the folder");
|
||||
}, function(err) {
|
||||
console.log(err.message);
|
||||
process.exit(-1);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
publish: publish,
|
||||
remote: remote
|
||||
};
|
||||
+23
-1
@@ -1,6 +1,7 @@
|
||||
var Q = require('q');
|
||||
var _ = require('lodash');
|
||||
|
||||
var exec = require('child_process').exec;
|
||||
var http = require('http');
|
||||
var send = require('send');
|
||||
|
||||
@@ -36,9 +37,30 @@ function logError(err) {
|
||||
return Q.reject(err);
|
||||
};
|
||||
|
||||
function runGitCommand(command, args) {
|
||||
var d = Q.defer(), child;
|
||||
args = ["git", command].concat(args).join(" ");
|
||||
|
||||
child = exec(args, function (error, stdout, stderr) {
|
||||
if (error !== null) {
|
||||
error.stdout = stdout;
|
||||
error.stderr = stderr;
|
||||
d.reject(error);
|
||||
} else {
|
||||
d.resolve({
|
||||
stdout: stdout,
|
||||
stderr: stderr
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
return d.promise;
|
||||
};
|
||||
|
||||
|
||||
// Exports
|
||||
module.exports = {
|
||||
watch: watch,
|
||||
logError: logError
|
||||
logError: logError,
|
||||
gitCmd: runGitCommand
|
||||
};
|
||||
|
||||
+2
-1
@@ -7,6 +7,7 @@
|
||||
"requirejs": "2.1.11",
|
||||
"URIjs": "1.13.1",
|
||||
"mousetrap": "1.4.6",
|
||||
"lunr.js": "0.5.2"
|
||||
"lunr.js": "0.5.2",
|
||||
"ace-builds": "1.1.3"
|
||||
}
|
||||
}
|
||||
+42
-19
@@ -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
|
||||
@@ -14,15 +15,6 @@ var BaseGenerator = require("../page");
|
||||
*/
|
||||
var Generator = function() {
|
||||
BaseGenerator.apply(this, arguments);
|
||||
|
||||
// Options for eBook generation
|
||||
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 +24,54 @@ 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).replace("\.", "") || "pdf";
|
||||
|
||||
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") {
|
||||
var pdfOptions = _.defaults(that.options.pdf || {}, {
|
||||
"fontSize": 12,
|
||||
"toc": true,
|
||||
"pageNumbers": false,
|
||||
"paperSize": "a4",
|
||||
"margin": {
|
||||
"right": 62,
|
||||
"left": 62,
|
||||
"top": 36,
|
||||
"bottom": 36
|
||||
}
|
||||
});
|
||||
|
||||
_.extend(_options, {
|
||||
"--margin-left": String(pdfOptions.margin.left),
|
||||
"--margin-right": String(pdfOptions.margin.right),
|
||||
"--margin-top": String(pdfOptions.margin.top),
|
||||
"--margin-bottom": String(pdfOptions.margin.bottom),
|
||||
"--pdf-add-toc": Boolean(pdfOptions.toc),
|
||||
"--pdf-default-font-size": String(pdfOptions.fontSize),
|
||||
"--pdf-mono-font-size": String(pdfOptions.fontSize),
|
||||
"--paper-size": String(pdfOptions.paperSize),
|
||||
"--pdf-page-numbers": Boolean(pdfOptions.pageNumbers)
|
||||
});
|
||||
}
|
||||
|
||||
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(" ")
|
||||
path.join(that.options.output, "index."+format),
|
||||
stringUtils.optionsToShellArgs(_options)
|
||||
].join(" ");
|
||||
|
||||
exec(command, function (error, stdout, stderr) {
|
||||
|
||||
+17
-1
@@ -16,9 +16,25 @@ var getFiles = function(path) {
|
||||
|
||||
// Add extra rules to ignore common folders
|
||||
ig.addIgnoreRules([
|
||||
// Skip Git stuff
|
||||
'.git/',
|
||||
'.gitignore',
|
||||
'.DS_Store'
|
||||
|
||||
// Skip OS X meta data
|
||||
'.DS_Store',
|
||||
|
||||
// Skip stuff installed by plugins
|
||||
'node_modules',
|
||||
|
||||
// Skip book outputs
|
||||
'*.pdf',
|
||||
'*.epub',
|
||||
'*.mobi',
|
||||
|
||||
// Skip config files
|
||||
'.ignore',
|
||||
'.bookignore',
|
||||
'book.json',
|
||||
], '__custom_stuff');
|
||||
|
||||
// Push each file to our list
|
||||
|
||||
@@ -14,13 +14,13 @@ var BaseGenerator = function(options) {
|
||||
};
|
||||
|
||||
BaseGenerator.prototype.callHook = function(name, data) {
|
||||
return this.plugins.hook(name, this, data);
|
||||
return this.plugins.hook(name, data);
|
||||
};
|
||||
|
||||
BaseGenerator.prototype.loadPlugins = function() {
|
||||
var that = this;
|
||||
|
||||
return Plugin.fromList(this.options.plugins, this.options.input)
|
||||
return Plugin.fromList(this.options.plugins, this.options.input, this)
|
||||
.then(function(_plugins) {
|
||||
that.plugins = _plugins;
|
||||
|
||||
@@ -45,6 +45,27 @@ BaseGenerator.prototype.transferFolder = function(input) {
|
||||
);
|
||||
};
|
||||
|
||||
BaseGenerator.prototype.copyCover = function() {
|
||||
var that = this;
|
||||
|
||||
return Q.all([
|
||||
fs.copy(path.join(this.options.input, "cover.jpg"), path.join(this.options.output, "cover.jpg")),
|
||||
fs.copy(path.join(this.options.input, "cover_small.jpg"), path.join(this.options.output, "cover_small.jpg"))
|
||||
])
|
||||
.fail(function() {
|
||||
// If orignally from multi-lang, try copy from originalInput
|
||||
if (!that.options.originalInput) return;
|
||||
|
||||
return Q.all([
|
||||
fs.copy(path.join(that.options.originalInput, "cover.jpg"), path.join(that.options.output, "cover.jpg")),
|
||||
fs.copy(path.join(that.options.originalInput, "cover_small.jpg"), path.join(that.options.output, "cover_small.jpg"))
|
||||
]);
|
||||
})
|
||||
.fail(function(err) {
|
||||
return Q();
|
||||
});
|
||||
};
|
||||
|
||||
BaseGenerator.prototype.langsIndex = function(langs) {
|
||||
return Q.reject(new Error("Langs index is not supported in this generator"));
|
||||
};
|
||||
|
||||
+75
-31
@@ -1,9 +1,9 @@
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
var path = require("path");
|
||||
var swig = require('swig');
|
||||
var tmp = require('tmp');
|
||||
|
||||
var swig = require('./template');
|
||||
var fs = require("./fs");
|
||||
var parse = require("../parse");
|
||||
var Plugin = require("./plugin");
|
||||
@@ -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")
|
||||
};
|
||||
@@ -26,13 +25,23 @@ var containsFiles = function(dir, files) {
|
||||
.then(_.all);
|
||||
};
|
||||
|
||||
// TEst if generator exists
|
||||
var checkGenerator = function(options) {
|
||||
if (!generators[options.generator]) {
|
||||
return Q.reject(new Error("Invalid generator (availables are: "+_.keys(generators).join(", ")+")"));
|
||||
}
|
||||
return Q();
|
||||
};
|
||||
|
||||
// Create the generator and load plugins
|
||||
var loadGenerator = function(options) {
|
||||
var generator = new generators[options.generator](options);
|
||||
return checkGenerator(options)
|
||||
.then(function() {
|
||||
var generator = new generators[options.generator](options);
|
||||
|
||||
return generator.loadPlugins()
|
||||
.then(_.constant(generator));
|
||||
return generator.loadPlugins()
|
||||
.then(_.constant(generator));
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -45,7 +54,7 @@ var generate = function(options) {
|
||||
"output": null,
|
||||
|
||||
// Config file (relative to input)
|
||||
"configFile": "book.json",
|
||||
"configFile": "book",
|
||||
|
||||
// Output generator
|
||||
"generator": "site",
|
||||
@@ -79,27 +88,20 @@ var generate = function(options) {
|
||||
return Q.reject(new Error("Need option input (book input directory)"));
|
||||
}
|
||||
|
||||
// Ensure generator exists
|
||||
if (!generators[options.generator]) {
|
||||
return Q.reject(new Error("Invalid generator (availables are: "+_.keys(generators).join(", ")));
|
||||
}
|
||||
|
||||
// Check files to get folder type (book, multilanguage book or neither)
|
||||
return Q()
|
||||
return checkGenerator(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', 'defaultsPlugins');
|
||||
try {
|
||||
var _config = require(path.resolve(options.input, options.configFile));
|
||||
|
||||
_.extend(options, _config);
|
||||
}, function() {
|
||||
_.extend(options, _.omit(_config, 'input', 'configFile', 'defaultsPlugins'));
|
||||
}
|
||||
catch(err) {
|
||||
// No config file: not a big deal
|
||||
return Q();
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
// Read readme
|
||||
@@ -134,8 +136,12 @@ var generateMultiLang = function(options) {
|
||||
var langsSummary;
|
||||
options.output = options.output || path.join(options.input, "_book");
|
||||
|
||||
return checkGenerator(options)
|
||||
|
||||
// Multi-languages book
|
||||
return fs.readFile(path.join(options.input, "LANGS.md"), "utf-8")
|
||||
.then(function() {
|
||||
return fs.readFile(path.join(options.input, "LANGS.md"), "utf-8")
|
||||
})
|
||||
|
||||
// Clean output folder
|
||||
.then(function(_langsSummary) {
|
||||
@@ -155,7 +161,9 @@ var generateMultiLang = function(options) {
|
||||
return prev.then(function() {
|
||||
return generate(_.extend({}, options, {
|
||||
input: path.join(options.input, entry.path),
|
||||
output: path.join(options.output, entry.path)
|
||||
output: path.join(options.output, entry.path),
|
||||
originalInput: options.input,
|
||||
originalOutput: options.output
|
||||
}));
|
||||
})
|
||||
}, Q());
|
||||
@@ -170,6 +178,17 @@ var generateMultiLang = function(options) {
|
||||
return generator.langsIndex(options.langsSummary);
|
||||
})
|
||||
|
||||
// Copy cover file
|
||||
.then(function() {
|
||||
return Q.all([
|
||||
fs.copy(path.join(options.input, "cover.jpg"), path.join(options.output, "cover.jpg")),
|
||||
fs.copy(path.join(options.input, "cover_small.jpg"), path.join(options.output, "cover_small.jpg"))
|
||||
])
|
||||
.fail(function() {
|
||||
return Q();
|
||||
})
|
||||
})
|
||||
|
||||
// Return options to caller
|
||||
.then(_.constant(options));
|
||||
};
|
||||
@@ -205,8 +224,15 @@ var generateBook = function(options) {
|
||||
options.github = null;
|
||||
return null;
|
||||
} else if(options.github) {
|
||||
// Git already specified in options
|
||||
return options.github;
|
||||
// Git already specified in options
|
||||
options.github = (
|
||||
// Support URLs in "github" entry of book.json
|
||||
parse.git.githubID(options.github) ||
|
||||
|
||||
// Fallback
|
||||
options.github
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try auto detecting
|
||||
@@ -238,20 +264,38 @@ var generateBook = function(options) {
|
||||
return loadGenerator(options);
|
||||
})
|
||||
|
||||
// Detect multi-languages book
|
||||
// Convert files
|
||||
.then(function(generator) {
|
||||
// Generate the book
|
||||
return Q()
|
||||
|
||||
// Get summary
|
||||
.then(function() {
|
||||
return fs.readFile(path.join(options.input, "SUMMARY.md"), "utf-8")
|
||||
.then(function(_summary) {
|
||||
options.summary = parse.summary(_summary);
|
||||
var summary = {
|
||||
path: path.join(options.input, "SUMMARY.md")
|
||||
};
|
||||
|
||||
// Parse navigation
|
||||
options.navigation = parse.navigation(options.summary);
|
||||
});
|
||||
var _callHook = function(name) {
|
||||
return generator.callHook(name, summary)
|
||||
.then(function(_summary) {
|
||||
summary = _summary;
|
||||
return summary;
|
||||
});
|
||||
};
|
||||
|
||||
return fs.readFile(summary.path, "utf-8")
|
||||
.then(function(_content) {
|
||||
summary.content = _content;
|
||||
return _callHook("summary:before");
|
||||
})
|
||||
.then(function() {
|
||||
summary.content = parse.summary(summary.content);
|
||||
return _callHook("summary:after");
|
||||
})
|
||||
.then(function() {
|
||||
options.summary = summary.content;
|
||||
options.navigation = parse.navigation(options.summary);
|
||||
})
|
||||
})
|
||||
|
||||
// Skip processing some files
|
||||
|
||||
+14
-11
@@ -2,22 +2,12 @@ var _ = require("lodash");
|
||||
var util = require("util");
|
||||
var path = require("path");
|
||||
var Q = require("q");
|
||||
var swig = require('swig');
|
||||
var hljs = require('highlight.js');
|
||||
var swig = require("../template");
|
||||
|
||||
var fs = require("../fs");
|
||||
var parse = require("../../parse");
|
||||
var BaseGenerator = require("../site");
|
||||
|
||||
// Swig filter: highlight coloration
|
||||
swig.setFilter('code', function(code, lang) {
|
||||
try {
|
||||
return hljs.highlight(lang, code).value;
|
||||
} catch(e) {
|
||||
return hljs.highlightAuto(code).value;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* This generator will generate a simple index.html which can be converted as a PDF
|
||||
@@ -61,22 +51,35 @@ Generator.prototype.convertFile = function(content, input) {
|
||||
});
|
||||
};
|
||||
|
||||
// Generate languages index
|
||||
Generator.prototype.langsIndex = function(langs) {
|
||||
return Q();
|
||||
};
|
||||
|
||||
Generator.prototype.finish = function() {
|
||||
var that = this;
|
||||
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"),
|
||||
}, output);
|
||||
})
|
||||
|
||||
// Copy cover
|
||||
.then(function() {
|
||||
return that.copyCover();
|
||||
})
|
||||
|
||||
// Copy assets
|
||||
.then(function() {
|
||||
return fs.copy(
|
||||
|
||||
@@ -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;
|
||||
+134
-78
@@ -10,11 +10,15 @@ var pkg = require("../../package.json");
|
||||
|
||||
var RESOURCES = ["js", "css"];
|
||||
|
||||
var Plugin = function(name, root) {
|
||||
var Plugin = function(name, root, generator) {
|
||||
this.name = name;
|
||||
this.root = root;
|
||||
this.packageInfos = {};
|
||||
this.infos = {};
|
||||
this.generator = generator;
|
||||
|
||||
// Bind methods
|
||||
_.bindAll(this);
|
||||
|
||||
_.each([
|
||||
"gitbook-plugin-"+name,
|
||||
@@ -43,28 +47,50 @@ Plugin.prototype.load = function(name, baseDir) {
|
||||
}
|
||||
};
|
||||
|
||||
// Return resources
|
||||
Plugin.prototype.getResources = function(resource) {
|
||||
if (!this.infos.book || !this.infos.book[resource]) {
|
||||
return [];
|
||||
Plugin.prototype.normalizeResource = function(resource) {
|
||||
// Parse the resource path
|
||||
var parsed = url.parse(resource);
|
||||
|
||||
// This is a remote resource
|
||||
// so we will simply link to using it's URL
|
||||
if (parsed.protocol) {
|
||||
return {
|
||||
"url": resource
|
||||
};
|
||||
}
|
||||
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();
|
||||
|
||||
// This will be copied over from disk
|
||||
// and shipped with the book's build
|
||||
return { "path": this.name+"/"+resource };
|
||||
};
|
||||
|
||||
// Return resources
|
||||
Plugin.prototype.getResources = function() {
|
||||
var book = this.infos.book;
|
||||
|
||||
// Nothing specified, fallback to default
|
||||
if (!book) {
|
||||
return Q({});
|
||||
}
|
||||
|
||||
// Dynamic function
|
||||
if(typeof book === "function") {
|
||||
// Call giving it the context of our generator
|
||||
return Q().then(book.bind(this.generator));
|
||||
}
|
||||
|
||||
// Plain data object
|
||||
return Q(book);
|
||||
};
|
||||
|
||||
// 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)
|
||||
this.packageInfos &&
|
||||
this.packageInfos.name &&
|
||||
this.packageInfos.engines &&
|
||||
this.packageInfos.engines.gitbook &&
|
||||
semver.satisfies(pkg.version, this.packageInfos.engines.gitbook)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -74,7 +100,10 @@ Plugin.prototype.resolveFile = function(filename) {
|
||||
};
|
||||
|
||||
// Resolve file path
|
||||
Plugin.prototype.callHook = function(name, context, data) {
|
||||
Plugin.prototype.callHook = function(name, data) {
|
||||
// Our generator will be the context to apply
|
||||
var context = this.generator;
|
||||
|
||||
var hookFunc = this.infos.hooks? this.infos.hooks[name] : null;
|
||||
data = data || {};
|
||||
|
||||
@@ -86,18 +115,20 @@ Plugin.prototype.callHook = function(name, context, data) {
|
||||
});
|
||||
};
|
||||
|
||||
// Has assets
|
||||
Plugin.prototype.hasAssets = function(out) {
|
||||
return (this.infos.book && this.infos.book.assets);
|
||||
};
|
||||
|
||||
// Copy plugin assets fodler
|
||||
Plugin.prototype.copyAssets = function(out) {
|
||||
if (!this.hasAssets()) return Q();
|
||||
return fs.copy(
|
||||
this.resolveFile(this.infos.book.assets),
|
||||
out
|
||||
);
|
||||
var that = this;
|
||||
|
||||
return this.getResources().get('assets')
|
||||
.then(function(assets) {
|
||||
// Assets are undefined
|
||||
if(!assets) return false;
|
||||
|
||||
return fs.copy(
|
||||
that.resolveFile(assets),
|
||||
out
|
||||
).then(_.constant(true));
|
||||
}, _.constant(false));
|
||||
};
|
||||
|
||||
|
||||
@@ -114,7 +145,7 @@ Plugin.normalizeNames = function(names) {
|
||||
return name.length > 0 && name[0] == "-";
|
||||
})
|
||||
.map(function(name) {
|
||||
return name.slice(1)
|
||||
return name.slice(1);
|
||||
})
|
||||
.value();
|
||||
|
||||
@@ -133,74 +164,99 @@ Plugin.normalizeNames = function(names) {
|
||||
};
|
||||
|
||||
// Extract data from a list of plugin
|
||||
Plugin.fromList = function(names, root) {
|
||||
Plugin.fromList = function(names, root, generator) {
|
||||
var that = this;
|
||||
var failed = [];
|
||||
|
||||
// Load plugins
|
||||
var plugins = _.map(names, function(name) {
|
||||
var plugin = new Plugin(name, root);
|
||||
var plugin = new Plugin(name, root, generator);
|
||||
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();
|
||||
resources.html = {}
|
||||
// The raw resources extracted from each plugin
|
||||
var pluginResources;
|
||||
|
||||
_.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);
|
||||
// Get resources of plugins
|
||||
return Q.all(_.map(plugins, function(plugin) {
|
||||
return plugin.getResources();
|
||||
}))
|
||||
// Extract resources out
|
||||
// css, js, etc ...
|
||||
.then(function(resources) {
|
||||
pluginResources = resources;
|
||||
// Group by resource types
|
||||
return _.chain(RESOURCES)
|
||||
.map(function(resourceType) {
|
||||
// Get resources from all the plugins for this current type
|
||||
return [
|
||||
// Key
|
||||
resourceType,
|
||||
// Value
|
||||
_.chain(resources)
|
||||
.pluck(resourceType)
|
||||
.compact()
|
||||
.flatten()
|
||||
.value()
|
||||
];
|
||||
})
|
||||
});
|
||||
|
||||
return Q({
|
||||
'list': plugins,
|
||||
'resources': resources,
|
||||
'hook': function(name, context, data) {
|
||||
return _.reduce(plugins, function(prev, plugin) {
|
||||
return prev.then(function(ret) {
|
||||
return plugin.callHook(name, context, ret);
|
||||
})
|
||||
}, Q(data));
|
||||
},
|
||||
'template': function(name) {
|
||||
var withTpl = _.find(plugins, function(plugin) {
|
||||
return (plugin.infos.templates
|
||||
&& plugin.infos.templates[name]);
|
||||
.object()
|
||||
.value();
|
||||
})
|
||||
// Extract html snippets
|
||||
.then(function(resources) {
|
||||
// Map of html resources by name added by each plugin
|
||||
resources.html = pluginResources.reduce(function(accu, resource) {
|
||||
var html = (resource && resource.html) || {};
|
||||
_.each(html, function(code, key) {
|
||||
// Turn into function if not one already
|
||||
if (!_.isFunction(code)) code = _.constant(code);
|
||||
// Append
|
||||
accu[key] = (accu[key] || []).concat([code]);
|
||||
});
|
||||
|
||||
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");
|
||||
}
|
||||
return accu;
|
||||
}, {});
|
||||
|
||||
return resources;
|
||||
})
|
||||
// Return big multi-plugin object
|
||||
.then(function(resources) {
|
||||
return {
|
||||
'list': plugins,
|
||||
'resources': resources,
|
||||
'hook': function(name, data) {
|
||||
return _.reduce(plugins, function(prev, plugin) {
|
||||
return prev.then(function(ret) {
|
||||
return plugin.callHook(name, ret);
|
||||
});
|
||||
}, Q(data));
|
||||
},
|
||||
'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]);
|
||||
},
|
||||
'html': function(tag, context, options) {
|
||||
return _.map(resources.html[tag] || [], function(code) {
|
||||
return code.call(context, options);
|
||||
}).join("\n");
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// Default plugins
|
||||
Plugin.defaults = [
|
||||
"mixpanel",
|
||||
"mathjax"
|
||||
];
|
||||
|
||||
|
||||
+13
-21
@@ -2,26 +2,16 @@ var util = require("util");
|
||||
var path = require("path");
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
var swig = require('swig');
|
||||
var swig = require("../template");
|
||||
|
||||
var fs = require("../fs");
|
||||
var parse = require("../../parse");
|
||||
var BaseGenerator = require("../generator");
|
||||
|
||||
var links = require("../../utils/links");
|
||||
var indexer = require('./search_indexer');
|
||||
var Manifest = require('../manifest');
|
||||
|
||||
// Swig filter for returning the count of lines in a code section
|
||||
swig.setFilter('lines', function(content) {
|
||||
return content.split('\n').length;
|
||||
});
|
||||
|
||||
// Swig filter for returning a link to the associated html file of a markdown file
|
||||
swig.setFilter('mdLink', function(link) {
|
||||
var link = link.replace(".md", ".html");
|
||||
if (link == "README.html") link = "index.html";
|
||||
return link;
|
||||
});
|
||||
|
||||
var Generator = function() {
|
||||
BaseGenerator.apply(this, arguments);
|
||||
@@ -63,7 +53,6 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate)
|
||||
var that = this;
|
||||
|
||||
interpolate = interpolate || _.identity;
|
||||
|
||||
return Q()
|
||||
.then(function(sections) {
|
||||
return tpl(_.extend({
|
||||
@@ -113,6 +102,7 @@ Generator.prototype.convertFile = function(content, _input) {
|
||||
|
||||
var page = {
|
||||
path: _input,
|
||||
rawPath: input, // path to raw md file
|
||||
content: content,
|
||||
progress: parse.progress(this.options.navigation, _input)
|
||||
};
|
||||
@@ -163,7 +153,7 @@ Generator.prototype.convertFile = function(content, _input) {
|
||||
content: page.sections,
|
||||
|
||||
basePath: basePath,
|
||||
staticBase: path.join(basePath, "gitbook"),
|
||||
staticBase: links.join(basePath, "gitbook"),
|
||||
}, output, function(html) {
|
||||
page.content = html;
|
||||
|
||||
@@ -199,7 +189,7 @@ Generator.prototype.copyAssets = function() {
|
||||
path.join(that.options.output, "gitbook")
|
||||
)
|
||||
|
||||
// Add to cach manifest
|
||||
// Add to cache manifest
|
||||
.then(function() {
|
||||
return that.manifest.addFolder(path.join(that.options.output, "gitbook"), "gitbook");
|
||||
})
|
||||
@@ -208,11 +198,11 @@ Generator.prototype.copyAssets = function() {
|
||||
.then(function() {
|
||||
return Q.all(
|
||||
_.map(that.plugins.list, function(plugin) {
|
||||
if (!plugin.hasAssets()) return Q();
|
||||
|
||||
var pluginAssets = path.join(that.options.output, "gitbook/plugins/", plugin.name);
|
||||
return plugin.copyAssets(pluginAssets)
|
||||
.then(function() {
|
||||
.then(function(copiedStuff) {
|
||||
// Nothing was copied
|
||||
if(!copiedStuff) return;
|
||||
return that.manifest.addFolder(pluginAssets, "gitbook/plugins/"+plugin.name);
|
||||
});
|
||||
})
|
||||
@@ -238,10 +228,12 @@ Generator.prototype.writeCacheManifest = function() {
|
||||
};
|
||||
|
||||
Generator.prototype.finish = function() {
|
||||
var deferred = this.copyAssets().then(this.writeSearchIndex);
|
||||
var deferred = this.copyAssets()
|
||||
.then(this.copyCover)
|
||||
.then(this.writeSearchIndex);
|
||||
|
||||
if (this.options.cache) {
|
||||
deferred = deferred.then(this.writeCacheManifest);
|
||||
if (this.options.cache !== false) {
|
||||
deferred = deferred.then(this.writeCacheManifest);
|
||||
}
|
||||
|
||||
return deferred;
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
var path = require("path");
|
||||
var swig = require('swig');
|
||||
var hljs = require('highlight.js');
|
||||
|
||||
var links = require('../utils/').links;
|
||||
var pkg = require('../../package.json');
|
||||
|
||||
swig.setDefaults({
|
||||
locals: {
|
||||
gitbook: {
|
||||
version: pkg.version
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Swig filter for returning the count of lines in a code section
|
||||
swig.setFilter('lines', function(content) {
|
||||
return content.split('\n').length;
|
||||
});
|
||||
|
||||
// Swig filter for returning a link to the associated html file of a markdown file
|
||||
swig.setFilter('mdLink', function(link) {
|
||||
var link = link.replace(".md", ".html");
|
||||
if (link == "README.html") link = "index.html";
|
||||
return link;
|
||||
});
|
||||
|
||||
// Swig filter: highlight coloration
|
||||
swig.setFilter('code', function(code, lang) {
|
||||
try {
|
||||
return hljs.highlight(lang, code).value;
|
||||
} catch(e) {
|
||||
return hljs.highlightAuto(code).value;
|
||||
}
|
||||
});
|
||||
|
||||
// Convert a level into a deep level
|
||||
swig.setFilter('lvl', function(lvl) {
|
||||
return lvl.split(".").length;
|
||||
});
|
||||
|
||||
// Join path
|
||||
swig.setFilter('pathJoin', function(base, _path) {
|
||||
return path.join(base, _path);
|
||||
});
|
||||
|
||||
// Is a link an absolute link
|
||||
swig.setFilter('isExternalLink', function(link) {
|
||||
return links.isExternal(link);
|
||||
});
|
||||
|
||||
module.exports = swig;
|
||||
@@ -0,0 +1,20 @@
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
module.exports = function(code, folder) {
|
||||
folder = folder || '';
|
||||
|
||||
return code.replace(/{{([\s\S]+?)}}/g, function(match, filename) {
|
||||
// Normalize filename
|
||||
var fname = path.join(folder, filename.trim());
|
||||
|
||||
// Try including snippet from FS
|
||||
try {
|
||||
// Trim trailing newlines/space of imported snippets
|
||||
return fs.readFileSync(fname, 'utf8').trimRight();
|
||||
} catch(err) {}
|
||||
|
||||
// If fails leave content as is
|
||||
return match;
|
||||
});
|
||||
};
|
||||
+1
-1
@@ -28,7 +28,7 @@ function gitURL(path) {
|
||||
// Parse a git URL to a github ID : username/reponame
|
||||
function githubID(_url) {
|
||||
// Remove .git if it's in _url
|
||||
var sliceEnd = _url.slice(-4) === '.git' ? -4 : _url.length;
|
||||
var sliceEnd = _url.slice(-4) === '.git' ? -4 : undefined;
|
||||
|
||||
// Detect HTTPS repos
|
||||
var parsed = url.parse(_url);
|
||||
|
||||
+8
-6
@@ -1,13 +1,14 @@
|
||||
var _ = require("lodash")
|
||||
var parseSummary = require("./summary");
|
||||
var _ = require("lodash");
|
||||
var parseEntries = require("./summary").entries;
|
||||
|
||||
|
||||
var parseLangs = function(content) {
|
||||
var summary = parseSummary(content);
|
||||
var entries = parseEntries(content);
|
||||
|
||||
return {
|
||||
list: _.chain(summary.chapters)
|
||||
list: _.chain(entries)
|
||||
.filter(function(entry) {
|
||||
return entry.path != null;
|
||||
return Boolean(entry.path);
|
||||
})
|
||||
.map(function(entry) {
|
||||
return {
|
||||
@@ -17,7 +18,8 @@ var parseLangs = function(content) {
|
||||
};
|
||||
})
|
||||
.value()
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
module.exports = parseLangs;
|
||||
@@ -118,6 +118,10 @@ function lexPage(src) {
|
||||
|
||||
return sections;
|
||||
}, [])
|
||||
.map(function(section) {
|
||||
section.links = nodes.links;
|
||||
return section;
|
||||
})
|
||||
.value();
|
||||
}
|
||||
|
||||
|
||||
@@ -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)));
|
||||
}, []);
|
||||
}
|
||||
|
||||
@@ -26,15 +26,9 @@ function navigation(summary, files) {
|
||||
// Support single files as well as list
|
||||
files = _.isArray(files) ? files : (_.isString(files) ? [files] : null);
|
||||
|
||||
// Special README nav
|
||||
var README_NAV = {
|
||||
path: 'README.md',
|
||||
title: 'Introduction',
|
||||
level: '0',
|
||||
};
|
||||
|
||||
// List of all navNodes
|
||||
var navNodes = [README_NAV].concat(flattenChapters(summary.chapters));
|
||||
// Flatten chapters, then add in default README node if ndeeded etc ...
|
||||
var navNodes = flattenChapters(summary.chapters);
|
||||
var prevNodes = [null].concat(navNodes.slice(0, -1));
|
||||
var nextNodes = navNodes.slice(1).concat([null]);
|
||||
|
||||
|
||||
+11
-6
@@ -5,6 +5,7 @@ var hljs = require('highlight.js');
|
||||
var lex = require('./lex');
|
||||
var renderer = require('./renderer');
|
||||
|
||||
var codeInclude = require('./code_include');
|
||||
var lnormalize = require('../utils/lang').normalize;
|
||||
|
||||
|
||||
@@ -12,10 +13,9 @@ var lnormalize = require('../utils/lang').normalize;
|
||||
// Render a section using our custom renderer
|
||||
function render(section, _options) {
|
||||
// Copy section
|
||||
var links = section.links || {};
|
||||
section = _.toArray(section);
|
||||
|
||||
// marked's Render expects this, we don't use it yet
|
||||
section.links = {};
|
||||
section.links = links;
|
||||
|
||||
// Build options using defaults and our custom renderer
|
||||
var options = _.extend({}, marked.defaults, {
|
||||
@@ -74,15 +74,20 @@ function parsePage(src, options) {
|
||||
// Main language
|
||||
var lang = validLangs ? langs[0] : null;
|
||||
|
||||
// codeInclude shortcut
|
||||
var ci = function(code) {
|
||||
return codeInclude(code, options.dir);
|
||||
};
|
||||
|
||||
return {
|
||||
id: section.id,
|
||||
type: section.type,
|
||||
content: render(nonCodeNodes),
|
||||
lang: lang,
|
||||
code: {
|
||||
base: codeNodes[0].text,
|
||||
solution: codeNodes[1].text,
|
||||
validation: codeNodes[2].text,
|
||||
base: ci(codeNodes[0].text),
|
||||
solution: ci(codeNodes[1].text),
|
||||
validation: ci(codeNodes[2].text),
|
||||
// Context is optional
|
||||
context: codeNodes[3] ? codeNodes[3].text : null,
|
||||
}
|
||||
|
||||
@@ -11,9 +11,6 @@ var calculProgress = function(navigation, current) {
|
||||
nav.path = path;
|
||||
return nav;
|
||||
})
|
||||
.sortBy(function(nav) {
|
||||
return parseFloat(nav.level);
|
||||
})
|
||||
.map(function(nav, i) {
|
||||
// Calcul percent
|
||||
nav.percent = (i * 100) / Math.max((n - 1), 1);
|
||||
|
||||
+18
-2
@@ -1,6 +1,7 @@
|
||||
var url = require('url');
|
||||
var inherits = require('util').inherits;
|
||||
var links = require('../utils').links;
|
||||
var codeInclude = require('./code_include');
|
||||
|
||||
|
||||
var path = require('path');
|
||||
@@ -56,7 +57,7 @@ GitBookRenderer.prototype.link = function(href, title, text) {
|
||||
|
||||
// Relative link, rewrite it to point to github repo
|
||||
if(links.isRelative(_href)) {
|
||||
if (path.extname(_href) == ".md") {
|
||||
if (path.extname(parsed.path) == ".md") {
|
||||
_href = links.toAbsolute(_href, o.dir || "./", o.outdir || "./");
|
||||
|
||||
if (o.singleFile) {
|
||||
@@ -125,7 +126,7 @@ GitBookRenderer.prototype._createCheckboxAndRadios = function(text) {
|
||||
var length = splittedText.length;
|
||||
var label = '<label class="quiz-label" for="' + quizIdentifier + '">' + splittedText[length - 1] + '</label>';
|
||||
return text.replace(fieldRegex, field).replace(splittedText[length - 1], label);
|
||||
}
|
||||
};
|
||||
|
||||
GitBookRenderer.prototype.tablecell = function(content, flags) {
|
||||
return GitBookRenderer.super_.prototype.tablecell(this._createCheckboxAndRadios(content), flags);
|
||||
@@ -135,5 +136,20 @@ GitBookRenderer.prototype.listitem = function(text) {
|
||||
return GitBookRenderer.super_.prototype.listitem(this._createCheckboxAndRadios(text));
|
||||
};
|
||||
|
||||
GitBookRenderer.prototype.code = function(code, lang, escaped) {
|
||||
return GitBookRenderer.super_.prototype.code.call(
|
||||
this,
|
||||
// Import code snippets
|
||||
codeInclude(code, this._extra_options.dir),
|
||||
lang,
|
||||
escaped
|
||||
);
|
||||
};
|
||||
|
||||
GitBookRenderer.prototype.heading = function(text, level, raw) {
|
||||
var id = this.options.headerPrefix + raw.toLowerCase().replace(/[^\w -]+/g, '').replace(/ /g, '-');
|
||||
return '<h' + level + ' id="' + id + '">' + text + '</h' + level + '>\n';
|
||||
};
|
||||
|
||||
// Exports
|
||||
module.exports = GitBookRenderer;
|
||||
|
||||
+43
-14
@@ -81,37 +81,66 @@ function parseTitle(src, nums) {
|
||||
title: matches[1],
|
||||
level: level,
|
||||
|
||||
// Replace .md references with .html
|
||||
path: matches[2].replace(/\\/g, '/'),
|
||||
// Normalize path
|
||||
// 1. Convert Window's "\" to "/"
|
||||
// 2. Remove leading "/" if exists
|
||||
path: matches[2].replace(/\\/g, '/').replace(/^\/+/, ''),
|
||||
};
|
||||
}
|
||||
|
||||
function parseArticle(chapterNum, nodes, idx) {
|
||||
return parseTitle(_.first(nodes).text, [chapterNum, idx+1]);
|
||||
}
|
||||
function parseChapter(nodes, nums) {
|
||||
// Convert single number to an array
|
||||
nums = _.isArray(nums) ? nums : [nums];
|
||||
|
||||
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))
|
||||
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));
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function parseSummary(src) {
|
||||
function defaultChapterList(chapterList) {
|
||||
var first = _.first(chapterList);
|
||||
|
||||
if (first) {
|
||||
var chapter = parseChapter(first, [0]);
|
||||
|
||||
// Already have README node, we're good to go
|
||||
if(chapter.path === 'README.md') {
|
||||
return chapterList;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
[ { type: 'text', text: '[Introduction](README.md)' } ]
|
||||
].concat(chapterList);
|
||||
}
|
||||
|
||||
function listGroups(src) {
|
||||
var nodes = marked.lexer(src);
|
||||
|
||||
// Get out list of chapters
|
||||
var chapterList = filterList(nodes);
|
||||
// Get out groups of lists
|
||||
return listSplit(
|
||||
filterList(nodes),
|
||||
'list_item_start', 'list_item_end'
|
||||
);
|
||||
}
|
||||
|
||||
function parseSummary(src) {
|
||||
// Split out chapter sections
|
||||
var chapters = _.chain(listSplit(chapterList, 'list_item_start', 'list_item_end'))
|
||||
.map(parseChapter)
|
||||
.value();
|
||||
var chapters = defaultChapterList(listGroups(src))
|
||||
.map(parseChapter);
|
||||
|
||||
return {
|
||||
chapters: chapters
|
||||
};
|
||||
}
|
||||
|
||||
function parseEntries (src) {
|
||||
return listGroups(src).map(parseChapter);
|
||||
}
|
||||
|
||||
|
||||
// Exports
|
||||
module.exports = parseSummary;
|
||||
module.exports.entries = parseEntries;
|
||||
|
||||
@@ -2,6 +2,7 @@ var MAP = {
|
||||
'py': 'python',
|
||||
'js': 'javascript',
|
||||
'rb': 'ruby',
|
||||
'csharp': 'cs',
|
||||
};
|
||||
|
||||
function normalize(lang) {
|
||||
|
||||
+31
-4
@@ -1,11 +1,24 @@
|
||||
var url = require('url');
|
||||
var path = require('path');
|
||||
|
||||
// Is the link an external link
|
||||
var isExternal = function(href) {
|
||||
try {
|
||||
return Boolean(url.parse(href).protocol);
|
||||
} catch(err) { }
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// Return true if the link is relative
|
||||
var isRelative = function(href) {
|
||||
var parsed = url.parse(href);
|
||||
try {
|
||||
var parsed = url.parse(href);
|
||||
|
||||
return !parsed.protocol && parsed.path && parsed.path[0] != '/';
|
||||
return !parsed.protocol && parsed.path && parsed.path[0] != '/';
|
||||
} catch(err) {}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Relative to absolute path
|
||||
@@ -26,8 +39,22 @@ var toAbsolute = function(_href, dir, outdir) {
|
||||
return _href;
|
||||
};
|
||||
|
||||
// Join links
|
||||
|
||||
var join = function() {
|
||||
var _href = path.join.apply(path, arguments);
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
_href = _href.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
return _href;
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
isRelative: isRelative,
|
||||
toAbsolute: toAbsolute
|
||||
};
|
||||
isExternal: isExternal,
|
||||
toAbsolute: toAbsolute,
|
||||
join: join
|
||||
};
|
||||
|
||||
@@ -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 || value == false) return null;
|
||||
if (value == true) return key;
|
||||
return key+"="+escapeShellArg(value);
|
||||
})
|
||||
.compact()
|
||||
.value()
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
escapeShellArg: escapeShellArg,
|
||||
optionsToShellArgs: optionsToShellArgs
|
||||
};
|
||||
+2
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gitbook",
|
||||
"version": "0.4.6",
|
||||
"version": "0.6.2",
|
||||
"homepage": "http://www.gitbook.io/",
|
||||
"description": "Library and cmd utility to generate GitBooks",
|
||||
"main": "lib/index.js",
|
||||
@@ -22,8 +22,7 @@
|
||||
"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.4",
|
||||
"gitbook-plugin-livereload": "0.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
# Summary
|
||||
|
||||
* [Custom name for Introduction](README.md)
|
||||
* [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)
|
||||
* Unfinished article
|
||||
* Unfinished Chapter
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
# Beautiful chapter
|
||||
|
||||
Here is a nice included snippet :
|
||||
|
||||
```c
|
||||
{{ included.c }}
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
An exercise using includes
|
||||
|
||||
```c
|
||||
{{ included.c }}
|
||||
|
||||
Remove this extra code at the end
|
||||
```
|
||||
|
||||
```c
|
||||
{{ included.c }}
|
||||
```
|
||||
|
||||
```c
|
||||
{{ included.c }}
|
||||
|
||||
This validation code is wrong but who cares ?
|
||||
```
|
||||
|
||||
----
|
||||
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)
|
||||
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
printf("All is well\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var assert = require('assert');
|
||||
|
||||
var page = require('../').parse.page;
|
||||
|
||||
var FIXTURES_DIR = path.join(__dirname, './fixtures/');
|
||||
|
||||
function loadPage (name, options) {
|
||||
var CONTENT = fs.readFileSync(FIXTURES_DIR + name + '.md', 'utf8');
|
||||
return page(CONTENT, options);
|
||||
}
|
||||
|
||||
|
||||
describe('Code includes', function() {
|
||||
|
||||
var LEXED = loadPage('INCLUDES', {
|
||||
'dir': FIXTURES_DIR,
|
||||
});
|
||||
|
||||
var INCLUDED_C = fs.readFileSync(path.join(FIXTURES_DIR, 'included.c'), 'utf8');
|
||||
|
||||
it('should work for snippets', function() {
|
||||
assert.equal(LEXED[0].type, 'normal');
|
||||
// Has replaced include
|
||||
assert.equal(
|
||||
LEXED[0].content.indexOf('{{ included.c }}'),
|
||||
-1
|
||||
);
|
||||
});
|
||||
|
||||
it('should work for exercises', function() {
|
||||
assert.equal(LEXED[1].type, 'exercise');
|
||||
|
||||
// Solution is trimmed version of source
|
||||
assert.equal(LEXED[1].code.solution, INCLUDED_C.trim());
|
||||
});
|
||||
});
|
||||
+29
-2
@@ -7,7 +7,9 @@ var navigation = require('../').parse.navigation;
|
||||
|
||||
|
||||
var CONTENT = fs.readFileSync(path.join(__dirname, './fixtures/SUMMARY.md'), 'utf8');
|
||||
var ALT_CONTENT = fs.readFileSync(path.join(__dirname, './fixtures/ALTERNATIVE_SUMMARY.md'), 'utf8');
|
||||
var LEXED = summary(CONTENT);
|
||||
var ALT_LEXED = summary(ALT_CONTENT);
|
||||
|
||||
|
||||
describe('Summary navigation', function() {
|
||||
@@ -18,6 +20,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 +30,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 +44,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');
|
||||
});
|
||||
|
||||
@@ -66,6 +78,21 @@ describe('Summary navigation', function() {
|
||||
assert.equal(nav['chapter-3/README.md'].level, '3');
|
||||
});
|
||||
|
||||
it('should have a default README node', function() {
|
||||
var nav = navigation(LEXED);
|
||||
|
||||
assert.equal(nav['README.md'].level, '0');
|
||||
assert.equal(nav['README.md'].title, 'Introduction');
|
||||
});
|
||||
|
||||
it('Should allow README node to be customized', function() {
|
||||
var nav = navigation(ALT_LEXED);
|
||||
|
||||
assert(nav['README.md']);
|
||||
assert.equal(nav['README.md'].level, '0');
|
||||
assert.notEqual(nav['README.md'].title, 'Introduction');
|
||||
});
|
||||
|
||||
it('should not accept null paths', function() {
|
||||
var nav = navigation(LEXED);
|
||||
|
||||
|
||||
+17
-12
@@ -12,13 +12,13 @@ var LEXED = summary(CONTENT);
|
||||
describe('Summary parsing', function () {
|
||||
|
||||
it('should detect chapters', function() {
|
||||
assert.equal(LEXED.chapters.length, 5);
|
||||
assert.equal(LEXED.chapters.length, 6);
|
||||
});
|
||||
|
||||
it('should support articles', function() {
|
||||
assert.equal(LEXED.chapters[0].articles.length, 2);
|
||||
assert.equal(LEXED.chapters[1].articles.length, 0);
|
||||
assert.equal(LEXED.chapters[1].articles.length, 2);
|
||||
assert.equal(LEXED.chapters[2].articles.length, 0);
|
||||
assert.equal(LEXED.chapters[3].articles.length, 0);
|
||||
});
|
||||
|
||||
it('should detect paths and titles', function() {
|
||||
@@ -26,29 +26,34 @@ describe('Summary parsing', function () {
|
||||
assert(LEXED.chapters[1].path);
|
||||
assert(LEXED.chapters[2].path);
|
||||
assert(LEXED.chapters[3].path);
|
||||
assert.equal(LEXED.chapters[4].path, null);
|
||||
assert(LEXED.chapters[4].path);
|
||||
assert.equal(LEXED.chapters[5].path, null);
|
||||
|
||||
assert(LEXED.chapters[0].title);
|
||||
assert(LEXED.chapters[1].title);
|
||||
assert(LEXED.chapters[2].title);
|
||||
assert(LEXED.chapters[3].title);
|
||||
assert(LEXED.chapters[4].title);
|
||||
assert(LEXED.chapters[5].title);
|
||||
});
|
||||
|
||||
it('should normalize paths from .md to .html', function() {
|
||||
assert.equal(LEXED.chapters[0].path,'chapter-1/README.md');
|
||||
assert.equal(LEXED.chapters[1].path,'chapter-2/README.md');
|
||||
assert.equal(LEXED.chapters[2].path,'chapter-3/README.md');
|
||||
assert.equal(LEXED.chapters[0].path,'README.md');
|
||||
assert.equal(LEXED.chapters[1].path,'chapter-1/README.md');
|
||||
assert.equal(LEXED.chapters[2].path,'chapter-2/README.md');
|
||||
assert.equal(LEXED.chapters[3].path,'chapter-3/README.md');
|
||||
});
|
||||
|
||||
it('should detect levels correctly', function() {
|
||||
var c = LEXED.chapters;
|
||||
|
||||
assert.equal(c[0].level, '1');
|
||||
assert.equal(c[1].level, '2');
|
||||
assert.equal(c[2].level, '3');
|
||||
assert.equal(c[0].level, '0');
|
||||
assert.equal(c[1].level, '1');
|
||||
assert.equal(c[2].level, '2');
|
||||
assert.equal(c[3].level, '3');
|
||||
|
||||
assert.equal(c[0].articles[0].level, '1.1');
|
||||
assert.equal(c[0].articles[1].level, '1.2');
|
||||
assert.equal(c[1].articles[0].level, '1.1');
|
||||
assert.equal(c[1].articles[1].level, '1.2');
|
||||
assert.equal(c[1].articles[1].articles[0].level, '1.2.1');
|
||||
});
|
||||
});
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,9 +1,13 @@
|
||||
define([
|
||||
"jQuery",
|
||||
"ace/ace",
|
||||
"ace/theme-tomorrow",
|
||||
"ace/mode-javascript",
|
||||
"utils/execute",
|
||||
"core/events",
|
||||
"core/state"
|
||||
], function($, execute, events, state){
|
||||
], function($, _ace, _aceTheme, _aceMode, execute, events, state){
|
||||
|
||||
// Bind an exercise
|
||||
var prepareExercise = function($exercise) {
|
||||
var codeSolution = $exercise.find(".code-solution").text();
|
||||
@@ -14,6 +18,14 @@ define([
|
||||
editor.setTheme("ace/theme/tomorrow");
|
||||
editor.getSession().setUseWorker(false);
|
||||
editor.getSession().setMode("ace/mode/javascript");
|
||||
|
||||
editor.commands.addCommand({
|
||||
name: "submit",
|
||||
bindKey: "Ctrl-Return|Cmd-Return",
|
||||
exec: function() {
|
||||
$exercise.find(".action-submit").click();
|
||||
}
|
||||
});
|
||||
|
||||
// Submit: test code
|
||||
$exercise.find(".action-submit").click(function(e) {
|
||||
@@ -48,4 +60,4 @@ define([
|
||||
init: init,
|
||||
prepare: prepareExercise
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
@@ -91,8 +91,8 @@ define([
|
||||
|
||||
// Instantiate font state object
|
||||
fontState = storage.get("fontState", {
|
||||
size: config.size || 1,
|
||||
family: FAMILY[config.family || "serif"],
|
||||
size: config.size || 2,
|
||||
family: FAMILY[config.family || "sans"],
|
||||
theme: THEMES[config.theme || "white"]
|
||||
});
|
||||
fontState.save = function(){
|
||||
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
@import "mixins.less";
|
||||
|
||||
@import "ebook/variables.less";
|
||||
@import "ebook/highlight.less";
|
||||
|
||||
.book-chapter {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
pre, blockquote {
|
||||
border: 1px solid #999;
|
||||
page-break-inside: avoid;
|
||||
background: #f1f1f1;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100% !important;
|
||||
page-break-inside: avoid;
|
||||
margin: 0px auto;
|
||||
}
|
||||
|
||||
.exercise, .quiz {
|
||||
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;
|
||||
}
|
||||
|
||||
.question {
|
||||
margin-top: 0.4cm;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
@font-size-base: 13px;
|
||||
@@ -4,4 +4,4 @@
|
||||
&:hover, &:focus {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
@import "vendors/bootstrap/bootstrap.less";
|
||||
@import "vendors/fontawesome/font-awesome.less";
|
||||
|
||||
@import "mixins.less";
|
||||
@import "variables.less";
|
||||
@import "fonts.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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
h1 { font-size: floor(@font-size-base * 2.15); }
|
||||
h2 { font-size: floor(@font-size-base * 1.70); }
|
||||
h3 { font-size: ceil(@font-size-base * 1.25); }
|
||||
h4 { font-size: ceil(@font-size-base * 1); }
|
||||
h5 { font-size: ceil(@font-size-base * 0.85); }
|
||||
h6 { font-size: ceil(@font-size-base * 0.65); }
|
||||
|
||||
pre, blockquote {
|
||||
border: 1px solid #999;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100% !important;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
section {
|
||||
page-break-after: always;
|
||||
|
||||
&#cover {
|
||||
padding: 3cm 0cm;
|
||||
text-align: center;
|
||||
|
||||
h1 {
|
||||
font-size: 1.5cm;
|
||||
}
|
||||
}
|
||||
|
||||
&#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,19 +2,19 @@
|
||||
@import "vendors/fontawesome/font-awesome.less";
|
||||
|
||||
@import "mixins.less";
|
||||
@import "variables.less";
|
||||
@import "fonts.less";
|
||||
|
||||
@import "book/languages.less";
|
||||
@import "book/header.less";
|
||||
@import "book/summary.less";
|
||||
@import "book/font-settings.less";
|
||||
@import "book/body.less";
|
||||
@import "book/exercise.less";
|
||||
@import "book/quiz.less";
|
||||
@import "book/markdown.less";
|
||||
@import "book/progress.less";
|
||||
@import "book/navigation.less";
|
||||
@import "website/fonts.less";
|
||||
@import "website/variables.less";
|
||||
@import "website/languages.less";
|
||||
@import "website/header.less";
|
||||
@import "website/summary.less";
|
||||
@import "website/font-settings.less";
|
||||
@import "website/body.less";
|
||||
@import "website/exercise.less";
|
||||
@import "website/quiz.less";
|
||||
@import "website/markdown.less";
|
||||
@import "website/progress.less";
|
||||
@import "website/navigation.less";
|
||||
|
||||
* {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
@@ -37,7 +37,8 @@
|
||||
|
||||
background: @page-background;
|
||||
border-radius: 2px;
|
||||
line-height: 1.5em;
|
||||
line-height: @content-line-height;
|
||||
font-size: @default-font-size;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
Executable → Regular
+8
-56
@@ -1,15 +1,3 @@
|
||||
@font-face {
|
||||
font-family: 'EB Garamond';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('EB Garamond 12 Regular'),url('@{FontPath}/ebgaramond/400.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'EB Garamond';
|
||||
font-style: italic;
|
||||
font-weight: 400i;
|
||||
src: local('EB Garamond 12 Italic'),url('@{FontPath}/ebgaramond/400i.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Merriweather';
|
||||
font-style: normal;
|
||||
@@ -19,7 +7,7 @@
|
||||
@font-face {
|
||||
font-family: 'Merriweather';
|
||||
font-style: italic;
|
||||
font-weight: 250i;
|
||||
font-weight: 250;
|
||||
src: local('Merriweather Light Italic'),url('@{FontPath}/merriweather/250i.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
@@ -31,21 +19,9 @@
|
||||
@font-face {
|
||||
font-family: 'Merriweather';
|
||||
font-style: italic;
|
||||
font-weight: 400i;
|
||||
font-weight: 400;
|
||||
src: local('Merriweather Italic'),url('@{FontPath}/merriweather/400i.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Merriweather';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local(''),url('@{FontPath}/merriweather/600.woff') format('woff');
|
||||
}
|
||||
/*@font-face {
|
||||
font-family: 'Merriweather';
|
||||
font-style: italic;
|
||||
font-weight: 600i;
|
||||
src: local(''),url('@{FontPath}/merriweather/600i.woff') format('woff');
|
||||
}*/
|
||||
@font-face {
|
||||
font-family: 'Merriweather';
|
||||
font-style: normal;
|
||||
@@ -55,7 +31,7 @@
|
||||
@font-face {
|
||||
font-family: 'Merriweather';
|
||||
font-style: italic;
|
||||
font-weight: 700i;
|
||||
font-weight: 700;
|
||||
src: local('Merriweather Bold Italic'),url('@{FontPath}/merriweather/700i.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
@@ -67,33 +43,9 @@
|
||||
@font-face {
|
||||
font-family: 'Merriweather';
|
||||
font-style: italic;
|
||||
font-weight: 900i;
|
||||
font-weight: 900;
|
||||
src: local('Merriweather Heavy Italic'),url('@{FontPath}/merriweather/900i.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Anonymous Pro';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Anonymous Pro'),url('@{FontPath}/anonymouspro/400.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Anonymous Pro';
|
||||
font-style: italic;
|
||||
font-weight: 400i;
|
||||
src: local('Anonymous Pro Italic'),url('@{FontPath}/anonymouspro/400i.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Anonymous Pro';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Anonymous Pro Bold'),url('@{FontPath}/anonymouspro/700.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Anonymous Pro';
|
||||
font-style: italic;
|
||||
font-weight: 700i;
|
||||
src: local('Anonymous Pro Bold Italic'),url('@{FontPath}/anonymouspro/700i.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
@@ -103,7 +55,7 @@
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300i;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light Italic'),url('@{FontPath}/opensans/300i.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
@@ -115,7 +67,7 @@
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400i;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'),url('@{FontPath}/opensans/400i.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
@@ -127,7 +79,7 @@
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600i;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans Semibold Italic'),url('@{FontPath}/opensans/600i.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
@@ -139,6 +91,6 @@
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700i;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold Italic'),url('@{FontPath}/opensans/700i.woff') format('woff');
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
font-size: 0.85em;
|
||||
color: @header-color;
|
||||
background: @header-background;
|
||||
box-shadow: 0 1px 2px hsla(200,10%,80%,0.6);
|
||||
border-bottom: 1px solid @header-border;
|
||||
|
||||
.btn {
|
||||
display: block;
|
||||
@@ -44,12 +44,16 @@
|
||||
padding-right: 200px;
|
||||
.transition(margin-left 0.5s ease);
|
||||
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
a, a:hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
@media (max-width: 1000px) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
flex-direction: column;
|
||||
|
||||
font-size: 40px;
|
||||
color: rgba(0,0,0,0.4);
|
||||
color: rgba(0, 0, 0, 0.2);
|
||||
|
||||
text-align: center;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
color: rgba(0,0,0,0.6);
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
&.navigation-next {
|
||||
@@ -49,10 +49,14 @@
|
||||
}
|
||||
}
|
||||
.book.color-theme-1 .book-body {
|
||||
.navigation:hover{
|
||||
}
|
||||
|
||||
}
|
||||
.book.color-theme-2 .book-body {
|
||||
.navigation:hover{
|
||||
.navigation {
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
|
||||
&:hover {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
width: @sidebar-width;
|
||||
color: @sidebar-color;
|
||||
background: @sidebar-background;
|
||||
box-shadow: inset -1px 0 5px hsla(0, 0%, 0%, 0.1);
|
||||
border-right: 1px solid @sidebar-border;
|
||||
|
||||
.transition(all 0.5s ease);
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
margin: 7px 0;
|
||||
overflow: hidden;
|
||||
background: @sidebar-divider-color;
|
||||
box-shadow: 0 1px rgba(255,255,255,0.7);
|
||||
}
|
||||
|
||||
i.fa-check {
|
||||
@@ -90,7 +89,6 @@
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
span {
|
||||
Executable → Regular
+30
-24
@@ -7,6 +7,7 @@
|
||||
@header-height: 50px;
|
||||
@header-color: hsl(194, 5%, 52%);
|
||||
@header-background: white;
|
||||
@header-border: rgba(0, 0, 0, 0.07);
|
||||
@header-button-color: hsl(194, 5%, 52%);
|
||||
@header-button-hover-color: #151515;
|
||||
@header-button-hover-background: hsl(207, 15%, 95%);
|
||||
@@ -18,28 +19,32 @@
|
||||
@sidebar-width: 250px;
|
||||
@sidebar-breakpoint: 600px;
|
||||
@sidebar-color: hsl(207, 15%, 80%);
|
||||
@sidebar-background: hsl(207, 15%, 95%);
|
||||
@sidebar-background: @body-background;
|
||||
@sidebar-border: @header-border;
|
||||
|
||||
@sidebar-nested-padding: 10px;
|
||||
@sidebar-nested-padding: 20px;
|
||||
|
||||
@sidebar-search-padding: 6px;
|
||||
@sidebar-search-background: transparent;
|
||||
@sidebar-search-input-border-color: hsl(207, 15%, 80%);
|
||||
@sidebar-search-input-border-color: @sidebar-border;
|
||||
|
||||
@sidebar-divider-color: hsl(207, 15%, 85%);
|
||||
@sidebar-divider-color: @sidebar-border;
|
||||
|
||||
@sidebar-link-color: hsl(207, 15%, 50%);
|
||||
@sidebar-link-color: hsl(207, 15%, 25%);
|
||||
@sidebar-link-background: transparent;
|
||||
@sidebar-link-hover-color: hsl(207, 100%, 50%);
|
||||
@sidebar-link-hover-background: transparent;
|
||||
|
||||
@sidebar-icon-color: @bar-progress-background;
|
||||
@sidebar-link-completed: hsl(207, 15%, 25%);
|
||||
@sidebar-link-completed: @sidebar-link-color;
|
||||
@sidebar-link-completed-weight: normal;
|
||||
|
||||
// Page
|
||||
@page-color: black;
|
||||
@page-background: white;
|
||||
@page-color: black;
|
||||
@page-background: @body-background;
|
||||
|
||||
// Content
|
||||
@content-line-height: 1.6em;
|
||||
|
||||
// Progress Bar
|
||||
@chapter-display: none;
|
||||
@@ -76,19 +81,20 @@
|
||||
@border-radius-small: 1px;
|
||||
|
||||
// Fonts
|
||||
@font-family-serif: Merriweather, "EB Garamond", Georgia, serif;
|
||||
@font-family-serif: "Merriweather", Georgia, 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-sans;
|
||||
@headings-font-family: inherit;
|
||||
|
||||
@FontPath: '@{staticPath}/fonts';
|
||||
@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;
|
||||
@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;
|
||||
@default-font-size: @l-font-size;
|
||||
|
||||
/*
|
||||
,--------.,--.
|
||||
@@ -116,15 +122,15 @@
|
||||
// Sidebar
|
||||
@sidebar-color-1: #AFA790;
|
||||
@sidebar-color-2: hsl(207, 15%, 80%);
|
||||
@sidebar-background-1: #F3EACB;
|
||||
@sidebar-background-2: #111111;
|
||||
@sidebar-background-1: @body-background-1;
|
||||
@sidebar-background-2: @body-background-2;
|
||||
|
||||
@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-search-input-border-color-2: #000;
|
||||
|
||||
@sidebar-divider-color-1: hsl(207, 15%, 85%);
|
||||
@sidebar-divider-color-1: @sidebar-divider-color;
|
||||
@sidebar-divider-color-2: #1d1f21;
|
||||
|
||||
@sidebar-link-color-1: #877F6A;
|
||||
@@ -138,14 +144,14 @@
|
||||
|
||||
@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-1: @sidebar-link-color-1;
|
||||
@sidebar-link-completed-2: @sidebar-link-color-2;
|
||||
@sidebar-link-completed-weight-1: normal;
|
||||
@sidebar-link-completed-weight-2: normal;
|
||||
|
||||
// Page
|
||||
@page-color-1: #704214;
|
||||
@page-color-2: #a4b1b1;
|
||||
@page-color-1: #704214;
|
||||
@page-color-2: #a4b1b1;
|
||||
@page-background-1: #F3EACB;
|
||||
@page-background-2: #1d1f21;
|
||||
|
||||
@@ -1,3 +1,37 @@
|
||||
{% macro articles(_articles) %}
|
||||
{% for item in _articles %}
|
||||
{% set externalLink = item.path|isExternalLink %}
|
||||
<li class="chapter {% if item.path == _input %}active{% endif %}" data-level="{{ item.level }}" {% if item.path && !externalLink %}data-path="{{ item.path|mdLink }}"{% endif %}>
|
||||
{% if item.path %}
|
||||
{% if !externalLink %}
|
||||
<a href="{{ basePath }}/{{ item.path|mdLink }}">
|
||||
<i class="fa fa-check"></i>
|
||||
{% if item.level !== "0" %}
|
||||
<b>{{ item.level }}.</b>
|
||||
{% endif %}
|
||||
{{ item.title }}
|
||||
</a>
|
||||
{% else %}
|
||||
<a target="_blank" href="{{ item.path }}">
|
||||
<i class="fa fa-check"></i>
|
||||
{% if item.level !== "0" %}
|
||||
<b>{{ item.level }}.</b>
|
||||
{% endif %}
|
||||
{{ item.title }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% 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" />
|
||||
@@ -14,7 +48,7 @@
|
||||
{% 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>
|
||||
<a href="{{ options.links.issues|default(githubHost+githubId+"/issues") }}" target="blank" class="issues-link">Questions and Issues</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
@@ -25,39 +59,20 @@
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if options.links.custom %}
|
||||
{% for link in options.links.custom %}
|
||||
{% set _divider = true %}
|
||||
<li>
|
||||
<a href="{{ options.links.custom[loop.key] }}" target="blank" class="custom-link">{{ loop.key }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% 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>
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block style %}
|
||||
<link rel="stylesheet" href="{{ staticBase }}/style.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="book-langs-index">
|
||||
<div class="inner">
|
||||
@@ -10,7 +14,7 @@
|
||||
<ul class="languages">
|
||||
{% for lang in langs %}
|
||||
<li>
|
||||
<a href="{{ basePath}}/{{ lang.path }}">{{ lang.title }}</a>
|
||||
<a href="{{ basePath}}/{{ lang.path|pathJoin("/index.html") }}">{{ lang.title }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
@@ -1,54 +1,26 @@
|
||||
<!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#">
|
||||
<head>
|
||||
{{ htmlSnippet("head:start")|default("") }}
|
||||
{% block head %}
|
||||
<meta charset="UTF-8">
|
||||
<title>{% block title %} | {{ title }}{% endblock %}</title>
|
||||
|
||||
<title>{% block title %}{% endblock %}</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="{{ githubAuthor }}">
|
||||
<meta name="description" content="{{ description }}">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
{% if progress.current.next and progress.current.next.path %}
|
||||
<link rel="next" href="{{ basePath }}/{{ progress.current.next.path|mdLink }}" />
|
||||
{% endif %}
|
||||
{% if progress.current.prev and progress.current.prev.path %}
|
||||
<link rel="prev" href="{{ basePath }}/{{ progress.current.prev.path|mdLink }}" />
|
||||
{% endif %}
|
||||
|
||||
<meta property="og:title" content="{% block title %} | {{ title }}{% endblock %}">
|
||||
<meta property="og:site_name" content="{{ title }}">
|
||||
<meta property="og:type" content="book">
|
||||
<meta property="og:locale" content="en_US">
|
||||
|
||||
<meta property="book:author" content="{{ githubHost }}{{ githubAuthor }}">
|
||||
<meta property="book:tag" content="GitBook">
|
||||
|
||||
<meta name="description" content="{% block description %}{% endblock %}">
|
||||
<meta name="generator" content="GitBook {{ gitbook.version }}">
|
||||
<meta name="HandheldFriendly" content="true"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
|
||||
<link rel="shortcut icon" href="{{ staticBase }}/images/favicon.ico" type="image/x-icon">
|
||||
{% endblock %}
|
||||
{% block head %}{% endblock %}
|
||||
{{ htmlSnippet("head:end")|default("") }}
|
||||
</head>
|
||||
<body>
|
||||
{{ htmlSnippet("body:start")|default("") }}
|
||||
{% block style %}
|
||||
<link rel="stylesheet" href="{{ staticBase }}/style.css">
|
||||
{% endblock %}
|
||||
{% block style %}{% endblock %}
|
||||
{% block content %}{% endblock %}
|
||||
{% 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/mode-javascript.js"></script>
|
||||
<script src="{{ staticBase }}/jsrepl/jsrepl.js" id="jsrepl-script"></script>
|
||||
<script src="{{ staticBase }}/app.js"></script>
|
||||
{% endblock %}
|
||||
{% block javascript %}{% endblock %}
|
||||
{{ htmlSnippet("body:end")|default("") }}
|
||||
</body>
|
||||
{{ htmlSnippet("html:end")|default("") }}
|
||||
|
||||
+46
-94
@@ -20,10 +20,15 @@
|
||||
</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">Quiz #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
{% for quiz in section.quiz %}
|
||||
<div class="question">
|
||||
<div class="question-header">Question {{ loop.index }} of {{ section.quiz.length }}</div>
|
||||
{% autoescape false %}{{ quiz.base }}{% endautoescape %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% set exercise = exercise + 1 %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
@@ -31,104 +36,51 @@
|
||||
|
||||
{% 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>
|
||||
<h1>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">Quiz #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
{% for quiz in section.quiz %}
|
||||
<div class="question">
|
||||
<div class="question-header">Question {{ loop.index }} of {{ section.quiz.length }}</div>
|
||||
{% autoescape false %}{{ quiz.solution }}{% endautoescape %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% set exercise = exercise + 1 %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</article>
|
||||
</section>
|
||||
</article>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -1,7 +1,23 @@
|
||||
{% extends "layout.html" %}
|
||||
|
||||
{% block htmlTag %}{% if options.cache !== false %}manifest="{{ basePath }}/manifest.appcache"{% endif %}{% endblock %}
|
||||
{% block title %}{{ progress.current.title }}{% parent %}{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
{% parent %}
|
||||
{% if githubAuthor %}
|
||||
<meta name="author" content="{{ githubAuthor }}">
|
||||
{% endif %}
|
||||
{% if progress.current.next and progress.current.next.path %}
|
||||
<link rel="next" href="{{ basePath }}/{{ progress.current.next.path|mdLink }}" />
|
||||
{% endif %}
|
||||
{% if progress.current.prev and progress.current.prev.path %}
|
||||
<link rel="prev" href="{{ basePath }}/{{ progress.current.prev.path|mdLink }}" />
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}{{ progress.current.title }} | {{ title }}{% endblock %}
|
||||
{% block description %}{% if progress.current.level == "0" %}{{ description }}{% endif %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="book" {% if githubId %}data-github="{{ githubId }}"{% endif %} data-level="{{ progress.current.level }}" data-basepath="{{ basePath }}" data-revision="{{ revision }}">
|
||||
{% include "includes/book/header.html" %}
|
||||
@@ -38,7 +54,8 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block javascript %}
|
||||
{% parent %}
|
||||
<script src="{{ staticBase }}/jsrepl/jsrepl.js" id="jsrepl-script"></script>
|
||||
<script src="{{ staticBase }}/app.js"></script>
|
||||
{% for resource in plugins.resources.js %}
|
||||
{% if resource.url %}
|
||||
<script src="{{ resource.url }}"></script>
|
||||
@@ -55,7 +72,7 @@ require(["gitbook"], function(gitbook) {
|
||||
{% endblock %}
|
||||
|
||||
{% block style %}
|
||||
{% parent %}
|
||||
<link rel="stylesheet" href="{{ staticBase }}/style.css">
|
||||
{% for resource in plugins.resources.css %}
|
||||
{% if resource.url %}
|
||||
<link rel="stylesheet" href="{{ resource.url }}">
|
||||
|
||||
Reference in New Issue
Block a user