Compare commits

..

109 Commits

Author SHA1 Message Date
Aaron O'Mullan 7fd7732345 Bump version to 1.4.2 2014-12-10 23:27:01 +01:00
Aaron O'Mullan 960ef4d945 Fix descriptor and me leak with bad write queues
Node.js for some reason queues up writes from fs.writeFile causing
memory and descriptor leaks, preventing building large books. This is
now fixed

Fixes #336, fixes #454
2014-12-10 16:32:49 +01:00
Aaron O'Mullan 92523fb4b5 Force process.exit at end of build 2014-12-09 17:31:23 +01:00
Samy Pessé 66bb46df22 Merge pull request #516 from jr0cket/patch-1
Add gitbook init command to "how to use" section
2014-12-08 00:46:04 +01:00
John Stevenson 0454710d3b Add gitbook init command to "how to use" section
Highlight that Gitbook can generate the files and directories for a book from its SUMMARY.md file.
2014-12-07 23:44:41 +00:00
Samy Pessé c9a31cc987 Add option isbn for books 2014-12-03 23:19:47 +01:00
Samy Pessé f03c316274 Bump version to 1.4.1 2014-11-30 17:01:47 +01:00
Samy Pessé 001a4f91be Fix command 'install' without arguments 2014-11-30 17:00:12 +01:00
Samy Pessé 44a3bfe296 Bump version to 1.4.0 2014-11-30 14:59:08 +01:00
Samy Pessé a9ce6f808d Add doc about gitbook install 2014-11-30 14:54:34 +01:00
Samy Pessé e5c667e0fa Add test for parsing of plugins list with version 2014-11-30 14:50:47 +01:00
Samy Pessé e36f274dbe Handle specific version for plugins 2014-11-30 14:46:21 +01:00
Samy Pessé 2a88427e74 Improve error message when plugins not found 2014-11-30 13:13:00 +01:00
Samy Pessé ed48f5f3ee Add command "install" to install plugins from book.json 2014-11-30 13:04:38 +01:00
Samy Pessé 7b4accb117 Bump version to 1.3.4 2014-11-25 18:55:33 +01:00
Samy Pessé d6437d31d4 Omit "generator" when building file with config file 2014-11-25 18:53:02 +01:00
Samy Pessé 3dd33f1f3c Merge pull request #502 from tonka3000/X-UA-Compatible-tag
add X-UA-Compatible meta tag for IE
2014-11-25 15:20:14 +01:00
Samy Pessé 3a0f00411b Fix #505: add glossary to ebook file 2014-11-24 22:08:19 +01:00
Samy Pessé 5b655bb29a Fx typo 2014-11-24 21:56:33 +01:00
Samy Pessé faf10605e8 Add hook finish:before 2014-11-24 21:41:50 +01:00
Samy Pessé 5c98b3350b Merge pull request #503 from jr0cket/ebook-command-fix
updated eBook generation commands - resolves #432
2014-11-22 16:41:19 +01:00
John Stevenson 6f4e1d10c1 made commands clearer by seperating output filename option 2014-11-22 14:47:12 +00:00
John Stevenson 870886599a updated eBook generation commands 2014-11-22 11:26:08 +00:00
Michael Aigner 1b9801b8a4 add X-UA-Compatible meta tag for IE (https://github.com/GitbookIO/gitbook/issues/501) 2014-11-20 10:16:40 +01:00
Samy Pessé 897463b22e Bump version to 1.3.3 2014-11-14 22:10:19 +01:00
Samy Pessé aa4894e94c Fix parsing of already lexed content 2014-11-14 22:09:30 +01:00
Samy Pessé 8a0180ae6b Bump version to 1.3.2 2014-11-14 18:53:57 +01:00
Samy Pessé d6776b98d3 Fix bundle task 2014-11-14 18:53:48 +01:00
Samy Pessé 6a04ba89a5 Update gitbook bundle 2014-11-14 18:53:39 +01:00
Samy Pessé 05eaeeb0d0 Fix #441: Improve validity of epub output 2014-11-13 08:34:35 +01:00
Samy Pessé 77043464b2 Fix #496: improve replacement of glossary terms in html 2014-11-12 22:10:38 +01:00
Samy Pessé b90f74a821 Merge pull request #494 from cnDelbert/master
Bug fix for issue#493: 2-level or more deep relative links go wrong when in win32
2014-11-12 19:06:33 +01:00
Aaron O'Mullan f804eb42ac Merge pull request #495 from romanlytkin/patch-1
Update README.md
2014-11-07 15:58:18 +01:00
romanlytkin 74fd8c159b Update README.md
Hi, i add 2 new plugins for gitbook, I hope it will be useful to someone.
2014-11-07 17:37:27 +03:00
Delbert a741ec1350 Bug fix for issue #493: 2-level or more deep relative links may go wrong when in win32 2014-11-06 15:50:59 +08:00
Samy Pessé 4b26c31b3b Remove unnecessary option "repo" from tests 2014-11-03 17:43:44 +01:00
Samy Pessé dfd487692a Adapt new page.parse api for tests 2014-11-03 17:40:38 +01:00
Samy Pessé 195374ecee Merge pull request #489 from codepiano/fork
bugfix,init page object's lexed attribute.
2014-11-03 17:29:54 +01:00
Samy Pessé 1659778a77 Bump version to 1.3.1 2014-11-03 16:58:37 +01:00
Samy Pessé 40c4035afb Fix #491: handle correctly url without path for links 2014-11-03 16:57:35 +01:00
codepiano 1502d65286 Merge branch 'master' of github.com:GitbookIO/gitbook into fork
Conflicts:
	lib/parse/page.js
2014-11-02 01:34:28 +08:00
Samy Pessé 0feb672d70 Bump version to 1.3.0 2014-11-01 15:36:10 +01:00
Samy Pessé 2a4ef5bbf4 Fix dependency in renderer 2014-11-01 15:21:16 +01:00
Samy Pessé c4540332a7 Bundle gitbook parser as a client side library 2014-11-01 15:09:58 +01:00
Samy Pessé 2104404cb3 Remove dependency for path in parser 2014-11-01 15:09:41 +01:00
Samy Pessé 002d685ebb Adapt include in page parsing to works on clientside 2014-11-01 15:00:13 +01:00
codepiano 9da10e2b25 bugfix,init page object's lexed attribute.
the page object's attribute 'lexed' is not correctly inited.so when
use page.lexed to generate the search index file,nothing will be
indexed.
2014-10-31 18:00:09 +08:00
Samy Pessé 1e4632080b Bump version to 1.2.0 2014-10-30 18:27:56 +01:00
Samy Pessé 5191106396 Update documentation in readme 2014-10-30 18:27:26 +01:00
Samy Pessé 6ab5cafaf3 Improve toc in ebook (Fixes #338, #322) 2014-10-30 18:14:06 +01:00
Samy Pessé d1c0cb9c71 Normalize template paths 2014-10-30 18:01:56 +01:00
Samy Pessé 38c750101c Fix #487: Use generated toc as base for ebook generation 2014-10-30 17:52:39 +01:00
Samy Pessé ba0f379a89 Add plugin infinitescroll 2014-10-30 16:19:19 +01:00
Samy Pessé 1d9509b3b6 Merge pull request #485 from citizenmatt/Nested_list_spacing
Fix bottom border on nested ul
2014-10-30 14:15:54 +01:00
Samy Pessé 3c8de4a8ab Add 'variables' to book.json doc 2014-10-29 17:20:30 +01:00
Samy Pessé 591d8fc9ee Use book.json to store variables 2014-10-29 17:19:19 +01:00
Samy Pessé d480e9cea4 Update highlight.js@8.3.0 2014-10-23 17:05:27 +02:00
Matt Ellis 32a71869da Fix bottom border on nested ul 2014-10-23 09:19:02 +01:00
Samy Pessé fdbfda64a6 Merge pull request #483 from dharmeshkakadia/patch-1
Corrected a small typo in Plugins description
2014-10-21 15:45:45 +02:00
Dharmesh Kakadia a6f48be39e Corrected a small typo in Plugins description 2014-10-21 00:47:37 +05:30
Aaron O'Mullan ce50b893e9 Merge pull request #482 from GitbookIO/feature/includes
Add include support, with variables and the whole shebang
2014-10-15 21:29:23 +02:00
Aaron O'Mullan 225ff27dd1 Fix small incompatibility mistake 2014-10-15 21:28:53 +02:00
Aaron O'Mullan 52e8361846 Add include support, with variables and the whole shebang
Fixes #462
2014-10-15 21:20:10 +02:00
Samy Pessé 49e3bbc3d6 Bump version to 1.1.1 2014-10-13 16:03:47 +02:00
Aaron O'Mullan c9ec911d0e Merge pull request #472 from GitbookIO/fix/quizzes
Fix/quizzes
2014-10-13 15:55:32 +02:00
Aaron O'Mullan 84d7662fd8 Allow quiz titles in validation 2014-10-13 15:55:59 +02:00
Aaron O'Mullan 676b2287ee Rewrite quiz logic to be more robust
Fixes #470
2014-10-13 15:50:12 +02:00
Samy Pessé 32a64be407 Fix #460: search for glossary term on all nodes 2014-10-13 15:23:51 +02:00
Samy Pessé 138cddf3d6 Ste GitBook as publisher 2014-10-13 15:23:00 +02:00
Aaron O'Mullan d730a3aac6 Split section classification to separate files
Maybe have a sections folder in the future and one per type
(quiz/exercise/normal) ?
2014-10-13 15:07:46 +02:00
Aaron O'Mullan ffb0b12012 Add unit test for quiz false positivies 2014-10-13 14:17:08 +02:00
Samy Pessé 78e25f8252 Add link to gitbook.gitignore 2014-10-13 11:10:28 +02:00
Samy Pessé 09f42f57e1 Improve comments 2014-10-13 11:01:00 +02:00
Samy Pessé 950ad1d060 Remove reference to single page output 2014-10-13 10:59:32 +02:00
Samy Pessé ed6b29c1a4 Adapt tests 2014-10-13 10:57:47 +02:00
Samy Pessé 36785120b1 Fix default for pdf templates 2014-10-13 10:56:14 +02:00
Samy Pessé 26fad5b888 Remove ref to github options 2014-10-13 10:53:51 +02:00
Samy Pessé f049576575 Merge branch 'alexandreaquiles-pdf_header_and_footer' 2014-10-13 10:48:33 +02:00
Samy Pessé a7ecbb87e4 Force type of header and footer templates 2014-10-13 10:48:02 +02:00
Samy Pessé 1da341741b Merge pull request #471 from GitbookIO/feature/multipages
Use multiple HTML files for ebook generation
2014-10-13 10:41:23 +02:00
Samy Pessé 2dd6192576 Remove old codes 2014-10-12 22:25:39 +02:00
Samy Pessé 52c9347b9c Improve style for exercises and quizzes in ebook 2014-10-12 22:07:37 +02:00
Samy Pessé e2295591ed Handle correctly quizzes 2014-10-12 11:42:32 +02:00
Samy Pessé 0c1cb724d7 Handle correctly exercises in ebook 2014-10-12 11:37:51 +02:00
Samy Pessé 7cee248426 Generate multiple page for ebook 2014-10-12 11:32:45 +02:00
Aaron O'Mullan 6ea5b2b539 Fix incorrect page breaks after h1 and h2 divs
Fixes #463, fixes #337, fixes #293
2014-10-08 20:08:22 +02:00
Alexandre Aquiles a56b559d43 Adding options to set header and footer in PDF generation 2014-10-06 13:17:34 -03:00
Samy Pessé df1a82e0e4 Add changelog for 1.1.0 2014-09-24 14:57:50 +02:00
Samy Pessé f15a1efe71 Bump version to 1.1.0 2014-09-24 14:56:09 +02:00
Samy Pessé b8379fc6fa Update kramed@0.4.3 2014-09-24 14:53:42 +02:00
Samy Pessé d69cc74647 Merge pull request #440 from GitbookIO/feature/ebookplugins
Apply plugins to ebook generation
2014-09-17 15:02:22 +02:00
Samy Pessé f83874a9ca Fix correct folder for assets 2014-09-17 14:16:04 +02:00
Samy Pessé 1968bb79c2 Use correct resources file for ebook 2014-09-17 13:17:30 +02:00
Samy Pessé 60c721db40 Enable css from plugins in page output 2014-09-17 13:10:36 +02:00
Samy Pessé 11b299653f Remove githubId
Enable hook for ebook
2014-09-17 13:08:30 +02:00
Samy Pessé c8c07fe8ce Add base to use plugins in page format 2014-09-17 13:00:28 +02:00
Samy Pessé be0a4f08e9 Merge pull request #435 from markcxz/master
fix radio input uncheck failed
2014-09-10 09:56:12 +02:00
markcxz 4da4e28d07 fix radio input uncheck failed 2014-09-10 10:27:27 +08:00
Samy Pessé 9dc19026c6 Add changelog for version 1.0.3 2014-09-05 17:15:04 +02:00
Samy Pessé 31ca9f7fcb Bump version to 1.0.3 2014-09-05 17:12:03 +02:00
Samy Pessé 6925d39e8d Fix GitbookIO/editor#136: update highlight.js@8.2.0 2014-09-05 17:10:47 +02:00
Samy Pessé a24cafd975 Update plugin-mathjax@0.0.6 2014-09-02 23:25:33 +02:00
Aaron O'Mullan e3fdf8b404 Bump version to 1.0.2 2014-08-20 17:03:31 -07:00
Aaron O'Mullan c861223378 Use gitbook-plugin-mathjax@0.0.5 2014-08-20 17:01:14 -07:00
Aaron O'Mullan f5c6d8df71 Bump version to 1.0.1 2014-08-20 16:30:04 -07:00
Aaron O'Mullan 452c846e90 Update framed to 0.4.2
This uses the new kramdown convention for inline math
2014-08-20 16:27:50 -07:00
Samy Pessé aedd875ab7 Fix url for sharing 2014-08-20 15:54:11 -07:00
Samy Pessé d2ec2f8f02 Add plugins exercises and quizzes by default 2014-08-20 10:33:34 -07:00
Samy Pessé 5af0df93c3 Normalize titles in readme 2014-08-19 16:39:54 -07:00
57 changed files with 21732 additions and 486 deletions
+57
View File
@@ -1,5 +1,62 @@
# Release notes
## 1.4.1
- Fix command 'install' without arguments
## 1.4.0
- Add command `gitbook install` to install plugins from book.json
- `package.json` is no longer necessary
## 1.3.4
- Add glossary to ebooks
- Fix autocover with new hook "finish:before"
- Add X-UA-Compatible meta tag for IE
## 1.3.3
- Fix parsing of lexed content using the client library
## 1.3.2
- ePub files are now passing validation from epubcheck
- Fix replacement of multiple glossary terms in a single sentence
- Fix on windows deep relative links
- Fix search indexer
## 1.3.1
- Fix error with links in markdown
## 1.3.0
- Bundle gitbook parsing library as a client side library in `gitbook.js` and `gitbook.min.js`
## 1.2.0
- Improvements on ebook generation
- Fix incorrect follow of links in ebook generation
- Move Table of Contents at the beginning of the ebook
- Update to last highlight.js (includes Swift)
- Includes of templates and variables (from book.json)
## 1.1.1
- Rewrite quiz logic to be more robust
- Improve integration of glossary
- Improve generation of ebook by using a multiple HTML pages input source
- Fix incorrect page breaks after h1 and h2 divs
- New options to set header and footer in PDF generation
## 1.1.0
- Plugins can now extend the ebook generation (pdf, epub, mobi)
- Update `kramed` to version 0.4.3
## 1.0.3
- Update `mathjax` plugin and MathJAx to version 2.4
- Update `highlight.js` to 8.2.0
## 1.0.2
- Update `mathjax` plugin, fixes issues with inline math rendering (no longer wanted)
## 1.0.1
- New inline math convention (kramdown's), using `$$` rather than `$` as delimiters
- Fix instapaper sharing
- The `exercises` & `quizzes` plugins are now by default
## 1.0.0
- New design
- Support for glossary
+34 -1
View File
@@ -6,6 +6,8 @@ module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-requirejs');
grunt.loadNpmTasks("grunt-bower-install-simple");
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-contrib-uglify');
// Init GRUNT configuraton
grunt.initConfig({
@@ -77,6 +79,30 @@ module.exports = function (grunt) {
}
]
}
},
browserify: {
dist: {
files: {
'gitbook.js': [
'./lib/parse/index.js'
],
},
options: {
postBundleCB: function (err, src, next) {
return next(null, '(function () { var define = undefined; '+src+'; })();')
},
browserifyOptions: {
'standalone': "gitbook"
}
}
}
},
uglify: {
dist: {
files: {
'gitbook.min.js': ['gitbook.js']
}
}
}
});
@@ -90,7 +116,14 @@ module.exports = function (grunt) {
'copy:vendors'
]);
// Bundle the library
grunt.registerTask('bundle', [
'browserify',
'uglify'
]);
grunt.registerTask('default', [
'build'
'build',
'bundle'
]);
};
+29 -14
View File
@@ -16,6 +16,11 @@ GitBook can be installed from **NPM** using:
$ npm install gitbook -g
```
Create the directories and files for a book from its [SUMMARY.md](https://github.com/GitbookIO/gitbook#book-format) file using
```
$ gitbook init
```
You can serve a repository as a book using:
```
@@ -52,9 +57,10 @@ Here are the options that can be stored in this file:
// It's not advised this option in the book.json
"generator": "site",
// Book title and description (defaults are extracted from the README)
// Book metadats (somes are extracted from the README by default)
"title": null,
"description": null,
"isbn": null,
// For ebook format, the extension to use for generation (default is detected from output extension)
// "epub", "pdf", "mobi"
@@ -62,10 +68,6 @@ Here are the options that can be stored in this file:
// 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/",
// Plugins list, can contain "-name" for removing default plugins
"plugins": [],
@@ -78,6 +80,9 @@ Here are the options that can be stored in this file:
}
},
// Variables for templating
"variables": {},
// Links in template (null: default, false: remove, string: new value)
"links": {
// Custom links at top of sidebar
@@ -98,9 +103,6 @@ Here are the options that can be stored in this file:
// 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,
@@ -118,7 +120,13 @@ Here are the options that can be stored in this file:
"left": 62,
"top": 36,
"bottom": 36
}
},
//Header HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
"headerTemplate": null,
//Footer HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
"footerTemplate": null
}
}
```
@@ -130,8 +138,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.
* **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```.
* **eBook**: A complete eBook with exercise solutions at the end of the book. You need to have [ebook-convert](http://manual.calibre-ebook.com/cli/ebook-convert.html) installed. You can specify the eBook filename with the `-o` option, otherwise `book` will be used.
* Generate a **PDF** using: `gitbook pdf ./myrepo`
* Generate a **ePub** using: `gitbook epub ./myrepo`
* Generate a **MOBI** using: `gitbook mobi ./myrepo`
* **JSON**: This format is used for debugging or extracting metadata from a book. Generate this format using: ```gitbook build ./myrepo -f json```.
## Book Format
@@ -174,7 +184,7 @@ GitBook supports building books written in multiple languages. Each language sho
You can see a complete example with the [Learn Git](https://github.com/GitbookIO/git) book.
#### GLOSSARY.md
#### Glossary
Allows you to specify terms and their respective definitions to be displayed in the glossary. Based on those terms, `gitbook` will automatically build an index and highlight those terms in pages.
@@ -193,7 +203,7 @@ With it's definition, this can contain bold text and all other kinds of inline m
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`).
Best practices for the `.gitignore` is to ignore build files from [**node.js**](https://github.com/github/gitignore/blob/master/Node.gitignore) (`node_modules`, ...) and build files from GitBook: `_book`, `*.epub`, `*.mobi` and `*.pdf`.
Best practices for the `.gitignore` is to ignore build files from **node.js** (`node_modules`, ...) and build files from GitBook: `_book`, `*.epub`, `*.mobi` and `*.pdf` ([Download GitBook.gitignore](https://github.com/github/gitignore/blob/master/GitBook.gitignore)).
#### Cover
@@ -208,7 +218,9 @@ The platform [GitBook.io](https://www.gitbook.io/) is like an "Heroku for books"
#### 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 be 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 needed to build a book can be installed using: `gitbook install ./`.
##### Official plugins:
@@ -218,6 +230,7 @@ Plugins can used to extend your book's functionality. Read [GitbookIO/plugin](ht
| [quizzes](https://github.com/GitbookIO/plugin-quizzes) | Add interactive quizzes to your book. |
| [mathjax](https://github.com/GitbookIO/plugin-mathjax) | Displays mathematical notation in the book. |
| [mixpanel](https://github.com/GitbookIO/plugin-mixpanel) | Mixpanel tracking for your book |
| [infinitescroll](https://github.com/GitbookIO/gitbook-plugin-infinitescroll) | Infinite Scrolling |
##### Other plugins:
@@ -234,6 +247,8 @@ Plugins can used to extend your book's functionality. Read [GitbookIO/plugin](ht
| [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) | Embedded jsbin frame into your book |
| [gitbook-grvis](https://github.com/romanlytkin/gitbook-grvis) | Gitbook GrViz plugin is used to select from markdown dot and converting it into a picture format svg |
| [gitbook-plantuml](https://github.com/romanlytkin/gitbook-plantuml) | Gitbook PlantUml plugin is used to select from markdown uml and converting it into a picture format svg |
#### Debugging
+5
View File
@@ -51,6 +51,11 @@ var makeBuildFunc = function(converter) {
// Exit process with failure code
process.exit(-1);
})
.then(function() {
// Prevent badly behaving plugins
// from making the process hang
process.exit(0);
});
};
};
+25 -1
View File
@@ -7,7 +7,7 @@ var prog = require('commander');
var tinylr = require('tiny-lr-fork');
var pkg = require('../package.json');
var generators = require("../lib/generate").generators;
var genbook = require("../lib/generate");
var initDir = require("../lib/generate/init");
var fs = require('../lib/generate/fs');
@@ -80,6 +80,30 @@ build.command(prog.command('serve [source_dir]'))
generate();
});
build.commandEbook(prog.command('install [source_dir]'))
.description('Install plugins for a book')
.action(function(dir, options) {
dir = dir || process.cwd();
console.log("Install plugins in", dir);
genbook.config.read({
input: dir
})
.then(function(options) {
return genbook.Plugin.install(options);
})
.then(function() {
console.log("Successfully installed plugins!");
})
.fail(function(err) {
// Log error
utils.logError(err);
// Exit process with failure code
process.exit(-1);
});
});
build.commandEbook(prog.command('pdf [source_dir]'))
.description('Build a gitbook as a PDF')
.action(function(dir, options) {
+20724
View File
File diff suppressed because one or more lines are too long
+10
View File
File diff suppressed because one or more lines are too long
+38 -5
View File
@@ -1,3 +1,4 @@
var Q = require('q');
var _ = require('lodash');
var path = require('path');
@@ -16,9 +17,10 @@ var CONFIG = {
// Configuration file to use
"configFile": "book",
// Book title and description (defaults are extracted from the README)
// Book metadats (somes are extracted from the README by default)
"title": null,
"description": null,
"isbn": null,
// For ebook format, the extension to use for generation (default is detected from output extension)
// "epub", "pdf", "mobi"
@@ -38,6 +40,9 @@ var CONFIG = {
}
},
// Variables for templating
"variables": {},
// 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
@@ -83,14 +88,42 @@ var CONFIG = {
"left": 62,
"top": 36,
"bottom": 36
}
},
//Header HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
"headerTemplate": "",
//Footer HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
"footerTemplate": ""
}
};
// Return complete configuration
var defaultsConfig = function(options) {
return _.merge(options || {}, CONFIG, _.defaults);
};
// Read configuration from book.json
var readConfig = function(options) {
options = defaultsConfig(options);
return Q()
.then(function() {
try {
var _config = require(path.resolve(options.input, options.configFile));
options = _.merge(options, _.omit(_config, 'input', 'configFile', 'defaultsPlugins', 'generator'));
}
catch(err) {
// No config file: not a big deal
return Q();
}
})
.thenResolve(options);
};
module.exports = {
CONFIG: CONFIG,
defaults: function(options) {
return _.merge(options || {}, CONFIG, _.defaults);
}
defaults: defaultsConfig,
read: readConfig
}
+10 -8
View File
@@ -9,10 +9,6 @@ var parse = require("../../parse");
var BaseGenerator = require("../page");
var stringUtils = require("../../utils/string");
/*
* This generator inherits from the single page generator
* and convert the page output to ebook
*/
var Generator = function() {
BaseGenerator.apply(this, arguments);
};
@@ -34,13 +30,18 @@ Generator.prototype.finish = function() {
"--cover": that.options.cover,
"--title": that.options.title,
"--comments": that.options.description,
"--isbn": that.options.isbn,
"--authors": that.options.author,
"--publisher": "GitBook",
"--chapter": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter ')]",
"--chapter-mark": "pagebreak",
"--page-breaks-before": "/",
"--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 ')]",
"--no-chapters-in-toc": true
"--no-chapters-in-toc": true,
"--max-levels": "1",
"--breadth-first": true
};
if (format == "pdf") {
@@ -51,17 +52,18 @@ Generator.prototype.finish = function() {
"--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)
"--pdf-page-numbers": Boolean(pdfOptions.pageNumbers),
"--pdf-header-template": String(pdfOptions.headerTemplate),
"--pdf-footer-template": String(pdfOptions.footerTemplate)
});
}
var command = [
"ebook-convert",
path.join(that.options.output, "index.html"),
path.join(that.options.output, "SUMMARY.html"),
path.join(that.options.output, "index."+format),
stringUtils.optionsToShellArgs(_options)
].join(" ");
+15 -1
View File
@@ -64,7 +64,20 @@ var getFiles = function(path) {
module.exports = {
list: getFiles,
readFile: Q.denodeify(fs.readFile),
writeFile: Q.denodeify(fs.writeFile),
//writeFile: Q.denodeify(fs.writeFile),
writeFile: function(filename, data, options) {
var d = Q.defer();
try {
fs.writeFileSync(filename, data, options)
} catch(err) {
d.reject(err);
}
d.resolve();
return d.promise;
},
mkdirp: Q.denodeify(fsExtra.mkdirp),
copy: Q.denodeify(fsExtra.copy),
remove: Q.denodeify(fsExtra.remove),
@@ -74,4 +87,5 @@ module.exports = {
fs.exists(path, d.resolve);
return d.promise;
},
readFileSync: fs.readFileSync.bind(fs)
};
+12 -4
View File
@@ -8,6 +8,9 @@ var Plugin = require("./plugin");
var BaseGenerator = function(options) {
this.options = options;
// Base for assets in plugins
this.pluginAssetsBase = "book";
this.options.plugins = Plugin.normalizeNames(this.options.plugins);
this.options.plugins = _.union(this.options.plugins, this.options.defaultsPlugins);
this.plugins = [];
@@ -17,10 +20,17 @@ BaseGenerator.prototype.callHook = function(name, data) {
return this.plugins.hook(name, data);
};
// Sets up generator
BaseGenerator.prototype.load = function() {
return this.loadPlugins();
};
BaseGenerator.prototype.loadPlugins = function() {
var that = this;
return Plugin.fromList(this.options.plugins, this.options.input, this)
return Plugin.fromList(this.options.plugins, this.options.input, this, {
assetsBase: this.pluginAssetsBase
})
.then(function(_plugins) {
that.plugins = _plugins;
@@ -74,6 +84,4 @@ BaseGenerator.prototype.finish = function() {
return Q.reject(new Error("Could not finish generation"));
};
module.exports = BaseGenerator;
module.exports = BaseGenerator;
+17 -21
View File
@@ -40,7 +40,7 @@ var loadGenerator = function(options) {
.then(function() {
var generator = new generators[options.generator](options);
return generator.loadPlugins()
return generator.load()
.then(_.constant(generator));
});
};
@@ -48,28 +48,18 @@ var loadGenerator = function(options) {
var generate = function(options) {
// Set defaults to options
options = defaultConfig.defaults(options);
// Validate options
if (!options.input) {
return Q.reject(new Error("Need option input (book input directory)"));
}
// Check files to get folder type (book, multilanguage book or neither)
return checkGenerator(options)
// Read config file
.then(function() {
try {
var _config = require(path.resolve(options.input, options.configFile));
return defaultConfig.read(options)
.then(function(_options) {
options = _options;
options = _.merge(options, _.omit(_config, 'input', 'configFile', 'defaultsPlugins'));
}
catch(err) {
// No config file: not a big deal
return Q();
// Validate options
if (!options.input) {
return Q.reject(new Error("Need option input (book input directory)"));
}
// Check files to get folder type (book, multilanguage book or neither)
return checkGenerator(options);
})
// Read readme
@@ -99,7 +89,9 @@ var generate = function(options) {
});
};
/*
* Generate a multilanguage book by generating a book for each folder.
*/
var generateMultiLang = function(options) {
var langsSummary;
options.output = options.output || path.join(options.input, "_book");
@@ -297,6 +289,9 @@ var generateBook = function(options) {
})
// Finish generation
.then(function() {
return generator.callHook("finish:before");
})
.then(function() {
return generator.finish();
})
@@ -375,4 +370,5 @@ module.exports = {
file: generateFile,
book: generateBook,
Plugin: Plugin,
config: defaultConfig
};
+4 -4
View File
@@ -26,12 +26,12 @@ Generator.prototype.convertFile = function(content, input) {
return Q()
.then(function() {
return parse.page(content, {
repo: that.options.githubId,
dir: path.dirname(input) || '/'
});
})
.then(function(sections) {
json.sections = sections;
.then(function(parsed) {
json.lexed = parsed.lexed;
json.sections = parsed.sections;
})
.then(function() {
return fs.writeFile(
@@ -74,4 +74,4 @@ Generator.prototype.finish = function() {
// ignore
};
module.exports = Generator;
module.exports = Generator;
+34 -45
View File
@@ -8,52 +8,39 @@ var fs = require("../fs");
var parse = require("../../parse");
var BaseGenerator = require("../site");
/*
* This generator will generate a simple index.html which can be converted as a PDF
*/
var Generator = function() {
BaseGenerator.apply(this, arguments);
// Base for assets in plugins
this.pluginAssetsBase = "ebook";
// List of pages content
this.pages = {};
};
util.inherits(Generator, BaseGenerator);
// Load all templates
Generator.prototype.loadTemplates = function() {
this.template = swig.compileFile(
this.plugins.template("page") || path.resolve(this.options.theme, 'templates/page.html')
this.plugins.template("ebook:page") || path.resolve(this.options.theme, 'templates/ebook/page.html')
);
this.summaryTemplate = swig.compileFile(
this.plugins.template("ebook:sumary") || path.resolve(this.options.theme, 'templates/ebook/summary.html')
);
this.glossaryTemplate = swig.compileFile(
this.plugins.template("ebook:glossary") || path.resolve(this.options.theme, 'templates/ebook/glossary.html')
);
};
Generator.prototype.convertFile = function(content, input) {
// Generate table of contents
Generator.prototype.writeToc = function() {
var that = this;
var json = {
path: input,
progress: parse.progress(this.options.navigation, input)
};
var basePath = ".";
return Q()
.then(function() {
return parse.page(content, {
repo: that.options.githubId,
dir: path.dirname(input) || '/',
outdir: './',
singleFile: true
});
})
.then(function(sections) {
json.content = sections;
})
.then(function() {
that.pages[input] = json;
});
};
// Generate languages index
Generator.prototype.langsIndex = function(langs) {
return Q();
return this._writeTemplate(this.summaryTemplate, {
toc: parse.progress(this.options.navigation, "README.md").chapters,
basePath: basePath,
staticBase: path.join(basePath, "gitbook"),
}, path.join(this.options.output, "SUMMARY.html"));
};
Generator.prototype.finish = function() {
@@ -62,17 +49,17 @@ Generator.prototype.finish = function() {
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);
// Write table of contents
.then(function() {
return that.writeToc();
})
// Write glossary
.then(function() {
return that.writeGlossary();
})
// Copy cover
@@ -82,11 +69,13 @@ Generator.prototype.finish = function() {
// Copy assets
.then(function() {
return fs.copy(
path.join(that.options.theme, "assets"),
path.join(that.options.output, "gitbook")
);
return that.copyAssets();
});
};
module.exports = Generator;
// Generate languages index
Generator.prototype.langsIndex = function(langs) {
return Q();
};
module.exports = Generator;
+73 -27
View File
@@ -4,11 +4,12 @@ var semver = require("semver");
var path = require("path");
var url = require("url");
var fs = require("./fs");
var npmi = require('npmi');
var resolve = require('resolve');
var pkg = require("../../package.json");
var RESOURCES = ["js", "css"];
var RESOURCES = ["js", "css"];
var Plugin = function(name, root, generator) {
this.name = name;
@@ -65,8 +66,9 @@ Plugin.prototype.normalizeResource = function(resource) {
};
// Return resources
Plugin.prototype._getResources = function() {
var book = this.infos.book;
Plugin.prototype._getResources = function(base) {
base = base || "book";
var book = this.infos[base];
// Nothing specified, fallback to default
if (!book) {
@@ -84,12 +86,10 @@ Plugin.prototype._getResources = function() {
};
// Normalize resources and return them
Plugin.prototype.getResources = function() {
Plugin.prototype.getResources = function(base) {
var that = this;
return this._getResources()
return this._getResources(base)
.then(function(resources) {
_.each(RESOURCES, function(resourceType) {
@@ -133,10 +133,14 @@ Plugin.prototype.callHook = function(name, data) {
};
// Copy plugin assets fodler
Plugin.prototype.copyAssets = function(out) {
Plugin.prototype.copyAssets = function(out, options) {
var that = this;
options = _.defaults(options || {}, {
base: "book"
});
return this.getResources().get('assets')
return this.getResources(options.base)
.get('assets')
.then(function(assets) {
// Assets are undefined
if(!assets) return false;
@@ -149,39 +153,81 @@ Plugin.prototype.copyAssets = function(out) {
};
// Install a list of plugin
Plugin.install = function(options) {
// Normalize list of plugins
var plugins = Plugin.normalizeList(options.plugins);
// Install plugins one by one
return _.reduce(plugins, function(prev, plugin) {
return prev.then(function() {
var fullname = "gitbook-plugin-"+plugin.name;
console.log("Install plugin", plugin.name, "from npm ("+fullname+") with version", (plugin.version || "*"));
return Q.nfcall(npmi, {
'name': fullname,
'version': plugin.version,
'path': options.input,
'npmLoad': {
'loglevel': 'silent',
'loaded': false,
'prefix': options.input
}
});
});
}, Q());
};
// Normalize a list of plugin name to use
Plugin.normalizeNames = function(names) {
// Normalize a list of plugins to use
Plugin.normalizeList = function(plugins) {
// Normalize list to an array
names = _.isString(names) ? names.split(",") : (names || []);
plugins = _.isString(plugins) ? plugins.split(",") : (plugins || []);
// Divide as {name, version} to handle format like "myplugin@1.0.0"
plugins = _.map(plugins, function(plugin) {
var parts = plugin.split("@");
return {
'name': parts[0],
'version': parts[1] // optional
}
});
// List plugins to remove
var toremove = _.chain(names)
.filter(function(name) {
return name.length > 0 && name[0] == "-";
var toremove = _.chain(plugins)
.filter(function(plugin) {
return plugin.name.length > 0 && plugin.name[0] == "-";
})
.map(function(name) {
return name.slice(1);
.map(function(plugin) {
return plugin.name.slice(1);
})
.value();
// Merge with defaults
names = _.chain(names)
.concat(Plugin.defaults)
plugins = _.chain(plugins)
.concat(_.map(Plugin.defaults, function(plugin) {
return { 'name': plugin }
}))
.uniq()
.value();
// Remove plugins starting with
names = _.filter(names, function(name) {
return !_.contains(toremove, name) && !(name.length > 0 && name[0] == "-");
// Build final list
plugins = _.filter(plugins, function(plugin) {
return !_.contains(toremove, plugin.name) && !(plugin.name.length > 0 && plugin.name[0] == "-");
});
return names;
return plugins;
};
// Normalize a list of plugin name to use
Plugin.normalizeNames = function(plugins) {
return _.pluck(Plugin.normalizeList(plugins), "name");
};
// Extract data from a list of plugin
Plugin.fromList = function(names, root, generator) {
Plugin.fromList = function(names, root, generator, options) {
options = _.defaults(options || {}, {
assetsBase: "book"
});
var failed = [];
// Load plugins
@@ -191,14 +237,14 @@ Plugin.fromList = function(names, root, generator) {
return plugin;
});
if (_.size(failed) > 0) return Q.reject(new Error("Error loading plugins: "+failed.join(",")));
if (_.size(failed) > 0) return Q.reject(new Error("Error loading plugins: "+failed.join(",")+". Run 'gitbook install' to install plugins from NPM."));
// The raw resources extracted from each plugin
var pluginResources;
// Get resources of plugins
return Q.all(_.map(plugins, function(plugin) {
return plugin.getResources();
return plugin.getResources(options.assetsBase);
}))
// Extract resources out
// css, js, etc ...
@@ -271,7 +317,7 @@ Plugin.fromList = function(names, root, generator) {
});
};
// Default plugins
// Default plugins added to each books
Plugin.defaults = [
"mathjax"
];
-1
View File
@@ -1,5 +1,4 @@
var _ = require("lodash");
var kramed = require('kramed');
var textRenderer = require('kramed-text-renderer');
+63 -44
View File
@@ -23,29 +23,29 @@ var Generator = function() {
};
util.inherits(Generator, BaseGenerator);
// Load all templates
Generator.prototype.loadTemplates = function() {
this.template = swig.compileFile(
this.plugins.template("site") || path.resolve(this.options.theme, 'templates/site.html')
);
this.langsTemplate = swig.compileFile(
this.plugins.template("langs") || path.resolve(this.options.theme, 'templates/langs.html')
);
this.glossaryTemplate = swig.compileFile(
this.plugins.template("glossary") || path.resolve(this.options.theme, 'templates/glossary.html')
);
};
// Load plugins
Generator.prototype.loadPlugins = function() {
// Add template loading to load
Generator.prototype.load = function() {
var that = this;
return BaseGenerator.prototype.loadPlugins.apply(this)
return BaseGenerator.prototype.load.apply(this)
.then(function() {
return that.loadTemplates();
});
};
// Load all templates
Generator.prototype.loadTemplates = function() {
this.template = swig.compileFile(
this.plugins.template("site:page") || path.resolve(this.options.theme, 'templates/book/page.html')
);
this.langsTemplate = swig.compileFile(
this.plugins.template("site:langs") || path.resolve(this.options.theme, 'templates/book/langs.html')
);
this.glossaryTemplate = swig.compileFile(
this.plugins.template("site:glossary") || path.resolve(this.options.theme, 'templates/book/glossary.html')
);
};
// Generate a template
Generator.prototype._writeTemplate = function(tpl, options, output, interpolate) {
var that = this;
@@ -59,10 +59,6 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate)
title: that.options.title,
description: that.options.description,
githubAuthor: that.options.github ? that.options.github.split("/")[0] : "",
githubId: that.options.github,
githubHost: that.options.githubHost,
glossary: that.options.glossary,
summary: that.options.summary,
@@ -95,20 +91,15 @@ Generator.prototype.indexPage = function(lexed, pagePath) {
return Q();
};
// Convert a markdown file to html
Generator.prototype.convertFile = function(content, _input) {
// Convert a markdown file into a normalized data set
Generator.prototype.prepareFile = function(content, _input) {
var that = this;
_output = _input.replace(".md", ".html");
if (_output == "README.html") _output = "index.html";
var input = path.join(this.options.input, _input);
var output = path.join(this.options.output, _output);
var basePath = path.relative(path.dirname(output), this.options.output) || ".";
var page = {
path: _input,
rawPath: input, // path to raw md file
rawPath: input,
content: content,
progress: parse.progress(this.options.navigation, _input)
};
@@ -127,33 +118,59 @@ Generator.prototype.convertFile = function(content, _input) {
return _callHook("page:before");
})
.then(function() {
// Lex page
return parse.lex(page.content);
})
.then(function(lexed) {
// Index page in search
return that.indexPage(lexed, _output)
.then(_.constant(lexed));
})
.then(function(lexed) {
// Lex, parse includes and get
// Get HTML generated sections
return parse.page(lexed, {
repo: that.options.githubId,
return parse.page(page.content, {
// Local files path
dir: path.dirname(_input) || '/',
// Output directory
outdir: path.dirname(_input) || '/',
// Includer for templating
includer: parse.includer(that.options.variables, [
path.dirname(_input) || '/',
path.join(that.options.input, '_includes'),
], path.join, fs.readFileSync)
});
})
.then(function(sections) {
page.sections = sections;
.then(function(parsed) {
page.lexed = parsed.lexed;
page.sections = parsed.sections;
// Use plugin hook
return _callHook("page");
})
.then(function() {
return page;
});
};
// Convert a markdown file to html
Generator.prototype.convertFile = function(content, _input) {
var that = this;
var _output = _input.replace(".md", ".html");
if (_output == "README.html") _output = "index.html";
var output = path.join(this.options.output, _output);
var basePath = path.relative(path.dirname(output), this.options.output) || ".";
// Bug fix for issue #493 which would occur when relative-links are 2-level or more deep in win32
if (process.platform === 'win32') {
basePath = basePath.replace(/\\/g, '/');
}
return this.prepareFile(content, _input)
.then(function(page) {
// Index page in search
return that.indexPage(page.lexed, _output).thenResolve(page);
})
.then(function(page) {
// Write file
return that._writeTemplate(that.template, {
progress: page.progress,
_input: _input,
_input: page.path,
content: page.sections,
basePath: basePath,
@@ -161,7 +178,7 @@ Generator.prototype.convertFile = function(content, _input) {
}, output, function(html) {
page.content = html;
return _callHook("page:after").get("content")
return that.callHook("page:after", page).get("content")
});
});
};
@@ -238,7 +255,9 @@ Generator.prototype.copyAssets = function() {
return Q.all(
_.map(that.plugins.list, function(plugin) {
var pluginAssets = path.join(that.options.output, "gitbook/plugins/", plugin.name);
return plugin.copyAssets(pluginAssets);
return plugin.copyAssets(pluginAssets, {
base: that.pluginAssetsBase
});
})
);
});
-20
View File
@@ -1,20 +0,0 @@
var fs = require('graceful-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;
});
};
+12
View File
@@ -0,0 +1,12 @@
var _ = require('lodash');
module.exports = function(markdown, includer) {
// Memoized include function (to cache lookups)
var _include = _.memoize(includer);
return markdown.replace(/{{([\s\S]+?)}}/g, function(match, key) {
// If fails leave content as is
key = key.trim();
return _include(key) || match;
});
};
+15
View File
@@ -0,0 +1,15 @@
// Return a fs inclduer
module.exports = function(ctx, folders, resolveFile, readFile) {
return function(name) {
return ctx[name] ||
folders.map(function(folder) {
// Try including snippet from FS
try {
var fname = resolveFile(folder, name);
// Trim trailing newlines/space of imported snippets
return readFile(fname, 'utf8').trimRight();
} catch(err) {}
})
.filter(Boolean)[0];
}
};
+2 -1
View File
@@ -6,5 +6,6 @@ module.exports = {
lex: require('./lex'),
progress: require('./progress'),
navigation: require('./navigation'),
readme: require('./readme')
readme: require('./readme'),
includer: require('./includer')
};
+17
View File
@@ -0,0 +1,17 @@
var _ = require('lodash');
function isExercise(nodes) {
var codeType = { type: 'code' };
// Number of code nodes in section
var len = _.filter(nodes, codeType).length;
return (
// Got 3 or 4 code blocks
(len === 3 || len === 4) &&
// Ensure all nodes are at the end
_.all(_.last(nodes, len), codeType)
);
}
module.exports = isExercise;
+87
View File
@@ -0,0 +1,87 @@
var _ = require('lodash');
function isQuizNode(node) {
return (/^[(\[][ x][)\]]/).test(node.text || node);
}
function isTableQuestion(nodes) {
var block = questionBlock(nodes);
return (
block.length === 1 &&
block[0].type === 'table' &&
_.all(block[0].cells[0].slice(1), isQuizNode)
);
}
function isListQuestion(nodes) {
var block = questionBlock(nodes);
// Counter of when we go in and out of lists
var inlist = 0;
// Number of lists we found
var lists = 0;
// Elements found outside a list
var outsiders = 0;
// Ensure that we have nothing except lists
_.each(block, function(node) {
if(node.type === 'list_start') {
inlist++;
} else if(node.type === 'list_end') {
inlist--;
lists++;
} else if(inlist === 0) {
// Found non list_start or list_end whilst outside a list
outsiders++;
}
});
return lists > 0 && outsiders === 0;
}
function isQuestion(nodes) {
return isListQuestion(nodes) || isTableQuestion(nodes);
}
// Remove (optional) paragraph header node and blockquote
function questionBlock(nodes) {
return nodes.slice(
nodes[0].type === 'paragraph' ? 1 : 0,
_.findIndex(nodes, { type: 'blockquote_start' })
);
}
function splitQuestions(nodes) {
// Represents nodes in current question
var buffer = [];
return _.reduce(nodes, function(accu, node) {
// Add node to buffer
buffer.push(node);
// Flush buffer once we hit the end of a question
if(node.type === 'blockquote_end') {
accu.push(buffer);
// Clear buffer
buffer = [];
}
return accu;
}, []);
}
function isQuiz(nodes) {
// Extract potential questions
var questions = splitQuestions(
// Skip quiz title if there
nodes.slice(
(nodes[0] && nodes[0].type) === 'paragraph' ? 1 : 0
)
);
// Nothing that looks like questions
if(questions.length === 0) {
return false;
}
// Ensure all questions are correctly structured
return _.all(questions, isQuestion);
}
module.exports = isQuiz;
+3 -53
View File
@@ -1,6 +1,9 @@
var _ = require('lodash');
var kramed = require('kramed');
var isExercise = require('./is_exercise');
var isQuiz = require('./is_quiz');
// Split a page up into sections (lesson, exercises, ...)
function splitSections(nodes) {
var section = [];
@@ -17,59 +20,6 @@ function splitSections(nodes) {
}, []).concat([section]); // Add remaining nodes
}
function isQuizNode(node) {
return (/^[(\[][ x][)\]]/).test(node.text || node);
}
function isExercise(nodes) {
var codeType = { type: 'code' };
// Number of code nodes in section
var len = _.filter(nodes, codeType).length;
return (
// Got 3 or 4 code blocks
(len === 3 || len === 4) &&
// Ensure all nodes are at the end
_.all(_.last(nodes, len), codeType)
);
}
function isQuiz(nodes) {
if (nodes.length < 3) {
return false;
}
// Support having a first paragraph block
// before our series of questions
var quizNodes = nodes.slice(nodes[0].type === 'paragraph' ? 1 : 0);
// No questions
if (!_.some(quizNodes, { type: 'blockquote_start' })) {
return false;
}
// Check if section has list of questions
// or table of questions
var listIdx = _.findIndex(quizNodes, { type: 'list_item_start' });
var tableIdx = _.findIndex(quizNodes, { type: 'table' });
if(
// List of questions
listIdx !== -1 && isQuizNode(quizNodes[listIdx + 1]) ||
// Table of questions
(
tableIdx !== -1 &&
_.every(quizNodes[tableIdx].cells[0].slice(1), isQuizNode)
)
) {
return true;
}
return false;
}
// What is the type of this section
function sectionType(nodes, idx) {
if(isExercise(nodes)) {
+10 -11
View File
@@ -5,7 +5,7 @@ var hljs = require('highlight.js');
var lex = require('./lex');
var renderer = require('./renderer');
var codeInclude = require('./code_include');
var include = require('./include');
var lnormalize = require('../utils/lang').normalize;
@@ -51,8 +51,10 @@ function parsePage(src, options) {
options = options || {};
// Lex if not already lexed
return (_.isArray(src) ? src : lex(src))
.map(function(section) {
var parsed = {
lexed: (_.isArray(src) ? src : lex(include(src, options.includer || function() { return undefined; })))
};
parsed.sections = parsed.lexed.map(function(section) {
// Transform given type
if(section.type === 'exercise') {
var nonCodeNodes = _.reject(section, {
@@ -74,20 +76,15 @@ 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, options),
lang: lang,
code: {
base: ci(codeNodes[0].text),
solution: ci(codeNodes[1].text),
validation: ci(codeNodes[2].text),
base: codeNodes[0].text,
solution: codeNodes[1].text,
validation: codeNodes[2].text,
// Context is optional
context: codeNodes[3] ? codeNodes[3].text : null,
}
@@ -155,6 +152,8 @@ function parsePage(src, options) {
content: render(section, options)
};
});
return parsed;
}
// Exports
+10 -24
View File
@@ -1,11 +1,7 @@
var url = require('url');
var _ = require('lodash');
var inherits = require('util').inherits;
var links = require('../utils').links;
var codeInclude = require('./code_include');
var path = require('path');
var kramed = require('kramed');
var rendererId = 0;
@@ -52,24 +48,13 @@ GitBookRenderer.prototype.link = function(href, title, text) {
// Parsed version of the url
var parsed = url.parse(href);
var o = this._extra_options;
var extname = parsed.path? _.last(parsed.path.split(".")) : "";
// Relative link, rewrite it to point to github repo
if(links.isRelative(_href)) {
if (path.extname(parsed.path) == ".md") {
_href = links.toAbsolute(_href, o.dir || "./", o.outdir || "./");
if (o.singleFile) {
_href = "#"+_href;
} else {
_href = _href.replace(".md", ".html");
}
} else if (o && o.repo && o.dir) {
href = url.resolve('https://github.com/' + o.repo + '/blob/', [o.dir, _href].join("/"));
parsed = url.parse(href);
_href = parsed.href;
}
if(links.isRelative(_href) && extname == "md") {
_href = links.toAbsolute(_href, o.dir || "./", o.outdir || "./");
_href = _href.replace(".md", ".html");
}
// Generate HTML for link
@@ -118,8 +103,10 @@ GitBookRenderer.prototype._createCheckboxAndRadios = function(text) {
if (!match) {
return text;
}
var quizIdentifier = 'quiz-row-' + this.id + '-' + this.quizRowId + '-' + this.quizIndex++;
var field = "<input name='" + quizIdentifier + "' id='" + quizIdentifier + "' type='";
//fix radio input uncheck failed
var quizFieldName='quiz-row-' + this.id + '-' + this.quizRowId ;
var quizIdentifier = quizFieldName + '-' + this.quizIndex++;
var field = "<input name='" + quizFieldName + "' id='" + quizIdentifier + "' type='";
field += match[1] === '(' ? "radio" : "checkbox";
field += match[2] === 'x' ? "' checked/>" : "'/>";
var splittedText = text.split(fieldRegex);
@@ -139,8 +126,7 @@ GitBookRenderer.prototype.listitem = function(text) {
GitBookRenderer.prototype.code = function(code, lang, escaped) {
return GitBookRenderer.super_.prototype.code.call(
this,
// Import code snippets
codeInclude(code, this._extra_options.dir),
code,
lang,
escaped
);
+2 -2
View File
@@ -11,8 +11,8 @@ function escapeShellArg(arg) {
function optionsToShellArgs(options) {
return _.chain(options)
.map(function(value, key) {
if (value == null || value == false) return null;
if (value == true) return key;
if (value == null || value === false) return null;
if (value === true) return key;
return key+"="+escapeShellArg(value);
})
.compact()
+11 -6
View File
@@ -1,13 +1,13 @@
{
"name": "gitbook",
"version": "1.0.0",
"version": "1.4.2",
"homepage": "http://www.gitbook.io/",
"description": "Library and cmd utility to generate GitBooks",
"main": "lib/index.js",
"dependencies": {
"q": "1.0.1",
"lodash": "2.4.1",
"kramed": "0.4.1",
"kramed": "0.4.3",
"kramed-text-renderer": "0.2.1",
"lunr": "0.5.2",
"swig": "1.3.2",
@@ -16,15 +16,18 @@
"commander": "2.2.0",
"graceful-fs": "3.0.2",
"fs-extra": "0.10.0",
"highlight.js": "8.1.0",
"highlight.js": "8.3.0",
"tmp": "0.0.23",
"semver": "2.2.1",
"npmi": "0.1.1",
"gaze": "~0.5.1",
"resolve": "0.6.3",
"tiny-lr-fork": "0.0.5",
"gitbook-plugin": "0.0.2",
"gitbook-plugin-mathjax": "0.0.4",
"gitbook-plugin-livereload": "0.0.1"
"gitbook-plugin-mathjax": "0.0.6",
"gitbook-plugin-livereload": "0.0.1",
"gitbook-plugin-exercises": "1.0.0",
"gitbook-plugin-quizzes": "1.0.0"
},
"devDependencies": {
"mocha": "1.18.2",
@@ -33,7 +36,9 @@
"grunt-contrib-copy": "0.5.0",
"grunt-contrib-less": "~0.5.0",
"grunt-contrib-requirejs": "0.4.1",
"grunt-bower-install-simple": "0.9.2"
"grunt-bower-install-simple": "0.9.2",
"grunt-browserify": "3.1.0",
"grunt-contrib-uglify": "0.6.0"
},
"scripts": {
"test": "export TESTING=true; mocha --reporter list"
+120
View File
@@ -0,0 +1,120 @@
# Learn Node.js by Example
## Requirements
- [x] A computer with internet access
- [ ] Time: 30h (e.g. 2 months 30 mins per day or **1 week intensive**)
## What is Node.js ?
Node.js lets you *easily* build networked software (websites, applications "apps",
using JavaScript).
Its not "*point-and-click*" like WordPress, SquareSpace or Salesforce;
you will need to write some "code". But as I will demonstrate, that's
a *lot* easier than it sounds and gives you more power/flexibility
and puts you in full control.
## Node.js is not "Version 1.0" yet can I used it in Production?
Yes! Some of the biggest organisations/companies in the world
are using Node.js in Production systems:
[Alibaba](https://github.com/alibaba/node-hbase-client),
[Ajax.org](Ajax.org),
[Box.com](http://tech.blog.box.com/2014/06/node-js-high-availability-at-box/), British Sky Broadcasting (Sky/Now TV),
CNN,
[Cloudup](https://cloudup.com/),
Conde Nast,
[DirectTV](http://strongloop.com/strongblog/node-summit-media-companies-embrace-node-js-for-rapidly-developing-responsive-apps/),
[Dow Jones](http://nodejs.org/industry),
eBay,
[FeedHenry](http://www.feedhenry.com/),
[GitHub](https://twitter.com/github/status/16979699217465344),
[Google](http://venturebeat.com/2012/01/24/node-at-google-mozilla-yahoo/),
[Groupon](http://nodeup.com/fiftyeight),
HBO,
Help.com,
[HP](https://github.com/joyent/node/wiki/Projects,-Applications,-and-Companies-Using-Node),
iTV,
[Joyent](https://www.joyent.com/) (duh!),
[Klout](https://klout.com),
LinkedIn,
McDonalds,
[Medium](https://medium.com/the-story),
Mozilla,
NetFlix,
[OpenTable](http://hapijs.com/community),
PayPal,
Pearson,
~~Q~~,
[Revolt](http://revolt.tv/),
[Square](https://modulus.io/companies-using-node),
Tesco,
ThomasCook,
Trello,
Uber,
Voxer,
Walmart,
Wikimedia (in progress of moving to SOA with node!)
Yahoo,
Yammer,
[Yandex](https://www.youtube.com/watch?v=zdCxgdH4wZo),
[Zendesk](http://radar.zendesk.com/)
Want more? See: http://nodejs.org/industry/ and <br />
https://github.com/joyent/node/wiki/Projects,-Applications,-and-Companies-Using-Node
# Try it!
## Download & Install
> http://nodejs.org/download/
## Node.js (Core) API
The node.js ("core") has many useful modules.
Bookmark: [http://nodejs.org/api](http://nodejs.org/api/) (you will come back to it)
## Stability (Can we use it?)
> *Which node.js* ***core*** *package(s) can/should I use?*
Every core module has a
["***Stability Index***"](http://nodejs.org/api/documentation.html#documentation_stability_index)
rating on the node.js API.
**General rule**: If you are being *paid* to write code
that runs in node.js, <br /> pick core modules/methods
with stability **Stable**, **API Frozen** and **Locked**.
![Node.js Stability Index](http://i.imgur.com/xIroFrS.png)
### Examples
- [**cluster**](http://nodejs.org/api/cluster.html) is ***Experimental*** - don't use
- [**domain**](http://nodejs.org/api/domain.html) is ***Unstable*** - don't use
- [**path**](http://nodejs.org/api/path.html) is ***Stable*** - use
- [**events**](http://nodejs.org/api/events.html) is ***Frozen*** - use
- [**assert**](http://nodejs.org/api/assert.html) is ***Locked*** - use
Core Modules to Learn
- path
- os
Community Modules to Learn:
- [jscs](https://www.npmjs.org/package/jscs) - code style checker
- [q](https://www.npmjs.org/package/q) - promises library
- [nd](https://www.npmjs.org/package/nd) - view documentation for a module
+5 -1
View File
@@ -3,12 +3,13 @@ var path = require('path');
var assert = require('assert');
var page = require('../').parse.page;
var includer = require('../').parse.includer;
var FIXTURES_DIR = path.join(__dirname, './fixtures/');
function loadPage (name, options) {
var CONTENT = fs.readFileSync(FIXTURES_DIR + name + '.md', 'utf8');
return page(CONTENT, options);
return page(CONTENT, options).sections;
}
@@ -16,6 +17,9 @@ describe('Code includes', function() {
var LEXED = loadPage('INCLUDES', {
'dir': FIXTURES_DIR,
'includer': includer({}, [
FIXTURES_DIR
], path.join, fs.readFileSync)
});
var INCLUDED_C = fs.readFileSync(path.join(FIXTURES_DIR, 'included.c'), 'utf8');
+9 -38
View File
@@ -6,7 +6,7 @@ var page = require('../').parse.page;
function loadPage (name, options) {
var CONTENT = fs.readFileSync(path.join(__dirname, './fixtures/' + name + '.md'), 'utf8');
return page(CONTENT, options);
return page(CONTENT, options).sections;
}
var LEXED = loadPage('PAGE', {
@@ -78,24 +78,8 @@ describe('Page parsing', function() {
describe('Relative links', function() {
it('should be resolved to their GitHub counterparts', function() {
var LEXED = loadPage('GITHUB_LINKS', {
// GitHub repo ID
repo: 'GitBookIO/javascript',
// Imaginary folder of markdown file
dir: 'course',
outdir: './'
});
assert(LEXED[0].content.indexOf('https://github.com/GitBookIO/javascript/blob/src/something.cpp') !== -1);
});
it('should replace link to .md by link to .html', function() {
var LEXED = loadPage('MARKDOWN_LINKS', {
// GitHub repo ID
repo: 'GitBookIO/javascript',
// Imaginary folder of markdown file
dir: 'course',
outdir: 'course'
@@ -104,29 +88,11 @@ describe('Relative links', function() {
assert(LEXED[0].content.indexOf('test.html') !== -1);
assert(LEXED[0].content.indexOf('../before.html') !== -1);
});
it('should replace link to .md by link to page in format single page', function() {
var LEXED = loadPage('MARKDOWN_LINKS', {
// GitHub repo ID
repo: 'GitBookIO/javascript',
// Imaginary folder of markdown file
dir: 'course',
outdir: './',
singleFile: true
});
assert(LEXED[0].content.indexOf('#course/test.md') !== -1);
assert(LEXED[0].content.indexOf('#before.md') !== -1);
});
});
describe('Relative images', function() {
it('should keep image relative with considering output directory in site format', function() {
var LEXED = loadPage('IMAGES', {
// GitHub repo ID
repo: 'GitBookIO/javascript',
// Imaginary folder of markdown file
dir: 'syntax',
outdir: 'syntax'
@@ -138,9 +104,6 @@ describe('Relative images', function() {
it('should keep image relative with considering output directory in page format', function() {
var LEXED = loadPage('IMAGES', {
// GitHub repo ID
repo: 'GitBookIO/javascript',
// Imaginary folder of markdown file
dir: 'syntax',
outdir: './'
@@ -150,3 +113,11 @@ describe('Relative images', function() {
assert(LEXED[0].content.indexOf('"preview2.png"') !== -1);
});
});
describe('Section parsing', function() {
it('should not have false positive quiz parsing', function() {
var LEXED = loadPage('FALSE_QUIZ');
assert.equal(LEXED[0].type, 'normal');
});
});
+10 -1
View File
@@ -12,7 +12,7 @@ describe('Plugin validation', function () {
});
});
describe('Plugin list of names', function () {
describe('Plugins list', function () {
var firstDefault = _.first(Plugin.defaults);
it('should convert string to array', function() {
@@ -27,6 +27,15 @@ describe('Plugin list of names', function () {
it('should remove name starting with -', function() {
assert(!_.contains(Plugin.normalizeNames(["-"+firstDefault]), firstDefault));
});
it('should accept version', function() {
var _name = "test@0.3.0,exercises@1.2.0,test2";
var plugins = Plugin.normalizeList(_name);
assert(_.find(plugins, {'name': "test"}).version = "0.3.0");
assert(_.find(plugins, {'name': "exercises"}).version = "1.2.0");
assert(!_.find(plugins, {'name': "test2"}).version);
});
});
describe('Plugin defaults loading', function () {
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+53 -18
View File
@@ -31,19 +31,57 @@ define([
return d.promise();
}
var pregQuote = function( str ) {
// http://kevin.vanzonneveld.net
// + original by: booeyOH
// + improved by: Ates Goral (http://magnetiq.com)
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + bugfixed by: Onno Marsman
// * example 1: preg_quote("$40");
// * returns 1: '\$40'
// * example 2: preg_quote("*RRRING* Hello?");
// * returns 2: '\*RRRING\* Hello\?'
// * example 3: preg_quote("\\.+*?[^]$(){}=!<>|:");
// * returns 3: '\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:'
$.fn.replaceText = function( search, replace, text_only ) {
return this.each(function(){
var node = this.firstChild,
val,
new_val,
// Elements to be removed at the end.
remove = [];
// Only continue if firstChild exists.
if ( node ) {
// Loop over all childNodes.
do {
// Only process text nodes.
if ( node.nodeType === 3 ) {
// The original node value.
val = node.nodeValue;
// The new value.
new_val = val.replace( search, replace );
// Only replace text if the new value is actually different!
if ( new_val !== val ) {
if ( !text_only && /</.test( new_val ) ) {
// The new value contains HTML, set it in a slower but far more
// robust way.
$(node).before( new_val );
// Don't remove the node yet, or the loop will lose its place.
remove.push( node );
} else {
// The new value contains no HTML, so it can be set in this
// very fast, simple way.
node.nodeValue = new_val;
}
}
}
} while ( node = node.nextSibling );
}
// Time to remove those elements!
remove.length && $(remove).remove();
});
};
var pregQuote = function( str ) {
return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1");
};
@@ -59,13 +97,10 @@ define([
var replaceTerm = function($el, term) {
var r = new RegExp( "\\b(" + pregQuote(term.name.toLowerCase()) + ")\\b" , 'gi' );
$el.find("p:contains('"+term.name+"')").each( function( i, element ) {
element = $(element);
var content = $(element).html();
content = content.replace(r, '<span class="glossary-term" data-glossary-term="' + term.id + '" title="' + term.description + '">$1</span>');
element.html(content);
$el.find("*").replaceText(r, function(match) {
return "<span class='glossary-term' data-glossary-term='"+term.id+"' title='"+term.description+"'>"+match+"</span>";
});
};
var prepare = function() {
-1
View File
@@ -7,7 +7,6 @@ define([
var $book = $(dom.find(".book"));
state.$book = $book;
state.githubId = $book.data("github");
state.level = $book.data("level");
state.basePath = $book.data("basepath");
state.revision = $book.data("revision");
-5
View File
@@ -18,11 +18,6 @@ define([
var $book;
$book = state.$book;
if (state.githubId) {
// Initialize storage
storage.setBaseKey(state.githubId);
}
// Init sidebar
sidebar.init();
+5 -8
View File
@@ -1,24 +1,21 @@
define([
"jQuery"
], function($) {
var url = location.href;
var title = $("title").text();
var types = {
"twitter": function($el) {
window.open("http://twitter.com/home?status="+encodeURIComponent(title+" "+url))
window.open("http://twitter.com/home?status="+encodeURIComponent($("title").text()+" "+location.href))
},
"facebook": function($el) {
window.open("http://www.facebook.com/sharer/sharer.php?s=100&p[url]="+encodeURIComponent(url))
window.open("http://www.facebook.com/sharer/sharer.php?s=100&p[url]="+encodeURIComponent(location.href))
},
"google-plus": function($el) {
window.open("https://plus.google.com/share?url="+encodeURIComponent(url))
window.open("https://plus.google.com/share?url="+encodeURIComponent(location.href))
},
"weibo": function($el) {
window.open("http://service.weibo.com/share/share.php?content=utf-8&url="+encodeURIComponent(url)+"&title="+encodeURIComponent(title))
window.open("http://service.weibo.com/share/share.php?content=utf-8&url="+encodeURIComponent(location.href)+"&title="+encodeURIComponent($("title").text()))
},
"instapaper": function($el) {
window.open("http://www.instapaper.com/text?u="+encodeURIComponent(url));
window.open("http://www.instapaper.com/text?u="+encodeURIComponent(location.href));
}
};
+2 -1
View File
@@ -256,7 +256,8 @@
}
ol ol,
ol ul {
ol ul,
ul ul {
margin-top: 0;
margin-bottom: 0;
}
+50 -19
View File
@@ -3,30 +3,61 @@
@import "ebook/variables.less";
@import "ebook/highlight.less";
.book-chapter {
display: none;
}
.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;
.page {
&.page-toc {
ol {
margin: 0px;
}
}
.question {
margin-top: 0.4cm;
.book-chapter {
display: none;
}
.exercise, .quiz {
margin: 1cm 0cm;
border: 2px solid #ddd;
.exercise-header {
padding: 0.1cm 0.3cm;
background: #f5f5f5;
border-bottom: 1px solid #ddd;
font-weight: bold;
page-break-inside: avoid;
}
.exercise-inner {
padding: 0.15cm 0.3cm;
p:last-child {
margin: 0px;
}
}
hr {
height: 2px;
}
.question {
margin-top: 0.1cm;
border-top: 1px solid #ddd;
.question-base {
}
.question-solution {
}
}
}
}
body {
font-family: sans-serif;
.markdown-content(#333, 1.6);
.page {
font-family: sans-serif;
.markdown-content(#333, 1.6);
}
}
@@ -1,4 +1,4 @@
{% extends "site.html" %}
{% extends "page.html" %}
{% block title %}Glossary | {{ title }}{% endblock %}
@@ -1,4 +1,4 @@
{% extends "layout.html" %}
{% extends "../layout.html" %}
{% block title %}{{ title }}{% endblock %}
@@ -1,10 +1,7 @@
{% extends "layout.html" %}
{% extends "../layout.html" %}
{% 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 %}
@@ -17,11 +14,11 @@
{% 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/summary.html" %}
<div class="book" data-level="{{ progress.current.level }}" data-basepath="{{ basePath }}" data-revision="{{ revision }}">
{% include "includes/summary.html" %}
<div class="book-body">
<div class="body-inner">
{% include "includes/book/header.html" %}
{% include "includes/header.html" %}
<div class="page-wrapper" tabindex="-1">
<div class="page-inner">
{% block page_inner %}
@@ -30,9 +27,9 @@
{% if section.type == "normal" %}
{% autoescape false %}{{ section.content }}{% endautoescape %}
{% elif section.type == "exercise" %}
{% include "./includes/book/exercise.html" with {section: section} %}
{% include "./includes/exercise.html" with {section: section} %}
{% elif section.type == "quiz" %}
{% include "./includes/book/quiz.html" with {section: section} %}
{% include "./includes/quiz.html" with {section: section} %}
{% endif %}
</section>
{% endfor %}
+15
View File
@@ -0,0 +1,15 @@
{% extends "./layout.html" %}
{% block title %}Glossary | {{ title }}{% endblock %}
{% block content %}
<div class="page page-toc">
<h1>Glossary</h1>
{% for item in glossaryIndex %}
<section class="normal glossary" id="{{ item.id }}">
<h2><a href="#{{ item.id }}">{{ item.name }}</a></h2>
<p>{{ item.description }}</p>
</section>
{% endfor %}
</div>
{% endblock %}
@@ -0,0 +1,11 @@
<div class="exercise-header">Exercise</div>
<div class="exercise-inner">
{% autoescape false %}{{ section.content }}{% endautoescape %}
</div>
<div class="exercise-inner">
<pre><code>{% autoescape false %}{{ section.code.base|code }}{% endautoescape %}</code></pre>
</div>
<hr>
<div class="exercise-inner">
<pre><code>{% autoescape false %}{{ section.code.solution|code }}{% endautoescape %}</code></pre>
</div>
+14
View File
@@ -0,0 +1,14 @@
<div class="exercise-header">Quiz</div>
<div class="exercise-inner">{% autoescape false %}{{ section.content }}{% endautoescape %}</div>
{% for quiz in section.quiz %}
<div class="question exercise-inner">
<div class="question-header">Question {{ loop.index }} of {{ section.quiz.length }}</div>
<div class="question-base">
{% autoescape false %}{{ quiz.base }}{% endautoescape %}
</div>
<div class="question-solution">
{% autoescape false %}{{ quiz.solution }}{% endautoescape %}
</div>
</div>
{% endfor %}
+22
View File
@@ -0,0 +1,22 @@
<!DOCTYPE HTML>
<html lang="en-US" {% block htmlTag %}{% endblock %}>
{{ htmlSnippet("html:start")|default("") }}
<head>
{{ htmlSnippet("head:start")|default("") }}
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="{% block description %}{% endblock %}">
<meta name="generator" content="GitBook {{ gitbook.version }}">
{% block head %}{% endblock %}
{{ htmlSnippet("head:end")|default("") }}
</head>
<body>
{{ htmlSnippet("body:start")|default("") }}
{% block style %}{% endblock %}
{% block content %}{% endblock %}
{% block javascript %}{% endblock %}
{{ htmlSnippet("body:end")|default("") }}
</body>
{{ htmlSnippet("html:end")|default("") }}
</html>
+32
View File
@@ -0,0 +1,32 @@
{% extends "./layout.html" %}
{% block title %}{{ progress.current.title }} | {{ title }}{% endblock %}
{% block style %}
<link rel="stylesheet" href="{{ staticBase }}/print.css">
{% for resource in plugins.resources.css %}
{% if resource.url %}
<link rel="stylesheet" href="{{ resource.url }}">
{% else %}
<link rel="stylesheet" href="{{ staticBase }}/plugins/{{ resource.path }}">
{% endif %}
{% endfor %}
{% endblock %}
{% block content %}
<div class="page">
<h1 class="book-chapter book-chapter-{{ progress.current.level|lvl }}">{{ progress.current.title }}</h1>
{% for section in content %}
<div class="{{ section.type }}" id="section-{{ section.id }}">
{% if section.type == "normal" %}
{% autoescape false %}{{ section.content }}{% endautoescape %}
{% elif section.type == "exercise" %}
{% include "./includes/exercise.html" with {section: section} %}
{% elif section.type == "quiz" %}
{% include "./includes/quiz.html" with {section: section} %}
{% endif %}
</div>
{% endfor %}
</div>
{% endblock %}
+50
View File
@@ -0,0 +1,50 @@
{% extends "./layout.html" %}
{% block title %}Table of Contents | {{ title }}{% endblock %}
{% block style %}
<link rel="stylesheet" href="{{ staticBase }}/print.css">
{% for resource in plugins.resources.css %}
{% if resource.url %}
<link rel="stylesheet" href="{{ resource.url }}">
{% else %}
<link rel="stylesheet" href="{{ staticBase }}/plugins/{{ resource.path }}">
{% endif %}
{% endfor %}
{% endblock %}
{% macro articles(_articles) %}
{% for item in _articles %}
{% set externalLink = item.path|isExternalLink %}
<li>
{% if item.path %}
{% if !externalLink %}
<a href="{{ basePath }}/{{ item.path|mdLink }}">{{ item.title }}</a>
{% else %}
<a target="_blank" href="{{ item.path }}">{{ item.title }}</a>
{% endif %}
{% else %}
{{ item.title }}
{% endif %}
{% if item.articles.length > 0 %}
<ol>
{{ articles(item.articles) }}
</ol>
{% endif %}
</li>
{% endfor %}
{% endmacro %}
{% block content %}
<div class="page page-toc">
<h1>Table of Contents</h1>
<ol>
{{ articles(summary.chapters) }}
{% if glossary.length > 0 %}
<li><a href="{{ basePath }}/GLOSSARY.html">Glossary</a></li>
{% endif %}
</ol>
</div>
{% endblock %}
+4
View File
@@ -4,6 +4,7 @@
<head>
{{ htmlSnippet("head:start")|default("") }}
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=11; IE=10; IE=9; IE=8; IE=7; IE=EDGE" />
<title>{% block title %}{% endblock %}</title>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="{% block description %}{% endblock %}">
@@ -14,6 +15,9 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="{{ staticBase }}/images/apple-touch-icon-precomposed-152.png">
<link rel="shortcut icon" href="{{ staticBase }}/images/favicon.ico" type="image/x-icon">
{% if options.isbn %}
<meta name="identifier" content="{{ options.isbn }}" scheme="ISBN">
{% endif %}
{% block head %}{% endblock %}
{{ htmlSnippet("head:end")|default("") }}
</head>
-87
View File
@@ -1,87 +0,0 @@
{% extends "layout.html" %}
{% block title %}{{ title }}{% endblock %}
{% block style %}
<link rel="stylesheet" href="{{ staticBase }}/print.css">
{% endblock %}
{% macro articleContent(content) %}
{% for section in content %}
{% if section.type == "normal" %}
{% autoescape false %}{{ section.content }}{% endautoescape %}
{% elif section.type == "exercise" %}
<div class="exercise">
<div class="exercise-header">Exercise #{{ exercise }}</div>
{% autoescape false %}{{ section.content }}{% endautoescape %}
<pre><code>{% autoescape false %}{{ section.code.base|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.base }}{% endautoescape %}
</div>
{% endfor %}
{% set exercise = exercise + 1 %}
</div>
{% endif %}
{% endfor %}
{% endmacro %}
{% set exercise = 1 %}
{% block content %}
{# Pages content #}
{% 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 %}
<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>
{% endif %}
{% endblock %}
{% block javascript %}{% endblock %}