Compare commits

...

31 Commits

Author SHA1 Message Date
Aaron O'Mullan 603b61e8c8 Bump version to 0.4.0 2014-04-28 13:26:40 -07:00
Samy Pessé efb066c8e3 Update doc about how to configure gitbook 2014-04-28 20:45:10 +02:00
Samy Pessé c30db427b1 Remove some options from command line (moved to book.json) 2014-04-28 20:33:25 +02:00
Samy Pessé 23056fc85d Use a global configuration file for a book (default to book.json) 2014-04-28 20:29:36 +02:00
Samy Pessé 5f1bfc0280 Fix #163: Move logic of git infos extraction to lib 2014-04-28 20:16:30 +02:00
Samy Pessé b715901f92 Add hooks "page:before" and "page:after" 2014-04-28 19:56:40 +02:00
Samy Pessé 38fea06cf9 Use hook to allow plugins to edit page content during generation (Fixes #176) 2014-04-28 17:21:06 +02:00
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
Nattaphoom Ch 8f4b799d3c Make quiz easier to click by surround it with label. 2014-04-24 01:19:13 +07:00
34 changed files with 809 additions and 213 deletions
+32 -6
View File
@@ -30,14 +30,37 @@ $ 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
--githubHost <url> The url of the github host (defaults to https://github.com/)
--plugins <plugins> List of plugins to use separated by ","
--pluginsConfig <json file> JSON File containing plugins configuration
--config <config file> Configuration file to use, defualt 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: {}
}
```
You can publish your books to our index by visiting [GitBook.io](http://www.gitbook.io)
@@ -147,3 +170,6 @@ Plugins can used to extend your book functionnalities. Read [GitbookIO/plugin](h
##### 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
+9 -36
View File
@@ -12,12 +12,7 @@ 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/');
.option('--config <config file>', 'Configuration file to use, defualt to book.json')
};
@@ -26,37 +21,15 @@ var makeBuildFunc = function(converter) {
dir = dir || process.cwd();
outputDir = options.output;
// Read plugins config
var pluginsConfig = {};
if (options.pluginsConfig) {
pluginsConfig = JSON.parse(fs.readFileSync(options.pluginsConfig))
}
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,
plugins: options.plugins,
pluginsConfig: pluginsConfig
})
);
})
return converter(
_.extend({}, options || {}, {
input: dir,
output: outputDir,
generator: options.format,
configFile: options.config
})
)
.then(function(output) {
console.log("Successfuly built !");
return output;
+32 -11
View File
@@ -12,6 +12,7 @@ var fs = require('../lib/generate/fs');
var utils = require('./utils');
var build = require('./build');
var Server = require('./server');
// General options
prog
@@ -24,19 +25,39 @@ build.command(prog.command('build [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 "+path.relative(dir, filepath));
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();
});
build.command(prog.command('pdf [source_dir]'))
+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;
+15 -74
View File
@@ -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
};
+1
View File
@@ -18,6 +18,7 @@ var getFiles = function(path) {
ig.addIgnoreRules([
'.git/',
'.gitignore',
'.DS_Store'
], '__custom_stuff');
// Push each file to our list
+3 -3
View File
@@ -12,14 +12,14 @@ var BaseGenerator = function(options) {
this.plugins = [];
};
BaseGenerator.prototype.callHook = function(name) {
return this.plugins.hook(name, this);
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)
return Plugin.fromList(this.options.plugins, this.options.input)
.then(function(_plugins) {
that.plugins = _plugins;
+33 -1
View File
@@ -31,6 +31,9 @@ var generate = function(options) {
input: null,
output: null,
// Config file (relative to input)
configFile: "book.json",
// Output generator
generator: "site",
@@ -63,6 +66,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);
})
@@ -166,7 +198,7 @@ var generate = function(options) {
);
})
// Finish gneration
// Finish generation
.then(function() {
return generator.finish();
})
+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;
+25 -18
View File
@@ -4,13 +4,15 @@ 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) {
var Plugin = function(name, root) {
this.name = name;
this.root = root;
this.packageInfos = {};
this.infos = {};
@@ -20,16 +22,21 @@ var Plugin = function(name) {
"gitbook-"+name,
name,
], function(_name) {
if (this.load(_name)) return false;
}.bind(this));
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) {
Plugin.prototype.load = function(name, baseDir) {
try {
this.packageInfos = require(name+"/package.json");
this.infos = require(name);
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;
@@ -63,19 +70,19 @@ Plugin.prototype.isValid = function() {
// Resolve file path
Plugin.prototype.resolveFile = function(filename) {
return path.resolve(path.dirname(require.resolve(this.name)), filename);
return path.resolve(this.baseDir, filename);
};
// Resolve file path
Plugin.prototype.callHook = function(name, args) {
Plugin.prototype.callHook = function(name, context, data) {
var hookFunc = this.infos.hooks? this.infos.hooks[name] : null;
args = _.isArray(args) ? args : [args];
data = data || {};
if (!hookFunc) return Q();
if (!hookFunc) return Q(data);
return Q()
.then(function() {
return hookFunc.apply(null, args);
return hookFunc.apply(context, [data]);
});
};
@@ -94,7 +101,7 @@ Plugin.prototype.copyAssets = function(out) {
// Normalize a list of plugin name to use
Plugin.normalizeNames = function(names) {
// Normalize list to an array
names = _.isString(names) ? names.split(":") : (names || []);
names = _.isString(names) ? names.split(",") : (names || []);
// List plugins to remove
var toremove = _.chain(names)
@@ -121,12 +128,12 @@ Plugin.normalizeNames = function(names) {
};
// Extract data from a list of plugin
Plugin.fromList = function(names) {
Plugin.fromList = function(names, root) {
var failed = [];
// Load plugins
var plugins = _.map(names, function(name) {
var plugin = new Plugin(name);
var plugin = new Plugin(name, root);
if (!plugin.isValid()) failed.push(name);
return plugin;
});
@@ -152,12 +159,12 @@ Plugin.fromList = function(names) {
return Q({
'list': plugins,
'resources': resources,
'hook': function(name, args) {
'hook': function(name, context, data) {
return _.reduce(plugins, function(prev, plugin) {
return prev.then(function() {
return plugin.callHook(name, args);
return prev.then(function(ret) {
return plugin.callHook(name, context, ret);
})
}, Q());
}, Q(data));
},
'template': function(name) {
var withTpl = _.find(plugins, function(plugin) {
+59 -6
View File
@@ -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,6 +31,10 @@ var Generator = function() {
this.revision = Date.now();
this.indexer = indexer();
this.manifest = new Manifest(Date.now());
this.manifest.add("NETWORK", [
'*'
]);
};
util.inherits(Generator, BaseGenerator);
@@ -54,8 +59,11 @@ Generator.prototype.loadPlugins = function() {
};
// 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({
@@ -75,6 +83,7 @@ Generator.prototype._writeTemplate = function(tpl, options, output) {
pluginsConfig: JSON.stringify(that.options.pluginsConfig)
}, options));
})
.then(interpolate)
.then(function(html) {
return fs.writeFile(
output,
@@ -102,6 +111,15 @@ Generator.prototype.convertFile = function(content, _input) {
return Q()
.then(function() {
// Send content to plugins
return that.callHook("page:before", {
path: _input,
content: content
});
})
.then(function(_content) {
content = _content.content;
// Lex page
return parse.lex(content);
})
@@ -119,15 +137,29 @@ Generator.prototype.convertFile = function(content, _input) {
});
})
.then(function(sections) {
// Use plugin hook
return that.callHook("page", {
path: _input,
sections: sections
})
})
.then(function(_page) {
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) {
return that.callHook("page:after", {
path: _input,
content: html
}).get("content")
});
});
};
@@ -157,11 +189,22 @@ Generator.prototype.copyAssets = function() {
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) {
return plugin.copyAssets(path.join(that.options.output, "gitbook/plugins/", plugin.name))
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);
});
})
);
})
@@ -175,9 +218,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;
+56
View File
@@ -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
View File
@@ -5,5 +5,6 @@ module.exports = {
lex: require('./lex'),
progress: require('./progress'),
navigation: require('./navigation'),
readme: require('./readme')
readme: require('./readme'),
git: require('./git')
};
+8 -3
View File
@@ -18,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);
@@ -66,7 +67,7 @@ GitBookRenderer.prototype.link = function(href, title, text) {
} 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;
_href = parsed.href;
}
}
@@ -116,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) {
+3 -1
View File
@@ -1,6 +1,6 @@
{
"name": "gitbook",
"version": "0.3.1",
"version": "0.4.0",
"homepage": "http://www.gitbook.io/",
"description": "Library and cmd utility to generate GitBooks",
"main": "lib/index.js",
@@ -18,6 +18,8 @@
"highlight.js": "8.0.0",
"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",
+2 -2
View File
@@ -5,7 +5,7 @@ var assert = require('assert');
var Plugin = require('../').generate.Plugin;
describe('Plugin validation', function () {
var plugin = new Plugin("plugin");
var plugin = new Plugin("plugin", __dirname);
it('should be valid', function() {
assert(plugin.isValid());
@@ -33,7 +33,7 @@ describe('Plugin defaults loading', function () {
var ret = true;
beforeEach(function(done){
Plugin.fromList(Plugin.defaults)
Plugin.fromList(Plugin.defaults, __dirname)
.then(function(_r) {
ret = _r;
}, function(err) {
+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
+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
};
});
+16 -14
View File
@@ -1,32 +1,35 @@
define([
"jQuery",
"utils/path",
"core/events",
"core/state",
"core/search",
"core/progress",
"core/exercise",
"core/quiz"
], function($, events, 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 usePushState = !navigator.userAgent.match('CriOS') && (typeof history.pushState !== "undefined");
var usePushState = (typeof history.pushState !== "undefined");
var updateHistory = function(url, title) {
history.pushState({ path: url }, title, url);
};
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);
var handleNavigation = function(url, push) {
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) {
if (push) updateHistory(url, null);
// 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 + '>';
});
@@ -55,9 +58,9 @@ define([
state.update($("html"));
preparePage();
})
.fail(function () {
location.href = url;
});
.fail(function (e) {
location.href = relativeUrl;
}));
};
var updateGitHubCounts = function() {
@@ -148,7 +151,6 @@ define([
if (event.state === null) {
return;
}
return handleNavigation(event.state.path, false);
};
+5 -1
View File
@@ -2,6 +2,7 @@ define([
"jQuery",
"utils/storage",
"utils/sharing",
"utils/appcache",
"core/events",
"core/font-settings",
@@ -11,7 +12,7 @@ define([
"core/progress",
"core/sidebar",
"core/search"
], function($, storage, sharing, events, fontSettings, state, keyboard, navigation, progress, sidebar, search){
], function($, storage, appCache, sharing, events, fontSettings, state, keyboard, navigation, progress, sidebar, search){
var start = function(config) {
var $book;
$book = state.$book;
@@ -30,6 +31,9 @@ define([
// Init keyboard
keyboard.init();
// Init appcache
appCache.init();
// Bind sharing button
sharing.init();
+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
};
})
+14
View File
@@ -52,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;
@@ -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.io/color-themes-for-highlightjs */
/* http://jmblog.github.com/color-themes-for-google-code-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;
}
}
+19 -17
View File
@@ -1,11 +1,27 @@
.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: 25px;
padding: 25px 0px;
padding-top: 15px;
color:@page-color;
@@ -276,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;
}
+6 -2
View File
@@ -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
View File
@@ -162,6 +162,7 @@
li {
&.divider {
background: @sidebar-divider-color-1;
box-shadow: none;
}
i.fa-check {
@@ -206,6 +207,7 @@
li {
&.divider {
background: @sidebar-divider-color-2;
box-shadow: none;
}
i.fa-check {
-1
View File
@@ -5,7 +5,6 @@
@import "variables.less";
@import "fonts.less";
@import "book/highlight.less";
@import "book/languages.less";
@import "book/header.less";
@import "book/summary.less";
+4 -1
View File
@@ -22,5 +22,8 @@
{% 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>
+1 -1
View File
@@ -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">
+1
View File
@@ -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 }}">