mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-26 12:18:01 +00:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 |
+23
@@ -1,5 +1,28 @@
|
||||
# Release notes
|
||||
|
||||
## 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": [],
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -83,7 +83,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,15 @@ 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": "1000"
|
||||
};
|
||||
|
||||
if (format == "pdf") {
|
||||
@@ -55,7 +54,9 @@ Generator.prototype.finish = function() {
|
||||
"--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)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = [];
|
||||
@@ -20,7 +23,9 @@ BaseGenerator.prototype.callHook = function(name, data) {
|
||||
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 +79,4 @@ BaseGenerator.prototype.finish = function() {
|
||||
return Q.reject(new Error("Could not finish generation"));
|
||||
};
|
||||
|
||||
|
||||
|
||||
module.exports = BaseGenerator;
|
||||
module.exports = BaseGenerator;
|
||||
|
||||
@@ -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;
|
||||
|
||||
+10
-49
@@ -8,54 +8,23 @@ 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')
|
||||
);
|
||||
};
|
||||
|
||||
Generator.prototype.convertFile = function(content, input) {
|
||||
var that = this;
|
||||
var json = {
|
||||
path: input,
|
||||
progress: parse.progress(this.options.navigation, input)
|
||||
};
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
Generator.prototype.finish = function() {
|
||||
var that = this;
|
||||
var basePath = ".";
|
||||
@@ -64,16 +33,6 @@ 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);
|
||||
})
|
||||
|
||||
// Copy cover
|
||||
.then(function() {
|
||||
@@ -82,11 +41,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');
|
||||
|
||||
|
||||
+30
-21
@@ -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)
|
||||
};
|
||||
@@ -131,14 +122,10 @@ Generator.prototype.convertFile = function(content, _input) {
|
||||
return parse.lex(page.content);
|
||||
})
|
||||
.then(function(lexed) {
|
||||
// Index page in search
|
||||
return that.indexPage(lexed, _output)
|
||||
.then(_.constant(lexed));
|
||||
})
|
||||
.then(function(lexed) {
|
||||
page.lexed = lexed;
|
||||
|
||||
// Get HTML generated sections
|
||||
return parse.page(lexed, {
|
||||
repo: that.options.githubId,
|
||||
dir: path.dirname(_input) || '/',
|
||||
outdir: path.dirname(_input) || '/',
|
||||
});
|
||||
@@ -150,10 +137,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 +168,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 +245,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
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
+7
-16
@@ -56,20 +56,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 +107,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);
|
||||
|
||||
+7
-5
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "gitbook",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.1",
|
||||
"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.2.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
@@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -3,30 +3,55 @@
|
||||
@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 {
|
||||
.book-chapter {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.question {
|
||||
margin-top: 0.4cm;
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 %}
|
||||
+18
-69
@@ -1,87 +1,36 @@
|
||||
{% extends "layout.html" %}
|
||||
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
{% 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 %}
|
||||
|
||||
|
||||
{% macro articleContent(content) %}
|
||||
{% 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" %}
|
||||
<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>
|
||||
{% include "./includes/page/exercise.html" with {section: section} %}
|
||||
{% 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>
|
||||
{% include "./includes/page/quiz.html" with {section: section} %}
|
||||
{% 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>
|
||||
</section>
|
||||
{% 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>
|
||||
{% if progress.current.next and progress.current.next.path %}
|
||||
<a href="{{ basePath }}/{{ progress.current.next.path|mdLink }}" />
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block javascript %}{% endblock %}
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
{% 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,7 +14,7 @@
|
||||
{% 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 }}">
|
||||
<div class="book" data-level="{{ progress.current.level }}" data-basepath="{{ basePath }}" data-revision="{{ revision }}">
|
||||
{% include "includes/book/summary.html" %}
|
||||
<div class="book-body">
|
||||
<div class="body-inner">
|
||||
|
||||
Reference in New Issue
Block a user