mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-26 12:18:01 +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
|
||||
- Herman Starikov
|
||||
|
||||
+1
-5
@@ -24,7 +24,7 @@ module.exports = function (grunt) {
|
||||
requirejs: {
|
||||
compile: {
|
||||
options: {
|
||||
name: "app",
|
||||
name: "gitbook",
|
||||
baseUrl: "theme/javascript/",
|
||||
out: "theme/assets/app.js",
|
||||
preserveLicenseComments: false,
|
||||
@@ -35,7 +35,6 @@ module.exports = function (grunt) {
|
||||
"lodash": 'vendors/lodash',
|
||||
"requireLib": 'vendors/require',
|
||||
"Mousetrap": 'vendors/mousetrap',
|
||||
"mixpanel": 'vendors/mixpanel',
|
||||
"lunr": path.join(__dirname, "node_modules/lunr/lunr")
|
||||
},
|
||||
shim: {
|
||||
@@ -48,9 +47,6 @@ module.exports = function (grunt) {
|
||||
'Mousetrap': {
|
||||
exports: 'Mousetrap'
|
||||
},
|
||||
'mixpanel': {
|
||||
exports: 'mixpanel'
|
||||
},
|
||||
'lunr': {
|
||||
exports: 'lunr'
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ Apache License
|
||||
same "printed page" as the copyright notice for easier
|
||||
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");
|
||||
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,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
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)
|
||||
|
||||
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:
|
||||
|
||||
```
|
||||
-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
|
||||
-f, --format <name> Change generation format, defaults to site, availables are: site, page, pdf, json
|
||||
-i, --intro <intro> Description of the book to generate
|
||||
--githubHost <url> The url of the github host (defaults to https://github.com/)
|
||||
--theme <path> Path to theme directory
|
||||
--config <config file> Configuration file to use, defaults to book.json
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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 Q = require('q');
|
||||
var _ = require('lodash');
|
||||
var fs = require('fs');
|
||||
|
||||
var utils = require('./utils');
|
||||
|
||||
var generate = require("../lib/generate");
|
||||
var parse = require("../lib/parse");
|
||||
var fs = require('../lib/generate/fs');
|
||||
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) {
|
||||
return function(dir, options) {
|
||||
dir = dir || process.cwd();
|
||||
outputDir = options.output
|
||||
outputDir = options.output;
|
||||
|
||||
console.log('Starting build ...');
|
||||
// Get repo's URL
|
||||
return utils.gitURL(dir)
|
||||
.then(function(url) {
|
||||
// Get ID of repo
|
||||
return utils.githubID(url);
|
||||
}, function(err) {
|
||||
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
|
||||
})
|
||||
);
|
||||
})
|
||||
return converter(
|
||||
_.extend({}, options || {}, {
|
||||
input: dir,
|
||||
output: outputDir,
|
||||
generator: options.format,
|
||||
configFile: options.config
|
||||
})
|
||||
)
|
||||
.then(function(output) {
|
||||
console.log("Successfuly built !");
|
||||
return output;
|
||||
}, utils.logError);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
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 build = require('./build');
|
||||
var Server = require('./server');
|
||||
|
||||
// General options
|
||||
prog
|
||||
.version(pkg.version);
|
||||
|
||||
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('-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]'))
|
||||
build.command(prog.command('build [source_dir]'))
|
||||
.description('Build a gitbook from a directory')
|
||||
.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')
|
||||
.option('-p, --port <port>', 'Port for server to listen on', 4000)
|
||||
.option('--no-watch', 'Disable restart with file watching')
|
||||
.action(function(dir, options) {
|
||||
build.folder(dir, options || {})
|
||||
.then(function(_options) {
|
||||
console.log();
|
||||
console.log('Starting server ...');
|
||||
return utils.serveDir(_options.output, options.port)
|
||||
var server = new Server();
|
||||
|
||||
var generate = function() {
|
||||
if (server.isRunning()) console.log("Stopping server");
|
||||
|
||||
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);
|
||||
})
|
||||
.then(function() {
|
||||
console.log('Serving book on http://localhost:'+options.port);
|
||||
console.log();
|
||||
console.log('Press CTRL+C to quit ...');
|
||||
});
|
||||
};
|
||||
|
||||
console.log('Press CTRL+C to quit ...');
|
||||
console.log('')
|
||||
generate();
|
||||
});
|
||||
|
||||
buildCommand(prog.command('pdf [source_dir]'))
|
||||
build.command(prog.command('pdf [source_dir]'))
|
||||
.description('Build a gitbook as a PDF')
|
||||
.option('-pf, --paperformat <format>', 'PDF paper format (default is A4): "5in*7.5in", "10cm*20cm", "A4", "Letter"')
|
||||
.action(function(dir, options) {
|
||||
@@ -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')
|
||||
.option('-c, --cover <path>', 'Cover image, default is cover.png if exists')
|
||||
.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 send = require('send');
|
||||
|
||||
var url = require('url');
|
||||
var cp = require('child_process');
|
||||
var path = require('path');
|
||||
var Gaze = require('gaze').Gaze;
|
||||
|
||||
|
||||
// Get the remote of a given repo
|
||||
function gitURL(path) {
|
||||
function watch(dir) {
|
||||
var d = Q.defer();
|
||||
dir = path.resolve(dir);
|
||||
|
||||
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);
|
||||
var gaze = new Gaze("**/*.md", {
|
||||
cwd: dir
|
||||
});
|
||||
|
||||
return d.promise
|
||||
.then(function(output) {
|
||||
return output.replace(/(\r\n|\n|\r)/gm, "");
|
||||
gaze.once("all", function(e, filepath) {
|
||||
gaze.close();
|
||||
|
||||
d.resolve(filepath);
|
||||
});
|
||||
}
|
||||
gaze.once("error", function(err) {
|
||||
gaze.close();
|
||||
|
||||
// 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();});
|
||||
}
|
||||
|
||||
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);
|
||||
d.reject(err);
|
||||
});
|
||||
|
||||
return d.promise;
|
||||
}
|
||||
@@ -93,9 +37,6 @@ function logError(err) {
|
||||
|
||||
// Exports
|
||||
module.exports = {
|
||||
gitURL: gitURL,
|
||||
githubID: githubID,
|
||||
titleCase: titleCase,
|
||||
serveDir: serveDir,
|
||||
watch: watch,
|
||||
logError: logError
|
||||
};
|
||||
|
||||
+3
-1
@@ -16,7 +16,9 @@ var getFiles = function(path) {
|
||||
|
||||
// Add extra rules to ignore common folders
|
||||
ig.addIgnoreRules([
|
||||
'.git/'
|
||||
'.git/',
|
||||
'.gitignore',
|
||||
'.DS_Store'
|
||||
], '__custom_stuff');
|
||||
|
||||
// Push each file to our list
|
||||
|
||||
@@ -1,9 +1,30 @@
|
||||
var _ = require("lodash");
|
||||
var path = require("path");
|
||||
var Q = require("q");
|
||||
var fs = require("./fs");
|
||||
|
||||
var Plugin = require("./plugin");
|
||||
|
||||
var BaseGenerator = function(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) {
|
||||
|
||||
+69
-13
@@ -6,6 +6,7 @@ var tmp = require('tmp');
|
||||
|
||||
var fs = require("./fs");
|
||||
var parse = require("../parse");
|
||||
var Plugin = require("./plugin");
|
||||
|
||||
var generators = {
|
||||
"site": require("./site"),
|
||||
@@ -27,22 +28,34 @@ var generate = function(options) {
|
||||
|
||||
options = _.defaults(options || {}, {
|
||||
// Folders to use
|
||||
input: null,
|
||||
output: null,
|
||||
"input": null,
|
||||
"output": null,
|
||||
|
||||
// Config file (relative to input)
|
||||
"configFile": "book.json",
|
||||
|
||||
// Output generator
|
||||
generator: "site",
|
||||
"generator": "site",
|
||||
|
||||
// Book title, keyword, description
|
||||
title: null,
|
||||
description: null,
|
||||
"title": null,
|
||||
"description": null,
|
||||
|
||||
// Origin github repository id
|
||||
github: null,
|
||||
githubHost: 'https://github.com/',
|
||||
"github": null,
|
||||
"githubHost": 'https://github.com/',
|
||||
|
||||
// Theming
|
||||
theme: path.resolve(__dirname, '../../theme')
|
||||
"theme": path.resolve(__dirname, '../../theme'),
|
||||
|
||||
// Plugins
|
||||
"plugins": [],
|
||||
"pluginsConfig": {},
|
||||
|
||||
// Links
|
||||
"links": {
|
||||
"about": null
|
||||
}
|
||||
});
|
||||
|
||||
if (!options.input) {
|
||||
@@ -58,6 +71,35 @@ var generate = function(options) {
|
||||
// Clean output folder
|
||||
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() {
|
||||
return fs.mkdirp(options.output);
|
||||
})
|
||||
@@ -70,9 +112,10 @@ var generate = function(options) {
|
||||
})
|
||||
})
|
||||
|
||||
// Create the generator
|
||||
// Create the generator and load plugins
|
||||
.then(function() {
|
||||
generator = new generators[options.generator](options);
|
||||
return generator.loadPlugins();
|
||||
})
|
||||
|
||||
// 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
|
||||
.then(function() {
|
||||
return Q.all(
|
||||
@@ -151,9 +203,12 @@ var generate = function(options) {
|
||||
);
|
||||
})
|
||||
|
||||
// Finish gneration
|
||||
// Finish generation
|
||||
.then(function() {
|
||||
return generator.finish();
|
||||
})
|
||||
.then(function() {
|
||||
return generator.callHook("finish");
|
||||
});
|
||||
}
|
||||
})
|
||||
@@ -177,7 +232,7 @@ var generateFile = function(options) {
|
||||
return Q.nfcall(tmp.dir)
|
||||
.then(function(tmpDir) {
|
||||
return generate(
|
||||
_.extend({},
|
||||
_.extend({},
|
||||
options,
|
||||
{
|
||||
output: tmpDir
|
||||
@@ -195,7 +250,7 @@ var generateFile = function(options) {
|
||||
_outputFile = _outputFile.slice(0, -path.extname(_outputFile).length)+"_"+lang+path.extname(_outputFile);
|
||||
_tmpDir = path.join(_tmpDir, lang);
|
||||
}
|
||||
|
||||
|
||||
return fs.copy(
|
||||
path.join(_tmpDir, "index."+ext),
|
||||
_outputFile
|
||||
@@ -225,5 +280,6 @@ var generateFile = function(options) {
|
||||
module.exports = {
|
||||
generators: generators,
|
||||
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() {
|
||||
BaseGenerator.apply(this, arguments);
|
||||
|
||||
// Load base template
|
||||
this.template = swig.compileFile(path.resolve(this.options.theme, 'templates/page.html'));
|
||||
|
||||
// List of pages content
|
||||
this.pages = {};
|
||||
};
|
||||
util.inherits(Generator, BaseGenerator);
|
||||
|
||||
// Load all templates
|
||||
Generator.prototype.loadTemplates = function() {
|
||||
this.template = swig.compileFile(
|
||||
this.plugins.template("page") || path.resolve(this.options.theme, 'templates/page.html')
|
||||
);
|
||||
};
|
||||
|
||||
Generator.prototype.convertFile = function(content, input) {
|
||||
var that = this;
|
||||
var json = {
|
||||
@@ -46,6 +50,7 @@ Generator.prototype.convertFile = function(content, input) {
|
||||
repo: that.options.githubId,
|
||||
dir: path.dirname(input) || '/',
|
||||
outdir: './',
|
||||
singleFile: true
|
||||
});
|
||||
})
|
||||
.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 indexer = require('./search_indexer');
|
||||
var Manifest = require('../manifest');
|
||||
|
||||
// Swig filter for returning the count of lines in a code section
|
||||
swig.setFilter('lines', function(content) {
|
||||
@@ -30,17 +31,39 @@ var Generator = function() {
|
||||
|
||||
this.revision = Date.now();
|
||||
this.indexer = indexer();
|
||||
|
||||
// Load base template
|
||||
this.template = swig.compileFile(path.resolve(this.options.theme, 'templates/site.html'));
|
||||
this.langsTemplate = swig.compileFile(path.resolve(this.options.theme, 'templates/langs.html'));
|
||||
this.manifest = new Manifest(Date.now());
|
||||
this.manifest.add("NETWORK", [
|
||||
'*'
|
||||
]);
|
||||
};
|
||||
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
|
||||
Generator.prototype._writeTemplate = function(tpl, options, output) {
|
||||
Generator.prototype._writeTemplate = function(tpl, options, output, interpolate) {
|
||||
var that = this;
|
||||
|
||||
interpolate = interpolate || _.identity;
|
||||
|
||||
return Q()
|
||||
.then(function(sections) {
|
||||
return tpl(_.extend({
|
||||
@@ -54,9 +77,15 @@ Generator.prototype._writeTemplate = function(tpl, options, output) {
|
||||
githubHost: that.options.githubHost,
|
||||
|
||||
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));
|
||||
})
|
||||
.then(interpolate)
|
||||
.then(function(html) {
|
||||
return fs.writeFile(
|
||||
output,
|
||||
@@ -73,7 +102,6 @@ Generator.prototype.indexPage = function(lexed, pagePath) {
|
||||
// Convert a markdown file to html
|
||||
Generator.prototype.convertFile = function(content, _input) {
|
||||
var that = this;
|
||||
var progress = parse.progress(this.options.navigation, _input);
|
||||
|
||||
_output = _input.replace(".md", ".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 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()
|
||||
.then(function() {
|
||||
// Send content to plugins
|
||||
return _callHook("page:before");
|
||||
})
|
||||
.then(function() {
|
||||
// Lex page
|
||||
return parse.lex(content);
|
||||
return parse.lex(page.content);
|
||||
})
|
||||
.then(function(lexed) {
|
||||
// Index page in search
|
||||
@@ -101,15 +147,27 @@ Generator.prototype.convertFile = function(content, _input) {
|
||||
});
|
||||
})
|
||||
.then(function(sections) {
|
||||
page.sections = sections;
|
||||
|
||||
// Use plugin hook
|
||||
return _callHook("page");
|
||||
})
|
||||
.then(function() {
|
||||
that.manifest.add("CACHE", _output);
|
||||
|
||||
return that._writeTemplate(that.template, {
|
||||
progress: progress,
|
||||
progress: page.progress,
|
||||
|
||||
_input: _input,
|
||||
content: sections,
|
||||
content: page.sections,
|
||||
|
||||
basePath: basePath,
|
||||
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() {
|
||||
var that = this;
|
||||
|
||||
// Copy gitbook assets
|
||||
return fs.copy(
|
||||
path.join(that.options.theme, "assets"),
|
||||
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
|
||||
@@ -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() {
|
||||
return this.copyAssets()
|
||||
.then(this.writeSearchIndex);
|
||||
.then(this.writeSearchIndex)
|
||||
.then(this.writeCacheManifest);
|
||||
};
|
||||
|
||||
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'),
|
||||
progress: require('./progress'),
|
||||
navigation: require('./navigation'),
|
||||
readme: require('./readme')
|
||||
readme: require('./readme'),
|
||||
git: require('./git')
|
||||
};
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ function isQuiz(nodes) {
|
||||
|
||||
if(
|
||||
// List of questions
|
||||
listIdx !== -1 && isQuizNode(quizNodes[listIdx + 1]) &&
|
||||
listIdx !== -1 && isQuizNode(quizNodes[listIdx + 1]) ||
|
||||
|
||||
// Table of questions
|
||||
(
|
||||
|
||||
+30
-49
@@ -6,6 +6,12 @@ function clean(obj) {
|
||||
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
|
||||
/*
|
||||
{
|
||||
@@ -20,62 +26,37 @@ function navigation(summary, files) {
|
||||
// Support single files as well as list
|
||||
files = _.isArray(files) ? files : (_.isString(files) ? [files] : null);
|
||||
|
||||
// Mapping of prev/next for a give path
|
||||
var mapping = {};
|
||||
|
||||
// Special README nav
|
||||
var README_NAV = {
|
||||
path: 'README.md',
|
||||
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',
|
||||
};
|
||||
|
||||
// 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
|
||||
if(files) {
|
||||
return _.pick(mapping, files);
|
||||
|
||||
+15
-12
@@ -8,17 +8,6 @@ var renderer = require('./renderer');
|
||||
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
|
||||
function render(section, _options) {
|
||||
@@ -30,7 +19,21 @@ function render(section, _options) {
|
||||
|
||||
// Build options using defaults and our custom renderer
|
||||
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);
|
||||
|
||||
+34
-10
@@ -1,5 +1,7 @@
|
||||
var url = require('url');
|
||||
var inherits = require('util').inherits;
|
||||
var links = require('../utils').links;
|
||||
|
||||
|
||||
var path = require('path');
|
||||
|
||||
@@ -16,6 +18,7 @@ function GitBookRenderer(options, extra_options) {
|
||||
this._extra_options = extra_options;
|
||||
this.quizRowId = 0;
|
||||
this.id = rendererId++;
|
||||
this.quizIndex = 0;
|
||||
}
|
||||
inherits(GitBookRenderer, marked.Renderer);
|
||||
|
||||
@@ -38,23 +41,38 @@ GitBookRenderer.prototype._unsanitized = function(href) {
|
||||
};
|
||||
|
||||
GitBookRenderer.prototype.link = function(href, title, text) {
|
||||
// Our "fixed" href
|
||||
var _href = href;
|
||||
|
||||
// Don't build if it looks malicious
|
||||
if (this.options.sanitize && this._unsanitized(href)) {
|
||||
return '';
|
||||
return text;
|
||||
}
|
||||
|
||||
// Parsed version of the url
|
||||
var parsed = url.parse(href);
|
||||
|
||||
var o = this._extra_options;
|
||||
|
||||
// Relative link, rewrite it to point to github repo
|
||||
if(!parsed.protocol && parsed.path && parsed.path[0] != '/' && o && o.repo && o.dir) {
|
||||
href = url.resolve('https://github.com/' + o.repo + '/blob/', [o.dir, href].join("/"));
|
||||
parsed = url.parse(href);
|
||||
if(links.isRelative(_href)) {
|
||||
if (path.extname(_href) == ".md") {
|
||||
_href = links.toAbsolute(_href, o.dir || "./", o.outdir || "./");
|
||||
|
||||
if (o.singleFile) {
|
||||
_href = "#"+_href;
|
||||
} else {
|
||||
_href = _href.replace(".md", ".html");
|
||||
}
|
||||
} else if (o && o.repo && o.dir) {
|
||||
href = url.resolve('https://github.com/' + o.repo + '/blob/', [o.dir, _href].join("/"));
|
||||
parsed = url.parse(href);
|
||||
_href = parsed.href;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate HTML for link
|
||||
var out = '<a href="' + parsed.href + '"';
|
||||
var out = '<a href="' + _href + '"';
|
||||
// Title if no null
|
||||
if (title) {
|
||||
out += ' title="' + title + '"';
|
||||
@@ -78,9 +96,11 @@ GitBookRenderer.prototype.image = function(href, title, text) {
|
||||
var o = this._extra_options;
|
||||
|
||||
// Relative image, rewrite it depending output
|
||||
if(!parsed.protocol && parsed.path && parsed.path[0] != '/' && o && o.dir && o.outdir) {
|
||||
var outdir = o.outdir.charAt(o.outdir.length - 1) === '/' ? o.outdir : o.outdir + '/';
|
||||
_href = url.resolve(outdir, [o.dir, href].join('/'));
|
||||
if(links.isRelative(href) && o && o.dir && o.outdir) {
|
||||
// o.dir: directory parent of the file currently in rendering process
|
||||
// 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);
|
||||
@@ -97,10 +117,14 @@ GitBookRenderer.prototype._createCheckboxAndRadios = function(text) {
|
||||
if (!match) {
|
||||
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[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) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
module.exports = {
|
||||
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",
|
||||
"version": "0.2.0",
|
||||
"version": "0.4.2",
|
||||
"homepage": "http://www.gitbook.io/",
|
||||
"description": "Library and cmd utility to generate GitBooks",
|
||||
"main": "lib/index.js",
|
||||
"dependencies": {
|
||||
@@ -15,7 +16,14 @@
|
||||
"commander": "2.2.0",
|
||||
"fs-extra": "0.8.1",
|
||||
"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": {
|
||||
"mocha": "1.18.2",
|
||||
@@ -39,9 +47,19 @@
|
||||
"book",
|
||||
"gitbook"
|
||||
],
|
||||
"author": "Aaron O'Mullan <aaron.omullan@friendco.de>",
|
||||
"author": "FriendCode <contact@friendco.de>",
|
||||
"license": "Apache 2",
|
||||
"bugs": {
|
||||
"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)
|
||||
|
||||
[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[1].type, 'quiz');
|
||||
assert.equal(QUIZ_LEXED[2].type, 'normal');
|
||||
assert.equal(QUIZ_LEXED[3].type, 'quiz');
|
||||
});
|
||||
|
||||
it('should gen content for normal sections', function() {
|
||||
@@ -35,10 +36,6 @@ describe('Page parsing', function() {
|
||||
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() {
|
||||
assert(LEXED[1].content);
|
||||
assert(LEXED[1].code);
|
||||
@@ -87,9 +84,69 @@ describe('Relative links', function() {
|
||||
repo: 'GitBookIO/javascript',
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
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([
|
||||
"jQuery",
|
||||
"utils/execute",
|
||||
"utils/analytic",
|
||||
"core/events",
|
||||
"core/state"
|
||||
], function($, execute, analytic, state){
|
||||
], function($, execute, events, state){
|
||||
// Bind an exercise
|
||||
var prepareExercise = function($exercise) {
|
||||
var codeSolution = $exercise.find(".code-solution").text();
|
||||
@@ -19,7 +19,7 @@ define([
|
||||
$exercise.find(".action-submit").click(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
analytic.track("exercise.submit", {type: "code"});
|
||||
events.trigger("exercise.submit", {type: "code"});
|
||||
|
||||
execute("javascript", editor.getValue(), codeValidation, codeContext, function(err, result) {
|
||||
$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([
|
||||
"jQuery",
|
||||
"utils/analytic",
|
||||
"utils/path",
|
||||
"core/events",
|
||||
"core/state",
|
||||
"core/search",
|
||||
"core/progress",
|
||||
"core/exercise",
|
||||
"core/quiz"
|
||||
], function($, analytic, state, search, progress, exercises, quiz) {
|
||||
"core/quiz",
|
||||
"core/loading"
|
||||
], function($, path, events, state, search, progress, exercises, quiz, loading) {
|
||||
var prev, next;
|
||||
var githubCountStars, githubCountWatch;
|
||||
|
||||
var updateHistory = function(url, title) {
|
||||
history.pushState({ path: url }, title, url);
|
||||
};
|
||||
var usePushState = (typeof history.pushState !== "undefined");
|
||||
|
||||
var handleNavigation = function(url, push) {
|
||||
if (typeof history.pushState === "undefined") {
|
||||
var handleNavigation = function(relativeUrl, push) {
|
||||
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
|
||||
location.href = url;
|
||||
location.href = relativeUrl;
|
||||
return
|
||||
}
|
||||
|
||||
return $.get(url)
|
||||
return loading.show($.get(url)
|
||||
.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){
|
||||
return '<' + b + 'div' + ( b ? '' : ' data-element="' + c + '"' ) + d + '>';
|
||||
});
|
||||
|
||||
var $page = $(html);
|
||||
var $pageHead = $page.find("[data-element=head]");
|
||||
var $pageBody = $page.find('.book');
|
||||
|
||||
// Merge heads
|
||||
var headContent = $pageHead.html()
|
||||
@@ -38,17 +46,20 @@ define([
|
||||
});
|
||||
$("head").html(headContent);
|
||||
|
||||
// Update header, body and summary
|
||||
$('.book-header').html($page.find('.book-header').html());
|
||||
$('.book-body').html($page.find('.book-body').html());
|
||||
$('.book-summary').html($page.find('.book-summary').html());
|
||||
// Merge body
|
||||
var scrollPosition = $('.book-summary .summary').scrollTop();
|
||||
$pageBody.toggleClass("with-summary", $(".book").hasClass("with-summary"))
|
||||
|
||||
if (push) updateHistory(url, null);
|
||||
$(".book").replaceWith($pageBody);
|
||||
$('.book-summary .summary').scrollTop(scrollPosition);
|
||||
|
||||
// Update state
|
||||
state.update($("html"));
|
||||
preparePage();
|
||||
})
|
||||
.fail(function () {
|
||||
location.href = url;
|
||||
});
|
||||
.fail(function (e) {
|
||||
location.href = relativeUrl;
|
||||
}));
|
||||
};
|
||||
|
||||
var updateGitHubCounts = function() {
|
||||
@@ -101,7 +112,7 @@ define([
|
||||
}
|
||||
|
||||
// Send to mixpanel
|
||||
analytic.track("page.view");
|
||||
events.trigger("page.change");
|
||||
};
|
||||
|
||||
var handlePagination = function (e) {
|
||||
@@ -122,7 +133,7 @@ define([
|
||||
if (url) handleNavigation(url, true);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
var init = function() {
|
||||
// Prevent cache so that using the back button works
|
||||
@@ -139,14 +150,13 @@ define([
|
||||
if (event.state === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
return handleNavigation(event.state.path, false);
|
||||
};
|
||||
|
||||
$(document).on('click', ".navigation-prev", handlePagination);
|
||||
$(document).on('click', ".navigation-next", handlePagination);
|
||||
$(document).on('click', ".summary [data-path] a", handlePagination);
|
||||
|
||||
|
||||
$(window).resize(updateNavigationPosition);
|
||||
|
||||
// Prepare current page
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
define([
|
||||
"jQuery",
|
||||
"utils/execute",
|
||||
"utils/analytic",
|
||||
"core/events",
|
||||
"core/state"
|
||||
], function($, execute, analytic, state){
|
||||
], function($, execute, events, state){
|
||||
// Bind an exercise
|
||||
var prepareQuiz = function($quiz) {
|
||||
|
||||
@@ -14,14 +14,17 @@ define([
|
||||
// Submit: test code
|
||||
$quiz.find(".action-submit").click(function(e) {
|
||||
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(".alert-success,.alert-danger").addClass("hidden");
|
||||
|
||||
$quiz.find(".question").each(function(q) {
|
||||
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");
|
||||
result = result && correct;
|
||||
if (!correct) {
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
define([
|
||||
"jQuery"
|
||||
], function() {
|
||||
var $book = $(".book");
|
||||
var state = {};
|
||||
|
||||
return {
|
||||
'$book': $book,
|
||||
state.update = function(dom) {
|
||||
var $book = $(dom.find(".book"));
|
||||
|
||||
'githubId': $book.data("github"),
|
||||
'level': $book.data("level"),
|
||||
'basePath': $book.data("basepath"),
|
||||
'revision': $book.data("revision")
|
||||
state.$book = $book;
|
||||
state.githubId = $book.data("github");
|
||||
state.level = $book.data("level");
|
||||
state.basePath = $book.data("basepath");
|
||||
state.revision = $book.data("revision");
|
||||
};
|
||||
|
||||
state.update($);
|
||||
|
||||
return state;
|
||||
});
|
||||
@@ -1,17 +1,19 @@
|
||||
require([
|
||||
define([
|
||||
"jQuery",
|
||||
"utils/storage",
|
||||
"utils/analytic",
|
||||
"utils/sharing",
|
||||
"utils/appcache",
|
||||
|
||||
"core/events",
|
||||
"core/font-settings",
|
||||
"core/state",
|
||||
"core/keyboard",
|
||||
"core/navigation",
|
||||
"core/progress",
|
||||
"core/sidebar",
|
||||
"core/search"
|
||||
], function($, storage, analytic, sharing, state, keyboard, navigation, progress, sidebar, search){
|
||||
$(document).ready(function() {
|
||||
], function($, storage, appCache, sharing, events, fontSettings, state, keyboard, navigation, progress, sidebar, search){
|
||||
var start = function(config) {
|
||||
var $book;
|
||||
$book = state.$book;
|
||||
|
||||
@@ -29,10 +31,23 @@ require([
|
||||
// Init keyboard
|
||||
keyboard.init();
|
||||
|
||||
// Init appcache
|
||||
appCache.init();
|
||||
|
||||
// Bind sharing button
|
||||
sharing.init();
|
||||
|
||||
// Init navigation
|
||||
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 {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.book-body {
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
top: @header-height;
|
||||
right: 0px;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
|
||||
color: @page-color;
|
||||
background: @body-background;
|
||||
.transition(left 0.5s ease);
|
||||
|
||||
@@ -23,7 +28,6 @@
|
||||
outline: none;
|
||||
|
||||
.page-inner {
|
||||
font-family: @font-family-serif;
|
||||
max-width: 800px;
|
||||
margin: 0px auto;
|
||||
|
||||
@@ -33,8 +37,6 @@
|
||||
|
||||
background: @page-background;
|
||||
border-radius: 2px;
|
||||
|
||||
font-size: 16px;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
@@ -57,6 +59,28 @@
|
||||
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 {
|
||||
@@ -72,4 +96,30 @@
|
||||
.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 {
|
||||
font-family: @font-family-sans;
|
||||
|
||||
position: fixed;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
overflow: visible;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
left: 0px;
|
||||
@@ -24,6 +24,7 @@
|
||||
text-transform: uppercase;
|
||||
line-height: @header-height;
|
||||
.box-shadow(none) !important;
|
||||
position:relative;
|
||||
|
||||
&:hover {
|
||||
position: relative;
|
||||
@@ -51,9 +52,23 @@
|
||||
@media (max-width: 800px) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
i {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-loading {
|
||||
.book-header h1 {
|
||||
i {
|
||||
display: inline-block;
|
||||
}
|
||||
a {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.with-summary {
|
||||
.book-header h1 {
|
||||
margin-left: 250px;
|
||||
@@ -64,4 +79,30 @@
|
||||
.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;
|
||||
font-size: 16px;
|
||||
|
||||
border-bottom: 1px solid #eee;
|
||||
|
||||
a {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,29 @@
|
||||
.book .book-body .page-wrapper .page-inner section.normal {
|
||||
padding: 25px;
|
||||
padding-top: 15px;
|
||||
background-color: white;
|
||||
.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal {
|
||||
color:@page-color-1;
|
||||
|
||||
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 {
|
||||
margin-top: 0 !important; }
|
||||
@@ -51,26 +72,25 @@
|
||||
font-size: inherit; }
|
||||
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
color: black; }
|
||||
font-size: 2.5em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
font-size: 2em;
|
||||
border-bottom: 1px solid #eee;
|
||||
color: black; }
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 18px; }
|
||||
font-size: 1.5em; }
|
||||
|
||||
h4 {
|
||||
font-size: 16px; }
|
||||
font-size: 1.2em; }
|
||||
|
||||
h5 {
|
||||
font-size: 14px; }
|
||||
font-size: 1em; }
|
||||
|
||||
h6 {
|
||||
color: #777777;
|
||||
font-size: 14px; }
|
||||
font-size: 1em; }
|
||||
|
||||
p, blockquote, ul, ol, dl, table, pre {
|
||||
margin: 15px 0; }
|
||||
@@ -272,29 +292,15 @@
|
||||
white-space: pre;
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.highlight pre {
|
||||
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;
|
||||
@import "./highlight/white.less";
|
||||
}
|
||||
|
||||
pre {
|
||||
color: hsl(204, 40%, 80%);
|
||||
background-color: hsl(204, 30%, 10%);
|
||||
border: 1px solid hsl(204, 30%, 10%);
|
||||
font-size: 16px;
|
||||
font-size: inherit;
|
||||
line-height: 1.5em;
|
||||
overflow: auto;
|
||||
padding: 20px;
|
||||
margin: 0 -20px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,21 @@
|
||||
width: 50%;
|
||||
display: inline-block;
|
||||
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%;
|
||||
position: relative;
|
||||
background: #fff;
|
||||
margin-bottom: 20px;
|
||||
z-index: 10;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.bar {
|
||||
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 {
|
||||
padding: 15px;
|
||||
.quiz-label {
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
table {
|
||||
margin-bottom: 10px;
|
||||
width: 100%;
|
||||
@@ -46,4 +50,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
.book-summary {
|
||||
font-family: @font-family-sans;
|
||||
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
top: @header-height;
|
||||
left: -@sidebar-width;
|
||||
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 "fonts.less";
|
||||
|
||||
@import "highlight.less";
|
||||
|
||||
@import "book/languages.less";
|
||||
@import "book/header.less";
|
||||
@import "book/summary.less";
|
||||
@import "book/font-settings.less";
|
||||
@import "book/body.less";
|
||||
@import "book/exercise.less";
|
||||
@import "book/quiz.less";
|
||||
@@ -25,10 +24,6 @@
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
.box-shadow(none);
|
||||
}
|
||||
|
||||
html, body {
|
||||
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 */
|
||||
.hljs-comment {
|
||||
color: hsl(207, 35%, 35%);
|
||||
.hljs-comment,
|
||||
.hljs-title {
|
||||
color: #8e908c;
|
||||
}
|
||||
|
||||
/* Tomorrow Red */
|
||||
@@ -27,9 +28,9 @@
|
||||
.hljs-pragma,
|
||||
.hljs-built_in,
|
||||
.hljs-literal,
|
||||
.hljs-constant,
|
||||
.hljs-function .hljs-title {
|
||||
color: hsl(50, 100%, 60%);
|
||||
.hljs-params,
|
||||
.hljs-constant {
|
||||
color: #f5871f;
|
||||
}
|
||||
|
||||
/* Tomorrow Yellow */
|
||||
@@ -45,7 +46,7 @@
|
||||
.hljs-header,
|
||||
.ruby .hljs-symbol,
|
||||
.xml .hljs-cdata {
|
||||
color: hsl(0, 100%, 70%);
|
||||
color: #718c00;
|
||||
}
|
||||
|
||||
/* Tomorrow Aqua */
|
||||
@@ -54,7 +55,7 @@
|
||||
}
|
||||
|
||||
/* Tomorrow Blue */
|
||||
.hljs-function .keyword,
|
||||
.hljs-function,
|
||||
.python .hljs-decorator,
|
||||
.python .hljs-title,
|
||||
.ruby .hljs-function .hljs-title,
|
||||
@@ -62,13 +63,13 @@
|
||||
.perl .hljs-sub,
|
||||
.javascript .hljs-title,
|
||||
.coffeescript .hljs-title {
|
||||
color: hsl(207, 70%, 60%);
|
||||
color: #4271ae;
|
||||
}
|
||||
|
||||
/* Tomorrow Purple */
|
||||
.hljs-keyword,
|
||||
.javascript .hljs-function {
|
||||
color: hsl(207, 95%, 70%);
|
||||
color: #8959a8;
|
||||
}
|
||||
|
||||
.hljs {
|
||||
@@ -76,7 +77,6 @@
|
||||
background: white;
|
||||
color: #4d4d4c;
|
||||
padding: 0.5em;
|
||||
font-family: "Anonymous Pro", "Inconsolata", "Monaco", monospace;
|
||||
}
|
||||
|
||||
.coffeescript .javascript,
|
||||
@@ -87,4 +87,4 @@
|
||||
.xml .css,
|
||||
.xml .hljs-cdata {
|
||||
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-before: auto;
|
||||
}
|
||||
|
||||
h1 { font-size: floor(@font-size-base * 2.15); }
|
||||
h2 { font-size: floor(@font-size-base * 1.70); }
|
||||
h3 { font-size: ceil(@font-size-base * 1.25); }
|
||||
h4 { font-size: ceil(@font-size-base * 1); }
|
||||
h5 { font-size: ceil(@font-size-base * 0.85); }
|
||||
h6 { font-size: ceil(@font-size-base * 0.65); }
|
||||
|
||||
pre, blockquote {
|
||||
border: 1px solid #999;
|
||||
page-break-inside: avoid;
|
||||
@@ -26,41 +65,36 @@ section {
|
||||
}
|
||||
|
||||
&#summary {
|
||||
text-align: center;
|
||||
margin: 1.5cm;
|
||||
|
||||
ul {
|
||||
font-size: 0.5cm;
|
||||
line-height: 1.8em;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
list-style: none;
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
> ul {
|
||||
> li {
|
||||
margin-bottom: 1cm;
|
||||
ol {
|
||||
list-style: none;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
margin-left: 1cm;
|
||||
}
|
||||
|
||||
> a {
|
||||
font-size: 0.6cm;
|
||||
}
|
||||
> ol {
|
||||
> li {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
article {
|
||||
margin: 1.5cm;
|
||||
|
||||
/* Big centered title */
|
||||
margin-bottom: 1.5cm;
|
||||
|
||||
&.new-chapter {
|
||||
page-break-after: always;
|
||||
font-size: 0.6cm;
|
||||
text-align: center;
|
||||
padding: 3cm 0cm;
|
||||
|
||||
border-top: 1px solid #ccc;
|
||||
|
||||
@media print {
|
||||
border: none;
|
||||
h1 {
|
||||
font-size: floor(@font-size-base * 2.7);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
@sidebar-link-completed-weight: normal;
|
||||
|
||||
// Page
|
||||
@page-color: black;
|
||||
@page-background: white;
|
||||
|
||||
// Progress Bar
|
||||
@@ -79,5 +80,94 @@
|
||||
@font-family-sans: "Open Sans", "Clear Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
@font-family-base: @font-family-serif;
|
||||
|
||||
|
||||
@FontPath: '@{staticPath}/fonts';
|
||||
@fa-font-path: "@{FontPath}/fontawesome";
|
||||
|
||||
@s-font-size: 1.2rem;
|
||||
@m-font-size: 1.4rem;
|
||||
@l-font-size: 1.6rem;
|
||||
@xl-font-size: 2.2rem;
|
||||
@xxl-font-size: 4.0rem;
|
||||
|
||||
/*
|
||||
,--------.,--.
|
||||
'--. .--'| ,---. ,---. ,--,--,--. ,---. ,---. .--.
|
||||
| | | .-. || .-. :| || .-. :( .-' '--'
|
||||
| | | | | |\ --.| | | |\ --..-' `).--.
|
||||
`--' `--' `--' `----'`--`--`--' `----'`----' '--'
|
||||
*/
|
||||
// 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">
|
||||
<!-- Actions Left -->
|
||||
{% if githubId %}
|
||||
<a href="{{ githubHost }}{{ githubId }}" target="_blank" class="btn pull-left"><i class="fa fa-github-alt"></i></a>
|
||||
{% if options.links.home !== false and (options.links.home != null or githubId) %}
|
||||
<a href="{{ options.links.home|default(githubHost+githubId) }}" target="_blank" class="btn pull-left"><i class="fa fa-bookmark-o"></i></a>
|
||||
{% endif %}
|
||||
<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>
|
||||
<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 -->
|
||||
{% 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>
|
||||
{% 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>
|
||||
{% 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>
|
||||
{% endif %}
|
||||
|
||||
{% 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 }}/watchers" target="_blank" class="btn pull-right count-watch"><i class="fa fa-eye"></i> Watch (<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 hidden-xs"><i class="fa fa-eye"></i> Watch (<span>-</span>)</a>
|
||||
{% endif %}
|
||||
|
||||
<!-- Title -->
|
||||
<h1><a href="{{ basePath }}/" >{{ title }}</a></h1>
|
||||
<h1>
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
<a href="{{ basePath }}/" >{{ title }}</a>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@@ -3,18 +3,28 @@
|
||||
<input type="text" placeholder="Search" class="form-control" />
|
||||
</div>
|
||||
<ul class="summary">
|
||||
{% if githubId %}
|
||||
{% if options.links.about !== false && (options.links.about != null || githubId) %}
|
||||
<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>
|
||||
{% endif %}
|
||||
|
||||
{% if options.links.issues !== false && (options.links.issues != null || githubId) %}
|
||||
<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>
|
||||
{% endif %}
|
||||
|
||||
{% if options.links.contribute !== false && (options.links.contribute != null || githubId) %}
|
||||
<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>
|
||||
{% endif %}
|
||||
|
||||
{% if options.links.contribute || options.links.issues || options.links.about || githubId %}
|
||||
<li class="divider"></li>
|
||||
{% endif %}
|
||||
|
||||
<li data-level="0" data-path="index.html">
|
||||
<a href="{{ basePath }}/"><i class="fa fa-check"></i> Introduction</a>
|
||||
</li>
|
||||
@@ -44,5 +54,12 @@
|
||||
{% endif %}
|
||||
</li>
|
||||
{% 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>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!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#">
|
||||
{% block head %}
|
||||
<meta charset="UTF-8">
|
||||
@@ -35,10 +35,10 @@
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
{% block content %}{% endblock %}
|
||||
{% block style %}
|
||||
<link rel="stylesheet" href="{{ staticBase }}/style.css">
|
||||
{% endblock %}
|
||||
{% block content %}{% endblock %}
|
||||
{% 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/mode-javascript.js"></script>
|
||||
|
||||
+49
-56
@@ -3,10 +3,32 @@
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block style %}
|
||||
{% parent %}
|
||||
<link rel="stylesheet" href="{{ staticBase }}/print.css">
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% macro articleContent(content) %}
|
||||
{% for section in content %}
|
||||
{% if section.type == "normal" %}
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
{% elif section.type == "exercise" %}
|
||||
<div class="exercise">
|
||||
<div class="exercise-header">Exercise #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
<pre><code>{% autoescape false %}{{ section.code.base|code }}{% endautoescape %}</code></pre>
|
||||
{% set exercise = exercise + 1 %}
|
||||
</div>
|
||||
{% elif section.type == "quiz" %}
|
||||
<div class="quiz">
|
||||
<div class="exercise-header">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 %}
|
||||
{% block content %}
|
||||
{# Cover #}
|
||||
@@ -19,87 +41,58 @@
|
||||
|
||||
{# Summary #}
|
||||
<section id="summary">
|
||||
<h1>Summary</h1>
|
||||
|
||||
<ul class="summary">
|
||||
<h1>Table of Contents</h1>
|
||||
<ol>
|
||||
<li>
|
||||
<a href="#README.md">Introduction</a>
|
||||
<a href="#README.md">
|
||||
<h2>Introduction</h2>
|
||||
</a>
|
||||
</li>
|
||||
{% for item in summary.chapters %}
|
||||
<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 %}
|
||||
<ul>
|
||||
<ol>
|
||||
{% for article in item.articles %}
|
||||
<li>
|
||||
<a href="#{{ article.path }}">{{ article.level }}) {{ article.title }}</a>
|
||||
<span>{{ article.level }}</span> <a href="#{{ article.path }}">{{ article.title }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</ol>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
{# Pages content #}
|
||||
<section>
|
||||
<article id="README.md">
|
||||
{% for section in 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 %}
|
||||
{{ articleContent(pages["README.md"].content) }}
|
||||
</article>
|
||||
</section>
|
||||
{% for item in summary.chapters %}
|
||||
{% if item.articles.length > 0 %}
|
||||
<section>
|
||||
<article id="{{ item.path }}" class="new-chapter">
|
||||
<h1>{{ item.title }}</h1>
|
||||
</article>
|
||||
|
||||
{% for article in item.articles %}
|
||||
{% if pages[article.path] %}
|
||||
<article id="{{ article.path }}">
|
||||
{% for section in pages[article.path].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>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if pages[item.path] %}
|
||||
<article>
|
||||
{{ articleContent(pages[item.path].content) }}
|
||||
</article>
|
||||
{% endif %}
|
||||
|
||||
{% if item.articles.length > 0 %}
|
||||
{% for article in item.articles %}
|
||||
{% if pages[article.path] %}
|
||||
<article id="{{ article.path }}">
|
||||
{{ articleContent(pages[article.path].content) }}
|
||||
</article>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{% extends "layout.html" %}
|
||||
|
||||
{% block htmlTag %}manifest="{{ basePath }}/manifest.appcache"{% endblock %}
|
||||
{% block title %}{{ progress.current.title }}{% parent %}{% endblock %}
|
||||
{% block content %}
|
||||
<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>
|
||||
|
||||
{% 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 %}
|
||||
{% 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 %}
|
||||
</div>
|
||||
</div>
|
||||
{% 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