mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-26 20:27:00 +00:00
Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1e4632080b | |||
| 5191106396 | |||
| 6ab5cafaf3 | |||
| d1c0cb9c71 | |||
| 38c750101c | |||
| ba0f379a89 | |||
| 1d9509b3b6 | |||
| 3c8de4a8ab | |||
| 591d8fc9ee | |||
| d480e9cea4 | |||
| 32a71869da | |||
| fdbfda64a6 | |||
| a6f48be39e | |||
| ce50b893e9 | |||
| 225ff27dd1 | |||
| 52e8361846 | |||
| 49e3bbc3d6 | |||
| c9ec911d0e | |||
| 84d7662fd8 | |||
| 676b2287ee | |||
| 32a64be407 | |||
| 138cddf3d6 | |||
| d730a3aac6 | |||
| ffb0b12012 | |||
| 78e25f8252 | |||
| 09f42f57e1 | |||
| 950ad1d060 | |||
| ed6b29c1a4 | |||
| 36785120b1 | |||
| 26fad5b888 | |||
| f049576575 | |||
| a7ecbb87e4 | |||
| 1da341741b | |||
| 2dd6192576 | |||
| 52c9347b9c | |||
| e2295591ed | |||
| 0c1cb724d7 | |||
| 7cee248426 | |||
| 6ea5b2b539 | |||
| a56b559d43 | |||
| df1a82e0e4 | |||
| f15a1efe71 | |||
| b8379fc6fa | |||
| d69cc74647 | |||
| f83874a9ca | |||
| 1968bb79c2 | |||
| 60c721db40 | |||
| 11b299653f | |||
| c8c07fe8ce | |||
| be0a4f08e9 | |||
| 4da4e28d07 | |||
| 9dc19026c6 | |||
| 31ca9f7fcb | |||
| 6925d39e8d | |||
| a24cafd975 | |||
| e3fdf8b404 | |||
| c861223378 | |||
| f5c6d8df71 | |||
| 452c846e90 | |||
| aedd875ab7 | |||
| d2ec2f8f02 | |||
| 5af0df93c3 |
+30
@@ -1,5 +1,35 @@
|
||||
# Release notes
|
||||
|
||||
## 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
|
||||
|
||||
@@ -62,10 +62,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 +74,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 +97,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 +114,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
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -131,7 +133,6 @@ 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```.
|
||||
* **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 +175,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 +194,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 +209,7 @@ 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.
|
||||
|
||||
##### Official plugins:
|
||||
|
||||
@@ -218,6 +219,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:
|
||||
|
||||
|
||||
+10
-1
@@ -38,6 +38,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,7 +86,13 @@ 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": ""
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
@@ -35,12 +31,16 @@ Generator.prototype.finish = function() {
|
||||
"--title": that.options.title,
|
||||
"--comments": that.options.description,
|
||||
"--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 +51,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(" ");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -26,7 +26,6 @@ Generator.prototype.convertFile = function(content, input) {
|
||||
return Q()
|
||||
.then(function() {
|
||||
return parse.page(content, {
|
||||
repo: that.options.githubId,
|
||||
dir: path.dirname(input) || '/'
|
||||
});
|
||||
})
|
||||
@@ -74,4 +73,4 @@ Generator.prototype.finish = function() {
|
||||
// ignore
|
||||
};
|
||||
|
||||
module.exports = Generator;
|
||||
module.exports = Generator;
|
||||
|
||||
+25
-44
@@ -8,52 +8,36 @@ 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')
|
||||
);
|
||||
};
|
||||
|
||||
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() {
|
||||
@@ -64,15 +48,10 @@ Generator.prototype.finish = function() {
|
||||
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();
|
||||
})
|
||||
|
||||
// Copy cover
|
||||
@@ -82,11 +61,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;
|
||||
|
||||
+18
-13
@@ -8,7 +8,7 @@ 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 +65,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 +85,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 +132,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,8 +152,6 @@ Plugin.prototype.copyAssets = function(out) {
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// Normalize a list of plugin name to use
|
||||
Plugin.normalizeNames = function(names) {
|
||||
// Normalize list to an array
|
||||
@@ -181,7 +182,11 @@ Plugin.normalizeNames = function(names) {
|
||||
};
|
||||
|
||||
// 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
|
||||
@@ -198,7 +203,7 @@ Plugin.fromList = function(names, root, generator) {
|
||||
|
||||
// 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 ...
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
var _ = require("lodash");
|
||||
|
||||
var kramed = require('kramed');
|
||||
var textRenderer = require('kramed-text-renderer');
|
||||
|
||||
|
||||
+52
-42
@@ -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,20 +118,17 @@ 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) || '/',
|
||||
// Project's include folder
|
||||
includes_dir: path.join(that.options.input, '_includes'),
|
||||
// Output directory
|
||||
outdir: path.dirname(_input) || '/',
|
||||
// Templating variables
|
||||
variables: that.options.variables,
|
||||
});
|
||||
})
|
||||
.then(function(sections) {
|
||||
@@ -150,10 +138,30 @@ Generator.prototype.convertFile = function(content, _input) {
|
||||
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) || ".";
|
||||
|
||||
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 +169,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 +246,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
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
var _ = require('lodash');
|
||||
var fs = require('graceful-fs');
|
||||
var path = require('path');
|
||||
|
||||
|
||||
// Include helper
|
||||
function importInclude(name, paths) {
|
||||
return paths
|
||||
.map(function(folder) {
|
||||
// Try including snippet from FS
|
||||
try {
|
||||
var fname = path.join(folder, name);
|
||||
// Trim trailing newlines/space of imported snippets
|
||||
return fs.readFileSync(fname, 'utf8').trimRight();
|
||||
} catch(err) {}
|
||||
})
|
||||
.filter(Boolean)[0];
|
||||
}
|
||||
|
||||
function includer(ctx, folders) {
|
||||
return function(key) {
|
||||
key = key.trim();
|
||||
return ctx[key] || importInclude(key, folders);
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = function(markdown, folder, ctx) {
|
||||
// List of folders to search for includes
|
||||
var folders = [];
|
||||
|
||||
// Handle folder arg (string or array)
|
||||
if(_.isString(folder)) {
|
||||
folders = [folder];
|
||||
} else if(_.isArray(folder)) {
|
||||
folders = folder;
|
||||
}
|
||||
|
||||
// variable context
|
||||
ctx = ctx || {};
|
||||
|
||||
// Memoized include function (to cache lookups)
|
||||
var _include = _.memoize(includer(ctx, folders));
|
||||
|
||||
return markdown.replace(/{{([\s\S]+?)}}/g, function(match, key) {
|
||||
// If fails leave content as is
|
||||
return _include(key) || match;
|
||||
});
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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
@@ -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)) {
|
||||
|
||||
+5
-10
@@ -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,7 +51,7 @@ function parsePage(src, options) {
|
||||
options = options || {};
|
||||
|
||||
// Lex if not already lexed
|
||||
return (_.isArray(src) ? src : lex(src))
|
||||
return (_.isArray(src) ? src : lex(include(src, [options.dir, options.includes_dir], options.variables)))
|
||||
.map(function(section) {
|
||||
// Transform given type
|
||||
if(section.type === 'exercise') {
|
||||
@@ -74,20 +74,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,
|
||||
}
|
||||
|
||||
+8
-20
@@ -1,8 +1,6 @@
|
||||
var url = require('url');
|
||||
var inherits = require('util').inherits;
|
||||
var links = require('../utils').links;
|
||||
var codeInclude = require('./code_include');
|
||||
|
||||
|
||||
var path = require('path');
|
||||
|
||||
@@ -56,20 +54,9 @@ GitBookRenderer.prototype.link = function(href, title, text) {
|
||||
var o = this._extra_options;
|
||||
|
||||
// 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) && path.extname(parsed.path) == ".md") {
|
||||
_href = links.toAbsolute(_href, o.dir || "./", o.outdir || "./");
|
||||
_href = _href.replace(".md", ".html");
|
||||
}
|
||||
|
||||
// Generate HTML for link
|
||||
@@ -118,8 +105,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 +128,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
@@ -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()
|
||||
|
||||
+7
-5
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "gitbook",
|
||||
"version": "1.0.0",
|
||||
"version": "1.2.0",
|
||||
"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,17 @@
|
||||
"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",
|
||||
"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",
|
||||
|
||||
Vendored
+120
@@ -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**.
|
||||
|
||||

|
||||
|
||||
|
||||
### 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
|
||||
+8
-28
@@ -78,19 +78,6 @@ 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
|
||||
@@ -104,21 +91,6 @@ 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() {
|
||||
@@ -150,3 +122,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');
|
||||
});
|
||||
});
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -31,6 +31,8 @@ define([
|
||||
return d.promise();
|
||||
}
|
||||
|
||||
|
||||
|
||||
var pregQuote = function( str ) {
|
||||
// http://kevin.vanzonneveld.net
|
||||
// + original by: booeyOH
|
||||
@@ -56,15 +58,47 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
var matchText = function(node, regex, callback, excludeElements) {
|
||||
excludeElements || (excludeElements = ['script', 'style', 'iframe', 'canvas']);
|
||||
var child = node.firstChild;
|
||||
|
||||
if (!child) return;
|
||||
|
||||
do {
|
||||
switch (child.nodeType) {
|
||||
case 1:
|
||||
if (excludeElements.indexOf(child.tagName.toLowerCase()) > -1) {
|
||||
continue;
|
||||
}
|
||||
matchText(child, regex, callback, excludeElements);
|
||||
break;
|
||||
case 3:
|
||||
child.data.replace(regex, function(all) {
|
||||
var args = [].slice.call(arguments),
|
||||
offset = args[args.length - 2],
|
||||
newTextNode = child.splitText(offset);
|
||||
|
||||
newTextNode.data = newTextNode.data.substr(all.length);
|
||||
callback.apply(window, [child].concat(args));
|
||||
child = newTextNode;
|
||||
});
|
||||
break;
|
||||
}
|
||||
} while (child = child.nextSibling);
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
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);
|
||||
matchText($el.get(0), r, function(node, match, offset) {
|
||||
var span = document.createElement("span");
|
||||
span.className = "glossary-term";
|
||||
span.textContent = match;
|
||||
span.setAttribute("data-glossary-term", term.id);
|
||||
span.setAttribute("title", term.description);
|
||||
node.parentNode.insertBefore(span, node.nextSibling);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -18,11 +18,6 @@ define([
|
||||
var $book;
|
||||
$book = state.$book;
|
||||
|
||||
if (state.githubId) {
|
||||
// Initialize storage
|
||||
storage.setBaseKey(state.githubId);
|
||||
}
|
||||
|
||||
// Init sidebar
|
||||
sidebar.init();
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -256,7 +256,8 @@
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ol ul {
|
||||
ol ul,
|
||||
ul ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@@ -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 %}
|
||||
@@ -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>
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
<section 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 %}
|
||||
</section>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{% 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) }}
|
||||
</ol>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -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 %}
|
||||
Reference in New Issue
Block a user