Compare commits

...

76 Commits

Author SHA1 Message Date
Aaron O'Mullan 4481b5f556 Bump version to 0.3.2 2014-04-27 13:46:20 -07:00
Samy Pessé 0e0d051f7c Merge pull request #173 from erixtekila/master
Update README.md
2014-04-27 22:26:56 +02:00
Eric Priou a1b649a279 Update README.md 2014-04-27 22:24:37 +02:00
Eric Priou e6bc0a0fb7 Update README.md
3rd party plugins done in the weekend
2014-04-27 22:18:09 +02:00
Samy Pessé 93e4016c09 Add link to disqus plugin 2014-04-27 21:55:50 +02:00
Samy Pessé 20c741a248 Fix stupid typo thanks to @erixtekila 2014-04-27 21:21:48 +02:00
Samy Pessé 54f530c7c3 Merge pull request #171 from GitbookIO/feature/offlineCache
Improve pages loading
2014-04-27 18:05:13 +02:00
Samy Pessé 9fdc5da22d Update page when new version of application cache 2014-04-27 17:57:34 +02:00
Samy Pessé 99ce48b146 Add base loading indicator for navigation
#161
2014-04-27 17:36:12 +02:00
Samy Pessé 6a33ad9ef7 Add base cache manifest 2014-04-27 17:14:57 +02:00
Samy Pessé ca94055adf Adapt tests for plugins 2014-04-27 15:45:16 +02:00
Samy Pessé 36a67e4c91 Fix #169: fix resolve of external plugins 2014-04-27 15:41:02 +02:00
Samy Pessé 69431e1bbb Fix #165: replace separator from ":" to "," 2014-04-27 09:14:39 +02:00
Samy Pessé a3fc4ee2b0 Fix display of code element in sepia/night 2014-04-25 18:14:41 +02:00
Samy Pessé e505fd6d54 Improve summary divider for theme sepia and night 2014-04-25 18:12:39 +02:00
Samy Pessé 93a3785e4c Improve highlight theme for sepia and night themes 2014-04-25 18:05:09 +02:00
Samy Pessé a02de84812 Change highlight theme to tomorrow 2014-04-25 17:56:48 +02:00
Samy Pessé c0521f0ec7 Improve display of pre in markdown content 2014-04-25 17:48:31 +02:00
Samy Pessé 83d8c16853 Add file watcher in "gitbook serve" command (Fix #47, #89, #140) 2014-04-25 16:23:13 +02:00
Samy Pessé 47e9506397 Fix #153 and Improve pushState navigation 2014-04-25 15:12:19 +02:00
Samy Pessé b8a3d0e254 Merge pull request #144 from attomos/master
Make quiz easier to click by surround it with label.
2014-04-25 14:29:04 +02:00
Samy Pessé 001d7ec44e Merge pull request #155 from pborreli/patch-1
Fixed typo
2014-04-24 22:44:36 +02:00
Pascal Borreli 5cb0ec447a Fixed typo 2014-04-24 21:43:32 +01:00
Aaron O'Mullan 67e7d77302 Bump version to 0.3.1 2014-04-24 10:45:55 -07:00
Aaron O'Mullan 8cbd8b2b48 Reorder plugin names
Improves #149 and improves GitBookIO/editor#12
2014-04-24 01:38:42 -07:00
Aaron O'Mullan 7c404dbf96 Fix plugin tests 2014-04-24 01:34:35 -07:00
Aaron O'Mullan c4a67b6f2f Require plugin names to contain gitbook-*
Fixes #149, fixes GitBookIO/editor#12
2014-04-24 01:28:04 -07:00
Samy Pessé 3b70347625 Fix #146: complete license 2014-04-24 09:00:15 +02:00
Aaron O'Mullan bb0a7935e9 Merge pull request #141 from frodsan/niceties
Niceties
2014-04-23 11:34:08 -07:00
Nattaphoom Ch 8f4b799d3c Make quiz easier to click by surround it with label. 2014-04-24 01:19:13 +07:00
Francesco Rodriguez 931e0c66c9 Update color themes for highlight.js link. 2014-04-23 12:30:05 -03:00
Francesco Rodriguez 0212084862 Fix typo. 2014-04-23 12:28:44 -03:00
Samy Pessé 9abbabbce3 Merge pull request #138 from Hermanya/patch-1
Add @Hermanya as a contributor
2014-04-22 23:47:13 +02:00
Hermanya ac610487e8 Add @Hermanya as a contributor 2014-04-22 17:45:41 -04:00
Aaron O'Mullan a9a0f89892 Bump version to 0.3.0 2014-04-22 14:05:52 -07:00
Samy Pessé 305f221077 Fix #136: replace links to .md by links to .html
Add more tests for links in page
2014-04-22 17:45:12 +02:00
Samy Pessé 53514f2602 Improve sepia theme sidebar 2014-04-22 14:27:48 +02:00
Samy Pessé 00327778cb Improve font-settings dropdown design 2014-04-22 14:12:53 +02:00
Samy Pessé 37fadec787 Merge branch 'master' of https://github.com/Hermanya/gitbook into Hermanya-master 2014-04-22 14:02:19 +02:00
Samy Pessé ada11d0b59 Add section in readme about plugins 2014-04-22 12:51:10 +02:00
Aaron O'Mullan 4c90fdb7b2 Merge pull request #123 from GitbookIO/feature/plugins
Fix #65: Plugins Architecture
2014-04-21 14:26:09 -07:00
Samy Pessé 98c133df3c Change plugins separator to "," 2014-04-21 23:23:53 +02:00
Samy Pessé f86c27b090 Add option --pluginsConfig 2014-04-21 22:58:51 +02:00
Samy Pessé b5b8ed72a5 Update default addons 2014-04-21 22:39:23 +02:00
Samy Pessé 5beff86548 Change js interface for plugins and add configuration 2014-04-21 22:20:41 +02:00
Samy Pessé dd395edae1 Use mathjax plugin version 0.0.2 2014-04-21 16:30:02 +02:00
Aaron O'Mullan d1c1d7b75b Bump version to 0.2.4 2014-04-20 16:41:09 -07:00
Samy Pessé 4a380e9279 Fix #36: add mathjax plugin as default 2014-04-20 15:20:21 +02:00
Samy Pessé 878c105def Use complete assets folder for plugins 2014-04-20 02:04:15 +02:00
Samy Pessé 74d628676b Add option to change templates from plugin 2014-04-20 00:10:46 +02:00
Samy Pessé 6fdd98ec3a Trigger event on document when ready 2014-04-19 20:39:58 +02:00
Samy Pessé 2ed1be3533 Add global js interface for plugins and remove mixpanel 2014-04-19 20:10:03 +02:00
Samy Pessé 0ac04e1584 Add call for hooks "init" and "finish" 2014-04-19 19:50:32 +02:00
Herman Starikov 2a38d33d8a Font settings, closes #102 2014-04-19 13:46:14 -04:00
Samy Pessé fb14caf5b3 Add resources to site build 2014-04-19 19:31:02 +02:00
Samy Pessé cf51772f54 Add command line option "plugins" 2014-04-19 18:00:24 +02:00
Samy Pessé cc8516df83 Load plugins during generation 2014-04-19 17:57:40 +02:00
Samy Pessé c6a624bc6b Add method to load a list of plugins from generator options 2014-04-19 17:54:15 +02:00
Samy Pessé 8be8b825ee Add base loading of default addons
Add mixpanel as default addon
2014-04-19 17:28:44 +02:00
Samy Pessé 56eb4a2897 Add base plugin object and test validation of plugin 2014-04-19 15:41:29 +02:00
Samy Pessé 7a461eaa61 Remove theme readme
Add semver dependency
2014-04-19 14:40:46 +02:00
Samy Pessé fd64c05809 Fix #118: fix single page generation when file from summary doesn't exists 2014-04-18 23:42:02 +02:00
Aaron O'Mullan 6bce93d893 Bump version to 0.2.3 2014-04-18 11:53:18 -07:00
Aaron O'Mullan c9db068227 Refactor navigation generation to be functional
This makes it a lot less error prone and far more robust. As well as a
lot easier to understand
2014-04-18 11:49:55 -07:00
Samy Pessé 8a6d36ac4d Improve stability of mixpanel tracking 2014-04-18 20:42:18 +02:00
Samy Pessé 64622c5945 Load css before content 2014-04-18 20:19:45 +02:00
Samy Pessé 837813edb0 Add warning about theme 2014-04-18 20:16:21 +02:00
Aaron O'Mullan 0d9d62f1a9 Merge pull request #117 from GitbookIO/fix/99
Fix #99
2014-04-18 10:17:20 -07:00
Samy Pessé f7e9abf984 Fix quiz interractivity 2014-04-18 19:10:13 +02:00
Samy Pessé dd0579b60d Update tests for relative images 2014-04-18 17:23:33 +02:00
Samy Pessé b5bb953e47 Fix image path calcul 2014-04-18 17:15:03 +02:00
Samy Pessé ef4a8ea1b6 Fix #116: update url before updating html content 2014-04-18 15:49:53 +02:00
Samy Pessé 49cde96ee7 Add contributors to package.json and change homepage 2014-04-18 15:00:40 +02:00
Samy Pessé 627be20e48 Add editor and twitter links in the readme 2014-04-18 14:48:03 +02:00
Samy Pessé e45bb84b02 Fix navigation on chrome for iOS: disable pushState 2014-04-18 13:09:16 +02:00
Aaron O'Mullan 4e8876c725 Update README.md 2014-04-17 16:39:06 -07:00
57 changed files with 1850 additions and 265 deletions
+1
View File
@@ -15,3 +15,4 @@ Contributors
------------ ------------
- Nijiko Yonskai - Nijiko Yonskai
- Herman Starikov
+1 -5
View File
@@ -24,7 +24,7 @@ module.exports = function (grunt) {
requirejs: { requirejs: {
compile: { compile: {
options: { options: {
name: "app", name: "gitbook",
baseUrl: "theme/javascript/", baseUrl: "theme/javascript/",
out: "theme/assets/app.js", out: "theme/assets/app.js",
preserveLicenseComments: false, preserveLicenseComments: false,
@@ -35,7 +35,6 @@ module.exports = function (grunt) {
"lodash": 'vendors/lodash', "lodash": 'vendors/lodash',
"requireLib": 'vendors/require', "requireLib": 'vendors/require',
"Mousetrap": 'vendors/mousetrap', "Mousetrap": 'vendors/mousetrap',
"mixpanel": 'vendors/mixpanel',
"lunr": path.join(__dirname, "node_modules/lunr/lunr") "lunr": path.join(__dirname, "node_modules/lunr/lunr")
}, },
shim: { shim: {
@@ -48,9 +47,6 @@ module.exports = function (grunt) {
'Mousetrap': { 'Mousetrap': {
exports: 'Mousetrap' exports: 'Mousetrap'
}, },
'mixpanel': {
exports: 'mixpanel'
},
'lunr': { 'lunr': {
exports: 'lunr' exports: 'lunr'
} }
+1 -1
View File
@@ -186,7 +186,7 @@ Apache License
same "printed page" as the copyright notice for easier same "printed page" as the copyright notice for easier
identification within third-party archives. identification within third-party archives.
Copyright {yyyy} {name of copyright owner} Copyright 2014 FriendCode Inc.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
+21 -2
View File
@@ -3,7 +3,7 @@ GitBook
[![Build Status](https://travis-ci.org/GitbookIO/gitbook.png?branch=master)](https://travis-ci.org/GitbookIO/gitbook) [![Build Status](https://travis-ci.org/GitbookIO/gitbook.png?branch=master)](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.
![Image](https://raw.github.com/GitbookIO/gitbook/master/preview.png) ![Image](https://raw.github.com/GitbookIO/gitbook/master/preview.png)
@@ -36,9 +36,12 @@ Options for commands `build` and `serve` are:
-o, --output <directory> Path to output directory, defaults to ./_book -o, --output <directory> Path to output directory, defaults to ./_book
-f, --format <name> Change generation format, defaults to site, availables are: site, page, pdf, json -f, --format <name> Change generation format, defaults to site, availables are: site, page, pdf, json
--githubHost <url> The url of the github host (defaults to https://github.com/) --githubHost <url> The url of the github host (defaults to https://github.com/)
--theme <path> Path to theme directory --plugins <plugins> List of plugins to use separated by ","
--pluginsConfig <json file> JSON File containing plugins configuration
``` ```
You can publish your books to our index by visiting [GitBook.io](http://www.gitbook.io)
## Output Formats ## Output Formats
GitBook can generate your book in the following formats: GitBook can generate your book in the following formats:
@@ -131,3 +134,19 @@ You can see a complete example with the [Learn Git](https://github.com/GitbookIO
#### Ignoring files & folders #### Ignoring files & folders
GitBook will read the `.gitignore`, `.bookignore` and `.ignore` files to get a list of files and folders to skip. (The format inside those files, follows the same convention as `.gitignore`) GitBook will read the `.gitignore`, `.bookignore` and `.ignore` files to get a list of files and folders to skip. (The format inside those files, follows the same convention as `.gitignore`)
#### Plugins
Plugins can used to extend your book functionnalities. Read [GitbookIO/plugin](https://github.com/GitbookIO/plugin) for more informations 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 comments to notes](https://github.com/erixtekila/gitbook-plugin-comments): Allow markdown comments to be inserted in HTML output
* [Send code to console](https://github.com/erixtekila/gitbook-plugin-toconsole): Evaluate javascript blockin the browser inspector's console
+28 -7
View File
@@ -1,18 +1,37 @@
var path = require('path'); var path = require('path');
var Q = require('q'); var Q = require('q');
var _ = require('lodash'); var _ = require('lodash');
var fs = require('fs');
var utils = require('./utils'); var utils = require('./utils');
var generate = require("../lib/generate"); var generate = require("../lib/generate");
var parse = require("../lib/parse"); var parse = require("../lib/parse");
var fs = require('../lib/generate/fs');
var generators = require("../lib/generate").generators; var generators = require("../lib/generate").generators;
var buildCommand = function(command) {
return command
.option('-o, --output <directory>', 'Path to output directory, defaults to ./_book')
.option('-f, --format <name>', 'Change generation format, defaults to site, availables are: '+_.keys(generators).join(", "))
.option('-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('--plugins <plugins>', 'List of plugins to use separated by ","')
.option('--pluginsConfig <json file>', 'JSON File containing plugins configuration')
.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/');
};
var makeBuildFunc = function(converter) { var makeBuildFunc = function(converter) {
return function(dir, options) { return function(dir, options) {
dir = dir || process.cwd(); dir = dir || process.cwd();
outputDir = options.output outputDir = options.output;
// Read plugins config
var pluginsConfig = {};
if (options.pluginsConfig) {
pluginsConfig = JSON.parse(fs.readFileSync(options.pluginsConfig))
}
console.log('Starting build ...'); console.log('Starting build ...');
// Get repo's URL // Get repo's URL
@@ -25,7 +44,7 @@ var makeBuildFunc = function(converter) {
}) })
.then(function(repoID) { .then(function(repoID) {
return converter( return converter(
_.extend(options || {}, { _.extend({}, options || {}, {
input: dir, input: dir,
output: outputDir, output: outputDir,
title: options.title, title: options.title,
@@ -33,7 +52,8 @@ var makeBuildFunc = function(converter) {
github: options.github || repoID, github: options.github || repoID,
githubHost: options.githubHost, githubHost: options.githubHost,
generator: options.format, generator: options.format,
theme: options.theme plugins: options.plugins,
pluginsConfig: pluginsConfig
}) })
); );
}) })
@@ -42,9 +62,10 @@ var makeBuildFunc = function(converter) {
return output; return output;
}, utils.logError); }, utils.logError);
}; };
} };
module.exports = { module.exports = {
folder: makeBuildFunc(generate.folder), folder: makeBuildFunc(generate.folder),
file: makeBuildFunc(generate.file) file: makeBuildFunc(generate.file),
command: buildCommand
}; };
+36 -26
View File
@@ -12,45 +12,55 @@ var fs = require('../lib/generate/fs');
var utils = require('./utils'); var utils = require('./utils');
var build = require('./build'); var build = require('./build');
var Server = require('./server');
// General options // General options
prog prog
.version(pkg.version); .version(pkg.version);
var buildCommand = function(command) { build.command(prog.command('build [source_dir]'))
return command
.option('-o, --output <directory>', 'Path to output directory, defaults to ./_book')
.option('-f, --format <name>', 'Change generation format, defaults to site, availables are: '+_.keys(generators).join(", "))
.option('-t, --title <name>', 'Name of the book to generate, default is extracted from readme')
.option('-i, --intro <intro>', 'Description of the book to generate, default is extracted from readme')
.option('-g, --github <repo_path>', 'ID of github repo like : username/repo')
.option('--githubHost <url>', 'The url of the github host (defaults to https://github.com/')
.option('--theme <path>', 'Path to theme directory');
};
buildCommand(prog.command('build [source_dir]'))
.description('Build a gitbook from a directory') .description('Build a gitbook from a directory')
.action(build.folder); .action(build.folder);
buildCommand(prog.command('serve [source_dir]')) build.command(prog.command('serve [source_dir]'))
.description('Build then serve a gitbook from a directory') .description('Build then serve a gitbook from a directory')
.option('-p, --port <port>', 'Port for server to listen on', 4000) .option('-p, --port <port>', 'Port for server to listen on', 4000)
.option('--no-watch', 'Disable restart with file watching')
.action(function(dir, options) { .action(function(dir, options) {
build.folder(dir, options || {}) var server = new Server();
.then(function(_options) {
console.log(); var generate = function() {
console.log('Starting server ...'); if (server.isRunning()) console.log("Stopping server");
return utils.serveDir(_options.output, options.port)
server.stop()
.then(function() {
return build.folder(dir, options);
})
.then(function(_options) {
console.log();
console.log('Starting server ...');
return server.start(_options.output, options.port)
.then(function() {
console.log('Serving book on http://localhost:'+options.port);
if (!options.watch) return;
return utils.watch(_options.input)
.then(function(filepath) {
console.log("Restart after change in "+path.relative(dir, filepath));
console.log('');
return generate();
})
})
})
.fail(utils.logError); .fail(utils.logError);
}) };
.then(function() {
console.log('Serving book on http://localhost:'+options.port); console.log('Press CTRL+C to quit ...');
console.log(); console.log('')
console.log('Press CTRL+C to quit ...'); generate();
});
}); });
buildCommand(prog.command('pdf [source_dir]')) build.command(prog.command('pdf [source_dir]'))
.description('Build a gitbook as a PDF') .description('Build a gitbook as a PDF')
.option('-pf, --paperformat <format>', 'PDF paper format (default is A4): "5in*7.5in", "10cm*20cm", "A4", "Letter"') .option('-pf, --paperformat <format>', 'PDF paper format (default is A4): "5in*7.5in", "10cm*20cm", "A4", "Letter"')
.action(function(dir, options) { .action(function(dir, options) {
@@ -60,7 +70,7 @@ buildCommand(prog.command('pdf [source_dir]'))
})); }));
}); });
buildCommand(prog.command('ebook [source_dir]')) build.command(prog.command('ebook [source_dir]'))
.description('Build a gitbook as a eBook') .description('Build a gitbook as a eBook')
.option('-c, --cover <path>', 'Cover image, default is cover.png if exists') .option('-c, --cover <path>', 'Cover image, default is cover.png if exists')
.action(function(dir, options) { .action(function(dir, options) {
+96
View File
@@ -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;
+18 -23
View File
@@ -4,8 +4,11 @@ var _ = require('lodash');
var http = require('http'); var http = require('http');
var send = require('send'); var send = require('send');
var url = require('url');
var cp = require('child_process'); var cp = require('child_process');
var path = require('path');
var url = require('url');
var Gaze = require('gaze').Gaze;
// Get the remote of a given repo // Get the remote of a given repo
@@ -55,32 +58,24 @@ function titleCase(str)
return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();}); return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
} }
function serveDir(dir, port) { function watch(dir) {
var d = Q.defer(); var d = Q.defer();
dir = path.resolve(dir);
var server = http.createServer(function(req, res){ var gaze = new Gaze("**/*.md", {
// Render error cwd: dir
function error(err) { });
res.statusCode = err.status || 500;
res.end(err.message);
}
// Redirect to directory's index.html gaze.once("all", function(e, filepath) {
function redirect() { gaze.close();
res.statusCode = 301;
res.setHeader('Location', req.url + '/');
res.end('Redirecting to ' + req.url + '/');
}
// Send file d.resolve(filepath);
send(req, url.parse(req.url).pathname) });
.root(dir) gaze.once("error", function(err) {
.on('error', error) gaze.close();
.on('directory', redirect)
.pipe(res);
}).listen(port);
d.resolve(server); d.reject(err);
});
return d.promise; return d.promise;
} }
@@ -96,6 +91,6 @@ module.exports = {
gitURL: gitURL, gitURL: gitURL,
githubID: githubID, githubID: githubID,
titleCase: titleCase, titleCase: titleCase,
serveDir: serveDir, watch: watch,
logError: logError logError: logError
}; };
+1
View File
@@ -18,6 +18,7 @@ var getFiles = function(path) {
ig.addIgnoreRules([ ig.addIgnoreRules([
'.git/', '.git/',
'.gitignore', '.gitignore',
'.DS_Store'
], '__custom_stuff'); ], '__custom_stuff');
// Push each file to our list // Push each file to our list
+21
View File
@@ -1,9 +1,30 @@
var _ = require("lodash");
var path = require("path"); var path = require("path");
var Q = require("q"); var Q = require("q");
var fs = require("./fs"); var fs = require("./fs");
var Plugin = require("./plugin");
var BaseGenerator = function(options) { var BaseGenerator = function(options) {
this.options = options; this.options = options;
this.options.plugins = Plugin.normalizeNames(this.options.plugins);
this.plugins = [];
};
BaseGenerator.prototype.callHook = function(name) {
return this.plugins.hook(name, this);
};
BaseGenerator.prototype.loadPlugins = function() {
var that = this;
return Plugin.fromList(this.options.plugins, this.options.input)
.then(function(_plugins) {
that.plugins = _plugins;
return that.callHook("init");
});
}; };
BaseGenerator.prototype.convertFile = function(content, input) { BaseGenerator.prototype.convertFile = function(content, input) {
+14 -4
View File
@@ -6,6 +6,7 @@ var tmp = require('tmp');
var fs = require("./fs"); var fs = require("./fs");
var parse = require("../parse"); var parse = require("../parse");
var Plugin = require("./plugin");
var generators = { var generators = {
"site": require("./site"), "site": require("./site"),
@@ -42,7 +43,11 @@ var generate = function(options) {
githubHost: 'https://github.com/', githubHost: 'https://github.com/',
// Theming // Theming
theme: path.resolve(__dirname, '../../theme') theme: path.resolve(__dirname, '../../theme'),
// Plugins
plugins: [],
pluginsConfig: {}
}); });
if (!options.input) { if (!options.input) {
@@ -70,9 +75,10 @@ var generate = function(options) {
}) })
}) })
// Create the generator // Create the generator and load plugins
.then(function() { .then(function() {
generator = new generators[options.generator](options); generator = new generators[options.generator](options);
return generator.loadPlugins();
}) })
// Detect multi-languages book // Detect multi-languages book
@@ -160,9 +166,12 @@ var generate = function(options) {
); );
}) })
// Finish gneration // Finish generation
.then(function() { .then(function() {
return generator.finish(); return generator.finish();
})
.then(function() {
return generator.callHook("finish");
}); });
} }
}) })
@@ -234,5 +243,6 @@ var generateFile = function(options) {
module.exports = { module.exports = {
generators: generators, generators: generators,
folder: generate, folder: generate,
file: generateFile file: generateFile,
Plugin: Plugin
}; };
+72
View File
@@ -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;
+8 -3
View File
@@ -25,14 +25,18 @@ swig.setFilter('code', function(code, lang) {
var Generator = function() { var Generator = function() {
BaseGenerator.apply(this, arguments); BaseGenerator.apply(this, arguments);
// Load base template
this.template = swig.compileFile(path.resolve(this.options.theme, 'templates/page.html'));
// List of pages content // List of pages content
this.pages = {}; this.pages = {};
}; };
util.inherits(Generator, BaseGenerator); util.inherits(Generator, BaseGenerator);
// Load all templates
Generator.prototype.loadTemplates = function() {
this.template = swig.compileFile(
this.plugins.template("page") || path.resolve(this.options.theme, 'templates/page.html')
);
};
Generator.prototype.convertFile = function(content, input) { Generator.prototype.convertFile = function(content, input) {
var that = this; var that = this;
var json = { var json = {
@@ -46,6 +50,7 @@ Generator.prototype.convertFile = function(content, input) {
repo: that.options.githubId, repo: that.options.githubId,
dir: path.dirname(input) || '/', dir: path.dirname(input) || '/',
outdir: './', outdir: './',
singleFile: true
}); });
}) })
.then(function(sections) { .then(function(sections) {
+187
View File
@@ -0,0 +1,187 @@
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, args) {
var hookFunc = this.infos.hooks? this.infos.hooks[name] : null;
args = _.isArray(args) ? args : [args];
if (!hookFunc) return Q();
return Q()
.then(function() {
return hookFunc.apply(null, args);
});
};
// Copy plugin assets fodler
Plugin.prototype.copyAssets = function(out) {
if (!this.infos.book || !this.infos.book.assets) 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, args) {
return _.reduce(plugins, function(prev, plugin) {
return prev.then(function() {
return plugin.callHook(name, args);
})
}, Q());
},
'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;
+62 -7
View File
@@ -9,6 +9,7 @@ var parse = require("../../parse");
var BaseGenerator = require("../generator"); var BaseGenerator = require("../generator");
var indexer = require('./search_indexer'); var indexer = require('./search_indexer');
var Manifest = require('../manifest');
// Swig filter for returning the count of lines in a code section // Swig filter for returning the count of lines in a code section
swig.setFilter('lines', function(content) { swig.setFilter('lines', function(content) {
@@ -30,13 +31,32 @@ var Generator = function() {
this.revision = Date.now(); this.revision = Date.now();
this.indexer = indexer(); this.indexer = indexer();
this.manifest = new Manifest(Date.now());
// Load base template this.manifest.add("NETWORK", [
this.template = swig.compileFile(path.resolve(this.options.theme, 'templates/site.html')); '*'
this.langsTemplate = swig.compileFile(path.resolve(this.options.theme, 'templates/langs.html')); ]);
}; };
util.inherits(Generator, BaseGenerator); util.inherits(Generator, BaseGenerator);
// Load all templates
Generator.prototype.loadTemplates = function() {
this.template = swig.compileFile(
this.plugins.template("site") || path.resolve(this.options.theme, 'templates/site.html')
);
this.langsTemplate = swig.compileFile(
this.plugins.template("langs") || path.resolve(this.options.theme, 'templates/langs.html')
);
};
// Load plugins
Generator.prototype.loadPlugins = function() {
var that = this;
return BaseGenerator.prototype.loadPlugins.apply(this)
.then(function() {
return that.loadTemplates();
});
};
// Generate a template // Generate a template
Generator.prototype._writeTemplate = function(tpl, options, output) { Generator.prototype._writeTemplate = function(tpl, options, output) {
@@ -54,7 +74,10 @@ Generator.prototype._writeTemplate = function(tpl, options, output) {
githubHost: that.options.githubHost, githubHost: that.options.githubHost,
summary: that.options.summary, summary: that.options.summary,
allNavigation: that.options.navigation allNavigation: that.options.navigation,
plugins: that.plugins,
pluginsConfig: JSON.stringify(that.options.pluginsConfig)
}, options)); }, options));
}) })
.then(function(html) { .then(function(html) {
@@ -101,6 +124,8 @@ Generator.prototype.convertFile = function(content, _input) {
}); });
}) })
.then(function(sections) { .then(function(sections) {
that.manifest.add("CACHE", _output);
return that._writeTemplate(that.template, { return that._writeTemplate(that.template, {
progress: progress, progress: progress,
@@ -134,10 +159,30 @@ Generator.prototype.langsIndex = function(langs) {
Generator.prototype.copyAssets = function() { Generator.prototype.copyAssets = function() {
var that = this; var that = this;
// Copy gitbook assets
return fs.copy( return fs.copy(
path.join(that.options.theme, "assets"), path.join(that.options.theme, "assets"),
path.join(that.options.output, "gitbook") path.join(that.options.output, "gitbook")
); )
// Add to cach manifest
.then(function() {
return that.manifest.addFolder(path.join(that.options.output, "gitbook"), "gitbook");
})
// Copy plugins assets
.then(function() {
return Q.all(
_.map(that.plugins.list, function(plugin) {
var pluginAssets = path.join(that.options.output, "gitbook/plugins/", plugin.name);
return plugin.copyAssets(pluginAssets)
.then(function() {
return that.manifest.addFolder(pluginAssets, "gitbook/plugins/"+plugin.name);
});
})
);
})
}; };
// Dump search index to disk // Dump search index to disk
@@ -148,9 +193,19 @@ Generator.prototype.writeSearchIndex = function() {
); );
}; };
// Add cache manifest
Generator.prototype.writeCacheManifest = function() {
return fs.writeFile(
path.join(this.options.output, 'manifest.appcache'),
this.manifest.dump()
);
};
Generator.prototype.finish = function() { Generator.prototype.finish = function() {
return this.copyAssets() return this.copyAssets()
.then(this.writeSearchIndex); .then(this.writeSearchIndex)
.then(this.writeCacheManifest);
}; };
module.exports = Generator; module.exports = Generator;
+30 -49
View File
@@ -6,6 +6,12 @@ function clean(obj) {
return obj && _.omit(obj, ['articles']); return obj && _.omit(obj, ['articles']);
} }
function flattenChapters(chapters) {
return _.reduce(chapters, function(accu, chapter) {
return accu.concat([clean(chapter)].concat(chapter.articles));
}, []);
}
// Returns from a summary a map of // Returns from a summary a map of
/* /*
{ {
@@ -20,62 +26,37 @@ function navigation(summary, files) {
// Support single files as well as list // Support single files as well as list
files = _.isArray(files) ? files : (_.isString(files) ? [files] : null); files = _.isArray(files) ? files : (_.isString(files) ? [files] : null);
// Mapping of prev/next for a give path
var mapping = {};
// Special README nav // Special README nav
var README_NAV = { var README_NAV = {
path: 'README.md', path: 'README.md',
title: 'Introduction', title: 'Introduction',
};
// Walk the chapter/article tree and create navigation entires
_.each(summary.chapters, function(chapter, idx, chapters) {
// Skip if no path
if(!chapter.path) return;
var currentChapter = clean(chapter);
var prevChapter = (idx-1 < 0) ? README_NAV : chapters[idx-1];
var nextChapter = (idx+1 >= chapters.length) ? null : chapters[idx+1];
var prev = (!prevChapter || _.isEmpty(prevChapter.articles)) ?
prevChapter : _.last(prevChapter.articles);
var next = (!chapter || _.isEmpty(chapter.articles)) ?
nextChapter : _.first(chapter.articles);
// Add chapter mapping
mapping[chapter.path] = {
title: chapter.title,
prev: clean(prev),
next: clean(next),
level: (idx+1).toString(),
};
// Check a chapter's articles
_.each(chapter.articles, function(article, _idx, articles) {
// Skip if no path
if(!article.path) return;
var prev = (_idx-1 < 0) ? currentChapter : clean(articles[_idx-1]);
var next = (_idx+1 >= articles.length) ? nextChapter : clean(articles[_idx+1]);
mapping[article.path] = {
title: article.title,
prev: clean(prev),
next: clean(next),
level: [idx+1, _idx+1].join('.'),
};
});
});
// Hack for README.html
mapping['README.md'] = {
title: README_NAV.title,
prev: null,
next: clean(summary.chapters[0]),
level: '0', level: '0',
}; };
// List of all navNodes
var navNodes = [README_NAV].concat(flattenChapters(summary.chapters));
var prevNodes = [null].concat(navNodes.slice(0, -1));
var nextNodes = navNodes.slice(1).concat([null]);
// Mapping of prev/next for a give path
var mapping = _.chain(_.zip(navNodes, prevNodes, nextNodes))
.map(function(nodes) {
var current = nodes[0], prev = nodes[1], next = nodes[2];
// Skip if no path
if(!current.path) return null;
return [current.path, {
title: current.title,
prev: prev,
next: next,
level: current.level,
}];
})
.filter()
.object()
.value();
// Filter for only files we want // Filter for only files we want
if(files) { if(files) {
return _.pick(mapping, files); return _.pick(mapping, files);
+34 -10
View File
@@ -1,5 +1,7 @@
var url = require('url'); var url = require('url');
var inherits = require('util').inherits; var inherits = require('util').inherits;
var links = require('../utils').links;
var path = require('path'); var path = require('path');
@@ -16,6 +18,7 @@ function GitBookRenderer(options, extra_options) {
this._extra_options = extra_options; this._extra_options = extra_options;
this.quizRowId = 0; this.quizRowId = 0;
this.id = rendererId++; this.id = rendererId++;
this.quizIndex = 0;
} }
inherits(GitBookRenderer, marked.Renderer); inherits(GitBookRenderer, marked.Renderer);
@@ -38,23 +41,38 @@ GitBookRenderer.prototype._unsanitized = function(href) {
}; };
GitBookRenderer.prototype.link = function(href, title, text) { GitBookRenderer.prototype.link = function(href, title, text) {
// Our "fixed" href
var _href = href;
// Don't build if it looks malicious // Don't build if it looks malicious
if (this.options.sanitize && this._unsanitized(href)) { if (this.options.sanitize && this._unsanitized(href)) {
return ''; return text;
} }
// Parsed version of the url // Parsed version of the url
var parsed = url.parse(href); var parsed = url.parse(href);
var o = this._extra_options; var o = this._extra_options;
// Relative link, rewrite it to point to github repo // Relative link, rewrite it to point to github repo
if(!parsed.protocol && parsed.path && parsed.path[0] != '/' && o && o.repo && o.dir) { if(links.isRelative(_href)) {
href = url.resolve('https://github.com/' + o.repo + '/blob/', [o.dir, href].join("/")); if (path.extname(_href) == ".md") {
parsed = url.parse(href); _href = links.toAbsolute(_href, o.dir || "./", o.outdir || "./");
if (o.singleFile) {
_href = "#"+_href;
} else {
_href = _href.replace(".md", ".html");
}
} else if (o && o.repo && o.dir) {
href = url.resolve('https://github.com/' + o.repo + '/blob/', [o.dir, _href].join("/"));
parsed = url.parse(href);
_href = parsed.href;
}
} }
// Generate HTML for link // Generate HTML for link
var out = '<a href="' + parsed.href + '"'; var out = '<a href="' + _href + '"';
// Title if no null // Title if no null
if (title) { if (title) {
out += ' title="' + title + '"'; out += ' title="' + title + '"';
@@ -78,9 +96,11 @@ GitBookRenderer.prototype.image = function(href, title, text) {
var o = this._extra_options; var o = this._extra_options;
// Relative image, rewrite it depending output // Relative image, rewrite it depending output
if(!parsed.protocol && parsed.path && parsed.path[0] != '/' && o && o.dir && o.outdir) { if(links.isRelative(href) && o && o.dir && o.outdir) {
var outdir = o.outdir.charAt(o.outdir.length - 1) === '/' ? o.outdir : o.outdir + '/'; // o.dir: directory parent of the file currently in rendering process
_href = url.resolve(outdir, [o.dir, href].join('/')); // o.outdir: directory parent from the html output
_href = links.toAbsolute(_href, o.dir, o.outdir);
} }
return GitBookRenderer.super_.prototype.image.call(this, _href, title, text); return GitBookRenderer.super_.prototype.image.call(this, _href, title, text);
@@ -97,10 +117,14 @@ GitBookRenderer.prototype._createCheckboxAndRadios = function(text) {
if (!match) { if (!match) {
return text; return text;
} }
var field = "<input name='quiz-row-" + this.id + "-" + this.quizRowId + "' type='"; var quizIdentifier = 'quiz-row-' + this.id + '-' + this.quizRowId + '-' + this.quizIndex++;
var field = "<input name='" + quizIdentifier + "' id='" + quizIdentifier + "' type='";
field += match[1] === '(' ? "radio" : "checkbox"; field += match[1] === '(' ? "radio" : "checkbox";
field += match[2] === 'x' ? "' checked/>" : "'/>"; field += match[2] === 'x' ? "' checked/>" : "'/>";
return text.replace(fieldRegex, field); var splittedText = text.split(fieldRegex);
var length = splittedText.length;
var label = '<label class="quiz-label" for="' + quizIdentifier + '">' + splittedText[length - 1] + '</label>';
return text.replace(fieldRegex, field).replace(splittedText[length - 1], label);
} }
GitBookRenderer.prototype.tablecell = function(content, flags) { GitBookRenderer.prototype.tablecell = function(content, flags) {
+1
View File
@@ -1,3 +1,4 @@
module.exports = { module.exports = {
lang: require('./lang'), lang: require('./lang'),
links: require('./links')
}; };
+29
View File
@@ -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
View File
@@ -1,6 +1,7 @@
{ {
"name": "gitbook", "name": "gitbook",
"version": "0.2.2", "version": "0.3.2",
"homepage": "http://www.gitbook.io/",
"description": "Library and cmd utility to generate GitBooks", "description": "Library and cmd utility to generate GitBooks",
"main": "lib/index.js", "main": "lib/index.js",
"dependencies": { "dependencies": {
@@ -15,7 +16,14 @@
"commander": "2.2.0", "commander": "2.2.0",
"fs-extra": "0.8.1", "fs-extra": "0.8.1",
"highlight.js": "8.0.0", "highlight.js": "8.0.0",
"tmp": "0.0.23" "tmp": "0.0.23",
"semver": "2.2.1",
"gaze": "0.6.4",
"resolve": "0.6.3",
"gitbook-plugin": "0.0.2",
"gitbook-plugin-mixpanel": "0.0.2",
"gitbook-plugin-mathjax": "0.0.3"
}, },
"devDependencies": { "devDependencies": {
"mocha": "1.18.2", "mocha": "1.18.2",
@@ -39,9 +47,19 @@
"book", "book",
"gitbook" "gitbook"
], ],
"author": "Aaron O'Mullan <aaron.omullan@friendco.de>", "author": "FriendCode <contact@friendco.de>",
"license": "Apache 2", "license": "Apache 2",
"bugs": { "bugs": {
"url": "https://github.com/GitbookIO/gitbook/issues" "url": "https://github.com/GitbookIO/gitbook/issues"
} },
"contributors": [
{
"name": "Aaron O'Mullan",
"email": "aaron.omullan@gmail.com"
},
{
"name": "Samy Pessé",
"email": "samypesse@gmail.com"
}
]
} }
+12
View File
@@ -0,0 +1,12 @@
# Images Test
This is a test for images path calculation. It supposed this fiel is in a syntax/ folder
### #
[![Screen](./preview.png)](./preview.png)
### 2
[![Screen](../preview2.png)](./preview2.png)
+5
View File
@@ -0,0 +1,5 @@
# Nice course
Check out this other chapter [Test](test.md)
Check out this other chapter [Test](../before.md)
+61 -5
View File
@@ -36,10 +36,6 @@ describe('Page parsing', function() {
assert(LEXED[2].content); assert(LEXED[2].content);
}); });
it('should make image URLs relative', function() {
assert(LEXED[2].content.indexOf('_book/assets/my-pretty-picture.png') !== -1);
});
it('should gen code and content for exercise sections', function() { it('should gen code and content for exercise sections', function() {
assert(LEXED[1].content); assert(LEXED[1].content);
assert(LEXED[1].code); assert(LEXED[1].code);
@@ -88,9 +84,69 @@ describe('Relative links', function() {
repo: 'GitBookIO/javascript', repo: 'GitBookIO/javascript',
// Imaginary folder of markdown file // Imaginary folder of markdown file
dir: 'course' dir: 'course',
outdir: './'
}); });
assert(LEXED[0].content.indexOf('https://github.com/GitBookIO/javascript/blob/src/something.cpp') !== -1); assert(LEXED[0].content.indexOf('https://github.com/GitBookIO/javascript/blob/src/something.cpp') !== -1);
}); });
it('should replace link to .md by link to .html', function() {
var LEXED = loadPage('MARKDOWN_LINKS', {
// GitHub repo ID
repo: 'GitBookIO/javascript',
// Imaginary folder of markdown file
dir: 'course',
outdir: 'course'
});
assert(LEXED[0].content.indexOf('test.html') !== -1);
assert(LEXED[0].content.indexOf('../before.html') !== -1);
});
it('should replace link to .md by link to page in format single page', function() {
var LEXED = loadPage('MARKDOWN_LINKS', {
// GitHub repo ID
repo: 'GitBookIO/javascript',
// Imaginary folder of markdown file
dir: 'course',
outdir: './',
singleFile: true
});
assert(LEXED[0].content.indexOf('#course/test.md') !== -1);
assert(LEXED[0].content.indexOf('#before.md') !== -1);
});
});
describe('Relative images', function() {
it('should keep image relative with considering output directory in site format', function() {
var LEXED = loadPage('IMAGES', {
// GitHub repo ID
repo: 'GitBookIO/javascript',
// Imaginary folder of markdown file
dir: 'syntax',
outdir: 'syntax'
});
assert(LEXED[0].content.indexOf('"preview.png"') !== -1);
assert(LEXED[0].content.indexOf('"../preview2.png"') !== -1);
});
it('should keep image relative with considering output directory in page format', function() {
var LEXED = loadPage('IMAGES', {
// GitHub repo ID
repo: 'GitBookIO/javascript',
// Imaginary folder of markdown file
dir: 'syntax',
outdir: './'
});
assert(LEXED[0].content.indexOf('"syntax/preview.png"') !== -1);
assert(LEXED[0].content.indexOf('"preview2.png"') !== -1);
});
}); });
+49
View File
@@ -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);
});
});
-5
View File
@@ -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
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+7
View File
@@ -0,0 +1,7 @@
define([
"jQuery"
], function($) {
var events = $({});
return events;
});
+3 -3
View File
@@ -1,9 +1,9 @@
define([ define([
"jQuery", "jQuery",
"utils/execute", "utils/execute",
"utils/analytic", "core/events",
"core/state" "core/state"
], function($, execute, analytic, state){ ], function($, execute, events, state){
// Bind an exercise // Bind an exercise
var prepareExercise = function($exercise) { var prepareExercise = function($exercise) {
var codeSolution = $exercise.find(".code-solution").text(); var codeSolution = $exercise.find(".code-solution").text();
@@ -19,7 +19,7 @@ define([
$exercise.find(".action-submit").click(function(e) { $exercise.find(".action-submit").click(function(e) {
e.preventDefault(); e.preventDefault();
analytic.track("exercise.submit", {type: "code"}); events.trigger("exercise.submit", {type: "code"});
execute("javascript", editor.getValue(), codeValidation, codeContext, function(err, result) { execute("javascript", editor.getValue(), codeValidation, codeContext, function(err, result) {
$exercise.toggleClass("return-error", err != null); $exercise.toggleClass("return-error", err != null);
+115
View File
@@ -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
}
});
+18
View File
@@ -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
};
});
+21 -17
View File
@@ -1,28 +1,35 @@
define([ define([
"jQuery", "jQuery",
"utils/analytic", "utils/path",
"core/events",
"core/state", "core/state",
"core/search", "core/search",
"core/progress", "core/progress",
"core/exercise", "core/exercise",
"core/quiz" "core/quiz",
], function($, analytic, state, search, progress, exercises, quiz) { "core/loading"
], function($, path, events, state, search, progress, exercises, quiz, loading) {
var prev, next; var prev, next;
var githubCountStars, githubCountWatch; var githubCountStars, githubCountWatch;
var updateHistory = function(url, title) { var usePushState = (typeof history.pushState !== "undefined");
history.pushState({ path: url }, title, url);
};
var handleNavigation = function(url, push) { var handleNavigation = function(relativeUrl, push) {
if (typeof history.pushState === "undefined") { var url = path.isAbsolute(relativeUrl) ? relativeUrl : path.join(path.dirname(window.location.pathname), relativeUrl);
console.log("navigate to ", url, "baseurl="+relativeUrl);
if (!usePushState) {
// Refresh the page to the new URL if pushState not supported // Refresh the page to the new URL if pushState not supported
location.href = url; location.href = relativeUrl;
return return
} }
return $.get(url) return loading.show($.get(url)
.done(function (html) { .done(function (html) {
// Push url to history
if (push) history.pushState({ path: url }, null, url);
// Replace html content
html = html.replace( /<(\/?)(html|head|body)([^>]*)>/ig, function(a,b,c,d){ html = html.replace( /<(\/?)(html|head|body)([^>]*)>/ig, function(a,b,c,d){
return '<' + b + 'div' + ( b ? '' : ' data-element="' + c + '"' ) + d + '>'; return '<' + b + 'div' + ( b ? '' : ' data-element="' + c + '"' ) + d + '>';
}); });
@@ -49,13 +56,11 @@ define([
// Update state // Update state
state.update($("html")); state.update($("html"));
if (push) updateHistory(url, null);
preparePage(); preparePage();
}) })
.fail(function () { .fail(function (e) {
location.href = url; location.href = relativeUrl;
}); }));
}; };
var updateGitHubCounts = function() { var updateGitHubCounts = function() {
@@ -108,7 +113,7 @@ define([
} }
// Send to mixpanel // Send to mixpanel
analytic.track("page.view"); events.trigger("page.change");
}; };
var handlePagination = function (e) { var handlePagination = function (e) {
@@ -146,7 +151,6 @@ define([
if (event.state === null) { if (event.state === null) {
return; return;
} }
return handleNavigation(event.state.path, false); return handleNavigation(event.state.path, false);
}; };
+8 -5
View File
@@ -1,9 +1,9 @@
define([ define([
"jQuery", "jQuery",
"utils/execute", "utils/execute",
"utils/analytic", "core/events",
"core/state" "core/state"
], function($, execute, analytic, state){ ], function($, execute, events, state){
// Bind an exercise // Bind an exercise
var prepareQuiz = function($quiz) { var prepareQuiz = function($quiz) {
@@ -14,14 +14,17 @@ define([
// Submit: test code // Submit: test code
$quiz.find(".action-submit").click(function(e) { $quiz.find(".action-submit").click(function(e) {
e.preventDefault(); e.preventDefault();
analytic.track("exercise.submit", {type: "quiz"}); events.trigger("exercise.submit", {type: "quiz"});
$quiz.find("tr.alert-danger,li.alert-danger").removeClass("alert-danger"); $quiz.find("tr.alert-danger,li.alert-danger").removeClass("alert-danger");
$quiz.find(".alert-success,.alert-danger").addClass("hidden"); $quiz.find(".alert-success,.alert-danger").addClass("hidden");
$quiz.find(".question").each(function(q) { $quiz.find(".question").each(function(q) {
var result = true; var result = true;
var $answers = $quiz.find(".question-answers").slice(q).find("input[type=radio], input[type=checkbox]");
$(this).find("input[type=radio],input[type=checkbox]").each(function(i) { var $questions = $(this).find(".question-content").find("input[type=radio], input[type=checkbox]");
var $answers = $(this).find(".question-answers").find("input[type=radio], input[type=checkbox]");
$questions.each(function(i) {
var correct = $(this).is(":checked") === $answers.slice(i).first().is(":checked"); var correct = $(this).is(":checked") === $answers.slice(i).first().is(":checked");
result = result && correct; result = result && correct;
if (!correct) { if (!correct) {
@@ -1,17 +1,19 @@
require([ define([
"jQuery", "jQuery",
"utils/storage", "utils/storage",
"utils/analytic",
"utils/sharing", "utils/sharing",
"utils/appcache",
"core/events",
"core/font-settings",
"core/state", "core/state",
"core/keyboard", "core/keyboard",
"core/navigation", "core/navigation",
"core/progress", "core/progress",
"core/sidebar", "core/sidebar",
"core/search" "core/search"
], function($, storage, analytic, sharing, state, keyboard, navigation, progress, sidebar, search){ ], function($, storage, appCache, sharing, events, fontSettings, state, keyboard, navigation, progress, sidebar, search){
$(document).ready(function() { var start = function(config) {
var $book; var $book;
$book = state.$book; $book = state.$book;
@@ -29,10 +31,23 @@ require([
// Init keyboard // Init keyboard
keyboard.init(); keyboard.init();
// Init appcache
appCache.init();
// Bind sharing button // Bind sharing button
sharing.init(); sharing.init();
// Init navigation // Init navigation
navigation.init(); navigation.init();
});
//Init font settings
fontSettings.init();
events.trigger("start", config);
}
return {
start: start,
events: events
};
}); });
-33
View File
@@ -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
};
});
+15
View File
@@ -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
};
});
+47
View File
@@ -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
};
})
-2
View File
@@ -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||[]);
+49 -3
View File
@@ -6,6 +6,7 @@
left: 0px; left: 0px;
bottom: 0px; bottom: 0px;
color: @page-color;
background: @body-background; background: @body-background;
.transition(left 0.5s ease); .transition(left 0.5s ease);
@@ -23,7 +24,6 @@
outline: none; outline: none;
.page-inner { .page-inner {
font-family: @font-family-serif;
max-width: 800px; max-width: 800px;
margin: 0px auto; margin: 0px auto;
@@ -33,8 +33,6 @@
background: @page-background; background: @page-background;
border-radius: 2px; border-radius: 2px;
font-size: 16px;
line-height: 1.5em; line-height: 1.5em;
} }
@@ -57,6 +55,28 @@
min-height: calc(~"100% - 57px") min-height: calc(~"100% - 57px")
} }
} }
&.font-size-0{
font-size:@s-font-size;
}
&.font-size-1{
font-size:@m-font-size;
}
&.font-size-2{
font-size:@l-font-size;
}
&.font-size-3{
font-size:@xl-font-size;
}
&.font-size-4{
font-size:@xxl-font-size;
}
&.font-family-0{
font-family: @font-family-serif;
}
&.font-family-1{
font-family: @font-family-sans;
}
} }
&.with-summary { &.with-summary {
@@ -72,4 +92,30 @@
.transition(none) !important; .transition(none) !important;
} }
} }
&.color-theme-1{
.book-body {
color: @page-color-1;
background: @body-background-1;
.page-wrapper {
.page-inner {
section{
background: @page-background-1;
}
}
}
}
}
&.color-theme-2{
.book-body {
color: @page-color-2;
background: @body-background-2;
.page-wrapper {
.page-inner {
section{
background: @page-background-2;
}
}
}
}
}
} }
+159
View File
@@ -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;
}
}
}
}
}
}
+42 -1
View File
@@ -3,7 +3,7 @@
font-family: @font-family-sans; font-family: @font-family-sans;
position: fixed; position: fixed;
overflow: hidden; overflow: visible;
top: 0px; top: 0px;
right: 0px; right: 0px;
left: 0px; left: 0px;
@@ -24,6 +24,7 @@
text-transform: uppercase; text-transform: uppercase;
line-height: @header-height; line-height: @header-height;
.box-shadow(none) !important; .box-shadow(none) !important;
position:relative;
&:hover { &:hover {
position: relative; position: relative;
@@ -51,9 +52,23 @@
@media (max-width: 800px) { @media (max-width: 800px) {
display: none; display: none;
} }
i {
display: none;
}
} }
} }
&.is-loading {
.book-header h1 {
i {
display: inline-block;
}
a {
display: none;
}
}
}
&.with-summary { &.with-summary {
.book-header h1 { .book-header h1 {
margin-left: 250px; margin-left: 250px;
@@ -64,4 +79,30 @@
.transition(none) !important; .transition(none) !important;
} }
} }
&.color-theme-1{
.book-header{
color: @header-color-1;
background: @header-background-1;
.btn {
color: @header-button-color-1;
&:hover {
color: @header-button-hover-color-1;
background: @header-button-hover-background-1;
}
}
}
}
&.color-theme-2{
.book-header{
color: @header-color-2;
background: @header-background-2;
.btn {
color: @header-button-color-2;
&:hover {
color: @header-button-hover-color-2;
background: @header-button-hover-background-2;
}
}
}
}
} }
@@ -0,0 +1,92 @@
/* Tomorrow Night Bright Theme */
/* Original theme - https://github.com/chriskempson/tomorrow-theme */
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
/* Tomorrow Comment */
.hljs-comment,
.hljs-title {
color: #969896;
}
/* Tomorrow Red */
.hljs-variable,
.hljs-attribute,
.hljs-tag,
.hljs-regexp,
.ruby .hljs-constant,
.xml .hljs-tag .hljs-title,
.xml .hljs-pi,
.xml .hljs-doctype,
.html .hljs-doctype,
.css .hljs-id,
.css .hljs-class,
.css .hljs-pseudo {
color: #d54e53;
}
/* Tomorrow Orange */
.hljs-number,
.hljs-preprocessor,
.hljs-pragma,
.hljs-built_in,
.hljs-literal,
.hljs-params,
.hljs-constant {
color: #e78c45;
}
/* Tomorrow Yellow */
.ruby .hljs-class .hljs-title,
.css .hljs-rules .hljs-attribute {
color: #e7c547;
}
/* Tomorrow Green */
.hljs-string,
.hljs-value,
.hljs-inheritance,
.hljs-header,
.ruby .hljs-symbol,
.xml .hljs-cdata {
color: #b9ca4a;
}
/* Tomorrow Aqua */
.css .hljs-hexcolor {
color: #70c0b1;
}
/* Tomorrow Blue */
.hljs-function,
.python .hljs-decorator,
.python .hljs-title,
.ruby .hljs-function .hljs-title,
.ruby .hljs-title .hljs-keyword,
.perl .hljs-sub,
.javascript .hljs-title,
.coffeescript .hljs-title {
color: #7aa6da;
}
/* Tomorrow Purple */
.hljs-keyword,
.javascript .hljs-function {
color: #c397d8;
}
.hljs {
display: block;
background: black;
color: #eaeaea;
padding: 0.5em;
}
.coffeescript .javascript,
.javascript .xml,
.tex .hljs-formula,
.xml .javascript,
.xml .vbscript,
.xml .css,
.xml .hljs-cdata {
opacity: 0.5;
}
+107
View File
@@ -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;
}
@@ -1,8 +1,9 @@
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
/* Tomorrow Comment */ /* Tomorrow Comment */
.hljs-comment { .hljs-comment,
color: hsl(207, 35%, 35%); .hljs-title {
color: #8e908c;
} }
/* Tomorrow Red */ /* Tomorrow Red */
@@ -27,9 +28,9 @@
.hljs-pragma, .hljs-pragma,
.hljs-built_in, .hljs-built_in,
.hljs-literal, .hljs-literal,
.hljs-constant, .hljs-params,
.hljs-function .hljs-title { .hljs-constant {
color: hsl(50, 100%, 60%); color: #f5871f;
} }
/* Tomorrow Yellow */ /* Tomorrow Yellow */
@@ -45,7 +46,7 @@
.hljs-header, .hljs-header,
.ruby .hljs-symbol, .ruby .hljs-symbol,
.xml .hljs-cdata { .xml .hljs-cdata {
color: hsl(0, 100%, 70%); color: #718c00;
} }
/* Tomorrow Aqua */ /* Tomorrow Aqua */
@@ -54,7 +55,7 @@
} }
/* Tomorrow Blue */ /* Tomorrow Blue */
.hljs-function .keyword, .hljs-function,
.python .hljs-decorator, .python .hljs-decorator,
.python .hljs-title, .python .hljs-title,
.ruby .hljs-function .hljs-title, .ruby .hljs-function .hljs-title,
@@ -62,13 +63,13 @@
.perl .hljs-sub, .perl .hljs-sub,
.javascript .hljs-title, .javascript .hljs-title,
.coffeescript .hljs-title { .coffeescript .hljs-title {
color: hsl(207, 70%, 60%); color: #4271ae;
} }
/* Tomorrow Purple */ /* Tomorrow Purple */
.hljs-keyword, .hljs-keyword,
.javascript .hljs-function { .javascript .hljs-function {
color: hsl(207, 95%, 70%); color: #8959a8;
} }
.hljs { .hljs {
@@ -76,7 +77,6 @@
background: white; background: white;
color: #4d4d4c; color: #4d4d4c;
padding: 0.5em; padding: 0.5em;
font-family: "Anonymous Pro", "Inconsolata", "Monaco", monospace;
} }
.coffeescript .javascript, .coffeescript .javascript,
+29 -23
View File
@@ -1,8 +1,29 @@
.book .book-body .page-wrapper .page-inner section.normal { .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal {
padding: 25px; color:@page-color-1;
padding-top: 15px;
background-color: white;
pre, code {
background: #fdf6e3;
color: #657b83;
border-color: darken(#fdf6e3, 15%);
@import "./highlight/sepia.less";
}
}
.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal {
color:@page-color-2;
pre, code {
background: black;
color: #eaeaea;
border-color: #000;
@import "./highlight/night.less";
}
}
.book .book-body .page-wrapper .page-inner section.normal {
padding: 25px 0px;
padding-top: 15px;
color:@page-color;
& > *:first-child { & > *:first-child {
margin-top: 0 !important; } margin-top: 0 !important; }
@@ -52,12 +73,12 @@
h1 { h1 {
font-size: 28px; font-size: 28px;
color: black; } }
h2 { h2 {
font-size: 24px; font-size: 24px;
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
color: black; } }
h3 { h3 {
font-size: 18px; } font-size: 18px; }
@@ -69,7 +90,6 @@
font-size: 14px; } font-size: 14px; }
h6 { h6 {
color: #777777;
font-size: 14px; } font-size: 14px; }
p, blockquote, ul, ol, dl, table, pre { p, blockquote, ul, ol, dl, table, pre {
@@ -272,29 +292,15 @@
white-space: pre; white-space: pre;
border: none; border: none;
background: transparent; background: transparent;
}
.highlight pre { @import "./highlight/white.less";
color: hsl(204, 40%, 80%);
background-color: hsl(204, 30%, 10%);
border: 1px solid hsl(204, 30%, 10%);
font-size: 16px;
line-height: 1.5em;
overflow: auto;
padding: 20px;
margin: 0 -20px;
border-radius: 3px;
} }
pre { pre {
color: hsl(204, 40%, 80%); font-size: inherit;
background-color: hsl(204, 30%, 10%);
border: 1px solid hsl(204, 30%, 10%);
font-size: 16px;
line-height: 1.5em; line-height: 1.5em;
overflow: auto; overflow: auto;
padding: 20px; padding: 20px;
margin: 0 -20px;
border-radius: 3px; border-radius: 3px;
} }
+10
View File
@@ -43,3 +43,13 @@
} }
} }
} }
.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;
}
}
+32 -1
View File
@@ -17,7 +17,6 @@
position: relative; position: relative;
background: #fff; background: #fff;
margin-bottom: 20px; margin-bottom: 20px;
z-index: 10;
.bar { .bar {
height: @bar-height; height: @bar-height;
@@ -82,3 +81,35 @@
} }
} }
} }
.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;
}
}
}
+4
View File
@@ -29,6 +29,10 @@
.question-inner { .question-inner {
padding: 15px; padding: 15px;
.quiz-label {
font-weight: normal;
cursor: pointer;
}
} }
table { table {
+90
View File
@@ -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;
}
}
}
}
}
+1 -1
View File
@@ -5,10 +5,10 @@
@import "variables.less"; @import "variables.less";
@import "fonts.less"; @import "fonts.less";
@import "book/highlight.less";
@import "book/languages.less"; @import "book/languages.less";
@import "book/header.less"; @import "book/header.less";
@import "book/summary.less"; @import "book/summary.less";
@import "book/font-settings.less";
@import "book/body.less"; @import "book/body.less";
@import "book/exercise.less"; @import "book/exercise.less";
@import "book/quiz.less"; @import "book/quiz.less";
+1 -1
View File
@@ -1,4 +1,4 @@
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ /* http://jmblog.github.io/color-themes-for-highlightjs */
/* Tomorrow Comment */ /* Tomorrow Comment */
.hljs-comment, .hljs-comment,
+90
View File
@@ -38,6 +38,7 @@
@sidebar-link-completed-weight: normal; @sidebar-link-completed-weight: normal;
// Page // Page
@page-color: black;
@page-background: white; @page-background: white;
// Progress Bar // Progress Bar
@@ -79,5 +80,94 @@
@font-family-sans: "Open Sans", "Clear Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; @font-family-sans: "Open Sans", "Clear Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
@font-family-base: @font-family-serif; @font-family-base: @font-family-serif;
@FontPath: '@{staticPath}/fonts'; @FontPath: '@{staticPath}/fonts';
@fa-font-path: "@{FontPath}/fontawesome"; @fa-font-path: "@{FontPath}/fontawesome";
@s-font-size: 1.2rem;
@m-font-size: 1.4rem;
@l-font-size: 1.6rem;
@xl-font-size: 2.2rem;
@xxl-font-size: 4.0rem;
/*
,--------.,--.
'--. .--'| ,---. ,---. ,--,--,--. ,---. ,---. .--.
| | | .-. || .-. :| || .-. :( .-' '--'
| | | | | |\ --.| | | |\ --..-' `).--.
`--' `--' `--' `----'`--`--`--' `----'`----' '--'
*/
// Header
@header-color-1: #AFA790;
@header-color-2: #7e888b;
@header-background-1: #ECE3C4;
@header-background-2: #1d1f21;
@header-button-color-1: #AFA790;
@header-button-color-2: #7e888b;
@header-button-hover-color-1: #73553C;
@header-button-hover-color-2: #C9C9C9;
@header-button-hover-background-1: #E2DABE;
@header-button-hover-background-2: #0B0D0E;
// Body
@body-background-1: #F3EACB;
@body-background-2: #1d1f21;
@body-pagination-background-1: #FFFAEA;
@body-pagination-background-2: #33404D;
// Sidebar
@sidebar-color-1: #AFA790;
@sidebar-color-2: hsl(207, 15%, 80%);
@sidebar-background-1: #F3EACB;
@sidebar-background-2: #111111;
@sidebar-search-background-1: transparent;
@sidebar-search-background-2: transparent;
@sidebar-search-input-border-color-1: #D6CFBA;
@sidebar-search-input-border-color-2: #c4cdd4;
@sidebar-divider-color-1: hsl(207, 15%, 85%);
@sidebar-divider-color-2: #1d1f21;
@sidebar-link-color-1: #877F6A;
@sidebar-link-color-2: hsl(207, 15%, 50%);
@sidebar-link-background-1: transparent;
@sidebar-link-background-2: transparent;
@sidebar-link-hover-color-1: #704214;
@sidebar-link-hover-color-2: hsl(207, 100%, 50%);
@sidebar-link-hover-background-1: transparent;
@sidebar-link-hover-background-2: transparent;
@sidebar-icon-color-1: @bar-progress-background;
@sidebar-icon-color-2: @bar-progress-background;
@sidebar-link-completed-1: hsl(207, 15%, 25%);
@sidebar-link-completed-2: hsl(207, 15%, 25%);
@sidebar-link-completed-weight-1: normal;
@sidebar-link-completed-weight-2: normal;
// Page
@page-color-1: #704214;
@page-color-2: #a4b1b1;
@page-background-1: #F3EACB;
@page-background-2: #1d1f21;
@bar-background-1: #F3EACB;
@bar-background-2: #1d1f21;
@bar-progress-background-1: #704214;
@bar-progress-background-2: hsl(120, 60%, 50%);
// Basics of a navbar
@navbar-default-border-1: #d5d5d5;
@navbar-default-border-2: #d5d5d5;
// Navbar brand label
@navbar-default-color-1: #333;
@navbar-default-color-2: #333;
@navbar-default-link-color-1: #333;
@navbar-default-link-color-2: #333;
@navbar-default-brand-color-1: @navbar-default-link-color;
@navbar-default-brand-color-2: @navbar-default-link-color;
@navbar-default-brand-hover-color-1: @navbar-default-link-color;
@navbar-default-brand-hover-color-2: @navbar-default-link-color;
@navbar-default-brand-hover-bg-1: transparent;
@navbar-default-brand-hover-bg-2: transparent;
@@ -0,0 +1,22 @@
<div class="dropdown-menu font-settings">
<div class="dropdown-caret">
<span class="caret-outer"></span>
<span class="caret-inner"></span>
</div>
<div class="btn-group btn-block">
<button id="reduce-font-size" class="btn btn-default">A</button>
<button id="enlarge-font-size" class="btn btn-default">A</button>
</div>
<ul class="list-group font-family-list">
<li class="list-group-item" data-font="0">Serif</li>
<li class="list-group-item" data-font="1">Sans</li>
</ul>
<div class="btn-group btn-group-xs btn-block color-theme-list">
<button type="button" class="btn btn-default" id="color-theme-preview-0" data-theme="0">White</button>
<button type="button" class="btn btn-default" id="color-theme-preview-1" data-theme="1">Sepia</button>
<button type="button" class="btn btn-default" id="color-theme-preview-2" data-theme="2">Night</button>
</div>
</div>
+9 -1
View File
@@ -5,6 +5,11 @@
{% endif %} {% endif %}
<a href="#" class="btn pull-left toggle-summary"><i class="fa fa-align-justify"></i></a> <a href="#" class="btn pull-left toggle-summary"><i class="fa fa-align-justify"></i></a>
<a href="#" class="btn pull-left toggle-search"><i class="fa fa-search"></i></a> <a href="#" class="btn pull-left toggle-search"><i class="fa fa-search"></i></a>
<span id="font-settings-wrapper">
<a href="#" class="btn pull-left toggle-font-settings"><i class="fa fa-font"></i>
</a>
{% include "./font-settings.html" %}
</span>
<!-- Actions Right --> <!-- Actions Right -->
<a href="#" target="_blank" class="btn pull-right" data-sharing="google-plus"><i class="fa fa-google-plus"></i></a> <a href="#" target="_blank" class="btn pull-right" data-sharing="google-plus"><i class="fa fa-google-plus"></i></a>
@@ -17,5 +22,8 @@
{% endif %} {% endif %}
<!-- Title --> <!-- Title -->
<h1><a href="{{ basePath }}/" >{{ title }}</a></h1> <h1>
<i class="fa fa-spinner fa-spin"></i>
<a href="{{ basePath }}/" >{{ title }}</a>
</h1>
</div> </div>
+2 -2
View File
@@ -1,5 +1,5 @@
<!DOCTYPE HTML> <!DOCTYPE HTML>
<html lang="en-US"> <html lang="en-US" {% block htmlTag %}{% endblock %}>
<head prefix="og: http://ogp.me/ns# book: http://ogp.me/ns/book#"> <head prefix="og: http://ogp.me/ns# book: http://ogp.me/ns/book#">
{% block head %} {% block head %}
<meta charset="UTF-8"> <meta charset="UTF-8">
@@ -35,10 +35,10 @@
{% endblock %} {% endblock %}
</head> </head>
<body> <body>
{% block content %}{% endblock %}
{% block style %} {% block style %}
<link rel="stylesheet" href="{{ staticBase }}/style.css"> <link rel="stylesheet" href="{{ staticBase }}/style.css">
{% endblock %} {% endblock %}
{% block content %}{% endblock %}
{% block javascript %} {% block javascript %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/ace.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/ace.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/mode-javascript.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/mode-javascript.js"></script>
+2
View File
@@ -77,9 +77,11 @@
<h1>{{ item.title }}</h1> <h1>{{ item.title }}</h1>
</article> </article>
{% if pages[item.path] %}
<article> <article>
{{ articleContent(pages[item.path].content) }} {{ articleContent(pages[item.path].content) }}
</article> </article>
{% endif %}
{% if item.articles.length > 0 %} {% if item.articles.length > 0 %}
{% for article in item.articles %} {% for article in item.articles %}
+29
View File
@@ -1,5 +1,6 @@
{% extends "layout.html" %} {% extends "layout.html" %}
{% block htmlTag %}manifest="{{ basePath }}/manifest.appcache"{% endblock %}
{% block title %}{{ progress.current.title }}{% parent %}{% endblock %} {% block title %}{{ progress.current.title }}{% parent %}{% endblock %}
{% block content %} {% block content %}
<div class="book" {% if githubId %}data-github="{{ githubId }}"{% endif %} data-level="{{ progress.current.level }}" data-basepath="{{ basePath }}" data-revision="{{ revision }}"> <div class="book" {% if githubId %}data-github="{{ githubId }}"{% endif %} data-level="{{ progress.current.level }}" data-basepath="{{ basePath }}" data-revision="{{ revision }}">
@@ -35,3 +36,31 @@
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block javascript %}
{% parent %}
{% for resource in plugins.resources.js %}
{% if resource.url %}
<script src="{{ resource.url }}"></script>
{% else %}
<script src="{{ staticBase }}/plugins/{{ resource.path }}"></script>
{% endif %}
{% endfor %}
<script>
require(["gitbook"], function(gitbook) {
var config = {% autoescape false %}{{ pluginsConfig }}{% endautoescape %};
gitbook.start(config);
});
</script>
{% endblock %}
{% block style %}
{% parent %}
{% for resource in plugins.resources.css %}
{% if resource.url %}
<link rel="stylesheet" href="{{ resource.url }}">
{% else %}
<link rel="stylesheet" href="{{ staticBase }}/plugins/{{ resource.path }}">
{% endif %}
{% endfor %}
{% endblock %}