mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-29 05:29:02 +00:00
Compare commits
129 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 799815d85a | |||
| 2946246834 | |||
| 6d276d1e79 | |||
| 6a3a369b3e | |||
| 5d8afa2135 | |||
| ab3e6aa8ce | |||
| 822092736e | |||
| 7497acd2e3 | |||
| ae137885b8 | |||
| b964cc7009 | |||
| c2ea126c5f | |||
| b668dba479 | |||
| 2cae5a0310 | |||
| b6a0f3ff67 | |||
| fa00b52fb5 | |||
| 2999d90907 | |||
| b6696bcad2 | |||
| 728c87c525 | |||
| ace394ee2d | |||
| 076ee98f64 | |||
| 2e472654ca | |||
| d46bd14678 | |||
| b3e5b3d106 | |||
| b830ef5dca | |||
| 45326c4ad2 | |||
| 603b61e8c8 | |||
| efb066c8e3 | |||
| c30db427b1 | |||
| 23056fc85d | |||
| 5f1bfc0280 | |||
| b715901f92 | |||
| 38fea06cf9 | |||
| 4481b5f556 | |||
| 0e0d051f7c | |||
| a1b649a279 | |||
| e6bc0a0fb7 | |||
| 93e4016c09 | |||
| 20c741a248 | |||
| 54f530c7c3 | |||
| 9fdc5da22d | |||
| 99ce48b146 | |||
| 6a33ad9ef7 | |||
| ca94055adf | |||
| 36a67e4c91 | |||
| 69431e1bbb | |||
| a3fc4ee2b0 | |||
| e505fd6d54 | |||
| 93a3785e4c | |||
| a02de84812 | |||
| c0521f0ec7 | |||
| 83d8c16853 | |||
| 47e9506397 | |||
| b8a3d0e254 | |||
| 001d7ec44e | |||
| 5cb0ec447a | |||
| 67e7d77302 | |||
| 8cbd8b2b48 | |||
| 7c404dbf96 | |||
| c4a67b6f2f | |||
| 3b70347625 | |||
| bb0a7935e9 | |||
| 8f4b799d3c | |||
| 931e0c66c9 | |||
| 0212084862 | |||
| 9abbabbce3 | |||
| ac610487e8 | |||
| a9a0f89892 | |||
| 305f221077 | |||
| 53514f2602 | |||
| 00327778cb | |||
| 37fadec787 | |||
| ada11d0b59 | |||
| 4c90fdb7b2 | |||
| 98c133df3c | |||
| f86c27b090 | |||
| b5b8ed72a5 | |||
| 5beff86548 | |||
| dd395edae1 | |||
| d1c1d7b75b | |||
| 4a380e9279 | |||
| 878c105def | |||
| 74d628676b | |||
| 6fdd98ec3a | |||
| 2ed1be3533 | |||
| 0ac04e1584 | |||
| 2a38d33d8a | |||
| fb14caf5b3 | |||
| cf51772f54 | |||
| cc8516df83 | |||
| c6a624bc6b | |||
| 8be8b825ee | |||
| 56eb4a2897 | |||
| 7a461eaa61 | |||
| fd64c05809 | |||
| 6bce93d893 | |||
| c9db068227 | |||
| 8a6d36ac4d | |||
| 64622c5945 | |||
| 837813edb0 | |||
| 0d9d62f1a9 | |||
| f7e9abf984 | |||
| dd0579b60d | |||
| b5bb953e47 | |||
| ef4a8ea1b6 | |||
| 49cde96ee7 | |||
| 627be20e48 | |||
| e45bb84b02 | |||
| 4e8876c725 | |||
| 6d5be15ec9 | |||
| 1a8db646f2 | |||
| d3aba2c906 | |||
| 457a83282f | |||
| 48e94c937d | |||
| 6e613e9f71 | |||
| b698d43d7d | |||
| a36b9a6088 | |||
| 20acd89c8a | |||
| 7bed9a257d | |||
| 19c83aa5a8 | |||
| 52610d7d61 | |||
| 9e4b393b7a | |||
| a0ae34a3b7 | |||
| 413dc5d004 | |||
| c9eb27bfe7 | |||
| b78505c290 | |||
| 95f9ae83d0 | |||
| 9304289315 | |||
| 1b726ca293 | |||
| 656ca18929 |
@@ -15,3 +15,4 @@ Contributors
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
- Nijiko Yonskai
|
- Nijiko Yonskai
|
||||||
|
- Herman Starikov
|
||||||
|
|||||||
+1
-5
@@ -24,7 +24,7 @@ module.exports = function (grunt) {
|
|||||||
requirejs: {
|
requirejs: {
|
||||||
compile: {
|
compile: {
|
||||||
options: {
|
options: {
|
||||||
name: "app",
|
name: "gitbook",
|
||||||
baseUrl: "theme/javascript/",
|
baseUrl: "theme/javascript/",
|
||||||
out: "theme/assets/app.js",
|
out: "theme/assets/app.js",
|
||||||
preserveLicenseComments: false,
|
preserveLicenseComments: false,
|
||||||
@@ -35,7 +35,6 @@ module.exports = function (grunt) {
|
|||||||
"lodash": 'vendors/lodash',
|
"lodash": 'vendors/lodash',
|
||||||
"requireLib": 'vendors/require',
|
"requireLib": 'vendors/require',
|
||||||
"Mousetrap": 'vendors/mousetrap',
|
"Mousetrap": 'vendors/mousetrap',
|
||||||
"mixpanel": 'vendors/mixpanel',
|
|
||||||
"lunr": path.join(__dirname, "node_modules/lunr/lunr")
|
"lunr": path.join(__dirname, "node_modules/lunr/lunr")
|
||||||
},
|
},
|
||||||
shim: {
|
shim: {
|
||||||
@@ -48,9 +47,6 @@ module.exports = function (grunt) {
|
|||||||
'Mousetrap': {
|
'Mousetrap': {
|
||||||
exports: 'Mousetrap'
|
exports: 'Mousetrap'
|
||||||
},
|
},
|
||||||
'mixpanel': {
|
|
||||||
exports: 'mixpanel'
|
|
||||||
},
|
|
||||||
'lunr': {
|
'lunr': {
|
||||||
exports: 'lunr'
|
exports: 'lunr'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ Apache License
|
|||||||
same "printed page" as the copyright notice for easier
|
same "printed page" as the copyright notice for easier
|
||||||
identification within third-party archives.
|
identification within third-party archives.
|
||||||
|
|
||||||
Copyright {yyyy} {name of copyright owner}
|
Copyright 2014 FriendCode Inc.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -198,4 +198,4 @@ Apache License
|
|||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ GitBook
|
|||||||
|
|
||||||
[](https://travis-ci.org/GitbookIO/gitbook)
|
[](https://travis-ci.org/GitbookIO/gitbook)
|
||||||
|
|
||||||
GitBook is a command line tool (and Node.js library) for building beautiful programming books and exercises using GitHub/Git and Markdown. You can see an example: [Learn Javascript](http://gitbookio.github.io/javascript/).
|
GitBook is a command line tool (and Node.js library) for building beautiful programming books and exercises using GitHub/Git and Markdown. You can see an example: [Learn Javascript](http://gitbookio.github.io/javascript/). An [editor](https://github.com/GitbookIO/editor) is available for Windows, Mac and Linux. You can follow [@GitBookIO](https://twitter.com/GitBookIO) on Twitter.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -30,16 +30,59 @@ $ gitbook build ./repository --output=./outputFolder
|
|||||||
Options for commands `build` and `serve` are:
|
Options for commands `build` and `serve` are:
|
||||||
|
|
||||||
```
|
```
|
||||||
-t, --title <name> Name of the book to generate, defaults to repo name
|
|
||||||
-i, --intro <intro> Description of the book to generate
|
|
||||||
-g, --github <repo_path> ID of github repo like : username/repo
|
|
||||||
-o, --output <directory> Path to output directory, defaults to ./_book
|
-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
|
-f, --format <name> Change generation format, defaults to site, availables are: site, page, pdf, json
|
||||||
-i, --intro <intro> Description of the book to generate
|
--config <config file> Configuration file to use, defaults to book.json
|
||||||
--githubHost <url> The url of the github host (defaults to https://github.com/)
|
|
||||||
--theme <path> Path to theme directory
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
GitBook load 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:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
// Folders to use for output (caution: it override the value from the command line)
|
||||||
|
"output": null,
|
||||||
|
|
||||||
|
// Generator to use for building (caution: it override the value from the command line)
|
||||||
|
"generator": "site",
|
||||||
|
|
||||||
|
// Book title and description (defaults are extracted from the README)
|
||||||
|
"title": null,
|
||||||
|
"description": null,
|
||||||
|
|
||||||
|
// GitHub informations (defaults are extracted using git)
|
||||||
|
"github": null,
|
||||||
|
"githubHost": "https://github.com/",
|
||||||
|
|
||||||
|
// Plugins list, can contain "-name" for removing default plugins
|
||||||
|
"plugins": [],
|
||||||
|
|
||||||
|
// Global configuration for plugins
|
||||||
|
"pluginsConfig": {},
|
||||||
|
|
||||||
|
// Links in template (null: default, false: remove, string: new value)
|
||||||
|
"links": {
|
||||||
|
// Link to home in the top-left corner
|
||||||
|
"home": null,
|
||||||
|
|
||||||
|
// Links in top of sidebar
|
||||||
|
"about": null,
|
||||||
|
"issues": null,
|
||||||
|
"edit": null,
|
||||||
|
|
||||||
|
// Sharing links
|
||||||
|
"sharing": {
|
||||||
|
"google": null,
|
||||||
|
"facebook": null,
|
||||||
|
"twitter": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can publish your books to our index by visiting [GitBook.io](http://www.gitbook.io)
|
||||||
|
|
||||||
## Output Formats
|
## Output Formats
|
||||||
|
|
||||||
GitBook can generate your book in the following formats:
|
GitBook can generate your book in the following formats:
|
||||||
@@ -132,3 +175,22 @@ You can see a complete example with the [Learn Git](https://github.com/GitbookIO
|
|||||||
#### Ignoring files & folders
|
#### Ignoring files & folders
|
||||||
|
|
||||||
GitBook will read the `.gitignore`, `.bookignore` and `.ignore` files to get a list of files and folders to skip. (The format inside those files, follows the same convention as `.gitignore`)
|
GitBook will read the `.gitignore`, `.bookignore` and `.ignore` files to get a list of files and folders to skip. (The format inside those files, follows the same convention as `.gitignore`)
|
||||||
|
|
||||||
|
#### 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.
|
||||||
|
|
||||||
|
##### Default plugins:
|
||||||
|
|
||||||
|
* [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
|
||||||
|
|
||||||
|
##### Other plugins:
|
||||||
|
|
||||||
|
* [Google Analytics](https://github.com/GitbookIO/plugin-ga): Google Analytics tracking for your book
|
||||||
|
* [Disqus](https://github.com/GitbookIO/plugin-disqus): Disqus comments integration in your book
|
||||||
|
* [Transform annoted quotes to notes](https://github.com/erixtekila/gitbook-plugin-richquotes): Allow extra markdown markup to render blockquotes as nice notes
|
||||||
|
* [Send code to console](https://github.com/erixtekila/gitbook-plugin-toconsole): Evaluate javascript blockin the browser inspector's console
|
||||||
|
* [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
|
||||||
|
|||||||
+21
-27
@@ -1,50 +1,44 @@
|
|||||||
var path = require('path');
|
var path = require('path');
|
||||||
var Q = require('q');
|
var Q = require('q');
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
var utils = require('./utils');
|
var utils = require('./utils');
|
||||||
|
|
||||||
var generate = require("../lib/generate");
|
var generate = require("../lib/generate");
|
||||||
var parse = require("../lib/parse");
|
var parse = require("../lib/parse");
|
||||||
var fs = require('../lib/generate/fs');
|
|
||||||
var generators = require("../lib/generate").generators;
|
var generators = require("../lib/generate").generators;
|
||||||
|
|
||||||
|
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')
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
var makeBuildFunc = function(converter) {
|
var makeBuildFunc = function(converter) {
|
||||||
return function(dir, options) {
|
return function(dir, options) {
|
||||||
dir = dir || process.cwd();
|
dir = dir || process.cwd();
|
||||||
outputDir = options.output
|
outputDir = options.output;
|
||||||
|
|
||||||
console.log('Starting build ...');
|
console.log('Starting build ...');
|
||||||
// Get repo's URL
|
return converter(
|
||||||
return utils.gitURL(dir)
|
_.extend({}, options || {}, {
|
||||||
.then(function(url) {
|
input: dir,
|
||||||
// Get ID of repo
|
output: outputDir,
|
||||||
return utils.githubID(url);
|
generator: options.format,
|
||||||
}, function(err) {
|
configFile: options.config
|
||||||
return null;
|
})
|
||||||
})
|
)
|
||||||
.then(function(repoID) {
|
|
||||||
return converter(
|
|
||||||
_.extend(options || {}, {
|
|
||||||
input: dir,
|
|
||||||
output: outputDir,
|
|
||||||
title: options.title,
|
|
||||||
description: options.intro,
|
|
||||||
github: options.github || repoID,
|
|
||||||
githubHost: options.githubHost,
|
|
||||||
generator: options.format,
|
|
||||||
theme: options.theme
|
|
||||||
})
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.then(function(output) {
|
.then(function(output) {
|
||||||
console.log("Successfuly built !");
|
console.log("Successfuly built !");
|
||||||
return output;
|
return output;
|
||||||
}, utils.logError);
|
}, utils.logError);
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
folder: makeBuildFunc(generate.folder),
|
folder: makeBuildFunc(generate.folder),
|
||||||
file: makeBuildFunc(generate.file)
|
file: makeBuildFunc(generate.file),
|
||||||
|
command: buildCommand
|
||||||
};
|
};
|
||||||
|
|||||||
+36
-26
@@ -12,45 +12,55 @@ var fs = require('../lib/generate/fs');
|
|||||||
|
|
||||||
var utils = require('./utils');
|
var utils = require('./utils');
|
||||||
var build = require('./build');
|
var build = require('./build');
|
||||||
|
var Server = require('./server');
|
||||||
|
|
||||||
// General options
|
// General options
|
||||||
prog
|
prog
|
||||||
.version(pkg.version);
|
.version(pkg.version);
|
||||||
|
|
||||||
var buildCommand = function(command) {
|
build.command(prog.command('build [source_dir]'))
|
||||||
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('-t, --title <name>', 'Name of the book to generate, default is extracted from readme')
|
|
||||||
.option('-i, --intro <intro>', 'Description of the book to generate, default is extracted from readme')
|
|
||||||
.option('-g, --github <repo_path>', 'ID of github repo like : username/repo')
|
|
||||||
.option('--githubHost <url>', 'The url of the github host (defaults to https://github.com/')
|
|
||||||
.option('--theme <path>', 'Path to theme directory');
|
|
||||||
};
|
|
||||||
|
|
||||||
buildCommand(prog.command('build [source_dir]'))
|
|
||||||
.description('Build a gitbook from a directory')
|
.description('Build a gitbook from a directory')
|
||||||
.action(build.folder);
|
.action(build.folder);
|
||||||
|
|
||||||
buildCommand(prog.command('serve [source_dir]'))
|
build.command(prog.command('serve [source_dir]'))
|
||||||
.description('Build then serve a gitbook from a directory')
|
.description('Build then serve a gitbook from a directory')
|
||||||
.option('-p, --port <port>', 'Port for server to listen on', 4000)
|
.option('-p, --port <port>', 'Port for server to listen on', 4000)
|
||||||
|
.option('--no-watch', 'Disable restart with file watching')
|
||||||
.action(function(dir, options) {
|
.action(function(dir, options) {
|
||||||
build.folder(dir, options || {})
|
var server = new Server();
|
||||||
.then(function(_options) {
|
|
||||||
console.log();
|
var generate = function() {
|
||||||
console.log('Starting server ...');
|
if (server.isRunning()) console.log("Stopping server");
|
||||||
return utils.serveDir(_options.output, options.port)
|
|
||||||
|
server.stop()
|
||||||
|
.then(function() {
|
||||||
|
return build.folder(dir, options);
|
||||||
|
})
|
||||||
|
.then(function(_options) {
|
||||||
|
console.log();
|
||||||
|
console.log('Starting server ...');
|
||||||
|
return server.start(_options.output, options.port)
|
||||||
|
.then(function() {
|
||||||
|
console.log('Serving book on http://localhost:'+options.port);
|
||||||
|
|
||||||
|
if (!options.watch) return;
|
||||||
|
return utils.watch(_options.input)
|
||||||
|
.then(function(filepath) {
|
||||||
|
console.log("Restart after change in files");
|
||||||
|
console.log('');
|
||||||
|
return generate();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
.fail(utils.logError);
|
.fail(utils.logError);
|
||||||
})
|
};
|
||||||
.then(function() {
|
|
||||||
console.log('Serving book on http://localhost:'+options.port);
|
console.log('Press CTRL+C to quit ...');
|
||||||
console.log();
|
console.log('')
|
||||||
console.log('Press CTRL+C to quit ...');
|
generate();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
buildCommand(prog.command('pdf [source_dir]'))
|
build.command(prog.command('pdf [source_dir]'))
|
||||||
.description('Build a gitbook as a PDF')
|
.description('Build a gitbook as a PDF')
|
||||||
.option('-pf, --paperformat <format>', 'PDF paper format (default is A4): "5in*7.5in", "10cm*20cm", "A4", "Letter"')
|
.option('-pf, --paperformat <format>', 'PDF paper format (default is A4): "5in*7.5in", "10cm*20cm", "A4", "Letter"')
|
||||||
.action(function(dir, options) {
|
.action(function(dir, options) {
|
||||||
@@ -60,7 +70,7 @@ buildCommand(prog.command('pdf [source_dir]'))
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
buildCommand(prog.command('ebook [source_dir]'))
|
build.command(prog.command('ebook [source_dir]'))
|
||||||
.description('Build a gitbook as a eBook')
|
.description('Build a gitbook as a eBook')
|
||||||
.option('-c, --cover <path>', 'Cover image, default is cover.png if exists')
|
.option('-c, --cover <path>', 'Cover image, default is cover.png if exists')
|
||||||
.action(function(dir, options) {
|
.action(function(dir, options) {
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
var Q = require('q');
|
||||||
|
var _ = require('lodash');
|
||||||
|
|
||||||
|
var events = require('events');
|
||||||
|
var http = require('http');
|
||||||
|
var send = require('send');
|
||||||
|
var util = require('util');
|
||||||
|
var url = require('url');
|
||||||
|
|
||||||
|
var Server = function() {
|
||||||
|
this.running = null;
|
||||||
|
this.dir = null;
|
||||||
|
this.port = 0;
|
||||||
|
this.sockets = [];
|
||||||
|
};
|
||||||
|
util.inherits(Server, events.EventEmitter);
|
||||||
|
|
||||||
|
// Return true if the server is running
|
||||||
|
Server.prototype.isRunning = function() {
|
||||||
|
return this.running != null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Stop the server
|
||||||
|
Server.prototype.stop = function() {
|
||||||
|
var that = this;
|
||||||
|
if (!this.isRunning()) return Q();
|
||||||
|
|
||||||
|
var d = Q.defer();
|
||||||
|
this.running.close(function(err) {
|
||||||
|
that.running = null;
|
||||||
|
that.emit("state", false);
|
||||||
|
|
||||||
|
if (err) d.reject(err);
|
||||||
|
else d.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
for (var i = 0; i < this.sockets.length; i++) {
|
||||||
|
this.sockets[i].destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
Server.prototype.start = function(dir, port) {
|
||||||
|
var that = this, pre = Q();
|
||||||
|
port = port || 8004;
|
||||||
|
|
||||||
|
if (that.isRunning()) pre = this.stop();
|
||||||
|
return pre
|
||||||
|
.then(function() {
|
||||||
|
var d = Q.defer();
|
||||||
|
|
||||||
|
that.running = http.createServer(function(req, res){
|
||||||
|
// Render error
|
||||||
|
function error(err) {
|
||||||
|
res.statusCode = err.status || 500;
|
||||||
|
res.end(err.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect to directory's index.html
|
||||||
|
function redirect() {
|
||||||
|
res.statusCode = 301;
|
||||||
|
res.setHeader('Location', req.url + '/');
|
||||||
|
res.end('Redirecting to ' + req.url + '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send file
|
||||||
|
send(req, url.parse(req.url).pathname)
|
||||||
|
.root(dir)
|
||||||
|
.on('error', error)
|
||||||
|
.on('directory', redirect)
|
||||||
|
.pipe(res);
|
||||||
|
});
|
||||||
|
|
||||||
|
that.running.on('connection', function (socket) {
|
||||||
|
that.sockets.push(socket);
|
||||||
|
socket.setTimeout(4000);
|
||||||
|
socket.on('close', function () {
|
||||||
|
that.sockets.splice(that.sockets.indexOf(socket), 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
that.running.listen(port, function(err) {
|
||||||
|
if (err) return d.reject(err);
|
||||||
|
|
||||||
|
that.port = port;
|
||||||
|
that.dir = dir;
|
||||||
|
that.emit("state", true);
|
||||||
|
d.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
return d.promise;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Server;
|
||||||
+15
-74
@@ -4,83 +4,27 @@ var _ = require('lodash');
|
|||||||
var http = require('http');
|
var http = require('http');
|
||||||
var send = require('send');
|
var send = require('send');
|
||||||
|
|
||||||
var url = require('url');
|
var path = require('path');
|
||||||
var cp = require('child_process');
|
var Gaze = require('gaze').Gaze;
|
||||||
|
|
||||||
|
function watch(dir) {
|
||||||
// Get the remote of a given repo
|
|
||||||
function gitURL(path) {
|
|
||||||
var d = Q.defer();
|
var d = Q.defer();
|
||||||
|
dir = path.resolve(dir);
|
||||||
|
|
||||||
cp.exec("git config --get remote.origin.url", {
|
var gaze = new Gaze("**/*.md", {
|
||||||
cwd: path,
|
cwd: dir
|
||||||
env: process.env,
|
|
||||||
}, function(err, stdout, stderr) {
|
|
||||||
if(err) {
|
|
||||||
return d.reject(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
return d.resolve(stdout);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return d.promise
|
gaze.once("all", function(e, filepath) {
|
||||||
.then(function(output) {
|
gaze.close();
|
||||||
return output.replace(/(\r\n|\n|\r)/gm, "");
|
|
||||||
|
d.resolve(filepath);
|
||||||
});
|
});
|
||||||
}
|
gaze.once("error", function(err) {
|
||||||
|
gaze.close();
|
||||||
|
|
||||||
// Poorman's parsing
|
d.reject(err);
|
||||||
// Parse a git URL to a github ID : username/reponame
|
});
|
||||||
function githubID(_url) {
|
|
||||||
// Remove .git if it's in _url
|
|
||||||
var sliceEnd = _url.slice(-4) === '.git' ? -4 : _url.length;
|
|
||||||
|
|
||||||
// Detect HTTPS repos
|
|
||||||
var parsed = url.parse(_url);
|
|
||||||
if(parsed.protocol === 'https:' && parsed.host === 'github.com') {
|
|
||||||
return parsed.path.slice(1, sliceEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect SSH repos
|
|
||||||
if(_url.indexOf('git@') === 0) {
|
|
||||||
return _url.split(':', 2)[1].slice(0, sliceEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
// None found
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function titleCase(str)
|
|
||||||
{
|
|
||||||
return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
|
|
||||||
}
|
|
||||||
|
|
||||||
function serveDir(dir, port) {
|
|
||||||
var d = Q.defer();
|
|
||||||
|
|
||||||
var server = http.createServer(function(req, res){
|
|
||||||
// Render error
|
|
||||||
function error(err) {
|
|
||||||
res.statusCode = err.status || 500;
|
|
||||||
res.end(err.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Redirect to directory's index.html
|
|
||||||
function redirect() {
|
|
||||||
res.statusCode = 301;
|
|
||||||
res.setHeader('Location', req.url + '/');
|
|
||||||
res.end('Redirecting to ' + req.url + '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send file
|
|
||||||
send(req, url.parse(req.url).pathname)
|
|
||||||
.root(dir)
|
|
||||||
.on('error', error)
|
|
||||||
.on('directory', redirect)
|
|
||||||
.pipe(res);
|
|
||||||
}).listen(port);
|
|
||||||
|
|
||||||
d.resolve(server);
|
|
||||||
|
|
||||||
return d.promise;
|
return d.promise;
|
||||||
}
|
}
|
||||||
@@ -93,9 +37,6 @@ function logError(err) {
|
|||||||
|
|
||||||
// Exports
|
// Exports
|
||||||
module.exports = {
|
module.exports = {
|
||||||
gitURL: gitURL,
|
watch: watch,
|
||||||
githubID: githubID,
|
|
||||||
titleCase: titleCase,
|
|
||||||
serveDir: serveDir,
|
|
||||||
logError: logError
|
logError: logError
|
||||||
};
|
};
|
||||||
|
|||||||
+3
-1
@@ -16,7 +16,9 @@ var getFiles = function(path) {
|
|||||||
|
|
||||||
// Add extra rules to ignore common folders
|
// Add extra rules to ignore common folders
|
||||||
ig.addIgnoreRules([
|
ig.addIgnoreRules([
|
||||||
'.git/'
|
'.git/',
|
||||||
|
'.gitignore',
|
||||||
|
'.DS_Store'
|
||||||
], '__custom_stuff');
|
], '__custom_stuff');
|
||||||
|
|
||||||
// Push each file to our list
|
// Push each file to our list
|
||||||
|
|||||||
@@ -1,9 +1,30 @@
|
|||||||
|
var _ = require("lodash");
|
||||||
var path = require("path");
|
var path = require("path");
|
||||||
var Q = require("q");
|
var Q = require("q");
|
||||||
var fs = require("./fs");
|
var fs = require("./fs");
|
||||||
|
|
||||||
|
var Plugin = require("./plugin");
|
||||||
|
|
||||||
var BaseGenerator = function(options) {
|
var BaseGenerator = function(options) {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
|
||||||
|
this.options.plugins = Plugin.normalizeNames(this.options.plugins);
|
||||||
|
this.plugins = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
BaseGenerator.prototype.callHook = function(name, data) {
|
||||||
|
return this.plugins.hook(name, this, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
BaseGenerator.prototype.loadPlugins = function() {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
return Plugin.fromList(this.options.plugins, this.options.input)
|
||||||
|
.then(function(_plugins) {
|
||||||
|
that.plugins = _plugins;
|
||||||
|
|
||||||
|
return that.callHook("init");
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
BaseGenerator.prototype.convertFile = function(content, input) {
|
BaseGenerator.prototype.convertFile = function(content, input) {
|
||||||
|
|||||||
+69
-13
@@ -6,6 +6,7 @@ var tmp = require('tmp');
|
|||||||
|
|
||||||
var fs = require("./fs");
|
var fs = require("./fs");
|
||||||
var parse = require("../parse");
|
var parse = require("../parse");
|
||||||
|
var Plugin = require("./plugin");
|
||||||
|
|
||||||
var generators = {
|
var generators = {
|
||||||
"site": require("./site"),
|
"site": require("./site"),
|
||||||
@@ -27,22 +28,34 @@ var generate = function(options) {
|
|||||||
|
|
||||||
options = _.defaults(options || {}, {
|
options = _.defaults(options || {}, {
|
||||||
// Folders to use
|
// Folders to use
|
||||||
input: null,
|
"input": null,
|
||||||
output: null,
|
"output": null,
|
||||||
|
|
||||||
|
// Config file (relative to input)
|
||||||
|
"configFile": "book.json",
|
||||||
|
|
||||||
// Output generator
|
// Output generator
|
||||||
generator: "site",
|
"generator": "site",
|
||||||
|
|
||||||
// Book title, keyword, description
|
// Book title, keyword, description
|
||||||
title: null,
|
"title": null,
|
||||||
description: null,
|
"description": null,
|
||||||
|
|
||||||
// Origin github repository id
|
// Origin github repository id
|
||||||
github: null,
|
"github": null,
|
||||||
githubHost: 'https://github.com/',
|
"githubHost": 'https://github.com/',
|
||||||
|
|
||||||
// Theming
|
// Theming
|
||||||
theme: path.resolve(__dirname, '../../theme')
|
"theme": path.resolve(__dirname, '../../theme'),
|
||||||
|
|
||||||
|
// Plugins
|
||||||
|
"plugins": [],
|
||||||
|
"pluginsConfig": {},
|
||||||
|
|
||||||
|
// Links
|
||||||
|
"links": {
|
||||||
|
"about": null
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!options.input) {
|
if (!options.input) {
|
||||||
@@ -58,6 +71,35 @@ var generate = function(options) {
|
|||||||
// Clean output folder
|
// Clean output folder
|
||||||
return fs.remove(options.output)
|
return fs.remove(options.output)
|
||||||
|
|
||||||
|
// 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');
|
||||||
|
|
||||||
|
_.extend(options, _config);
|
||||||
|
}, function() {
|
||||||
|
// No config file: not a big deal
|
||||||
|
return Q();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Get repo's URL
|
||||||
|
.then(function() {
|
||||||
|
return parse.git.url(options.input)
|
||||||
|
.then(function(_url) {
|
||||||
|
// Get ID of repo
|
||||||
|
return parse.git.githubID(_url);
|
||||||
|
}, function(err) {
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.then(function(repoId) {
|
||||||
|
options.github = options.github || repoId;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return fs.mkdirp(options.output);
|
return fs.mkdirp(options.output);
|
||||||
})
|
})
|
||||||
@@ -70,9 +112,10 @@ var generate = function(options) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Create the generator
|
// Create the generator and load plugins
|
||||||
.then(function() {
|
.then(function() {
|
||||||
generator = new generators[options.generator](options);
|
generator = new generators[options.generator](options);
|
||||||
|
return generator.loadPlugins();
|
||||||
})
|
})
|
||||||
|
|
||||||
// Detect multi-languages book
|
// Detect multi-languages book
|
||||||
@@ -129,6 +172,15 @@ var generate = function(options) {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Skip processing some files
|
||||||
|
.then(function() {
|
||||||
|
files = _.filter(files, function (file) {
|
||||||
|
return !(
|
||||||
|
file === 'SUMMARY.md'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
// Copy file and replace markdown file
|
// Copy file and replace markdown file
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return Q.all(
|
return Q.all(
|
||||||
@@ -151,9 +203,12 @@ var generate = function(options) {
|
|||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
||||||
// Finish gneration
|
// Finish generation
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return generator.finish();
|
return generator.finish();
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
return generator.callHook("finish");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -177,7 +232,7 @@ var generateFile = function(options) {
|
|||||||
return Q.nfcall(tmp.dir)
|
return Q.nfcall(tmp.dir)
|
||||||
.then(function(tmpDir) {
|
.then(function(tmpDir) {
|
||||||
return generate(
|
return generate(
|
||||||
_.extend({},
|
_.extend({},
|
||||||
options,
|
options,
|
||||||
{
|
{
|
||||||
output: tmpDir
|
output: tmpDir
|
||||||
@@ -195,7 +250,7 @@ var generateFile = function(options) {
|
|||||||
_outputFile = _outputFile.slice(0, -path.extname(_outputFile).length)+"_"+lang+path.extname(_outputFile);
|
_outputFile = _outputFile.slice(0, -path.extname(_outputFile).length)+"_"+lang+path.extname(_outputFile);
|
||||||
_tmpDir = path.join(_tmpDir, lang);
|
_tmpDir = path.join(_tmpDir, lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
return fs.copy(
|
return fs.copy(
|
||||||
path.join(_tmpDir, "index."+ext),
|
path.join(_tmpDir, "index."+ext),
|
||||||
_outputFile
|
_outputFile
|
||||||
@@ -225,5 +280,6 @@ var generateFile = function(options) {
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
generators: generators,
|
generators: generators,
|
||||||
folder: generate,
|
folder: generate,
|
||||||
file: generateFile
|
file: generateFile,
|
||||||
|
Plugin: Plugin
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
var _ = require('lodash');
|
||||||
|
var path = require('path');
|
||||||
|
var Q = require('q');
|
||||||
|
|
||||||
|
var fs = require("./fs");
|
||||||
|
|
||||||
|
var extsToIgnore = [".gz"]
|
||||||
|
|
||||||
|
var Manifest = function() {
|
||||||
|
this.revision = 0;
|
||||||
|
this.clear(Date.now());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Regenerate manifest
|
||||||
|
Manifest.prototype.clear = function(revision) {
|
||||||
|
if (revision) this.revision = revision;
|
||||||
|
this.sections = {
|
||||||
|
'CACHE': {},
|
||||||
|
'NETWORK': {},
|
||||||
|
'FALLBACK': {}
|
||||||
|
};
|
||||||
|
return Q(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add a resource
|
||||||
|
Manifest.prototype.add = function(category, resource, value) {
|
||||||
|
if (_.isArray(resource)) {
|
||||||
|
_.each(resource, function(subres) {
|
||||||
|
this.add(category, subres, value);
|
||||||
|
}, this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.sections[category][resource] = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add a directory in cache
|
||||||
|
Manifest.prototype.addFolder = function(folder, root, except) {
|
||||||
|
var that = this;
|
||||||
|
root = root || "/";
|
||||||
|
|
||||||
|
return fs.list(folder)
|
||||||
|
.then(function(files) {
|
||||||
|
_.each(
|
||||||
|
// Ignore diretcories
|
||||||
|
_.filter(files, function(file) {
|
||||||
|
return file.substr(-1) != "/" && !_.contains(except, path.join(root, file)) && !_.contains(extsToIgnore, path.extname(file));
|
||||||
|
}),
|
||||||
|
function(file) {
|
||||||
|
that.add("CACHE", path.join(root, file));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get manifest content
|
||||||
|
Manifest.prototype.dump = function() {
|
||||||
|
var lines = [
|
||||||
|
"CACHE MANIFEST",
|
||||||
|
"# Revision "+this.revision
|
||||||
|
];
|
||||||
|
|
||||||
|
_.each(this.sections, function(content, section) {
|
||||||
|
if (_.size(content) == 0) return;
|
||||||
|
lines.push("");
|
||||||
|
lines.push(section+":");
|
||||||
|
lines = lines.concat(_.keys(content));
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
return Q(lines.join("\n"));
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = Manifest;
|
||||||
@@ -25,14 +25,18 @@ swig.setFilter('code', function(code, lang) {
|
|||||||
var Generator = function() {
|
var Generator = function() {
|
||||||
BaseGenerator.apply(this, arguments);
|
BaseGenerator.apply(this, arguments);
|
||||||
|
|
||||||
// Load base template
|
|
||||||
this.template = swig.compileFile(path.resolve(this.options.theme, 'templates/page.html'));
|
|
||||||
|
|
||||||
// List of pages content
|
// List of pages content
|
||||||
this.pages = {};
|
this.pages = {};
|
||||||
};
|
};
|
||||||
util.inherits(Generator, BaseGenerator);
|
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) {
|
Generator.prototype.convertFile = function(content, input) {
|
||||||
var that = this;
|
var that = this;
|
||||||
var json = {
|
var json = {
|
||||||
@@ -46,6 +50,7 @@ Generator.prototype.convertFile = function(content, input) {
|
|||||||
repo: that.options.githubId,
|
repo: that.options.githubId,
|
||||||
dir: path.dirname(input) || '/',
|
dir: path.dirname(input) || '/',
|
||||||
outdir: './',
|
outdir: './',
|
||||||
|
singleFile: true
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(function(sections) {
|
.then(function(sections) {
|
||||||
|
|||||||
@@ -0,0 +1,192 @@
|
|||||||
|
var _ = require("lodash");
|
||||||
|
var Q = require("q");
|
||||||
|
var semver = require("semver");
|
||||||
|
var path = require("path");
|
||||||
|
var url = require("url");
|
||||||
|
var fs = require("./fs");
|
||||||
|
var resolve = require('resolve');
|
||||||
|
|
||||||
|
var pkg = require("../../package.json");
|
||||||
|
|
||||||
|
var RESOURCES = ["js", "css"];
|
||||||
|
|
||||||
|
var Plugin = function(name, root) {
|
||||||
|
this.name = name;
|
||||||
|
this.root = root;
|
||||||
|
this.packageInfos = {};
|
||||||
|
this.infos = {};
|
||||||
|
|
||||||
|
_.each([
|
||||||
|
"gitbook-plugin-"+name,
|
||||||
|
"gitbook-theme-"+name,
|
||||||
|
"gitbook-"+name,
|
||||||
|
name,
|
||||||
|
], function(_name) {
|
||||||
|
if (this.load(_name, __dirname)) return false;
|
||||||
|
if (this.load(_name, path.resolve(root))) return false;
|
||||||
|
}, this);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load from a name
|
||||||
|
Plugin.prototype.load = function(name, baseDir) {
|
||||||
|
try {
|
||||||
|
var res = resolve.sync(name+"/package.json", { basedir: baseDir });
|
||||||
|
|
||||||
|
this.baseDir = path.dirname(res);
|
||||||
|
this.packageInfos = require(res);
|
||||||
|
this.infos = require(resolve.sync(name, { basedir: baseDir }));
|
||||||
|
this.name = name;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return resources
|
||||||
|
Plugin.prototype.getResources = function(resource) {
|
||||||
|
if (!this.infos.book || !this.infos.book[resource]) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return _.chain(this.infos.book[resource])
|
||||||
|
.map(function(resource) {
|
||||||
|
var parsed = url.parse(resource);
|
||||||
|
if (parsed.protocol) return {"url": resource}
|
||||||
|
else return { "path": this.name+"/"+resource };
|
||||||
|
}.bind(this))
|
||||||
|
.value();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test if it's a valid plugin
|
||||||
|
Plugin.prototype.isValid = function() {
|
||||||
|
return (
|
||||||
|
this.packageInfos
|
||||||
|
&& this.packageInfos.name
|
||||||
|
&& this.packageInfos.engines
|
||||||
|
&& this.packageInfos.engines.gitbook
|
||||||
|
&& semver.satisfies(pkg.version, this.packageInfos.engines.gitbook)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Resolve file path
|
||||||
|
Plugin.prototype.resolveFile = function(filename) {
|
||||||
|
return path.resolve(this.baseDir, filename);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Resolve file path
|
||||||
|
Plugin.prototype.callHook = function(name, context, data) {
|
||||||
|
var hookFunc = this.infos.hooks? this.infos.hooks[name] : null;
|
||||||
|
data = data || {};
|
||||||
|
|
||||||
|
if (!hookFunc) return Q(data);
|
||||||
|
|
||||||
|
return Q()
|
||||||
|
.then(function() {
|
||||||
|
return hookFunc.apply(context, [data]);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Has assets
|
||||||
|
Plugin.prototype.hasAssets = function(out) {
|
||||||
|
return (this.infos.book && this.infos.book.assets);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Copy plugin assets fodler
|
||||||
|
Plugin.prototype.copyAssets = function(out) {
|
||||||
|
if (!this.hasAssets()) return Q();
|
||||||
|
return fs.copy(
|
||||||
|
this.resolveFile(this.infos.book.assets),
|
||||||
|
out
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Normalize a list of plugin name to use
|
||||||
|
Plugin.normalizeNames = function(names) {
|
||||||
|
// Normalize list to an array
|
||||||
|
names = _.isString(names) ? names.split(",") : (names || []);
|
||||||
|
|
||||||
|
// List plugins to remove
|
||||||
|
var toremove = _.chain(names)
|
||||||
|
.filter(function(name) {
|
||||||
|
return name.length > 0 && name[0] == "-";
|
||||||
|
})
|
||||||
|
.map(function(name) {
|
||||||
|
return name.slice(1)
|
||||||
|
})
|
||||||
|
.value();
|
||||||
|
|
||||||
|
// Merge with defaults
|
||||||
|
names = _.chain(names)
|
||||||
|
.concat(Plugin.defaults)
|
||||||
|
.uniq()
|
||||||
|
.value();
|
||||||
|
|
||||||
|
// Remove plugins starting with
|
||||||
|
names = _.filter(names, function(name) {
|
||||||
|
return !_.contains(toremove, name) && !(name.length > 0 && name[0] == "-");
|
||||||
|
});
|
||||||
|
|
||||||
|
return names;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Extract data from a list of plugin
|
||||||
|
Plugin.fromList = function(names, root) {
|
||||||
|
var failed = [];
|
||||||
|
|
||||||
|
// Load plugins
|
||||||
|
var plugins = _.map(names, function(name) {
|
||||||
|
var plugin = new Plugin(name, root);
|
||||||
|
if (!plugin.isValid()) failed.push(name);
|
||||||
|
return plugin;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (_.size(failed) > 0) return Q.reject(new Error("Error loading plugins: "+failed.join(",")));
|
||||||
|
|
||||||
|
// Get all resources
|
||||||
|
var resources = _.chain(RESOURCES)
|
||||||
|
.map(function(resource) {
|
||||||
|
return [
|
||||||
|
resource,
|
||||||
|
_.chain(plugins)
|
||||||
|
.map(function(plugin) {
|
||||||
|
return plugin.getResources(resource);
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.value()
|
||||||
|
];
|
||||||
|
})
|
||||||
|
.object()
|
||||||
|
.value();
|
||||||
|
|
||||||
|
return Q({
|
||||||
|
'list': plugins,
|
||||||
|
'resources': resources,
|
||||||
|
'hook': function(name, context, data) {
|
||||||
|
return _.reduce(plugins, function(prev, plugin) {
|
||||||
|
return prev.then(function(ret) {
|
||||||
|
return plugin.callHook(name, context, ret);
|
||||||
|
})
|
||||||
|
}, Q(data));
|
||||||
|
},
|
||||||
|
'template': function(name) {
|
||||||
|
var withTpl = _.find(plugins, function(plugin) {
|
||||||
|
return (plugin.infos.templates
|
||||||
|
&& plugin.infos.templates[name]);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!withTpl) return null;
|
||||||
|
return withTpl.resolveFile(withTpl.infos.templates[name]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Default plugins
|
||||||
|
Plugin.defaults = [
|
||||||
|
"mixpanel",
|
||||||
|
"mathjax"
|
||||||
|
];
|
||||||
|
|
||||||
|
module.exports = Plugin;
|
||||||
+102
-13
@@ -9,6 +9,7 @@ var parse = require("../../parse");
|
|||||||
var BaseGenerator = require("../generator");
|
var BaseGenerator = require("../generator");
|
||||||
|
|
||||||
var indexer = require('./search_indexer');
|
var indexer = require('./search_indexer');
|
||||||
|
var Manifest = require('../manifest');
|
||||||
|
|
||||||
// Swig filter for returning the count of lines in a code section
|
// Swig filter for returning the count of lines in a code section
|
||||||
swig.setFilter('lines', function(content) {
|
swig.setFilter('lines', function(content) {
|
||||||
@@ -30,17 +31,39 @@ var Generator = function() {
|
|||||||
|
|
||||||
this.revision = Date.now();
|
this.revision = Date.now();
|
||||||
this.indexer = indexer();
|
this.indexer = indexer();
|
||||||
|
this.manifest = new Manifest(Date.now());
|
||||||
// Load base template
|
this.manifest.add("NETWORK", [
|
||||||
this.template = swig.compileFile(path.resolve(this.options.theme, 'templates/site.html'));
|
'*'
|
||||||
this.langsTemplate = swig.compileFile(path.resolve(this.options.theme, 'templates/langs.html'));
|
]);
|
||||||
};
|
};
|
||||||
util.inherits(Generator, BaseGenerator);
|
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')
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load plugins
|
||||||
|
Generator.prototype.loadPlugins = function() {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
return BaseGenerator.prototype.loadPlugins.apply(this)
|
||||||
|
.then(function() {
|
||||||
|
return that.loadTemplates();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Generate a template
|
// Generate a template
|
||||||
Generator.prototype._writeTemplate = function(tpl, options, output) {
|
Generator.prototype._writeTemplate = function(tpl, options, output, interpolate) {
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
|
interpolate = interpolate || _.identity;
|
||||||
|
|
||||||
return Q()
|
return Q()
|
||||||
.then(function(sections) {
|
.then(function(sections) {
|
||||||
return tpl(_.extend({
|
return tpl(_.extend({
|
||||||
@@ -54,9 +77,15 @@ Generator.prototype._writeTemplate = function(tpl, options, output) {
|
|||||||
githubHost: that.options.githubHost,
|
githubHost: that.options.githubHost,
|
||||||
|
|
||||||
summary: that.options.summary,
|
summary: that.options.summary,
|
||||||
allNavigation: that.options.navigation
|
allNavigation: that.options.navigation,
|
||||||
|
|
||||||
|
plugins: that.plugins,
|
||||||
|
pluginsConfig: JSON.stringify(that.options.pluginsConfig),
|
||||||
|
|
||||||
|
options: that.options
|
||||||
}, options));
|
}, options));
|
||||||
})
|
})
|
||||||
|
.then(interpolate)
|
||||||
.then(function(html) {
|
.then(function(html) {
|
||||||
return fs.writeFile(
|
return fs.writeFile(
|
||||||
output,
|
output,
|
||||||
@@ -73,7 +102,6 @@ Generator.prototype.indexPage = function(lexed, pagePath) {
|
|||||||
// Convert a markdown file to html
|
// Convert a markdown file to html
|
||||||
Generator.prototype.convertFile = function(content, _input) {
|
Generator.prototype.convertFile = function(content, _input) {
|
||||||
var that = this;
|
var that = this;
|
||||||
var progress = parse.progress(this.options.navigation, _input);
|
|
||||||
|
|
||||||
_output = _input.replace(".md", ".html");
|
_output = _input.replace(".md", ".html");
|
||||||
if (_output == "README.html") _output = "index.html";
|
if (_output == "README.html") _output = "index.html";
|
||||||
@@ -82,10 +110,28 @@ Generator.prototype.convertFile = function(content, _input) {
|
|||||||
var output = path.join(this.options.output, _output);
|
var output = path.join(this.options.output, _output);
|
||||||
var basePath = path.relative(path.dirname(output), this.options.output) || ".";
|
var basePath = path.relative(path.dirname(output), this.options.output) || ".";
|
||||||
|
|
||||||
|
var page = {
|
||||||
|
path: _input,
|
||||||
|
content: content,
|
||||||
|
progress: parse.progress(this.options.navigation, _input)
|
||||||
|
};
|
||||||
|
|
||||||
|
var _callHook = function(name) {
|
||||||
|
return that.callHook(name, page)
|
||||||
|
.then(function(_page) {
|
||||||
|
page = _page;
|
||||||
|
return page;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return Q()
|
return Q()
|
||||||
|
.then(function() {
|
||||||
|
// Send content to plugins
|
||||||
|
return _callHook("page:before");
|
||||||
|
})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
// Lex page
|
// Lex page
|
||||||
return parse.lex(content);
|
return parse.lex(page.content);
|
||||||
})
|
})
|
||||||
.then(function(lexed) {
|
.then(function(lexed) {
|
||||||
// Index page in search
|
// Index page in search
|
||||||
@@ -101,15 +147,27 @@ Generator.prototype.convertFile = function(content, _input) {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(function(sections) {
|
.then(function(sections) {
|
||||||
|
page.sections = sections;
|
||||||
|
|
||||||
|
// Use plugin hook
|
||||||
|
return _callHook("page");
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
that.manifest.add("CACHE", _output);
|
||||||
|
|
||||||
return that._writeTemplate(that.template, {
|
return that._writeTemplate(that.template, {
|
||||||
progress: progress,
|
progress: page.progress,
|
||||||
|
|
||||||
_input: _input,
|
_input: _input,
|
||||||
content: sections,
|
content: page.sections,
|
||||||
|
|
||||||
basePath: basePath,
|
basePath: basePath,
|
||||||
staticBase: path.join(basePath, "gitbook"),
|
staticBase: path.join(basePath, "gitbook"),
|
||||||
}, output);
|
}, output, function(html) {
|
||||||
|
page.content = html;
|
||||||
|
|
||||||
|
return _callHook("page:after").get("content")
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -134,10 +192,31 @@ Generator.prototype.langsIndex = function(langs) {
|
|||||||
Generator.prototype.copyAssets = function() {
|
Generator.prototype.copyAssets = function() {
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
|
// Copy gitbook assets
|
||||||
return fs.copy(
|
return fs.copy(
|
||||||
path.join(that.options.theme, "assets"),
|
path.join(that.options.theme, "assets"),
|
||||||
path.join(that.options.output, "gitbook")
|
path.join(that.options.output, "gitbook")
|
||||||
);
|
)
|
||||||
|
|
||||||
|
// Add to cach manifest
|
||||||
|
.then(function() {
|
||||||
|
return that.manifest.addFolder(path.join(that.options.output, "gitbook"), "gitbook");
|
||||||
|
})
|
||||||
|
|
||||||
|
// Copy plugins assets
|
||||||
|
.then(function() {
|
||||||
|
return Q.all(
|
||||||
|
_.map(that.plugins.list, function(plugin) {
|
||||||
|
if (!plugin.hasAssets()) return Q();
|
||||||
|
|
||||||
|
var pluginAssets = path.join(that.options.output, "gitbook/plugins/", plugin.name);
|
||||||
|
return plugin.copyAssets(pluginAssets)
|
||||||
|
.then(function() {
|
||||||
|
return that.manifest.addFolder(pluginAssets, "gitbook/plugins/"+plugin.name);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
// Dump search index to disk
|
// Dump search index to disk
|
||||||
@@ -148,9 +227,19 @@ Generator.prototype.writeSearchIndex = function() {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Add cache manifest
|
||||||
|
Generator.prototype.writeCacheManifest = function() {
|
||||||
|
return fs.writeFile(
|
||||||
|
path.join(this.options.output, 'manifest.appcache'),
|
||||||
|
this.manifest.dump()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
Generator.prototype.finish = function() {
|
Generator.prototype.finish = function() {
|
||||||
return this.copyAssets()
|
return this.copyAssets()
|
||||||
.then(this.writeSearchIndex);
|
.then(this.writeSearchIndex)
|
||||||
|
.then(this.writeCacheManifest);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Generator;
|
module.exports = Generator;
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
var Q = require('q');
|
||||||
|
var _ = require('lodash');
|
||||||
|
var cp = require('child_process');
|
||||||
|
var url = require('url');
|
||||||
|
|
||||||
|
// Get the remote of a given repo
|
||||||
|
function gitURL(path) {
|
||||||
|
var d = Q.defer();
|
||||||
|
|
||||||
|
cp.exec("git config --get remote.origin.url", {
|
||||||
|
cwd: path,
|
||||||
|
env: process.env,
|
||||||
|
}, function(err, stdout, stderr) {
|
||||||
|
if(err) {
|
||||||
|
return d.reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.resolve(stdout);
|
||||||
|
});
|
||||||
|
|
||||||
|
return d.promise
|
||||||
|
.then(function(output) {
|
||||||
|
return output.replace(/(\r\n|\n|\r)/gm, "");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poorman's parsing
|
||||||
|
// Parse a git URL to a github ID : username/reponame
|
||||||
|
function githubID(_url) {
|
||||||
|
// Remove .git if it's in _url
|
||||||
|
var sliceEnd = _url.slice(-4) === '.git' ? -4 : _url.length;
|
||||||
|
|
||||||
|
// Detect HTTPS repos
|
||||||
|
var parsed = url.parse(_url);
|
||||||
|
if(parsed.protocol === 'https:' && parsed.host === 'github.com') {
|
||||||
|
return parsed.path.slice(1, sliceEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect SSH repos
|
||||||
|
if(_url.indexOf('git@') === 0) {
|
||||||
|
return _url.split(':', 2)[1].slice(0, sliceEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// None found
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function titleCase(str) {
|
||||||
|
return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
url: gitURL,
|
||||||
|
githubID: githubID,
|
||||||
|
titleCase: titleCase
|
||||||
|
};
|
||||||
+2
-1
@@ -5,5 +5,6 @@ module.exports = {
|
|||||||
lex: require('./lex'),
|
lex: require('./lex'),
|
||||||
progress: require('./progress'),
|
progress: require('./progress'),
|
||||||
navigation: require('./navigation'),
|
navigation: require('./navigation'),
|
||||||
readme: require('./readme')
|
readme: require('./readme'),
|
||||||
|
git: require('./git')
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-1
@@ -56,7 +56,7 @@ function isQuiz(nodes) {
|
|||||||
|
|
||||||
if(
|
if(
|
||||||
// List of questions
|
// List of questions
|
||||||
listIdx !== -1 && isQuizNode(quizNodes[listIdx + 1]) &&
|
listIdx !== -1 && isQuizNode(quizNodes[listIdx + 1]) ||
|
||||||
|
|
||||||
// Table of questions
|
// Table of questions
|
||||||
(
|
(
|
||||||
|
|||||||
+30
-49
@@ -6,6 +6,12 @@ function clean(obj) {
|
|||||||
return obj && _.omit(obj, ['articles']);
|
return obj && _.omit(obj, ['articles']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function flattenChapters(chapters) {
|
||||||
|
return _.reduce(chapters, function(accu, chapter) {
|
||||||
|
return accu.concat([clean(chapter)].concat(chapter.articles));
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
// Returns from a summary a map of
|
// Returns from a summary a map of
|
||||||
/*
|
/*
|
||||||
{
|
{
|
||||||
@@ -20,62 +26,37 @@ function navigation(summary, files) {
|
|||||||
// Support single files as well as list
|
// Support single files as well as list
|
||||||
files = _.isArray(files) ? files : (_.isString(files) ? [files] : null);
|
files = _.isArray(files) ? files : (_.isString(files) ? [files] : null);
|
||||||
|
|
||||||
// Mapping of prev/next for a give path
|
|
||||||
var mapping = {};
|
|
||||||
|
|
||||||
// Special README nav
|
// Special README nav
|
||||||
var README_NAV = {
|
var README_NAV = {
|
||||||
path: 'README.md',
|
path: 'README.md',
|
||||||
title: 'Introduction',
|
title: 'Introduction',
|
||||||
};
|
|
||||||
|
|
||||||
// Walk the chapter/article tree and create navigation entires
|
|
||||||
_.each(summary.chapters, function(chapter, idx, chapters) {
|
|
||||||
// Skip if no path
|
|
||||||
if(!chapter.path) return;
|
|
||||||
|
|
||||||
var currentChapter = clean(chapter);
|
|
||||||
var prevChapter = (idx-1 < 0) ? README_NAV : chapters[idx-1];
|
|
||||||
var nextChapter = (idx+1 >= chapters.length) ? null : chapters[idx+1];
|
|
||||||
|
|
||||||
var prev = (!prevChapter || _.isEmpty(prevChapter.articles)) ?
|
|
||||||
prevChapter : _.last(prevChapter.articles);
|
|
||||||
var next = (!chapter || _.isEmpty(chapter.articles)) ?
|
|
||||||
nextChapter : _.first(chapter.articles);
|
|
||||||
|
|
||||||
// Add chapter mapping
|
|
||||||
mapping[chapter.path] = {
|
|
||||||
title: chapter.title,
|
|
||||||
prev: clean(prev),
|
|
||||||
next: clean(next),
|
|
||||||
level: (idx+1).toString(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check a chapter's articles
|
|
||||||
_.each(chapter.articles, function(article, _idx, articles) {
|
|
||||||
// Skip if no path
|
|
||||||
if(!article.path) return;
|
|
||||||
|
|
||||||
var prev = (_idx-1 < 0) ? currentChapter : clean(articles[_idx-1]);
|
|
||||||
var next = (_idx+1 >= articles.length) ? nextChapter : clean(articles[_idx+1]);
|
|
||||||
|
|
||||||
mapping[article.path] = {
|
|
||||||
title: article.title,
|
|
||||||
prev: clean(prev),
|
|
||||||
next: clean(next),
|
|
||||||
level: [idx+1, _idx+1].join('.'),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Hack for README.html
|
|
||||||
mapping['README.md'] = {
|
|
||||||
title: README_NAV.title,
|
|
||||||
prev: null,
|
|
||||||
next: clean(summary.chapters[0]),
|
|
||||||
level: '0',
|
level: '0',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// List of all navNodes
|
||||||
|
var navNodes = [README_NAV].concat(flattenChapters(summary.chapters));
|
||||||
|
var prevNodes = [null].concat(navNodes.slice(0, -1));
|
||||||
|
var nextNodes = navNodes.slice(1).concat([null]);
|
||||||
|
|
||||||
|
// Mapping of prev/next for a give path
|
||||||
|
var mapping = _.chain(_.zip(navNodes, prevNodes, nextNodes))
|
||||||
|
.map(function(nodes) {
|
||||||
|
var current = nodes[0], prev = nodes[1], next = nodes[2];
|
||||||
|
|
||||||
|
// Skip if no path
|
||||||
|
if(!current.path) return null;
|
||||||
|
|
||||||
|
return [current.path, {
|
||||||
|
title: current.title,
|
||||||
|
prev: prev,
|
||||||
|
next: next,
|
||||||
|
level: current.level,
|
||||||
|
}];
|
||||||
|
})
|
||||||
|
.filter()
|
||||||
|
.object()
|
||||||
|
.value();
|
||||||
|
|
||||||
// Filter for only files we want
|
// Filter for only files we want
|
||||||
if(files) {
|
if(files) {
|
||||||
return _.pick(mapping, files);
|
return _.pick(mapping, files);
|
||||||
|
|||||||
+15
-12
@@ -8,17 +8,6 @@ var renderer = require('./renderer');
|
|||||||
var lnormalize = require('../utils/lang').normalize;
|
var lnormalize = require('../utils/lang').normalize;
|
||||||
|
|
||||||
|
|
||||||
// Synchronous highlighting with highlight.js
|
|
||||||
marked.setOptions({
|
|
||||||
highlight: function (code, lang) {
|
|
||||||
try {
|
|
||||||
return hljs.highlight(lang, code).value;
|
|
||||||
} catch(e) {
|
|
||||||
return hljs.highlightAuto(code).value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// Render a section using our custom renderer
|
// Render a section using our custom renderer
|
||||||
function render(section, _options) {
|
function render(section, _options) {
|
||||||
@@ -30,7 +19,21 @@ function render(section, _options) {
|
|||||||
|
|
||||||
// Build options using defaults and our custom renderer
|
// Build options using defaults and our custom renderer
|
||||||
var options = _.extend({}, marked.defaults, {
|
var options = _.extend({}, marked.defaults, {
|
||||||
renderer: renderer(null, _options)
|
renderer: renderer(null, _options),
|
||||||
|
|
||||||
|
// Synchronous highlighting with highlight.js
|
||||||
|
highlight: function (code, lang) {
|
||||||
|
if(!lang) return code;
|
||||||
|
|
||||||
|
// Normalize lang
|
||||||
|
lang = lnormalize(lang);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return hljs.highlight(lang, code).value;
|
||||||
|
} catch(e) { }
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return marked.parser(section, options);
|
return marked.parser(section, options);
|
||||||
|
|||||||
+34
-10
@@ -1,5 +1,7 @@
|
|||||||
var url = require('url');
|
var url = require('url');
|
||||||
var inherits = require('util').inherits;
|
var inherits = require('util').inherits;
|
||||||
|
var links = require('../utils').links;
|
||||||
|
|
||||||
|
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
|
||||||
@@ -16,6 +18,7 @@ function GitBookRenderer(options, extra_options) {
|
|||||||
this._extra_options = extra_options;
|
this._extra_options = extra_options;
|
||||||
this.quizRowId = 0;
|
this.quizRowId = 0;
|
||||||
this.id = rendererId++;
|
this.id = rendererId++;
|
||||||
|
this.quizIndex = 0;
|
||||||
}
|
}
|
||||||
inherits(GitBookRenderer, marked.Renderer);
|
inherits(GitBookRenderer, marked.Renderer);
|
||||||
|
|
||||||
@@ -38,23 +41,38 @@ GitBookRenderer.prototype._unsanitized = function(href) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
GitBookRenderer.prototype.link = function(href, title, text) {
|
GitBookRenderer.prototype.link = function(href, title, text) {
|
||||||
|
// Our "fixed" href
|
||||||
|
var _href = href;
|
||||||
|
|
||||||
// Don't build if it looks malicious
|
// Don't build if it looks malicious
|
||||||
if (this.options.sanitize && this._unsanitized(href)) {
|
if (this.options.sanitize && this._unsanitized(href)) {
|
||||||
return '';
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parsed version of the url
|
// Parsed version of the url
|
||||||
var parsed = url.parse(href);
|
var parsed = url.parse(href);
|
||||||
|
|
||||||
var o = this._extra_options;
|
var o = this._extra_options;
|
||||||
|
|
||||||
// Relative link, rewrite it to point to github repo
|
// Relative link, rewrite it to point to github repo
|
||||||
if(!parsed.protocol && parsed.path && parsed.path[0] != '/' && o && o.repo && o.dir) {
|
if(links.isRelative(_href)) {
|
||||||
href = url.resolve('https://github.com/' + o.repo + '/blob/', [o.dir, href].join("/"));
|
if (path.extname(_href) == ".md") {
|
||||||
parsed = url.parse(href);
|
_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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate HTML for link
|
// Generate HTML for link
|
||||||
var out = '<a href="' + parsed.href + '"';
|
var out = '<a href="' + _href + '"';
|
||||||
// Title if no null
|
// Title if no null
|
||||||
if (title) {
|
if (title) {
|
||||||
out += ' title="' + title + '"';
|
out += ' title="' + title + '"';
|
||||||
@@ -78,9 +96,11 @@ GitBookRenderer.prototype.image = function(href, title, text) {
|
|||||||
var o = this._extra_options;
|
var o = this._extra_options;
|
||||||
|
|
||||||
// Relative image, rewrite it depending output
|
// Relative image, rewrite it depending output
|
||||||
if(!parsed.protocol && parsed.path && parsed.path[0] != '/' && o && o.dir && o.outdir) {
|
if(links.isRelative(href) && o && o.dir && o.outdir) {
|
||||||
var outdir = o.outdir.charAt(o.outdir.length - 1) === '/' ? o.outdir : o.outdir + '/';
|
// o.dir: directory parent of the file currently in rendering process
|
||||||
_href = url.resolve(outdir, [o.dir, href].join('/'));
|
// o.outdir: directory parent from the html output
|
||||||
|
|
||||||
|
_href = links.toAbsolute(_href, o.dir, o.outdir);
|
||||||
}
|
}
|
||||||
|
|
||||||
return GitBookRenderer.super_.prototype.image.call(this, _href, title, text);
|
return GitBookRenderer.super_.prototype.image.call(this, _href, title, text);
|
||||||
@@ -97,10 +117,14 @@ GitBookRenderer.prototype._createCheckboxAndRadios = function(text) {
|
|||||||
if (!match) {
|
if (!match) {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
var field = "<input name='quiz-row-" + this.id + "-" + this.quizRowId + "' type='";
|
var quizIdentifier = 'quiz-row-' + this.id + '-' + this.quizRowId + '-' + this.quizIndex++;
|
||||||
|
var field = "<input name='" + quizIdentifier + "' id='" + quizIdentifier + "' type='";
|
||||||
field += match[1] === '(' ? "radio" : "checkbox";
|
field += match[1] === '(' ? "radio" : "checkbox";
|
||||||
field += match[2] === 'x' ? "' checked/>" : "'/>";
|
field += match[2] === 'x' ? "' checked/>" : "'/>";
|
||||||
return text.replace(fieldRegex, field);
|
var splittedText = text.split(fieldRegex);
|
||||||
|
var length = splittedText.length;
|
||||||
|
var label = '<label class="quiz-label" for="' + quizIdentifier + '">' + splittedText[length - 1] + '</label>';
|
||||||
|
return text.replace(fieldRegex, field).replace(splittedText[length - 1], label);
|
||||||
}
|
}
|
||||||
|
|
||||||
GitBookRenderer.prototype.tablecell = function(content, flags) {
|
GitBookRenderer.prototype.tablecell = function(content, flags) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
lang: require('./lang'),
|
lang: require('./lang'),
|
||||||
|
links: require('./links')
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
var url = require('url');
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
// Return true if the link is relative
|
||||||
|
var isRelative = function(href) {
|
||||||
|
var parsed = url.parse(href);
|
||||||
|
|
||||||
|
return !parsed.protocol && parsed.path && parsed.path[0] != '/';
|
||||||
|
};
|
||||||
|
|
||||||
|
// Relative to absolute path
|
||||||
|
// dir: directory parent of the file currently in rendering process
|
||||||
|
// outdir: directory parent from the html output
|
||||||
|
|
||||||
|
var toAbsolute = function(_href, dir, outdir) {
|
||||||
|
// Absolute file in source
|
||||||
|
_href = path.join(dir, _href);
|
||||||
|
|
||||||
|
// make it relative to output
|
||||||
|
_href = path.relative(outdir, _href);
|
||||||
|
|
||||||
|
return _href;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
isRelative: isRelative,
|
||||||
|
toAbsolute: toAbsolute
|
||||||
|
};
|
||||||
+22
-4
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "gitbook",
|
"name": "gitbook",
|
||||||
"version": "0.2.0",
|
"version": "0.4.2",
|
||||||
|
"homepage": "http://www.gitbook.io/",
|
||||||
"description": "Library and cmd utility to generate GitBooks",
|
"description": "Library and cmd utility to generate GitBooks",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -15,7 +16,14 @@
|
|||||||
"commander": "2.2.0",
|
"commander": "2.2.0",
|
||||||
"fs-extra": "0.8.1",
|
"fs-extra": "0.8.1",
|
||||||
"highlight.js": "8.0.0",
|
"highlight.js": "8.0.0",
|
||||||
"tmp": "0.0.23"
|
"tmp": "0.0.23",
|
||||||
|
"semver": "2.2.1",
|
||||||
|
"gaze": "~0.5.1",
|
||||||
|
"resolve": "0.6.3",
|
||||||
|
|
||||||
|
"gitbook-plugin": "0.0.2",
|
||||||
|
"gitbook-plugin-mixpanel": "0.0.2",
|
||||||
|
"gitbook-plugin-mathjax": "0.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"mocha": "1.18.2",
|
"mocha": "1.18.2",
|
||||||
@@ -39,9 +47,19 @@
|
|||||||
"book",
|
"book",
|
||||||
"gitbook"
|
"gitbook"
|
||||||
],
|
],
|
||||||
"author": "Aaron O'Mullan <aaron.omullan@friendco.de>",
|
"author": "FriendCode <contact@friendco.de>",
|
||||||
"license": "Apache 2",
|
"license": "Apache 2",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/GitbookIO/gitbook/issues"
|
"url": "https://github.com/GitbookIO/gitbook/issues"
|
||||||
}
|
},
|
||||||
|
"contributors": [
|
||||||
|
{
|
||||||
|
"name": "Aaron O'Mullan",
|
||||||
|
"email": "aaron.omullan@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Samy Pessé",
|
||||||
|
"email": "samypesse@gmail.com"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 164 KiB |
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
# Images Test
|
||||||
|
|
||||||
|
This is a test for images path calculation. It supposed this fiel is in a syntax/ folder
|
||||||
|
|
||||||
|
### #
|
||||||
|
|
||||||
|
[](./preview.png)
|
||||||
|
|
||||||
|
### 2
|
||||||
|
|
||||||
|
[](./preview2.png)
|
||||||
|
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
# Nice course
|
||||||
|
|
||||||
|
Check out this other chapter [Test](test.md)
|
||||||
|
|
||||||
|
Check out this other chapter [Test](../before.md)
|
||||||
Vendored
+15
@@ -31,3 +31,18 @@ Some more nice content ....
|
|||||||
[Cool stuff](http://gitbook.io)
|
[Cool stuff](http://gitbook.io)
|
||||||
|
|
||||||
[Link to another Markdown file](./xyz/file.md)
|
[Link to another Markdown file](./xyz/file.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Quiz test 2: What does Gitbook support?
|
||||||
|
- [x] Table-based questions with radio buttons
|
||||||
|
- [x] Table-based questions with checkboxes
|
||||||
|
- [ ] Telepathy
|
||||||
|
- [x] List-based questions with checkboxes
|
||||||
|
- [x] List-based questions with radio buttons
|
||||||
|
- [ ] Moon-on-a-stick
|
||||||
|
|
||||||
|
> Gitbook supports table and list based quiz questions using either radio buttons or checkboxes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|||||||
+62
-5
@@ -28,6 +28,7 @@ describe('Page parsing', function() {
|
|||||||
assert.equal(QUIZ_LEXED[0].type, 'normal');
|
assert.equal(QUIZ_LEXED[0].type, 'normal');
|
||||||
assert.equal(QUIZ_LEXED[1].type, 'quiz');
|
assert.equal(QUIZ_LEXED[1].type, 'quiz');
|
||||||
assert.equal(QUIZ_LEXED[2].type, 'normal');
|
assert.equal(QUIZ_LEXED[2].type, 'normal');
|
||||||
|
assert.equal(QUIZ_LEXED[3].type, 'quiz');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should gen content for normal sections', function() {
|
it('should gen content for normal sections', function() {
|
||||||
@@ -35,10 +36,6 @@ describe('Page parsing', function() {
|
|||||||
assert(LEXED[2].content);
|
assert(LEXED[2].content);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should make image URLs relative', function() {
|
|
||||||
assert(LEXED[2].content.indexOf('_book/assets/my-pretty-picture.png') !== -1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should gen code and content for exercise sections', function() {
|
it('should gen code and content for exercise sections', function() {
|
||||||
assert(LEXED[1].content);
|
assert(LEXED[1].content);
|
||||||
assert(LEXED[1].code);
|
assert(LEXED[1].code);
|
||||||
@@ -87,9 +84,69 @@ describe('Relative links', function() {
|
|||||||
repo: 'GitBookIO/javascript',
|
repo: 'GitBookIO/javascript',
|
||||||
|
|
||||||
// Imaginary folder of markdown file
|
// Imaginary folder of markdown file
|
||||||
dir: 'course'
|
dir: 'course',
|
||||||
|
outdir: './'
|
||||||
});
|
});
|
||||||
|
|
||||||
assert(LEXED[0].content.indexOf('https://github.com/GitBookIO/javascript/blob/src/something.cpp') !== -1);
|
assert(LEXED[0].content.indexOf('https://github.com/GitBookIO/javascript/blob/src/something.cpp') !== -1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should replace link to .md by link to .html', function() {
|
||||||
|
var LEXED = loadPage('MARKDOWN_LINKS', {
|
||||||
|
// GitHub repo ID
|
||||||
|
repo: 'GitBookIO/javascript',
|
||||||
|
|
||||||
|
// Imaginary folder of markdown file
|
||||||
|
dir: 'course',
|
||||||
|
outdir: 'course'
|
||||||
|
});
|
||||||
|
|
||||||
|
assert(LEXED[0].content.indexOf('test.html') !== -1);
|
||||||
|
assert(LEXED[0].content.indexOf('../before.html') !== -1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should replace link to .md by link to page in format single page', function() {
|
||||||
|
var LEXED = loadPage('MARKDOWN_LINKS', {
|
||||||
|
// GitHub repo ID
|
||||||
|
repo: 'GitBookIO/javascript',
|
||||||
|
|
||||||
|
// Imaginary folder of markdown file
|
||||||
|
dir: 'course',
|
||||||
|
outdir: './',
|
||||||
|
singleFile: true
|
||||||
|
});
|
||||||
|
|
||||||
|
assert(LEXED[0].content.indexOf('#course/test.md') !== -1);
|
||||||
|
assert(LEXED[0].content.indexOf('#before.md') !== -1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Relative images', function() {
|
||||||
|
it('should keep image relative with considering output directory in site format', function() {
|
||||||
|
var LEXED = loadPage('IMAGES', {
|
||||||
|
// GitHub repo ID
|
||||||
|
repo: 'GitBookIO/javascript',
|
||||||
|
|
||||||
|
// Imaginary folder of markdown file
|
||||||
|
dir: 'syntax',
|
||||||
|
outdir: 'syntax'
|
||||||
|
});
|
||||||
|
|
||||||
|
assert(LEXED[0].content.indexOf('"preview.png"') !== -1);
|
||||||
|
assert(LEXED[0].content.indexOf('"../preview2.png"') !== -1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should keep image relative with considering output directory in page format', function() {
|
||||||
|
var LEXED = loadPage('IMAGES', {
|
||||||
|
// GitHub repo ID
|
||||||
|
repo: 'GitBookIO/javascript',
|
||||||
|
|
||||||
|
// Imaginary folder of markdown file
|
||||||
|
dir: 'syntax',
|
||||||
|
outdir: './'
|
||||||
|
});
|
||||||
|
|
||||||
|
assert(LEXED[0].content.indexOf('"syntax/preview.png"') !== -1);
|
||||||
|
assert(LEXED[0].content.indexOf('"preview2.png"') !== -1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
var _ = require('lodash');
|
||||||
|
var path = require('path');
|
||||||
|
var assert = require('assert');
|
||||||
|
|
||||||
|
var Plugin = require('../').generate.Plugin;
|
||||||
|
|
||||||
|
describe('Plugin validation', function () {
|
||||||
|
var plugin = new Plugin("plugin", __dirname);
|
||||||
|
|
||||||
|
it('should be valid', function() {
|
||||||
|
assert(plugin.isValid());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Plugin list of names', function () {
|
||||||
|
var firstDefault = _.first(Plugin.defaults);
|
||||||
|
|
||||||
|
it('should convert string to array', function() {
|
||||||
|
var _name = "test";
|
||||||
|
assert(_.contains(Plugin.normalizeNames(_name), _name));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should contains default plugins', function() {
|
||||||
|
assert(_.contains(Plugin.normalizeNames([]), firstDefault));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove name starting with -', function() {
|
||||||
|
assert(!_.contains(Plugin.normalizeNames(["-"+firstDefault]), firstDefault));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Plugin defaults loading', function () {
|
||||||
|
var ret = true;
|
||||||
|
|
||||||
|
beforeEach(function(done){
|
||||||
|
Plugin.fromList(Plugin.defaults, __dirname)
|
||||||
|
.then(function(_r) {
|
||||||
|
ret = _r;
|
||||||
|
}, function(err) {
|
||||||
|
ret = null;
|
||||||
|
})
|
||||||
|
.fin(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should load defaults addons', function() {
|
||||||
|
assert(ret != null);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
# GitBook Theme
|
|
||||||
|
|
||||||
For creating a theme for GitBook, just copy this folder. The theme folder should at least contains two folders: `assets` and `templates`.
|
|
||||||
|
|
||||||
The theme can be changed using the option ```--theme=<path>```
|
|
||||||
+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
@@ -0,0 +1,7 @@
|
|||||||
|
define([
|
||||||
|
"jQuery"
|
||||||
|
], function($) {
|
||||||
|
var events = $({});
|
||||||
|
|
||||||
|
return events;
|
||||||
|
});
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
define([
|
define([
|
||||||
"jQuery",
|
"jQuery",
|
||||||
"utils/execute",
|
"utils/execute",
|
||||||
"utils/analytic",
|
"core/events",
|
||||||
"core/state"
|
"core/state"
|
||||||
], function($, execute, analytic, state){
|
], function($, execute, events, state){
|
||||||
// Bind an exercise
|
// Bind an exercise
|
||||||
var prepareExercise = function($exercise) {
|
var prepareExercise = function($exercise) {
|
||||||
var codeSolution = $exercise.find(".code-solution").text();
|
var codeSolution = $exercise.find(".code-solution").text();
|
||||||
@@ -19,7 +19,7 @@ define([
|
|||||||
$exercise.find(".action-submit").click(function(e) {
|
$exercise.find(".action-submit").click(function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
analytic.track("exercise.submit", {type: "code"});
|
events.trigger("exercise.submit", {type: "code"});
|
||||||
|
|
||||||
execute("javascript", editor.getValue(), codeValidation, codeContext, function(err, result) {
|
execute("javascript", editor.getValue(), codeValidation, codeContext, function(err, result) {
|
||||||
$exercise.toggleClass("return-error", err != null);
|
$exercise.toggleClass("return-error", err != null);
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
define([
|
||||||
|
"jQuery",
|
||||||
|
"utils/storage"
|
||||||
|
], function($, storage) {
|
||||||
|
var fontState;
|
||||||
|
|
||||||
|
var togglePopover = function(e) {
|
||||||
|
var $dropdown = $("#font-settings-wrapper .dropdown-menu");
|
||||||
|
|
||||||
|
$dropdown.toggleClass("open");
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
var closePopover = function(e) {
|
||||||
|
var $dropdown = $("#font-settings-wrapper .dropdown-menu");
|
||||||
|
|
||||||
|
$dropdown.removeClass("open");
|
||||||
|
};
|
||||||
|
|
||||||
|
var enlargeFontSize = function(e){
|
||||||
|
var $bookBody = $(".book-body");
|
||||||
|
|
||||||
|
if (fontState.size < 4){
|
||||||
|
$bookBody.toggleClass("font-size-"+fontState.size, false);
|
||||||
|
fontState.size++;
|
||||||
|
|
||||||
|
$bookBody.toggleClass("font-size-"+fontState.size, true);
|
||||||
|
fontState.save();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var reduceFontSize = function(e){
|
||||||
|
var $bookBody = $(".book-body");
|
||||||
|
|
||||||
|
if (fontState.size > 0){
|
||||||
|
$bookBody.toggleClass("font-size-"+fontState.size);
|
||||||
|
fontState.size--;
|
||||||
|
|
||||||
|
$bookBody.toggleClass("font-size-"+fontState.size);
|
||||||
|
fontState.save();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var changeFontFamily = function(){
|
||||||
|
var $bookBody = $(".book-body");
|
||||||
|
var index = $(this).data("font");
|
||||||
|
|
||||||
|
$bookBody.toggleClass("font-family-"+fontState.family);
|
||||||
|
$(".font-settings .font-family-list li:nth-child("+(fontState.family+1)+")")
|
||||||
|
.removeClass("active");
|
||||||
|
|
||||||
|
fontState.family = index;
|
||||||
|
$bookBody.toggleClass("font-family-"+fontState.family);
|
||||||
|
$(this).addClass("active");
|
||||||
|
fontState.save();
|
||||||
|
};
|
||||||
|
|
||||||
|
var changeColorTheme = function(){
|
||||||
|
var $book = $(".book");
|
||||||
|
var index = $(this).data("theme");
|
||||||
|
|
||||||
|
if (fontState.theme !== 0)
|
||||||
|
$book.removeClass("color-theme-"+fontState.theme);
|
||||||
|
|
||||||
|
fontState.theme = index;
|
||||||
|
if (fontState.theme !== 0)
|
||||||
|
$book.addClass("color-theme-"+fontState.theme);
|
||||||
|
|
||||||
|
fontState.save();
|
||||||
|
};
|
||||||
|
|
||||||
|
var init = function() {
|
||||||
|
var $toggle, $bookBody, $dropdown, $book;
|
||||||
|
|
||||||
|
//Find DOM elements.
|
||||||
|
$book = $(".book");
|
||||||
|
$toggle = $(".book-header .toggle-font-settings");
|
||||||
|
$dropdown = $("#font-settings-wrapper .dropdown-menu");
|
||||||
|
$bookBody = $(".book-body");
|
||||||
|
|
||||||
|
//Instantiate font state object
|
||||||
|
fontState = storage.get("fontState", {
|
||||||
|
size:1,
|
||||||
|
family:0,
|
||||||
|
theme:0
|
||||||
|
});
|
||||||
|
fontState.save = function(){
|
||||||
|
storage.set("fontState",fontState);
|
||||||
|
};
|
||||||
|
|
||||||
|
$bookBody.addClass("font-size-"+fontState.size);
|
||||||
|
$bookBody.addClass("font-family-"+fontState.family);
|
||||||
|
|
||||||
|
$(".font-settings .font-family-list li:nth-child("+(fontState.family+1)+")").addClass("active");
|
||||||
|
|
||||||
|
if(fontState.theme !== 0)
|
||||||
|
$book.addClass("color-theme-"+fontState.theme);
|
||||||
|
|
||||||
|
//Add event listeners
|
||||||
|
$(document).on('click', "#enlarge-font-size", enlargeFontSize);
|
||||||
|
$(document).on('click', "#reduce-font-size", reduceFontSize);
|
||||||
|
|
||||||
|
$(document).on('click', "#font-settings-wrapper .font-family-list li", changeFontFamily);
|
||||||
|
$(document).on('click', "#font-settings-wrapper .color-theme-list button", changeColorTheme);
|
||||||
|
|
||||||
|
$(document).on('click', ".book-header .toggle-font-settings", togglePopover);
|
||||||
|
$(document).on('click', "#font-settings-wrapper .dropdown-menu", function(e){ e.stopPropagation(); });
|
||||||
|
$(document).on("click", closePopover);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
init: init
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
define([
|
||||||
|
"jQuery"
|
||||||
|
], function($) {
|
||||||
|
var showLoading = function(p) {
|
||||||
|
var $book = $(".book");
|
||||||
|
|
||||||
|
$book.addClass("is-loading");
|
||||||
|
p.always(function() {
|
||||||
|
$book.removeClass("is-loading");
|
||||||
|
});
|
||||||
|
|
||||||
|
return p;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
show: showLoading
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -1,34 +1,42 @@
|
|||||||
define([
|
define([
|
||||||
"jQuery",
|
"jQuery",
|
||||||
"utils/analytic",
|
"utils/path",
|
||||||
|
"core/events",
|
||||||
"core/state",
|
"core/state",
|
||||||
"core/search",
|
"core/search",
|
||||||
"core/progress",
|
"core/progress",
|
||||||
"core/exercise",
|
"core/exercise",
|
||||||
"core/quiz"
|
"core/quiz",
|
||||||
], function($, analytic, state, search, progress, exercises, quiz) {
|
"core/loading"
|
||||||
|
], function($, path, events, state, search, progress, exercises, quiz, loading) {
|
||||||
var prev, next;
|
var prev, next;
|
||||||
var githubCountStars, githubCountWatch;
|
var githubCountStars, githubCountWatch;
|
||||||
|
|
||||||
var updateHistory = function(url, title) {
|
var usePushState = (typeof history.pushState !== "undefined");
|
||||||
history.pushState({ path: url }, title, url);
|
|
||||||
};
|
|
||||||
|
|
||||||
var handleNavigation = function(url, push) {
|
var handleNavigation = function(relativeUrl, push) {
|
||||||
if (typeof history.pushState === "undefined") {
|
var url = path.isAbsolute(relativeUrl) ? relativeUrl : path.join(path.dirname(window.location.pathname), relativeUrl);
|
||||||
|
console.log("navigate to ", url, "baseurl="+relativeUrl);
|
||||||
|
|
||||||
|
if (!usePushState) {
|
||||||
// Refresh the page to the new URL if pushState not supported
|
// Refresh the page to the new URL if pushState not supported
|
||||||
location.href = url;
|
location.href = relativeUrl;
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return $.get(url)
|
return loading.show($.get(url)
|
||||||
.done(function (html) {
|
.done(function (html) {
|
||||||
|
// Push url to history
|
||||||
|
if (push) history.pushState({ path: url }, null, url);
|
||||||
|
|
||||||
|
// Replace html content
|
||||||
html = html.replace( /<(\/?)(html|head|body)([^>]*)>/ig, function(a,b,c,d){
|
html = html.replace( /<(\/?)(html|head|body)([^>]*)>/ig, function(a,b,c,d){
|
||||||
return '<' + b + 'div' + ( b ? '' : ' data-element="' + c + '"' ) + d + '>';
|
return '<' + b + 'div' + ( b ? '' : ' data-element="' + c + '"' ) + d + '>';
|
||||||
});
|
});
|
||||||
|
|
||||||
var $page = $(html);
|
var $page = $(html);
|
||||||
var $pageHead = $page.find("[data-element=head]");
|
var $pageHead = $page.find("[data-element=head]");
|
||||||
|
var $pageBody = $page.find('.book');
|
||||||
|
|
||||||
// Merge heads
|
// Merge heads
|
||||||
var headContent = $pageHead.html()
|
var headContent = $pageHead.html()
|
||||||
@@ -38,17 +46,20 @@ define([
|
|||||||
});
|
});
|
||||||
$("head").html(headContent);
|
$("head").html(headContent);
|
||||||
|
|
||||||
// Update header, body and summary
|
// Merge body
|
||||||
$('.book-header').html($page.find('.book-header').html());
|
var scrollPosition = $('.book-summary .summary').scrollTop();
|
||||||
$('.book-body').html($page.find('.book-body').html());
|
$pageBody.toggleClass("with-summary", $(".book").hasClass("with-summary"))
|
||||||
$('.book-summary').html($page.find('.book-summary').html());
|
|
||||||
|
|
||||||
if (push) updateHistory(url, null);
|
$(".book").replaceWith($pageBody);
|
||||||
|
$('.book-summary .summary').scrollTop(scrollPosition);
|
||||||
|
|
||||||
|
// Update state
|
||||||
|
state.update($("html"));
|
||||||
preparePage();
|
preparePage();
|
||||||
})
|
})
|
||||||
.fail(function () {
|
.fail(function (e) {
|
||||||
location.href = url;
|
location.href = relativeUrl;
|
||||||
});
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
var updateGitHubCounts = function() {
|
var updateGitHubCounts = function() {
|
||||||
@@ -101,7 +112,7 @@ define([
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send to mixpanel
|
// Send to mixpanel
|
||||||
analytic.track("page.view");
|
events.trigger("page.change");
|
||||||
};
|
};
|
||||||
|
|
||||||
var handlePagination = function (e) {
|
var handlePagination = function (e) {
|
||||||
@@ -122,7 +133,7 @@ define([
|
|||||||
if (url) handleNavigation(url, true);
|
if (url) handleNavigation(url, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var init = function() {
|
var init = function() {
|
||||||
// Prevent cache so that using the back button works
|
// Prevent cache so that using the back button works
|
||||||
@@ -139,14 +150,13 @@ define([
|
|||||||
if (event.state === null) {
|
if (event.state === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return handleNavigation(event.state.path, false);
|
return handleNavigation(event.state.path, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).on('click', ".navigation-prev", handlePagination);
|
$(document).on('click', ".navigation-prev", handlePagination);
|
||||||
$(document).on('click', ".navigation-next", handlePagination);
|
$(document).on('click', ".navigation-next", handlePagination);
|
||||||
$(document).on('click', ".summary [data-path] a", handlePagination);
|
$(document).on('click', ".summary [data-path] a", handlePagination);
|
||||||
|
|
||||||
$(window).resize(updateNavigationPosition);
|
$(window).resize(updateNavigationPosition);
|
||||||
|
|
||||||
// Prepare current page
|
// Prepare current page
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
define([
|
define([
|
||||||
"jQuery",
|
"jQuery",
|
||||||
"utils/execute",
|
"utils/execute",
|
||||||
"utils/analytic",
|
"core/events",
|
||||||
"core/state"
|
"core/state"
|
||||||
], function($, execute, analytic, state){
|
], function($, execute, events, state){
|
||||||
// Bind an exercise
|
// Bind an exercise
|
||||||
var prepareQuiz = function($quiz) {
|
var prepareQuiz = function($quiz) {
|
||||||
|
|
||||||
@@ -14,14 +14,17 @@ define([
|
|||||||
// Submit: test code
|
// Submit: test code
|
||||||
$quiz.find(".action-submit").click(function(e) {
|
$quiz.find(".action-submit").click(function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
analytic.track("exercise.submit", {type: "quiz"});
|
events.trigger("exercise.submit", {type: "quiz"});
|
||||||
$quiz.find("tr.alert-danger,li.alert-danger").removeClass("alert-danger");
|
$quiz.find("tr.alert-danger,li.alert-danger").removeClass("alert-danger");
|
||||||
$quiz.find(".alert-success,.alert-danger").addClass("hidden");
|
$quiz.find(".alert-success,.alert-danger").addClass("hidden");
|
||||||
|
|
||||||
$quiz.find(".question").each(function(q) {
|
$quiz.find(".question").each(function(q) {
|
||||||
var result = true;
|
var result = true;
|
||||||
var $answers = $quiz.find(".question-answers").slice(q).find("input[type=radio], input[type=checkbox]");
|
|
||||||
$(this).find("input[type=radio],input[type=checkbox]").each(function(i) {
|
var $questions = $(this).find(".question-content").find("input[type=radio], input[type=checkbox]");
|
||||||
|
var $answers = $(this).find(".question-answers").find("input[type=radio], input[type=checkbox]");
|
||||||
|
|
||||||
|
$questions.each(function(i) {
|
||||||
var correct = $(this).is(":checked") === $answers.slice(i).first().is(":checked");
|
var correct = $(this).is(":checked") === $answers.slice(i).first().is(":checked");
|
||||||
result = result && correct;
|
result = result && correct;
|
||||||
if (!correct) {
|
if (!correct) {
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
define([
|
define([
|
||||||
"jQuery"
|
"jQuery"
|
||||||
], function() {
|
], function() {
|
||||||
var $book = $(".book");
|
var state = {};
|
||||||
|
|
||||||
return {
|
state.update = function(dom) {
|
||||||
'$book': $book,
|
var $book = $(dom.find(".book"));
|
||||||
|
|
||||||
'githubId': $book.data("github"),
|
state.$book = $book;
|
||||||
'level': $book.data("level"),
|
state.githubId = $book.data("github");
|
||||||
'basePath': $book.data("basepath"),
|
state.level = $book.data("level");
|
||||||
'revision': $book.data("revision")
|
state.basePath = $book.data("basepath");
|
||||||
|
state.revision = $book.data("revision");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
state.update($);
|
||||||
|
|
||||||
|
return state;
|
||||||
});
|
});
|
||||||
@@ -1,17 +1,19 @@
|
|||||||
require([
|
define([
|
||||||
"jQuery",
|
"jQuery",
|
||||||
"utils/storage",
|
"utils/storage",
|
||||||
"utils/analytic",
|
|
||||||
"utils/sharing",
|
"utils/sharing",
|
||||||
|
"utils/appcache",
|
||||||
|
|
||||||
|
"core/events",
|
||||||
|
"core/font-settings",
|
||||||
"core/state",
|
"core/state",
|
||||||
"core/keyboard",
|
"core/keyboard",
|
||||||
"core/navigation",
|
"core/navigation",
|
||||||
"core/progress",
|
"core/progress",
|
||||||
"core/sidebar",
|
"core/sidebar",
|
||||||
"core/search"
|
"core/search"
|
||||||
], function($, storage, analytic, sharing, state, keyboard, navigation, progress, sidebar, search){
|
], function($, storage, appCache, sharing, events, fontSettings, state, keyboard, navigation, progress, sidebar, search){
|
||||||
$(document).ready(function() {
|
var start = function(config) {
|
||||||
var $book;
|
var $book;
|
||||||
$book = state.$book;
|
$book = state.$book;
|
||||||
|
|
||||||
@@ -29,10 +31,23 @@ require([
|
|||||||
// Init keyboard
|
// Init keyboard
|
||||||
keyboard.init();
|
keyboard.init();
|
||||||
|
|
||||||
|
// Init appcache
|
||||||
|
appCache.init();
|
||||||
|
|
||||||
// Bind sharing button
|
// Bind sharing button
|
||||||
sharing.init();
|
sharing.init();
|
||||||
|
|
||||||
// Init navigation
|
// Init navigation
|
||||||
navigation.init();
|
navigation.init();
|
||||||
});
|
|
||||||
|
//Init font settings
|
||||||
|
fontSettings.init();
|
||||||
|
|
||||||
|
events.trigger("start", config);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
start: start,
|
||||||
|
events: events
|
||||||
|
};
|
||||||
});
|
});
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
define([
|
|
||||||
"lodash",
|
|
||||||
"mixpanel"
|
|
||||||
], function(_, mixpanel) {
|
|
||||||
mixpanel.init("01eb2b950ae09a5fdb15a98dcc5ff20e", {
|
|
||||||
loaded: function() {
|
|
||||||
track("page.start");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var isAvailable = function() {
|
|
||||||
return (
|
|
||||||
typeof mixpanel !== "undefined" &&
|
|
||||||
typeof mixpanel.track === "function"
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
var track = function(e, data) {
|
|
||||||
if (!isAvailable()) {
|
|
||||||
console.warn("tracking not available!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log("track", e);
|
|
||||||
mixpanel.track(e, _.extend(data || {}, {
|
|
||||||
'domain': window.location.host
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
isAvailable: isAvailable,
|
|
||||||
track: track
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
define([], function() {
|
||||||
|
var isAvailable = (typeof applicationCache !== "undefined");
|
||||||
|
|
||||||
|
var init = function() {
|
||||||
|
if (!isAvailable) return;
|
||||||
|
|
||||||
|
window.applicationCache.addEventListener('updateready', function() {
|
||||||
|
window.location.reload();
|
||||||
|
}, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
init: init
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
define([], function() {
|
||||||
|
// Joins path segments. Preserves initial "/" and resolves ".." and "."
|
||||||
|
// Does not support using ".." to go above/outside the root.
|
||||||
|
// This means that join("foo", "../../bar") will not resolve to "../bar"
|
||||||
|
function join(/* path segments */) {
|
||||||
|
// Split the inputs into a list of path commands.
|
||||||
|
var parts = [];
|
||||||
|
for (var i = 0, l = arguments.length; i < l; i++) {
|
||||||
|
parts = parts.concat(arguments[i].split("/"));
|
||||||
|
}
|
||||||
|
// Interpret the path commands to get the new resolved path.
|
||||||
|
var newParts = [];
|
||||||
|
for (i = 0, l = parts.length; i < l; i++) {
|
||||||
|
var part = parts[i];
|
||||||
|
// Remove leading and trailing slashes
|
||||||
|
// Also remove "." segments
|
||||||
|
if (!part || part === ".") continue;
|
||||||
|
// Interpret ".." to pop the last segment
|
||||||
|
if (part === "..") newParts.pop();
|
||||||
|
// Push new path segments.
|
||||||
|
else newParts.push(part);
|
||||||
|
}
|
||||||
|
// Preserve the initial slash if there was one.
|
||||||
|
if (parts[0] === "") newParts.unshift("");
|
||||||
|
// Turn back into a single string path.
|
||||||
|
return newParts.join("/") || (newParts.length ? "/" : ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
// A simple function to get the dirname of a path
|
||||||
|
// Trailing slashes are ignored. Leading slash is preserved.
|
||||||
|
function dirname(path) {
|
||||||
|
return join(path, "..");
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if a path or url is absolute
|
||||||
|
function isAbsolute(path) {
|
||||||
|
if (!path) return false;
|
||||||
|
|
||||||
|
return (path[0] == "/" || path.indexOf("http://") == 0 || path.indexOf("https://") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
dirname: dirname,
|
||||||
|
join: join,
|
||||||
|
isAbsolute: isAbsolute
|
||||||
|
};
|
||||||
|
})
|
||||||
Vendored
-2
@@ -1,2 +0,0 @@
|
|||||||
(function(e,b){if(!b.__SV){var a,f,i,g;window.mixpanel=b;b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!==typeof d?c=b[d]=[]:d="mixpanel";c.people=c.people||[];c.toString=function(b){var a="mixpanel";"mixpanel"!==d&&(a+="."+d);b||(a+=" (stub)");return a};c.people.toString=function(){return c.toString(1)+".people (stub)"};i="disable track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.track_charge people.clear_charges people.delete_user".split(" ");
|
|
||||||
for(g=0;g<i.length;g++)f(c,i[g]);b._i.push([a,e,d])};b.__SV=1.2;a=e.createElement("script");a.type="text/javascript";a.async=!0;a.src=("https:"===e.location.protocol?"https:":"http:")+'//cdn.mxpnl.com/libs/mixpanel-2.2.min.js';f=e.getElementsByTagName("script")[0];f.parentNode.insertBefore(a,f)}})(document,window.mixpanel||[]);
|
|
||||||
@@ -1,11 +1,16 @@
|
|||||||
.book {
|
.book {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
.book-body {
|
.book-body {
|
||||||
position: fixed;
|
position: absolute;
|
||||||
top: @header-height;
|
top: @header-height;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
left: 0px;
|
left: 0px;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
|
|
||||||
|
color: @page-color;
|
||||||
background: @body-background;
|
background: @body-background;
|
||||||
.transition(left 0.5s ease);
|
.transition(left 0.5s ease);
|
||||||
|
|
||||||
@@ -23,7 +28,6 @@
|
|||||||
outline: none;
|
outline: none;
|
||||||
|
|
||||||
.page-inner {
|
.page-inner {
|
||||||
font-family: @font-family-serif;
|
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
margin: 0px auto;
|
margin: 0px auto;
|
||||||
|
|
||||||
@@ -33,8 +37,6 @@
|
|||||||
|
|
||||||
background: @page-background;
|
background: @page-background;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +59,28 @@
|
|||||||
min-height: calc(~"100% - 57px")
|
min-height: calc(~"100% - 57px")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.font-size-0{
|
||||||
|
font-size:@s-font-size;
|
||||||
|
}
|
||||||
|
&.font-size-1{
|
||||||
|
font-size:@m-font-size;
|
||||||
|
}
|
||||||
|
&.font-size-2{
|
||||||
|
font-size:@l-font-size;
|
||||||
|
}
|
||||||
|
&.font-size-3{
|
||||||
|
font-size:@xl-font-size;
|
||||||
|
}
|
||||||
|
&.font-size-4{
|
||||||
|
font-size:@xxl-font-size;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.font-family-0{
|
||||||
|
font-family: @font-family-serif;
|
||||||
|
}
|
||||||
|
&.font-family-1{
|
||||||
|
font-family: @font-family-sans;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.with-summary {
|
&.with-summary {
|
||||||
@@ -72,4 +96,30 @@
|
|||||||
.transition(none) !important;
|
.transition(none) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.color-theme-1{
|
||||||
|
.book-body {
|
||||||
|
color: @page-color-1;
|
||||||
|
background: @body-background-1;
|
||||||
|
.page-wrapper {
|
||||||
|
.page-inner {
|
||||||
|
section{
|
||||||
|
background: @page-background-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.color-theme-2{
|
||||||
|
.book-body {
|
||||||
|
color: @page-color-2;
|
||||||
|
background: @body-background-2;
|
||||||
|
.page-wrapper {
|
||||||
|
.page-inner {
|
||||||
|
section{
|
||||||
|
background: @page-background-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
.book-header{
|
||||||
|
#font-settings-wrapper{
|
||||||
|
position:relative;
|
||||||
|
.dropdown-menu{
|
||||||
|
background-color:@header-background;
|
||||||
|
border-color:@sidebar-divider-color;
|
||||||
|
padding: 0px;
|
||||||
|
|
||||||
|
.dropdown-caret{
|
||||||
|
position: absolute;
|
||||||
|
top: 14px;
|
||||||
|
left: -8px;
|
||||||
|
width: 10px;
|
||||||
|
height: 18px;
|
||||||
|
float: left;
|
||||||
|
overflow: hidden;
|
||||||
|
.caret-outer{
|
||||||
|
position: absolute;
|
||||||
|
border-bottom: 9px solid transparent;
|
||||||
|
border-top: 9px solid transparent;
|
||||||
|
border-right: 9px solid rgba(0,0,0,0.1);
|
||||||
|
height: auto;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: auto;
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: -1px;
|
||||||
|
}
|
||||||
|
.caret-inner{
|
||||||
|
position: absolute;
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: -1px;
|
||||||
|
top: 0;
|
||||||
|
left: 1px;
|
||||||
|
border-bottom: 9px solid transparent;
|
||||||
|
border-top: 9px solid transparent;
|
||||||
|
border-right: 9px solid @header-background;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button{
|
||||||
|
border: 0;
|
||||||
|
background-color:transparent;
|
||||||
|
color:@header-button-color;
|
||||||
|
&:hover{
|
||||||
|
color:@header-button-hover-color;
|
||||||
|
background-color:@header-button-hover-background;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#enlarge-font-size{
|
||||||
|
width: 50%;
|
||||||
|
font-size:1.4em;
|
||||||
|
}
|
||||||
|
#reduce-font-size{
|
||||||
|
width: 50.5%;
|
||||||
|
font-size:1em;
|
||||||
|
}
|
||||||
|
.btn-group-xs{
|
||||||
|
.btn{
|
||||||
|
width: 33.7%;
|
||||||
|
padding: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.list-group{
|
||||||
|
margin: 0px 0;
|
||||||
|
|
||||||
|
.list-group-item{
|
||||||
|
cursor:pointer;
|
||||||
|
background-color:transparent;
|
||||||
|
border-color: @sidebar-divider-color;
|
||||||
|
border-width: 1px 0 !important;
|
||||||
|
|
||||||
|
&:hover{
|
||||||
|
color:@header-button-hover-color;
|
||||||
|
background-color:@header-button-hover-background !important;
|
||||||
|
}
|
||||||
|
&.active{
|
||||||
|
color:@header-button-hover-color;
|
||||||
|
background-color:@header-button-hover-background !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.open{
|
||||||
|
display:block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Theme 1
|
||||||
|
*/
|
||||||
|
|
||||||
|
.color-theme-1{
|
||||||
|
#font-settings-wrapper{
|
||||||
|
.dropdown-menu{
|
||||||
|
background-color:@sidebar-background-1;
|
||||||
|
border-color:@sidebar-divider-color-1;
|
||||||
|
.dropdown-caret .caret-inner{
|
||||||
|
border-right: 9px solid @sidebar-background-1;
|
||||||
|
}
|
||||||
|
button{
|
||||||
|
color:@header-button-color-1;
|
||||||
|
&:hover{
|
||||||
|
color:@header-button-hover-color-1;
|
||||||
|
background-color:@header-button-hover-background-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.list-group{
|
||||||
|
.list-group-item{
|
||||||
|
border-color:@sidebar-divider-color-1;
|
||||||
|
&:hover{
|
||||||
|
color:@header-button-hover-color-1;
|
||||||
|
background-color:@header-button-hover-background-1 !important;
|
||||||
|
}
|
||||||
|
&.active{
|
||||||
|
color:@header-button-hover-color-1;
|
||||||
|
background-color:@header-button-hover-background-1 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Theme 2
|
||||||
|
*/
|
||||||
|
|
||||||
|
.color-theme-2{
|
||||||
|
#font-settings-wrapper{
|
||||||
|
.dropdown-menu{
|
||||||
|
background-color:@sidebar-background-2;
|
||||||
|
border-color:@sidebar-divider-color-2;
|
||||||
|
.dropdown-caret .caret-inner{
|
||||||
|
border-right: 9px solid @sidebar-background-2;
|
||||||
|
}
|
||||||
|
button{
|
||||||
|
color:@header-button-color-2;
|
||||||
|
&:hover{
|
||||||
|
color:@header-button-hover-color-2;
|
||||||
|
background-color:@header-button-hover-background-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.list-group{
|
||||||
|
.list-group-item{
|
||||||
|
border-color:@sidebar-divider-color-2;
|
||||||
|
&:hover{
|
||||||
|
color:@header-button-hover-color-2;
|
||||||
|
background-color:@header-button-hover-background-2 !important;
|
||||||
|
}
|
||||||
|
&.active{
|
||||||
|
color:@header-button-hover-color-2;
|
||||||
|
background-color:@header-button-hover-background-2 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
.book-header {
|
.book-header {
|
||||||
font-family: @font-family-sans;
|
font-family: @font-family-sans;
|
||||||
|
|
||||||
position: fixed;
|
position: absolute;
|
||||||
overflow: hidden;
|
overflow: visible;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
left: 0px;
|
left: 0px;
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
line-height: @header-height;
|
line-height: @header-height;
|
||||||
.box-shadow(none) !important;
|
.box-shadow(none) !important;
|
||||||
|
position:relative;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -51,9 +52,23 @@
|
|||||||
@media (max-width: 800px) {
|
@media (max-width: 800px) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.is-loading {
|
||||||
|
.book-header h1 {
|
||||||
|
i {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
&.with-summary {
|
&.with-summary {
|
||||||
.book-header h1 {
|
.book-header h1 {
|
||||||
margin-left: 250px;
|
margin-left: 250px;
|
||||||
@@ -64,4 +79,30 @@
|
|||||||
.transition(none) !important;
|
.transition(none) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.color-theme-1{
|
||||||
|
.book-header{
|
||||||
|
color: @header-color-1;
|
||||||
|
background: @header-background-1;
|
||||||
|
.btn {
|
||||||
|
color: @header-button-color-1;
|
||||||
|
&:hover {
|
||||||
|
color: @header-button-hover-color-1;
|
||||||
|
background: @header-button-hover-background-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.color-theme-2{
|
||||||
|
.book-header{
|
||||||
|
color: @header-color-2;
|
||||||
|
background: @header-background-2;
|
||||||
|
.btn {
|
||||||
|
color: @header-button-color-2;
|
||||||
|
&:hover {
|
||||||
|
color: @header-button-hover-color-2;
|
||||||
|
background: @header-button-hover-background-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/* Tomorrow Night Bright Theme */
|
||||||
|
/* Original theme - https://github.com/chriskempson/tomorrow-theme */
|
||||||
|
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
|
||||||
|
|
||||||
|
/* Tomorrow Comment */
|
||||||
|
.hljs-comment,
|
||||||
|
.hljs-title {
|
||||||
|
color: #969896;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tomorrow Red */
|
||||||
|
.hljs-variable,
|
||||||
|
.hljs-attribute,
|
||||||
|
.hljs-tag,
|
||||||
|
.hljs-regexp,
|
||||||
|
.ruby .hljs-constant,
|
||||||
|
.xml .hljs-tag .hljs-title,
|
||||||
|
.xml .hljs-pi,
|
||||||
|
.xml .hljs-doctype,
|
||||||
|
.html .hljs-doctype,
|
||||||
|
.css .hljs-id,
|
||||||
|
.css .hljs-class,
|
||||||
|
.css .hljs-pseudo {
|
||||||
|
color: #d54e53;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tomorrow Orange */
|
||||||
|
.hljs-number,
|
||||||
|
.hljs-preprocessor,
|
||||||
|
.hljs-pragma,
|
||||||
|
.hljs-built_in,
|
||||||
|
.hljs-literal,
|
||||||
|
.hljs-params,
|
||||||
|
.hljs-constant {
|
||||||
|
color: #e78c45;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tomorrow Yellow */
|
||||||
|
.ruby .hljs-class .hljs-title,
|
||||||
|
.css .hljs-rules .hljs-attribute {
|
||||||
|
color: #e7c547;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tomorrow Green */
|
||||||
|
.hljs-string,
|
||||||
|
.hljs-value,
|
||||||
|
.hljs-inheritance,
|
||||||
|
.hljs-header,
|
||||||
|
.ruby .hljs-symbol,
|
||||||
|
.xml .hljs-cdata {
|
||||||
|
color: #b9ca4a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tomorrow Aqua */
|
||||||
|
.css .hljs-hexcolor {
|
||||||
|
color: #70c0b1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tomorrow Blue */
|
||||||
|
.hljs-function,
|
||||||
|
.python .hljs-decorator,
|
||||||
|
.python .hljs-title,
|
||||||
|
.ruby .hljs-function .hljs-title,
|
||||||
|
.ruby .hljs-title .hljs-keyword,
|
||||||
|
.perl .hljs-sub,
|
||||||
|
.javascript .hljs-title,
|
||||||
|
.coffeescript .hljs-title {
|
||||||
|
color: #7aa6da;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tomorrow Purple */
|
||||||
|
.hljs-keyword,
|
||||||
|
.javascript .hljs-function {
|
||||||
|
color: #c397d8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs {
|
||||||
|
display: block;
|
||||||
|
background: black;
|
||||||
|
color: #eaeaea;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coffeescript .javascript,
|
||||||
|
.javascript .xml,
|
||||||
|
.tex .hljs-formula,
|
||||||
|
.xml .javascript,
|
||||||
|
.xml .vbscript,
|
||||||
|
.xml .css,
|
||||||
|
.xml .hljs-cdata {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull <sourdrums@gmail.com>
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
.hljs {
|
||||||
|
display: block;
|
||||||
|
padding: 0.5em;
|
||||||
|
background: #fdf6e3;
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-comment,
|
||||||
|
.hljs-template_comment,
|
||||||
|
.diff .hljs-header,
|
||||||
|
.hljs-doctype,
|
||||||
|
.hljs-pi,
|
||||||
|
.lisp .hljs-string,
|
||||||
|
.hljs-javadoc {
|
||||||
|
color: #93a1a1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Solarized Green */
|
||||||
|
.hljs-keyword,
|
||||||
|
.hljs-winutils,
|
||||||
|
.method,
|
||||||
|
.hljs-addition,
|
||||||
|
.css .hljs-tag,
|
||||||
|
.hljs-request,
|
||||||
|
.hljs-status,
|
||||||
|
.nginx .hljs-title {
|
||||||
|
color: #859900;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Solarized Cyan */
|
||||||
|
.hljs-number,
|
||||||
|
.hljs-command,
|
||||||
|
.hljs-string,
|
||||||
|
.hljs-tag .hljs-value,
|
||||||
|
.hljs-rules .hljs-value,
|
||||||
|
.hljs-phpdoc,
|
||||||
|
.tex .hljs-formula,
|
||||||
|
.hljs-regexp,
|
||||||
|
.hljs-hexcolor,
|
||||||
|
.hljs-link_url {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Solarized Blue */
|
||||||
|
.hljs-title,
|
||||||
|
.hljs-localvars,
|
||||||
|
.hljs-chunk,
|
||||||
|
.hljs-decorator,
|
||||||
|
.hljs-built_in,
|
||||||
|
.hljs-identifier,
|
||||||
|
.vhdl .hljs-literal,
|
||||||
|
.hljs-id,
|
||||||
|
.css .hljs-function {
|
||||||
|
color: #268bd2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Solarized Yellow */
|
||||||
|
.hljs-attribute,
|
||||||
|
.hljs-variable,
|
||||||
|
.lisp .hljs-body,
|
||||||
|
.smalltalk .hljs-number,
|
||||||
|
.hljs-constant,
|
||||||
|
.hljs-class .hljs-title,
|
||||||
|
.hljs-parent,
|
||||||
|
.haskell .hljs-type,
|
||||||
|
.hljs-link_reference {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Solarized Orange */
|
||||||
|
.hljs-preprocessor,
|
||||||
|
.hljs-preprocessor .hljs-keyword,
|
||||||
|
.hljs-pragma,
|
||||||
|
.hljs-shebang,
|
||||||
|
.hljs-symbol,
|
||||||
|
.hljs-symbol .hljs-string,
|
||||||
|
.diff .hljs-change,
|
||||||
|
.hljs-special,
|
||||||
|
.hljs-attr_selector,
|
||||||
|
.hljs-subst,
|
||||||
|
.hljs-cdata,
|
||||||
|
.clojure .hljs-title,
|
||||||
|
.css .hljs-pseudo,
|
||||||
|
.hljs-header {
|
||||||
|
color: #cb4b16;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Solarized Red */
|
||||||
|
.hljs-deletion,
|
||||||
|
.hljs-important {
|
||||||
|
color: #dc322f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Solarized Violet */
|
||||||
|
.hljs-link_label {
|
||||||
|
color: #6c71c4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tex .hljs-formula {
|
||||||
|
background: #eee8d5;
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
|
||||||
|
|
||||||
|
/* Tomorrow Comment */
|
||||||
|
.hljs-comment,
|
||||||
|
.hljs-title {
|
||||||
|
color: #8e908c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tomorrow Red */
|
||||||
|
.hljs-variable,
|
||||||
|
.hljs-attribute,
|
||||||
|
.hljs-tag,
|
||||||
|
.hljs-regexp,
|
||||||
|
.ruby .hljs-constant,
|
||||||
|
.xml .hljs-tag .hljs-title,
|
||||||
|
.xml .hljs-pi,
|
||||||
|
.xml .hljs-doctype,
|
||||||
|
.html .hljs-doctype,
|
||||||
|
.css .hljs-id,
|
||||||
|
.css .hljs-class,
|
||||||
|
.css .hljs-pseudo {
|
||||||
|
color: #c82829;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tomorrow Orange */
|
||||||
|
.hljs-number,
|
||||||
|
.hljs-preprocessor,
|
||||||
|
.hljs-pragma,
|
||||||
|
.hljs-built_in,
|
||||||
|
.hljs-literal,
|
||||||
|
.hljs-params,
|
||||||
|
.hljs-constant {
|
||||||
|
color: #f5871f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tomorrow Yellow */
|
||||||
|
.ruby .hljs-class .hljs-title,
|
||||||
|
.css .hljs-rules .hljs-attribute {
|
||||||
|
color: #eab700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tomorrow Green */
|
||||||
|
.hljs-string,
|
||||||
|
.hljs-value,
|
||||||
|
.hljs-inheritance,
|
||||||
|
.hljs-header,
|
||||||
|
.ruby .hljs-symbol,
|
||||||
|
.xml .hljs-cdata {
|
||||||
|
color: #718c00;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tomorrow Aqua */
|
||||||
|
.css .hljs-hexcolor {
|
||||||
|
color: #3e999f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tomorrow Blue */
|
||||||
|
.hljs-function,
|
||||||
|
.python .hljs-decorator,
|
||||||
|
.python .hljs-title,
|
||||||
|
.ruby .hljs-function .hljs-title,
|
||||||
|
.ruby .hljs-title .hljs-keyword,
|
||||||
|
.perl .hljs-sub,
|
||||||
|
.javascript .hljs-title,
|
||||||
|
.coffeescript .hljs-title {
|
||||||
|
color: #4271ae;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tomorrow Purple */
|
||||||
|
.hljs-keyword,
|
||||||
|
.javascript .hljs-function {
|
||||||
|
color: #8959a8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs {
|
||||||
|
display: block;
|
||||||
|
background: white;
|
||||||
|
color: #4d4d4c;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coffeescript .javascript,
|
||||||
|
.javascript .xml,
|
||||||
|
.tex .hljs-formula,
|
||||||
|
.xml .javascript,
|
||||||
|
.xml .vbscript,
|
||||||
|
.xml .css,
|
||||||
|
.xml .hljs-cdata {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
@@ -37,8 +37,6 @@
|
|||||||
padding: 10px 5px;
|
padding: 10px 5px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,29 @@
|
|||||||
.book .book-body .page-wrapper .page-inner section.normal {
|
.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal {
|
||||||
padding: 25px;
|
color:@page-color-1;
|
||||||
padding-top: 15px;
|
|
||||||
background-color: white;
|
|
||||||
|
|
||||||
|
pre, code {
|
||||||
|
background: #fdf6e3;
|
||||||
|
color: #657b83;
|
||||||
|
border-color: darken(#fdf6e3, 15%);
|
||||||
|
|
||||||
|
@import "./highlight/sepia.less";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal {
|
||||||
|
color:@page-color-2;
|
||||||
|
|
||||||
|
pre, code {
|
||||||
|
background: black;
|
||||||
|
color: #eaeaea;
|
||||||
|
border-color: #000;
|
||||||
|
|
||||||
|
@import "./highlight/night.less";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.book .book-body .page-wrapper .page-inner section.normal {
|
||||||
|
padding-bottom: 25px;
|
||||||
|
padding-top: 15px;
|
||||||
|
color:@page-color;
|
||||||
|
|
||||||
& > *:first-child {
|
& > *:first-child {
|
||||||
margin-top: 0 !important; }
|
margin-top: 0 !important; }
|
||||||
@@ -51,26 +72,25 @@
|
|||||||
font-size: inherit; }
|
font-size: inherit; }
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 28px;
|
font-size: 2.5em;
|
||||||
color: black; }
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 24px;
|
font-size: 2em;
|
||||||
border-bottom: 1px solid #eee;
|
border-bottom: 1px solid #eee;
|
||||||
color: black; }
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 18px; }
|
font-size: 1.5em; }
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
font-size: 16px; }
|
font-size: 1.2em; }
|
||||||
|
|
||||||
h5 {
|
h5 {
|
||||||
font-size: 14px; }
|
font-size: 1em; }
|
||||||
|
|
||||||
h6 {
|
h6 {
|
||||||
color: #777777;
|
font-size: 1em; }
|
||||||
font-size: 14px; }
|
|
||||||
|
|
||||||
p, blockquote, ul, ol, dl, table, pre {
|
p, blockquote, ul, ol, dl, table, pre {
|
||||||
margin: 15px 0; }
|
margin: 15px 0; }
|
||||||
@@ -272,29 +292,15 @@
|
|||||||
white-space: pre;
|
white-space: pre;
|
||||||
border: none;
|
border: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
|
||||||
|
|
||||||
.highlight pre {
|
@import "./highlight/white.less";
|
||||||
color: hsl(204, 40%, 80%);
|
|
||||||
background-color: hsl(204, 30%, 10%);
|
|
||||||
border: 1px solid hsl(204, 30%, 10%);
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 1.5em;
|
|
||||||
overflow: auto;
|
|
||||||
padding: 20px;
|
|
||||||
margin: 0 -20px;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
color: hsl(204, 40%, 80%);
|
font-size: inherit;
|
||||||
background-color: hsl(204, 30%, 10%);
|
|
||||||
border: 1px solid hsl(204, 30%, 10%);
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
margin: 0 -20px;
|
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,21 @@
|
|||||||
width: 50%;
|
width: 50%;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
float: left;
|
float: left;
|
||||||
|
|
||||||
|
&.navigation-unique {
|
||||||
|
max-width: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
.book.color-theme-1 .book-body {
|
||||||
|
.navigation:hover{
|
||||||
|
background-color: @body-pagination-background-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.book.color-theme-2 .book-body {
|
||||||
|
.navigation:hover{
|
||||||
|
background-color: @body-pagination-background-2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -16,8 +16,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 10px;
|
||||||
z-index: 10;
|
|
||||||
|
|
||||||
.bar {
|
.bar {
|
||||||
height: @bar-height;
|
height: @bar-height;
|
||||||
@@ -81,4 +80,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
.book.color-theme-1 .book-body {
|
||||||
|
.book-progress {
|
||||||
|
.bar {
|
||||||
|
background: @bar-background-1;
|
||||||
|
.inner {
|
||||||
|
background: @bar-progress-background-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.chapters .chapter{
|
||||||
|
background: @bar-background-1;
|
||||||
|
&.done{
|
||||||
|
background: @bar-progress-background-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.book.color-theme-2 .book-body {
|
||||||
|
.book-progress {
|
||||||
|
.bar {
|
||||||
|
background: @bar-background-2;
|
||||||
|
.inner {
|
||||||
|
background: @bar-progress-background-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.chapters .chapter{
|
||||||
|
background: @bar-background-2;
|
||||||
|
&.done{
|
||||||
|
background: @bar-progress-background-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -29,8 +29,12 @@
|
|||||||
|
|
||||||
.question-inner {
|
.question-inner {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
|
.quiz-label {
|
||||||
|
font-weight: normal;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -46,4 +50,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
.book-summary {
|
.book-summary {
|
||||||
font-family: @font-family-sans;
|
font-family: @font-family-sans;
|
||||||
|
|
||||||
position: fixed;
|
position: absolute;
|
||||||
top: @header-height;
|
top: @header-height;
|
||||||
left: -@sidebar-width;
|
left: -@sidebar-width;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
@@ -141,3 +141,93 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Theme 1
|
||||||
|
*/
|
||||||
|
.book.color-theme-1 {
|
||||||
|
.book-summary {
|
||||||
|
color: @sidebar-color-1;
|
||||||
|
background: @sidebar-background-1;
|
||||||
|
|
||||||
|
.book-search {
|
||||||
|
background: @sidebar-search-background-1;
|
||||||
|
|
||||||
|
input, input:focus {
|
||||||
|
border: 1px solid @sidebar-search-input-border-color-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.summary {
|
||||||
|
li {
|
||||||
|
&.divider {
|
||||||
|
background: @sidebar-divider-color-1;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
i.fa-check {
|
||||||
|
color: @sidebar-icon-color-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.done > a {
|
||||||
|
color: @sidebar-link-completed-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
a, span {
|
||||||
|
color: @sidebar-link-color-1;
|
||||||
|
background: @sidebar-link-background-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active > a, a:hover {
|
||||||
|
color: @sidebar-link-hover-color-1;
|
||||||
|
background: @sidebar-link-hover-background-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Theme 2
|
||||||
|
*/
|
||||||
|
.book.color-theme-2{
|
||||||
|
.book-summary {
|
||||||
|
color: @sidebar-color-2;
|
||||||
|
background: @sidebar-background-2;
|
||||||
|
|
||||||
|
.book-search {
|
||||||
|
background: @sidebar-search-background-2;
|
||||||
|
|
||||||
|
input, input:focus {
|
||||||
|
border: 1px solid @sidebar-search-input-border-color-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.summary {
|
||||||
|
li {
|
||||||
|
&.divider {
|
||||||
|
background: @sidebar-divider-color-2;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
i.fa-check {
|
||||||
|
color: @sidebar-icon-color-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.done > a {
|
||||||
|
color: @sidebar-link-completed-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
a, span {
|
||||||
|
color: @sidebar-link-color-2;
|
||||||
|
background: @sidebar-link-background-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active > a, a:hover {
|
||||||
|
color: @sidebar-link-hover-color-2;
|
||||||
|
background: @sidebar-link-hover-background-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,11 +5,10 @@
|
|||||||
@import "variables.less";
|
@import "variables.less";
|
||||||
@import "fonts.less";
|
@import "fonts.less";
|
||||||
|
|
||||||
@import "highlight.less";
|
|
||||||
|
|
||||||
@import "book/languages.less";
|
@import "book/languages.less";
|
||||||
@import "book/header.less";
|
@import "book/header.less";
|
||||||
@import "book/summary.less";
|
@import "book/summary.less";
|
||||||
|
@import "book/font-settings.less";
|
||||||
@import "book/body.less";
|
@import "book/body.less";
|
||||||
@import "book/exercise.less";
|
@import "book/exercise.less";
|
||||||
@import "book/quiz.less";
|
@import "book/quiz.less";
|
||||||
@@ -25,10 +24,6 @@
|
|||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control {
|
|
||||||
.box-shadow(none);
|
|
||||||
}
|
|
||||||
|
|
||||||
html, body {
|
html, body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
Executable → Regular
+12
-12
@@ -1,8 +1,9 @@
|
|||||||
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
|
/* http://jmblog.github.io/color-themes-for-highlightjs */
|
||||||
|
|
||||||
/* Tomorrow Comment */
|
/* Tomorrow Comment */
|
||||||
.hljs-comment {
|
.hljs-comment,
|
||||||
color: hsl(207, 35%, 35%);
|
.hljs-title {
|
||||||
|
color: #8e908c;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tomorrow Red */
|
/* Tomorrow Red */
|
||||||
@@ -27,9 +28,9 @@
|
|||||||
.hljs-pragma,
|
.hljs-pragma,
|
||||||
.hljs-built_in,
|
.hljs-built_in,
|
||||||
.hljs-literal,
|
.hljs-literal,
|
||||||
.hljs-constant,
|
.hljs-params,
|
||||||
.hljs-function .hljs-title {
|
.hljs-constant {
|
||||||
color: hsl(50, 100%, 60%);
|
color: #f5871f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tomorrow Yellow */
|
/* Tomorrow Yellow */
|
||||||
@@ -45,7 +46,7 @@
|
|||||||
.hljs-header,
|
.hljs-header,
|
||||||
.ruby .hljs-symbol,
|
.ruby .hljs-symbol,
|
||||||
.xml .hljs-cdata {
|
.xml .hljs-cdata {
|
||||||
color: hsl(0, 100%, 70%);
|
color: #718c00;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tomorrow Aqua */
|
/* Tomorrow Aqua */
|
||||||
@@ -54,7 +55,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Tomorrow Blue */
|
/* Tomorrow Blue */
|
||||||
.hljs-function .keyword,
|
.hljs-function,
|
||||||
.python .hljs-decorator,
|
.python .hljs-decorator,
|
||||||
.python .hljs-title,
|
.python .hljs-title,
|
||||||
.ruby .hljs-function .hljs-title,
|
.ruby .hljs-function .hljs-title,
|
||||||
@@ -62,13 +63,13 @@
|
|||||||
.perl .hljs-sub,
|
.perl .hljs-sub,
|
||||||
.javascript .hljs-title,
|
.javascript .hljs-title,
|
||||||
.coffeescript .hljs-title {
|
.coffeescript .hljs-title {
|
||||||
color: hsl(207, 70%, 60%);
|
color: #4271ae;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tomorrow Purple */
|
/* Tomorrow Purple */
|
||||||
.hljs-keyword,
|
.hljs-keyword,
|
||||||
.javascript .hljs-function {
|
.javascript .hljs-function {
|
||||||
color: hsl(207, 95%, 70%);
|
color: #8959a8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hljs {
|
.hljs {
|
||||||
@@ -76,7 +77,6 @@
|
|||||||
background: white;
|
background: white;
|
||||||
color: #4d4d4c;
|
color: #4d4d4c;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
font-family: "Anonymous Pro", "Inconsolata", "Monaco", monospace;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.coffeescript .javascript,
|
.coffeescript .javascript,
|
||||||
@@ -87,4 +87,4 @@
|
|||||||
.xml .css,
|
.xml .css,
|
||||||
.xml .hljs-cdata {
|
.xml .hljs-cdata {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,47 @@
|
|||||||
h1, h2 {
|
@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-after: avoid;
|
||||||
page-break-before: auto;
|
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 {
|
pre, blockquote {
|
||||||
border: 1px solid #999;
|
border: 1px solid #999;
|
||||||
page-break-inside: avoid;
|
page-break-inside: avoid;
|
||||||
@@ -26,41 +65,36 @@ section {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&#summary {
|
&#summary {
|
||||||
text-align: center;
|
margin: 1.5cm;
|
||||||
|
|
||||||
ul {
|
h1 {
|
||||||
font-size: 0.5cm;
|
text-align: center;
|
||||||
line-height: 1.8em;
|
|
||||||
padding: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
list-style: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
> ul {
|
ol {
|
||||||
> li {
|
list-style: none;
|
||||||
margin-bottom: 1cm;
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
margin-left: 1cm;
|
||||||
|
}
|
||||||
|
|
||||||
> a {
|
> ol {
|
||||||
font-size: 0.6cm;
|
> li {
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
article {
|
article {
|
||||||
margin: 1.5cm;
|
margin-bottom: 1.5cm;
|
||||||
|
|
||||||
/* Big centered title */
|
|
||||||
&.new-chapter {
|
&.new-chapter {
|
||||||
page-break-after: always;
|
page-break-after: always;
|
||||||
font-size: 0.6cm;
|
font-size: 0.6cm;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 3cm 0cm;
|
padding: 3cm 0cm;
|
||||||
|
|
||||||
border-top: 1px solid #ccc;
|
h1 {
|
||||||
|
font-size: floor(@font-size-base * 2.7);
|
||||||
@media print {
|
|
||||||
border: none;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
@sidebar-link-completed-weight: normal;
|
@sidebar-link-completed-weight: normal;
|
||||||
|
|
||||||
// Page
|
// Page
|
||||||
|
@page-color: black;
|
||||||
@page-background: white;
|
@page-background: white;
|
||||||
|
|
||||||
// Progress Bar
|
// Progress Bar
|
||||||
@@ -79,5 +80,94 @@
|
|||||||
@font-family-sans: "Open Sans", "Clear Sans", "Helvetica Neue", Helvetica, Arial, sans-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-serif;
|
||||||
|
|
||||||
|
|
||||||
@FontPath: '@{staticPath}/fonts';
|
@FontPath: '@{staticPath}/fonts';
|
||||||
@fa-font-path: "@{FontPath}/fontawesome";
|
@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;
|
||||||
|
|
||||||
|
/*
|
||||||
|
,--------.,--.
|
||||||
|
'--. .--'| ,---. ,---. ,--,--,--. ,---. ,---. .--.
|
||||||
|
| | | .-. || .-. :| || .-. :( .-' '--'
|
||||||
|
| | | | | |\ --.| | | |\ --..-' `).--.
|
||||||
|
`--' `--' `--' `----'`--`--`--' `----'`----' '--'
|
||||||
|
*/
|
||||||
|
// Header
|
||||||
|
@header-color-1: #AFA790;
|
||||||
|
@header-color-2: #7e888b;
|
||||||
|
@header-background-1: #ECE3C4;
|
||||||
|
@header-background-2: #1d1f21;
|
||||||
|
@header-button-color-1: #AFA790;
|
||||||
|
@header-button-color-2: #7e888b;
|
||||||
|
@header-button-hover-color-1: #73553C;
|
||||||
|
@header-button-hover-color-2: #C9C9C9;
|
||||||
|
@header-button-hover-background-1: #E2DABE;
|
||||||
|
@header-button-hover-background-2: #0B0D0E;
|
||||||
|
|
||||||
|
// Body
|
||||||
|
@body-background-1: #F3EACB;
|
||||||
|
@body-background-2: #1d1f21;
|
||||||
|
@body-pagination-background-1: #FFFAEA;
|
||||||
|
@body-pagination-background-2: #33404D;
|
||||||
|
|
||||||
|
// Sidebar
|
||||||
|
@sidebar-color-1: #AFA790;
|
||||||
|
@sidebar-color-2: hsl(207, 15%, 80%);
|
||||||
|
@sidebar-background-1: #F3EACB;
|
||||||
|
@sidebar-background-2: #111111;
|
||||||
|
|
||||||
|
@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-divider-color-1: hsl(207, 15%, 85%);
|
||||||
|
@sidebar-divider-color-2: #1d1f21;
|
||||||
|
|
||||||
|
@sidebar-link-color-1: #877F6A;
|
||||||
|
@sidebar-link-color-2: hsl(207, 15%, 50%);
|
||||||
|
@sidebar-link-background-1: transparent;
|
||||||
|
@sidebar-link-background-2: transparent;
|
||||||
|
@sidebar-link-hover-color-1: #704214;
|
||||||
|
@sidebar-link-hover-color-2: hsl(207, 100%, 50%);
|
||||||
|
@sidebar-link-hover-background-1: transparent;
|
||||||
|
@sidebar-link-hover-background-2: transparent;
|
||||||
|
|
||||||
|
@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-weight-1: normal;
|
||||||
|
@sidebar-link-completed-weight-2: normal;
|
||||||
|
|
||||||
|
// Page
|
||||||
|
@page-color-1: #704214;
|
||||||
|
@page-color-2: #a4b1b1;
|
||||||
|
@page-background-1: #F3EACB;
|
||||||
|
@page-background-2: #1d1f21;
|
||||||
|
|
||||||
|
@bar-background-1: #F3EACB;
|
||||||
|
@bar-background-2: #1d1f21;
|
||||||
|
@bar-progress-background-1: #704214;
|
||||||
|
@bar-progress-background-2: hsl(120, 60%, 50%);
|
||||||
|
|
||||||
|
// Basics of a navbar
|
||||||
|
@navbar-default-border-1: #d5d5d5;
|
||||||
|
@navbar-default-border-2: #d5d5d5;
|
||||||
|
|
||||||
|
// Navbar brand label
|
||||||
|
@navbar-default-color-1: #333;
|
||||||
|
@navbar-default-color-2: #333;
|
||||||
|
@navbar-default-link-color-1: #333;
|
||||||
|
@navbar-default-link-color-2: #333;
|
||||||
|
@navbar-default-brand-color-1: @navbar-default-link-color;
|
||||||
|
@navbar-default-brand-color-2: @navbar-default-link-color;
|
||||||
|
@navbar-default-brand-hover-color-1: @navbar-default-link-color;
|
||||||
|
@navbar-default-brand-hover-color-2: @navbar-default-link-color;
|
||||||
|
@navbar-default-brand-hover-bg-1: transparent;
|
||||||
|
@navbar-default-brand-hover-bg-2: transparent;
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<div class="dropdown-menu font-settings">
|
||||||
|
<div class="dropdown-caret">
|
||||||
|
<span class="caret-outer"></span>
|
||||||
|
<span class="caret-inner"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-group btn-block">
|
||||||
|
<button id="reduce-font-size" class="btn btn-default">A</button>
|
||||||
|
<button id="enlarge-font-size" class="btn btn-default">A</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="list-group font-family-list">
|
||||||
|
<li class="list-group-item" data-font="0">Serif</li>
|
||||||
|
<li class="list-group-item" data-font="1">Sans</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="btn-group btn-group-xs btn-block color-theme-list">
|
||||||
|
<button type="button" class="btn btn-default" id="color-theme-preview-0" data-theme="0">White</button>
|
||||||
|
<button type="button" class="btn btn-default" id="color-theme-preview-1" data-theme="1">Sepia</button>
|
||||||
|
<button type="button" class="btn btn-default" id="color-theme-preview-2" data-theme="2">Night</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1,21 +1,35 @@
|
|||||||
<div class="book-header">
|
<div class="book-header">
|
||||||
<!-- Actions Left -->
|
<!-- Actions Left -->
|
||||||
{% if githubId %}
|
{% if options.links.home !== false and (options.links.home != null or githubId) %}
|
||||||
<a href="{{ githubHost }}{{ githubId }}" target="_blank" class="btn pull-left"><i class="fa fa-github-alt"></i></a>
|
<a href="{{ options.links.home|default(githubHost+githubId) }}" target="_blank" class="btn pull-left"><i class="fa fa-bookmark-o"></i></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="#" class="btn pull-left toggle-summary"><i class="fa fa-align-justify"></i></a>
|
<a href="#" class="btn pull-left toggle-summary"><i class="fa fa-align-justify"></i></a>
|
||||||
<a href="#" class="btn pull-left toggle-search"><i class="fa fa-search"></i></a>
|
<a href="#" class="btn pull-left toggle-search"><i class="fa fa-search"></i></a>
|
||||||
|
<span id="font-settings-wrapper">
|
||||||
|
<a href="#" class="btn pull-left toggle-font-settings"><i class="fa fa-font"></i>
|
||||||
|
</a>
|
||||||
|
{% include "./font-settings.html" %}
|
||||||
|
</span>
|
||||||
|
|
||||||
<!-- Actions Right -->
|
<!-- Actions Right -->
|
||||||
|
{% if options.links.sharing.google !== false %}
|
||||||
<a href="#" target="_blank" class="btn pull-right" data-sharing="google-plus"><i class="fa fa-google-plus"></i></a>
|
<a href="#" target="_blank" class="btn pull-right" data-sharing="google-plus"><i class="fa fa-google-plus"></i></a>
|
||||||
|
{% endif %}
|
||||||
|
{% if options.links.sharing.facebook !== false %}
|
||||||
<a href="#" target="_blank" class="btn pull-right" data-sharing="facebook"><i class="fa fa-facebook"></i></a>
|
<a href="#" target="_blank" class="btn pull-right" data-sharing="facebook"><i class="fa fa-facebook"></i></a>
|
||||||
|
{% endif %}
|
||||||
|
{% if options.links.sharing.twitter !== false %}
|
||||||
<a href="#" target="_blank" class="btn pull-right" data-sharing="twitter"><i class="fa fa-twitter"></i></a>
|
<a href="#" target="_blank" class="btn pull-right" data-sharing="twitter"><i class="fa fa-twitter"></i></a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if githubId %}
|
{% if githubId %}
|
||||||
<a href="{{ githubHost }}{{ githubId }}/stargazers" target="_blank" class="btn pull-right count-star"><i class="fa fa-star-o"></i> Star (<span>-</span>)</a>
|
<a href="{{ githubHost }}{{ githubId }}/stargazers" target="_blank" class="btn pull-right count-star hidden-xs"><i class="fa fa-star-o"></i> Star (<span>-</span>)</a>
|
||||||
<a href="{{ githubHost }}{{ githubId }}/watchers" target="_blank" class="btn pull-right count-watch"><i class="fa fa-eye"></i> Watch (<span>-</span>)</a>
|
<a href="{{ githubHost }}{{ githubId }}/watchers" target="_blank" class="btn pull-right count-watch hidden-xs"><i class="fa fa-eye"></i> Watch (<span>-</span>)</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<h1><a href="{{ basePath }}/" >{{ title }}</a></h1>
|
<h1>
|
||||||
|
<i class="fa fa-spinner fa-spin"></i>
|
||||||
|
<a href="{{ basePath }}/" >{{ title }}</a>
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,18 +3,28 @@
|
|||||||
<input type="text" placeholder="Search" class="form-control" />
|
<input type="text" placeholder="Search" class="form-control" />
|
||||||
</div>
|
</div>
|
||||||
<ul class="summary">
|
<ul class="summary">
|
||||||
{% if githubId %}
|
{% if options.links.about !== false && (options.links.about != null || githubId) %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ githubHost }}{{ githubAuthor }}" target="blank">About the author</a>
|
<a href="{{ options.links.about|default(githubHost+githubAuthor) }}" target="blank">About the author</a>
|
||||||
</li>
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if options.links.issues !== false && (options.links.issues != null || githubId) %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ githubHost }}{{ githubId }}/issues" target="blank">Questions and Issues</a>
|
<a href="{{ options.links.issues|default(githubHost+githubId+"/issues") }}" target="blank">Questions and Issues</a>
|
||||||
</li>
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if options.links.contribute !== false && (options.links.contribute != null || githubId) %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ githubHost }}{{ githubId }}/edit/master/{{ _input }}" target="blank">Edit and Contribute</a>
|
<a href="{{ options.links.contribute|default(githubHost+githubId+"/edit/master/"+_input) }}" target="blank">Edit and Contribute</a>
|
||||||
</li>
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if options.links.contribute || options.links.issues || options.links.about || githubId %}
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<li data-level="0" data-path="index.html">
|
<li data-level="0" data-path="index.html">
|
||||||
<a href="{{ basePath }}/"><i class="fa fa-check"></i> Introduction</a>
|
<a href="{{ basePath }}/"><i class="fa fa-check"></i> Introduction</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -44,5 +54,12 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
{% if options.links.gitbook !== false %}
|
||||||
|
<li class="divider"></li>
|
||||||
|
<li>
|
||||||
|
<a href="http://www.gitbook.io/" target="blank">Generated using GitBook</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE HTML>
|
<!DOCTYPE HTML>
|
||||||
<html lang="en-US">
|
<html lang="en-US" {% block htmlTag %}{% endblock %}>
|
||||||
<head prefix="og: http://ogp.me/ns# book: http://ogp.me/ns/book#">
|
<head prefix="og: http://ogp.me/ns# book: http://ogp.me/ns/book#">
|
||||||
{% block head %}
|
{% block head %}
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
@@ -35,10 +35,10 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% block content %}{% endblock %}
|
|
||||||
{% block style %}
|
{% block style %}
|
||||||
<link rel="stylesheet" href="{{ staticBase }}/style.css">
|
<link rel="stylesheet" href="{{ staticBase }}/style.css">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
{% block content %}{% endblock %}
|
||||||
{% block javascript %}
|
{% block javascript %}
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/ace.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/ace.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/mode-javascript.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/mode-javascript.js"></script>
|
||||||
|
|||||||
+49
-56
@@ -3,10 +3,32 @@
|
|||||||
{% block title %}{{ title }}{% endblock %}
|
{% block title %}{{ title }}{% endblock %}
|
||||||
|
|
||||||
{% block style %}
|
{% block style %}
|
||||||
{% parent %}
|
|
||||||
<link rel="stylesheet" href="{{ staticBase }}/print.css">
|
<link rel="stylesheet" href="{{ staticBase }}/print.css">
|
||||||
{% endblock %}
|
{% 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">Exercise #{{ exercise }}</div>
|
||||||
|
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||||
|
{% autoescape false %}{{ section.quiz.base }}{% endautoescape %}
|
||||||
|
{% set exercise = exercise + 1 %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% set exercise = 1 %}
|
{% set exercise = 1 %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{# Cover #}
|
{# Cover #}
|
||||||
@@ -19,87 +41,58 @@
|
|||||||
|
|
||||||
{# Summary #}
|
{# Summary #}
|
||||||
<section id="summary">
|
<section id="summary">
|
||||||
<h1>Summary</h1>
|
<h1>Table of Contents</h1>
|
||||||
|
<ol>
|
||||||
<ul class="summary">
|
|
||||||
<li>
|
<li>
|
||||||
<a href="#README.md">Introduction</a>
|
<a href="#README.md">
|
||||||
|
<h2>Introduction</h2>
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% for item in summary.chapters %}
|
{% for item in summary.chapters %}
|
||||||
<li>
|
<li>
|
||||||
<a href="#{{ item.path }}">{{ item.level }}) {{ item.title }}</a>
|
<h2><span>{{ item.level }}.</span> <a href="#{{ item.path }}">{{ item.title }}</a></h2>
|
||||||
{% if item.articles.length > 0 %}
|
{% if item.articles.length > 0 %}
|
||||||
<ul>
|
<ol>
|
||||||
{% for article in item.articles %}
|
{% for article in item.articles %}
|
||||||
<li>
|
<li>
|
||||||
<a href="#{{ article.path }}">{{ article.level }}) {{ article.title }}</a>
|
<span>{{ article.level }}</span> <a href="#{{ article.path }}">{{ article.title }}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ol>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ol>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{# Pages content #}
|
{# Pages content #}
|
||||||
<section>
|
<section>
|
||||||
<article id="README.md">
|
<article id="README.md">
|
||||||
{% for section in pages["README.md"].content %}
|
{{ articleContent(pages["README.md"].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">Exercise #{{ exercise }}</div>
|
|
||||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
|
||||||
{% autoescape false %}{{ section.quiz.base }}{% endautoescape %}
|
|
||||||
{% set exercise = exercise + 1 %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</article>
|
</article>
|
||||||
</section>
|
</section>
|
||||||
{% for item in summary.chapters %}
|
{% for item in summary.chapters %}
|
||||||
{% if item.articles.length > 0 %}
|
|
||||||
<section>
|
<section>
|
||||||
<article id="{{ item.path }}" class="new-chapter">
|
<article id="{{ item.path }}" class="new-chapter">
|
||||||
<h1>{{ item.title }}</h1>
|
<h1>{{ item.title }}</h1>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
{% for article in item.articles %}
|
{% if pages[item.path] %}
|
||||||
{% if pages[article.path] %}
|
<article>
|
||||||
<article id="{{ article.path }}">
|
{{ articleContent(pages[item.path].content) }}
|
||||||
{% for section in pages[article.path].content %}
|
</article>
|
||||||
{% if section.type == "normal" %}
|
{% endif %}
|
||||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
|
||||||
{% elif section.type == "exercise" %}
|
{% if item.articles.length > 0 %}
|
||||||
<div class="exercise">
|
{% for article in item.articles %}
|
||||||
<div class="exercise-header">Exercise #{{ exercise }}</div>
|
{% if pages[article.path] %}
|
||||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
<article id="{{ article.path }}">
|
||||||
<pre><code>{% autoescape false %}{{ section.code.base|code }}{% endautoescape %}</code></pre>
|
{{ articleContent(pages[article.path].content) }}
|
||||||
{% set exercise = exercise + 1 %}
|
</article>
|
||||||
</div>
|
{% endif %}
|
||||||
{% elif section.type == "quiz" %}
|
{% endfor %}
|
||||||
<div class="quiz">
|
{% endif %}
|
||||||
<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 %}
|
|
||||||
</article>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</section>
|
</section>
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
|
|
||||||
|
{% block htmlTag %}manifest="{{ basePath }}/manifest.appcache"{% endblock %}
|
||||||
{% block title %}{{ progress.current.title }}{% parent %}{% endblock %}
|
{% block title %}{{ progress.current.title }}{% parent %}{% endblock %}
|
||||||
{% block content %}
|
{% 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" {% if githubId %}data-github="{{ githubId }}"{% endif %} data-level="{{ progress.current.level }}" data-basepath="{{ basePath }}" data-revision="{{ revision }}">
|
||||||
@@ -27,11 +28,39 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if progress.current.prev and progress.current.prev.path %}
|
{% if progress.current.prev and progress.current.prev.path %}
|
||||||
<a href="{{ basePath }}/{{ progress.current.prev.path|mdLink }}" class="navigation navigation-prev"><i class="fa fa-angle-left"></i></a>
|
<a href="{{ basePath }}/{{ progress.current.prev.path|mdLink }}" class="navigation navigation-prev {% if !progress.current.next or !progress.current.next.path %}navigation-unique{% endif %}"><i class="fa fa-angle-left"></i></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if progress.current.next and progress.current.next.path %}
|
{% if progress.current.next and progress.current.next.path %}
|
||||||
<a href="{{ basePath }}/{{ progress.current.next.path|mdLink }}" class="navigation navigation-next"><i class="fa fa-angle-right"></i></a>
|
<a href="{{ basePath }}/{{ progress.current.next.path|mdLink }}" class="navigation navigation-next {% if !progress.current.prev or !progress.current.prev.path %}navigation-unique{% endif %}"><i class="fa fa-angle-right"></i></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block javascript %}
|
||||||
|
{% parent %}
|
||||||
|
{% for resource in plugins.resources.js %}
|
||||||
|
{% if resource.url %}
|
||||||
|
<script src="{{ resource.url }}"></script>
|
||||||
|
{% else %}
|
||||||
|
<script src="{{ staticBase }}/plugins/{{ resource.path }}"></script>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
<script>
|
||||||
|
require(["gitbook"], function(gitbook) {
|
||||||
|
var config = {% autoescape false %}{{ pluginsConfig }}{% endautoescape %};
|
||||||
|
gitbook.start(config);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block style %}
|
||||||
|
{% parent %}
|
||||||
|
{% 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 %}
|
||||||
|
|||||||
Reference in New Issue
Block a user