mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-26 12:18:01 +00:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 65319e8fd6 | |||
| 55db44a3b4 | |||
| 13b6045300 | |||
| b7ff879c31 | |||
| 57ade87fe1 | |||
| 907e9ea5a7 | |||
| 215488dc45 | |||
| a195b3eac2 | |||
| 77c6f0e967 | |||
| f0e18afec1 | |||
| b6d11d88b7 | |||
| c803270af8 | |||
| 5d51263a58 | |||
| 7e30720b2c | |||
| 771c5e8b68 | |||
| 8f53afab6b | |||
| 0ad0403d41 | |||
| 2515c1dd73 | |||
| 7dfcd102dd | |||
| a91245d575 | |||
| 5f207f5e14 | |||
| 718692de3b | |||
| 9856a51255 | |||
| 58eaaf5514 | |||
| c0ae0138f8 | |||
| b1b9dc5b0a | |||
| f85c5a6498 | |||
| 32eafefc45 | |||
| 336999a34e | |||
| 1bf5f3b984 | |||
| c76d070237 | |||
| 8c7da1351f | |||
| 34588c6092 | |||
| 9f00bf8060 | |||
| f4a17b182f | |||
| 5c3b345fa6 | |||
| 46321fffd9 |
+2
-2
@@ -24,8 +24,8 @@ module.exports = function (grunt) {
|
||||
optimization: 2
|
||||
},
|
||||
files: {
|
||||
"theme/assets/style.css": "theme/stylesheets/main.less",
|
||||
"theme/assets/print.css": "theme/stylesheets/print.less"
|
||||
"theme/assets/style.css": "theme/stylesheets/website.less",
|
||||
"theme/assets/print.css": "theme/stylesheets/ebook.less"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -31,26 +31,36 @@ Options for commands `build` and `serve` are:
|
||||
|
||||
```
|
||||
-o, --output <directory> Path to output directory, defaults to ./_book
|
||||
-f, --format <name> Change generation format, defaults to site, availables are: site, page, pdf, json
|
||||
--config <config file> Configuration file to use, defaults to book.json
|
||||
-f, --format <name> Change generation format, defaults to site, availables are: site, page, ebook, json
|
||||
--config <config file> Configuration file to use, defaults to book.js or book.json
|
||||
```
|
||||
|
||||
GitBook loads the default configuration from a `book.json` file in the repository if it exists.
|
||||
|
||||
Here are the options that can be stored in this file:
|
||||
|
||||
```
|
||||
```js
|
||||
{
|
||||
// Folders to use for output (caution: it overrides the value from the command line)
|
||||
// Folders to use for output
|
||||
// Caution: it overrides the value from the command line
|
||||
// It's not advised this option in the book.json
|
||||
"output": null,
|
||||
|
||||
// Generator to use for building (caution: it overrides the value from the command line)
|
||||
// Generator to use for building
|
||||
// Caution: it overrides the value from the command line
|
||||
// It's not advised this option in the book.json
|
||||
"generator": "site",
|
||||
|
||||
// Book title and description (defaults are extracted from the README)
|
||||
"title": null,
|
||||
"description": null,
|
||||
|
||||
// For ebook format, the extension to use for generation (default is detected from output extension)
|
||||
// "epub", "pdf", "mobi"
|
||||
// Caution: it overrides the value from the command line
|
||||
// It's not advised this option in the book.json
|
||||
"extension": null,
|
||||
|
||||
// GitHub information (defaults are extracted using git)
|
||||
"github": null,
|
||||
"githubHost": "https://github.com/",
|
||||
@@ -88,6 +98,32 @@ Here are the options that can be stored in this file:
|
||||
"facebook": null,
|
||||
"twitter": null
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// Options for PDF generation
|
||||
"pdf": {
|
||||
// Add toc at the end of the file
|
||||
"toc": true,
|
||||
|
||||
// Add page numbers to the bottom of every page
|
||||
"pageNumbers": false,
|
||||
|
||||
// Font size for the fiel content
|
||||
"fontSize": 12,
|
||||
|
||||
// Paper size for the pdf
|
||||
// Choices are [u’a0’, u’a1’, u’a2’, u’a3’, u’a4’, u’a5’, u’a6’, u’b0’, u’b1’, u’b2’, u’b3’, u’b4’, u’b5’, u’b6’, u’legal’, u’letter’]
|
||||
"paperSize": "a4",
|
||||
|
||||
// Margin (in pts)
|
||||
// Note: 72 pts equals 1 inch
|
||||
"margin": {
|
||||
"right": 62,
|
||||
"left": 62,
|
||||
"top": 36,
|
||||
"bottom": 36
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -99,8 +135,7 @@ You can publish your books to our index by visiting [GitBook.io](http://www.gitb
|
||||
GitBook can generate your book in the following formats:
|
||||
|
||||
* **Static Website**: This is the default format. It generates a complete interactive static website that can be, for example, hosted on GitHub Pages.
|
||||
* **PDF**: A complete PDF book with exercise solutions at the end of the book. Generate this format using: ```gitbook pdf ./myrepo```. You need to have [gitbook-pdf](https://github.com/GitbookIO/gitbook-pdf) installed.
|
||||
* **eBook**: A complete eBook with exercise solutions at the end of the book. Generate this format using: ```gitbook ebook ./myrepo```. You need to have [ebook-convert](http://manual.calibre-ebook.com/cli/ebook-convert.html) installed.
|
||||
* **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```.
|
||||
|
||||
@@ -217,3 +252,4 @@ Plugins can used to extend your book's functionality. Read [GitbookIO/plugin](ht
|
||||
* [Revealable sections](https://github.com/mrpotes/gitbook-plugin-reveal): Reveal sections of the page using buttons made from the first title in each section
|
||||
* [Markdown within HTML](https://github.com/mrpotes/gitbook-plugin-nestedmd): Process markdown within HTML blocks - allows custom layout options for individual pages
|
||||
* [Bootstrap JavaScript plugins](https://github.com/mrpotes/gitbook-plugin-bootstrapjs): Use the [Bootstrap JavaScript plugins](http://getbootstrap.com/javascript) in your online GitBook
|
||||
* [Piwik Open Analytics](https://github.com/emmanuel-keller/gitbook-plugin-piwik): Piwik Open Analytics tracking for your book
|
||||
|
||||
+9
-3
@@ -12,10 +12,15 @@ var buildCommand = function(command) {
|
||||
return command
|
||||
.option('-o, --output <directory>', 'Path to output directory, defaults to ./_book')
|
||||
.option('-f, --format <name>', 'Change generation format, defaults to site, availables are: '+_.keys(generators).join(", "))
|
||||
.option('--config <config file>', 'Configuration file to use, defualt to book.json')
|
||||
.option('--config <config file>', 'Configuration file to use, defaults to book.js or book.json')
|
||||
};
|
||||
|
||||
|
||||
var buildEbookCommand = function(command) {
|
||||
return buildCommand(command)
|
||||
.option('-c, --cover <path>', 'Cover image, default is cover.jpg if exists');
|
||||
};
|
||||
|
||||
var makeBuildFunc = function(converter) {
|
||||
return function(dir, options) {
|
||||
dir = dir || process.cwd();
|
||||
@@ -31,7 +36,7 @@ var makeBuildFunc = function(converter) {
|
||||
})
|
||||
)
|
||||
.then(function(output) {
|
||||
console.log("Successfuly built !");
|
||||
console.log("Successfully built!");
|
||||
return output;
|
||||
}, utils.logError)
|
||||
.fail(function() {
|
||||
@@ -43,5 +48,6 @@ var makeBuildFunc = function(converter) {
|
||||
module.exports = {
|
||||
folder: makeBuildFunc(generate.folder),
|
||||
file: makeBuildFunc(generate.file),
|
||||
command: buildCommand
|
||||
command: buildCommand,
|
||||
commandEbook: buildEbookCommand
|
||||
};
|
||||
|
||||
+14
-9
@@ -80,24 +80,29 @@ build.command(prog.command('serve [source_dir]'))
|
||||
generate();
|
||||
});
|
||||
|
||||
build.command(prog.command('pdf [source_dir]'))
|
||||
build.commandEbook(prog.command('pdf [source_dir]'))
|
||||
.description('Build a gitbook as a PDF')
|
||||
.option('-pf, --paperformat <format>', 'PDF paper format (default is A4): "5in*7.5in", "10cm*20cm", "A4", "Letter"')
|
||||
.action(function(dir, options) {
|
||||
build.file(dir, _.extend(options, {
|
||||
extension: "pdf",
|
||||
format: "pdf"
|
||||
format: "ebook"
|
||||
}));
|
||||
});
|
||||
|
||||
build.command(prog.command('ebook [source_dir]'))
|
||||
.description('Build a gitbook as a eBook')
|
||||
.option('-c, --cover <path>', 'Cover image, default is cover.jpg if exists')
|
||||
build.commandEbook(prog.command('epub [source_dir]'))
|
||||
.description('Build a gitbook as a ePub book')
|
||||
.action(function(dir, options) {
|
||||
var ext = options.output ? path.extname(options.output) : "epub";
|
||||
|
||||
build.file(dir, _.extend(options, {
|
||||
extension: ext,
|
||||
extension: "epub",
|
||||
format: "ebook"
|
||||
}));
|
||||
});
|
||||
|
||||
build.commandEbook(prog.command('mobi [source_dir]'))
|
||||
.description('Build a gitbook as a Mobi book')
|
||||
.action(function(dir, options) {
|
||||
build.file(dir, _.extend(options, {
|
||||
extension: "mobi",
|
||||
format: "ebook"
|
||||
}));
|
||||
});
|
||||
|
||||
+37
-15
@@ -7,6 +7,7 @@ var exec = require('child_process').exec;
|
||||
var fs = require("fs");
|
||||
var parse = require("../../parse");
|
||||
var BaseGenerator = require("../page");
|
||||
var stringUtils = require("../../utils/string");
|
||||
|
||||
/*
|
||||
* This generator inherits from the single page generator
|
||||
@@ -14,11 +15,6 @@ var BaseGenerator = require("../page");
|
||||
*/
|
||||
var Generator = function() {
|
||||
BaseGenerator.apply(this, arguments);
|
||||
|
||||
// Options for eBook generation
|
||||
this.options = _.defaults(this.options, {
|
||||
extension: "epub"
|
||||
});
|
||||
};
|
||||
util.inherits(Generator, BaseGenerator);
|
||||
|
||||
@@ -28,27 +24,53 @@ Generator.prototype.finish = function() {
|
||||
return BaseGenerator.prototype.finish.apply(this)
|
||||
.then(function() {
|
||||
var d = Q.defer();
|
||||
var format = that.options.extension || path.extname(that.options.output).replace("\.", "") || "pdf";
|
||||
|
||||
if (!that.options.cover && fs.existsSync(path.join(that.options.output, "cover.jpg"))) {
|
||||
that.options.cover = path.join(that.options.output, "cover.jpg");
|
||||
}
|
||||
|
||||
var _options = {
|
||||
"--cover": that.options.cover
|
||||
"--cover": that.options.cover,
|
||||
"--title": that.options.title,
|
||||
"--comments": that.options.description,
|
||||
"--authors": that.options.author,
|
||||
"--level1-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-1 ')]",
|
||||
"--level2-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-2 ')]",
|
||||
"--level3-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-3 ')]"
|
||||
};
|
||||
|
||||
if (format == "pdf") {
|
||||
var pdfOptions = _.defaults(that.options.pdf || {}, {
|
||||
"fontSize": 12,
|
||||
"toc": true,
|
||||
"pageNumbers": false,
|
||||
"paperSize": "a4",
|
||||
"margin": {
|
||||
"right": 62,
|
||||
"left": 62,
|
||||
"top": 36,
|
||||
"bottom": 36
|
||||
}
|
||||
});
|
||||
|
||||
_.extend(_options, {
|
||||
"--margin-left": String(pdfOptions.margin.left),
|
||||
"--margin-right": String(pdfOptions.margin.right),
|
||||
"--margin-top": String(pdfOptions.margin.top),
|
||||
"--margin-bottom": String(pdfOptions.margin.bottom),
|
||||
"--pdf-add-toc": Boolean(pdfOptions.toc),
|
||||
"--pdf-default-font-size": String(pdfOptions.fontSize),
|
||||
"--paper-size": String(pdfOptions.paperSize),
|
||||
"--pdf-page-numbers": Boolean(pdfOptions.pageNumbers)
|
||||
});
|
||||
}
|
||||
|
||||
var command = [
|
||||
"ebook-convert",
|
||||
path.join(that.options.output, "index.html"),
|
||||
path.join(that.options.output, "index."+that.options.extension),
|
||||
_.chain(_options)
|
||||
.map(function(value, key) {
|
||||
if (value == null) return null;
|
||||
return key+"="+value;
|
||||
})
|
||||
.compact()
|
||||
.value()
|
||||
.join(" ")
|
||||
path.join(that.options.output, "index."+format),
|
||||
stringUtils.optionsToShellArgs(_options)
|
||||
].join(" ");
|
||||
|
||||
exec(command, function (error, stdout, stderr) {
|
||||
|
||||
+26
-20
@@ -11,7 +11,6 @@ var Plugin = require("./plugin");
|
||||
var generators = {
|
||||
"site": require("./site"),
|
||||
"page": require("./page"),
|
||||
"pdf": require("./pdf"),
|
||||
"ebook": require("./ebook"),
|
||||
"json": require("./json")
|
||||
};
|
||||
@@ -26,13 +25,23 @@ var containsFiles = function(dir, files) {
|
||||
.then(_.all);
|
||||
};
|
||||
|
||||
// TEst if generator exists
|
||||
var checkGenerator = function(options) {
|
||||
if (!generators[options.generator]) {
|
||||
return Q.reject(new Error("Invalid generator (availables are: "+_.keys(generators).join(", ")+")"));
|
||||
}
|
||||
return Q();
|
||||
};
|
||||
|
||||
// Create the generator and load plugins
|
||||
var loadGenerator = function(options) {
|
||||
var generator = new generators[options.generator](options);
|
||||
return checkGenerator(options)
|
||||
.then(function() {
|
||||
var generator = new generators[options.generator](options);
|
||||
|
||||
return generator.loadPlugins()
|
||||
.then(_.constant(generator));
|
||||
return generator.loadPlugins()
|
||||
.then(_.constant(generator));
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -45,7 +54,7 @@ var generate = function(options) {
|
||||
"output": null,
|
||||
|
||||
// Config file (relative to input)
|
||||
"configFile": "book.json",
|
||||
"configFile": "book",
|
||||
|
||||
// Output generator
|
||||
"generator": "site",
|
||||
@@ -79,27 +88,20 @@ var generate = function(options) {
|
||||
return Q.reject(new Error("Need option input (book input directory)"));
|
||||
}
|
||||
|
||||
// Ensure generator exists
|
||||
if (!generators[options.generator]) {
|
||||
return Q.reject(new Error("Invalid generator (availables are: "+_.keys(generators).join(", ")));
|
||||
}
|
||||
|
||||
// Check files to get folder type (book, multilanguage book or neither)
|
||||
return Q()
|
||||
return checkGenerator(options)
|
||||
|
||||
// Read config file
|
||||
.then(function() {
|
||||
return fs.readFile(path.resolve(options.input, options.configFile))
|
||||
.then(function(_config) {
|
||||
// Extend current config
|
||||
_config = JSON.parse(_config);
|
||||
_config = _.omit(_config, 'input', 'configFile', 'defaultsPlugins');
|
||||
try {
|
||||
var _config = require(path.resolve(options.input, options.configFile));
|
||||
|
||||
_.extend(options, _config);
|
||||
}, function() {
|
||||
_.extend(options, _.omit(_config, 'input', 'configFile', 'defaultsPlugins'));
|
||||
}
|
||||
catch(err) {
|
||||
// No config file: not a big deal
|
||||
return Q();
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
// Read readme
|
||||
@@ -134,8 +136,12 @@ var generateMultiLang = function(options) {
|
||||
var langsSummary;
|
||||
options.output = options.output || path.join(options.input, "_book");
|
||||
|
||||
return checkGenerator(options)
|
||||
|
||||
// Multi-languages book
|
||||
return fs.readFile(path.join(options.input, "LANGS.md"), "utf-8")
|
||||
.then(function() {
|
||||
return fs.readFile(path.join(options.input, "LANGS.md"), "utf-8")
|
||||
})
|
||||
|
||||
// Clean output folder
|
||||
.then(function(_langsSummary) {
|
||||
|
||||
@@ -18,6 +18,16 @@ swig.setFilter('code', function(code, lang) {
|
||||
}
|
||||
});
|
||||
|
||||
// Convert a level into a deep level
|
||||
swig.setFilter('lvl', function(lvl) {
|
||||
return lvl.split(".").length;
|
||||
});
|
||||
|
||||
// Join path
|
||||
swig.setFilter('pathJoin', function(base, _path) {
|
||||
return path.join(base, _path);
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* This generator will generate a simple index.html which can be converted as a PDF
|
||||
@@ -61,16 +71,24 @@ Generator.prototype.convertFile = function(content, input) {
|
||||
});
|
||||
};
|
||||
|
||||
// Generate languages index
|
||||
Generator.prototype.langsIndex = function(langs) {
|
||||
return Q();
|
||||
};
|
||||
|
||||
Generator.prototype.finish = function() {
|
||||
var that = this;
|
||||
var basePath = ".";
|
||||
var output = path.join(this.options.output, "index.html");
|
||||
|
||||
var progress = parse.progress(this.options.navigation, "README.md");
|
||||
|
||||
return Q()
|
||||
// Generate html
|
||||
.then(function(pages) {
|
||||
return that._writeTemplate(that.template, {
|
||||
pages: that.pages,
|
||||
progress: progress,
|
||||
|
||||
basePath: basePath,
|
||||
staticBase: path.join(basePath, "gitbook"),
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
var util = require("util");
|
||||
var path = require("path");
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
var exec = require('child_process').exec;
|
||||
|
||||
var fs = require("../fs");
|
||||
var parse = require("../../parse");
|
||||
var BaseGenerator = require("../page");
|
||||
|
||||
/*
|
||||
* This generator inherits from the single page generator
|
||||
* and convert the page output to pdf using gitbook-pdf
|
||||
*/
|
||||
var Generator = function() {
|
||||
BaseGenerator.apply(this, arguments);
|
||||
|
||||
// Options for PDF generation
|
||||
this.options = _.defaults(this.options, {
|
||||
paperformat: "A4"
|
||||
});
|
||||
};
|
||||
util.inherits(Generator, BaseGenerator);
|
||||
|
||||
Generator.prototype.finish = function() {
|
||||
var that = this;
|
||||
|
||||
return BaseGenerator.prototype.finish.apply(this)
|
||||
.then(function() {
|
||||
var d = Q.defer();
|
||||
|
||||
var command = [
|
||||
"gitbook-pdf",
|
||||
"generate",
|
||||
path.join(that.options.output, "index.html"),
|
||||
path.join(that.options.output, "index.pdf"),
|
||||
"--format="+that.options.paperformat
|
||||
].join(" ");
|
||||
|
||||
exec(command, function (error, stdout, stderr) {
|
||||
if (error) {
|
||||
if (error.code == 127) {
|
||||
error.message = "Need to install gitbook-pdf using: npm install gitbook-pdf -g";
|
||||
} else {
|
||||
error.message = error.message + " "+stdout;
|
||||
}
|
||||
return d.reject(error);
|
||||
}
|
||||
d.resolve();
|
||||
});
|
||||
|
||||
return d.promise;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = Generator;
|
||||
@@ -163,6 +163,7 @@ Plugin.fromList = function(names, root) {
|
||||
resources.html = {}
|
||||
|
||||
_.each(plugins, function(plugin) {
|
||||
if (!plugin.infos.book || !plugin.infos.book.html) return;
|
||||
var html = plugin.infos.book.html || {};
|
||||
_.each(html, function(code, key) {
|
||||
if (!_.isFunction(code)) code = _.constant(code);
|
||||
@@ -200,7 +201,6 @@ Plugin.fromList = function(names, root) {
|
||||
|
||||
// Default plugins
|
||||
Plugin.defaults = [
|
||||
"mixpanel",
|
||||
"mathjax"
|
||||
];
|
||||
|
||||
|
||||
@@ -63,7 +63,6 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate)
|
||||
var that = this;
|
||||
|
||||
interpolate = interpolate || _.identity;
|
||||
|
||||
return Q()
|
||||
.then(function(sections) {
|
||||
return tpl(_.extend({
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
var _ = require("lodash");
|
||||
|
||||
function escapeShellArg(arg) {
|
||||
var ret = '';
|
||||
|
||||
ret = arg.replace(/"/g, '\\"');
|
||||
|
||||
return "\"" + ret + "\"";
|
||||
}
|
||||
|
||||
function optionsToShellArgs(options) {
|
||||
return _.chain(options)
|
||||
.map(function(value, key) {
|
||||
if (value == null || value == false) return null;
|
||||
if (value == true) return key;
|
||||
return key+"="+escapeShellArg(value);
|
||||
})
|
||||
.compact()
|
||||
.value()
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
escapeShellArg: escapeShellArg,
|
||||
optionsToShellArgs: optionsToShellArgs
|
||||
};
|
||||
+1
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gitbook",
|
||||
"version": "0.4.7",
|
||||
"version": "0.5.0",
|
||||
"homepage": "http://www.gitbook.io/",
|
||||
"description": "Library and cmd utility to generate GitBooks",
|
||||
"main": "lib/index.js",
|
||||
@@ -22,7 +22,6 @@
|
||||
"resolve": "0.6.3",
|
||||
"tiny-lr-fork": "0.0.5",
|
||||
"gitbook-plugin": "0.0.2",
|
||||
"gitbook-plugin-mixpanel": "0.0.2",
|
||||
"gitbook-plugin-mathjax": "0.0.3",
|
||||
"gitbook-plugin-livereload": "0.0.1"
|
||||
},
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -92,7 +92,7 @@ define([
|
||||
// Instantiate font state object
|
||||
fontState = storage.get("fontState", {
|
||||
size: config.size || 1,
|
||||
family: FAMILY[config.family || "serif"],
|
||||
family: FAMILY[config.family || "sans"],
|
||||
theme: THEMES[config.theme || "white"]
|
||||
});
|
||||
fontState.save = function(){
|
||||
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
@import "vendors/bootstrap/bootstrap.less";
|
||||
@import "mixins.less";
|
||||
|
||||
@import "ebook/variables.less";
|
||||
@import "ebook/highlight.less";
|
||||
|
||||
.book-chapter {
|
||||
display: none;
|
||||
}
|
||||
|
||||
article {
|
||||
page-break-after: always;
|
||||
}
|
||||
|
||||
h1 { font-size: floor(@font-size-base * 2.15); }
|
||||
h2 { font-size: floor(@font-size-base * 1.70); }
|
||||
h3 { font-size: ceil(@font-size-base * 1.25); }
|
||||
h4 { font-size: ceil(@font-size-base * 1); }
|
||||
h5 { font-size: ceil(@font-size-base * 0.85); }
|
||||
h6 { font-size: ceil(@font-size-base * 0.65); }
|
||||
|
||||
pre, blockquote {
|
||||
border: 1px solid #999;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100% !important;
|
||||
page-break-inside: avoid;
|
||||
margin: 0px auto;
|
||||
}
|
||||
|
||||
.exercise {
|
||||
margin: 1cm 0cm;
|
||||
padding: 0.4cm;
|
||||
page-break-inside: avoid;
|
||||
|
||||
border: 3px solid #ddd;
|
||||
|
||||
.exercise-header {
|
||||
margin-bottom: 0.4cm;
|
||||
padding-bottom: 0.2cm;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
@font-size-base: 13px;
|
||||
@@ -4,4 +4,4 @@
|
||||
&:hover, &:focus {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
@import "vendors/bootstrap/bootstrap.less";
|
||||
@import "vendors/fontawesome/font-awesome.less";
|
||||
|
||||
@import "mixins.less";
|
||||
@import "variables.less";
|
||||
@import "fonts.less";
|
||||
|
||||
@import "page/highlight.less";
|
||||
|
||||
|
||||
@font-size-base: 13px;
|
||||
|
||||
|
||||
* {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
-webkit-text-size-adjust: none;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
text-rendering: optimizeLegibility;
|
||||
font-smoothing: antialiased;
|
||||
font-family: @font-family-base;
|
||||
}
|
||||
|
||||
|
||||
h1, h2, h3 {
|
||||
page-break-after: avoid;
|
||||
page-break-before: auto;
|
||||
}
|
||||
|
||||
h1 { font-size: floor(@font-size-base * 2.15); }
|
||||
h2 { font-size: floor(@font-size-base * 1.70); }
|
||||
h3 { font-size: ceil(@font-size-base * 1.25); }
|
||||
h4 { font-size: ceil(@font-size-base * 1); }
|
||||
h5 { font-size: ceil(@font-size-base * 0.85); }
|
||||
h6 { font-size: ceil(@font-size-base * 0.65); }
|
||||
|
||||
pre, blockquote {
|
||||
border: 1px solid #999;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100% !important;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
section {
|
||||
page-break-after: always;
|
||||
|
||||
&#cover {
|
||||
padding: 3cm 0cm;
|
||||
text-align: center;
|
||||
|
||||
h1 {
|
||||
font-size: 1.5cm;
|
||||
}
|
||||
}
|
||||
|
||||
&#summary {
|
||||
margin: 1.5cm;
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style: none;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
margin-left: 1cm;
|
||||
}
|
||||
|
||||
> ol {
|
||||
> li {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
article {
|
||||
margin-bottom: 1.5cm;
|
||||
|
||||
&.new-chapter {
|
||||
page-break-after: always;
|
||||
font-size: 0.6cm;
|
||||
text-align: center;
|
||||
padding: 3cm 0cm;
|
||||
|
||||
h1 {
|
||||
font-size: floor(@font-size-base * 2.7);
|
||||
}
|
||||
}
|
||||
|
||||
.exercise {
|
||||
margin: 1cm 0cm;
|
||||
padding: 0.4cm;
|
||||
page-break-inside: avoid;
|
||||
|
||||
border: 3px solid #ddd;
|
||||
|
||||
.exercise-header {
|
||||
margin-bottom: 0.4cm;
|
||||
padding-bottom: 0.2cm;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,19 +2,19 @@
|
||||
@import "vendors/fontawesome/font-awesome.less";
|
||||
|
||||
@import "mixins.less";
|
||||
@import "variables.less";
|
||||
@import "fonts.less";
|
||||
|
||||
@import "book/languages.less";
|
||||
@import "book/header.less";
|
||||
@import "book/summary.less";
|
||||
@import "book/font-settings.less";
|
||||
@import "book/body.less";
|
||||
@import "book/exercise.less";
|
||||
@import "book/quiz.less";
|
||||
@import "book/markdown.less";
|
||||
@import "book/progress.less";
|
||||
@import "book/navigation.less";
|
||||
@import "website/fonts.less";
|
||||
@import "website/variables.less";
|
||||
@import "website/languages.less";
|
||||
@import "website/header.less";
|
||||
@import "website/summary.less";
|
||||
@import "website/font-settings.less";
|
||||
@import "website/body.less";
|
||||
@import "website/exercise.less";
|
||||
@import "website/quiz.less";
|
||||
@import "website/markdown.less";
|
||||
@import "website/progress.less";
|
||||
@import "website/navigation.less";
|
||||
|
||||
* {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
Executable → Regular
+8
-56
@@ -1,15 +1,3 @@
|
||||
@font-face {
|
||||
font-family: 'EB Garamond';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('EB Garamond 12 Regular'),url('@{FontPath}/ebgaramond/400.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'EB Garamond';
|
||||
font-style: italic;
|
||||
font-weight: 400i;
|
||||
src: local('EB Garamond 12 Italic'),url('@{FontPath}/ebgaramond/400i.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Merriweather';
|
||||
font-style: normal;
|
||||
@@ -19,7 +7,7 @@
|
||||
@font-face {
|
||||
font-family: 'Merriweather';
|
||||
font-style: italic;
|
||||
font-weight: 250i;
|
||||
font-weight: 250;
|
||||
src: local('Merriweather Light Italic'),url('@{FontPath}/merriweather/250i.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
@@ -31,21 +19,9 @@
|
||||
@font-face {
|
||||
font-family: 'Merriweather';
|
||||
font-style: italic;
|
||||
font-weight: 400i;
|
||||
font-weight: 400;
|
||||
src: local('Merriweather Italic'),url('@{FontPath}/merriweather/400i.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Merriweather';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local(''),url('@{FontPath}/merriweather/600.woff') format('woff');
|
||||
}
|
||||
/*@font-face {
|
||||
font-family: 'Merriweather';
|
||||
font-style: italic;
|
||||
font-weight: 600i;
|
||||
src: local(''),url('@{FontPath}/merriweather/600i.woff') format('woff');
|
||||
}*/
|
||||
@font-face {
|
||||
font-family: 'Merriweather';
|
||||
font-style: normal;
|
||||
@@ -55,7 +31,7 @@
|
||||
@font-face {
|
||||
font-family: 'Merriweather';
|
||||
font-style: italic;
|
||||
font-weight: 700i;
|
||||
font-weight: 700;
|
||||
src: local('Merriweather Bold Italic'),url('@{FontPath}/merriweather/700i.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
@@ -67,33 +43,9 @@
|
||||
@font-face {
|
||||
font-family: 'Merriweather';
|
||||
font-style: italic;
|
||||
font-weight: 900i;
|
||||
font-weight: 900;
|
||||
src: local('Merriweather Heavy Italic'),url('@{FontPath}/merriweather/900i.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Anonymous Pro';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Anonymous Pro'),url('@{FontPath}/anonymouspro/400.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Anonymous Pro';
|
||||
font-style: italic;
|
||||
font-weight: 400i;
|
||||
src: local('Anonymous Pro Italic'),url('@{FontPath}/anonymouspro/400i.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Anonymous Pro';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Anonymous Pro Bold'),url('@{FontPath}/anonymouspro/700.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Anonymous Pro';
|
||||
font-style: italic;
|
||||
font-weight: 700i;
|
||||
src: local('Anonymous Pro Bold Italic'),url('@{FontPath}/anonymouspro/700i.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
@@ -103,7 +55,7 @@
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300i;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light Italic'),url('@{FontPath}/opensans/300i.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
@@ -115,7 +67,7 @@
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400i;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'),url('@{FontPath}/opensans/400i.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
@@ -127,7 +79,7 @@
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600i;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans Semibold Italic'),url('@{FontPath}/opensans/600i.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
@@ -139,6 +91,6 @@
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700i;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold Italic'),url('@{FontPath}/opensans/700i.woff') format('woff');
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
font-size: 0.85em;
|
||||
color: @header-color;
|
||||
background: @header-background;
|
||||
box-shadow: 0 1px 2px hsla(200,10%,80%,0.6);
|
||||
border-bottom: 1px solid @header-border;
|
||||
|
||||
.btn {
|
||||
display: block;
|
||||
@@ -11,7 +11,7 @@
|
||||
width: @sidebar-width;
|
||||
color: @sidebar-color;
|
||||
background: @sidebar-background;
|
||||
box-shadow: inset -1px 0 5px hsla(0, 0%, 0%, 0.1);
|
||||
border-right: 1px solid @sidebar-border;
|
||||
|
||||
.transition(all 0.5s ease);
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
margin: 7px 0;
|
||||
overflow: hidden;
|
||||
background: @sidebar-divider-color;
|
||||
box-shadow: 0 1px rgba(255,255,255,0.7);
|
||||
}
|
||||
|
||||
i.fa-check {
|
||||
Executable → Regular
+25
-23
@@ -7,6 +7,7 @@
|
||||
@header-height: 50px;
|
||||
@header-color: hsl(194, 5%, 52%);
|
||||
@header-background: white;
|
||||
@header-border: rgba(0, 0, 0, 0.07);
|
||||
@header-button-color: hsl(194, 5%, 52%);
|
||||
@header-button-hover-color: #151515;
|
||||
@header-button-hover-background: hsl(207, 15%, 95%);
|
||||
@@ -18,28 +19,29 @@
|
||||
@sidebar-width: 250px;
|
||||
@sidebar-breakpoint: 600px;
|
||||
@sidebar-color: hsl(207, 15%, 80%);
|
||||
@sidebar-background: hsl(207, 15%, 95%);
|
||||
@sidebar-background: @body-background;
|
||||
@sidebar-border: @header-border;
|
||||
|
||||
@sidebar-nested-padding: 20px;
|
||||
|
||||
@sidebar-search-padding: 6px;
|
||||
@sidebar-search-background: transparent;
|
||||
@sidebar-search-input-border-color: hsl(207, 15%, 80%);
|
||||
@sidebar-search-input-border-color: @sidebar-border;
|
||||
|
||||
@sidebar-divider-color: hsl(207, 15%, 85%);
|
||||
@sidebar-divider-color: @sidebar-border;
|
||||
|
||||
@sidebar-link-color: hsl(207, 15%, 50%);
|
||||
@sidebar-link-color: hsl(207, 15%, 25%);
|
||||
@sidebar-link-background: transparent;
|
||||
@sidebar-link-hover-color: hsl(207, 100%, 50%);
|
||||
@sidebar-link-hover-background: transparent;
|
||||
|
||||
@sidebar-icon-color: @bar-progress-background;
|
||||
@sidebar-link-completed: hsl(207, 15%, 25%);
|
||||
@sidebar-link-completed: @sidebar-link-color;
|
||||
@sidebar-link-completed-weight: normal;
|
||||
|
||||
// Page
|
||||
@page-color: black;
|
||||
@page-background: white;
|
||||
@page-color: black;
|
||||
@page-background: @body-background;
|
||||
|
||||
// Progress Bar
|
||||
@chapter-display: none;
|
||||
@@ -76,19 +78,19 @@
|
||||
@border-radius-small: 1px;
|
||||
|
||||
// Fonts
|
||||
@font-family-serif: Merriweather, "EB Garamond", Georgia, serif;
|
||||
@font-family-serif: "Merriweather", Georgia, serif;
|
||||
@font-family-sans: "Open Sans", "Clear Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
@font-family-base: @font-family-serif;
|
||||
|
||||
@font-family-base: @font-family-sans;
|
||||
@headings-font-family: inherit;
|
||||
|
||||
@FontPath: '@{staticPath}/fonts';
|
||||
@fa-font-path: "@{FontPath}/fontawesome";
|
||||
|
||||
@s-font-size: 1.2rem;
|
||||
@m-font-size: 1.4rem;
|
||||
@l-font-size: 1.6rem;
|
||||
@xl-font-size: 2.2rem;
|
||||
@xxl-font-size: 4.0rem;
|
||||
@s-font-size: 1.2rem;
|
||||
@m-font-size: 1.4rem;
|
||||
@l-font-size: 1.6rem;
|
||||
@xl-font-size: 2.2rem;
|
||||
@xxl-font-size: 4.0rem;
|
||||
|
||||
/*
|
||||
,--------.,--.
|
||||
@@ -116,15 +118,15 @@
|
||||
// Sidebar
|
||||
@sidebar-color-1: #AFA790;
|
||||
@sidebar-color-2: hsl(207, 15%, 80%);
|
||||
@sidebar-background-1: #F3EACB;
|
||||
@sidebar-background-2: #111111;
|
||||
@sidebar-background-1: @body-background-1;
|
||||
@sidebar-background-2: @body-background-2;
|
||||
|
||||
@sidebar-search-background-1: transparent;
|
||||
@sidebar-search-background-2: transparent;
|
||||
@sidebar-search-input-border-color-1: #D6CFBA;
|
||||
@sidebar-search-input-border-color-2: #c4cdd4;
|
||||
@sidebar-search-input-border-color-2: #000;
|
||||
|
||||
@sidebar-divider-color-1: hsl(207, 15%, 85%);
|
||||
@sidebar-divider-color-1: @sidebar-divider-color;
|
||||
@sidebar-divider-color-2: #1d1f21;
|
||||
|
||||
@sidebar-link-color-1: #877F6A;
|
||||
@@ -138,14 +140,14 @@
|
||||
|
||||
@sidebar-icon-color-1: @bar-progress-background;
|
||||
@sidebar-icon-color-2: @bar-progress-background;
|
||||
@sidebar-link-completed-1: hsl(207, 15%, 25%);
|
||||
@sidebar-link-completed-2: hsl(207, 15%, 25%);
|
||||
@sidebar-link-completed-1: @sidebar-link-color-1;
|
||||
@sidebar-link-completed-2: @sidebar-link-color-2;
|
||||
@sidebar-link-completed-weight-1: normal;
|
||||
@sidebar-link-completed-weight-2: normal;
|
||||
|
||||
// Page
|
||||
@page-color-1: #704214;
|
||||
@page-color-2: #a4b1b1;
|
||||
@page-color-1: #704214;
|
||||
@page-color-2: #a4b1b1;
|
||||
@page-background-1: #F3EACB;
|
||||
@page-background-2: #1d1f21;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<ul class="languages">
|
||||
{% for lang in langs %}
|
||||
<li>
|
||||
<a href="{{ basePath}}/{{ lang.path }}">{{ lang.title }}</a>
|
||||
<a href="{{ basePath}}/{{ lang.path|pathJoin("/index.html") }}">{{ lang.title }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
+35
-94
@@ -20,10 +20,10 @@
|
||||
</div>
|
||||
{% elif section.type == "quiz" %}
|
||||
<div class="quiz">
|
||||
<div class="exercise-header">Exercise #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
{% autoescape false %}{{ section.quiz.base }}{% endautoescape %}
|
||||
{% set exercise = exercise + 1 %}
|
||||
<div class="exercise-header">Exercise #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
{% autoescape false %}{{ section.quiz.base }}{% endautoescape %}
|
||||
{% set exercise = exercise + 1 %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
@@ -31,104 +31,45 @@
|
||||
|
||||
{% set exercise = 1 %}
|
||||
{% block content %}
|
||||
{# Cover #}
|
||||
<section id="cover">
|
||||
<h1>{{ title }}</h1>
|
||||
{% if githubId %}
|
||||
<h2>By <a href="{{ githubHost }}{{ githubAuthor }}">@{{ githubAuthor }}</a></h2>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
{# Summary #}
|
||||
<section id="summary">
|
||||
<h1>Table of Contents</h1>
|
||||
<ol>
|
||||
<li>
|
||||
<a href="#README.md">
|
||||
<h2>Introduction</h2>
|
||||
</a>
|
||||
</li>
|
||||
{% for item in summary.chapters %}
|
||||
<li>
|
||||
<h2><span>{{ item.level }}.</span> <a href="#{{ item.path }}">{{ item.title }}</a></h2>
|
||||
{% if item.articles.length > 0 %}
|
||||
<ol>
|
||||
{% for article in item.articles %}
|
||||
<li>
|
||||
<span>{{ article.level }}</span> <a href="#{{ article.path }}">{{ article.title }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
{# Pages content #}
|
||||
<section>
|
||||
<article id="README.md">
|
||||
{{ articleContent(pages["README.md"].content) }}
|
||||
</article>
|
||||
</section>
|
||||
{% for item in summary.chapters %}
|
||||
<section>
|
||||
<article id="{{ item.path }}" class="new-chapter">
|
||||
<h1>{{ item.title }}</h1>
|
||||
</article>
|
||||
|
||||
{% if pages[item.path] %}
|
||||
<article>
|
||||
{{ articleContent(pages[item.path].content) }}
|
||||
</article>
|
||||
{% endif %}
|
||||
|
||||
{% if item.articles.length > 0 %}
|
||||
{% for article in item.articles %}
|
||||
{% if pages[article.path] %}
|
||||
<article id="{{ article.path }}">
|
||||
{{ articleContent(pages[article.path].content) }}
|
||||
</article>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</section>
|
||||
{% for item in progress.chapters %}
|
||||
<article id="{{ article.path }}">
|
||||
<h1 class="book-chapter book-chapter-{{ item.level|lvl }}">{{ item.title }}</h1>
|
||||
{% if pages[item.path] %}
|
||||
{{ articleContent(pages[item.path].content) }}
|
||||
{% endif %}
|
||||
</article>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{# Exercise solutions #}
|
||||
{% if exercise > 1 %}
|
||||
{% set exercise = 1 %}
|
||||
<section>
|
||||
<article class="new-chapter">
|
||||
<h1>Exercise Solutions</h1>
|
||||
</article>
|
||||
<article>
|
||||
{% for item in summary.chapters %}
|
||||
{% for article in item.articles %}
|
||||
{% if pages[article.path] %}
|
||||
{% for section in pages[article.path].content %}
|
||||
{% if section.type == "exercise" %}
|
||||
<div class="exercise">
|
||||
<div class="exercise-header">Exercise #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
<pre><code>{% autoescape false %}{{ section.code.solution|code }}{% endautoescape %}</code></pre>
|
||||
{% set exercise = exercise + 1 %}
|
||||
</div>
|
||||
{% elif section.type == "quiz" %}
|
||||
<div class="quiz">
|
||||
<div class="exercise-header">Exercise #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
{% autoescape false %}{{ section.quiz.solution }}{% endautoescape %}
|
||||
{% set exercise = exercise + 1 %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<article>
|
||||
<h1 class="book-chapter book-chapter-1">Exercise Solutions</h1>
|
||||
|
||||
{% for item in progress.chapters %}
|
||||
{% if pages[item.path] %}
|
||||
{% for section in pages[item.path].content %}
|
||||
{% if section.type == "exercise" %}
|
||||
<div class="exercise">
|
||||
<div class="exercise-header">Exercise #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
<pre><code>{% autoescape false %}{{ section.code.solution|code }}{% endautoescape %}</code></pre>
|
||||
{% set exercise = exercise + 1 %}
|
||||
</div>
|
||||
{% elif section.type == "quiz" %}
|
||||
<div class="quiz">
|
||||
<div class="exercise-header">Exercise #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
{% autoescape false %}{{ section.quiz.solution }}{% endautoescape %}
|
||||
{% set exercise = exercise + 1 %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</article>
|
||||
</section>
|
||||
</article>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user