mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-26 20:27:00 +00:00
Compare commits
81 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b629b78dba | |||
| f62c218735 | |||
| 725510028f | |||
| ad7927242d | |||
| 5692bdd229 | |||
| 3402746719 | |||
| 30a8414b06 | |||
| 9d46899ea7 | |||
| 58f14fd9ca | |||
| b6af14b13e | |||
| ba2134c480 | |||
| 4705548860 | |||
| 7d51ae3689 | |||
| b9360de3e3 | |||
| 9f241457bb | |||
| 60a7c28e66 | |||
| 5dff9b795e | |||
| 0134677e0e | |||
| 39ef4fa199 | |||
| b507a30301 | |||
| 539c2d0c57 | |||
| aa6f3530f9 | |||
| 98057af219 | |||
| bb48c8233c | |||
| 8f14db64df | |||
| 37d40dc66c | |||
| 6c96680cca | |||
| 4e475857d5 | |||
| f8ff0453dd | |||
| 3cc5c47535 | |||
| a2046814b4 | |||
| 82f814fdbd | |||
| cd382dc8fc | |||
| 3842e5bf4a | |||
| f05b138fd6 | |||
| 1b87cec893 | |||
| 4a894b1e18 | |||
| c7c98009e7 | |||
| 356322e0ed | |||
| 7a47049367 | |||
| a9f473260c | |||
| d16577e8bf | |||
| 15b4053d6b | |||
| 925a9b1388 | |||
| fc942fdeca | |||
| 75a6ecbf52 | |||
| 520045441b | |||
| 00c3e966d3 | |||
| 8b6ebe86b8 | |||
| d5bd3453e1 | |||
| ddacbbe73b | |||
| f7d52f0eff | |||
| 39c50640ea | |||
| 16128c7e60 | |||
| 8b6a80cffb | |||
| 86f8a9d8fb | |||
| 790dfd601e | |||
| 3426428142 | |||
| 6c5c8e6eb7 | |||
| 3aefcaea73 | |||
| 04c025774d | |||
| 80aac7f581 | |||
| 266f7ca73d | |||
| 6bbefad24e | |||
| d6eb8e4c60 | |||
| c87edb155a | |||
| ef0468ad5d | |||
| f03153eb0c | |||
| 3d8b6e550a | |||
| 4c3dbca5ca | |||
| a258feea37 | |||
| 3a3332d0b9 | |||
| 4259283f32 | |||
| 243d977cd6 | |||
| 94bbbecedf | |||
| f49eab42a9 | |||
| ce6b2bc47d | |||
| 3245d7e430 | |||
| 1f4cf33dd0 | |||
| 7274aa6940 | |||
| 125e804cd5 |
@@ -2,4 +2,3 @@ language: node_js
|
||||
node_js:
|
||||
- "0.11"
|
||||
- "0.10"
|
||||
- "0.8"
|
||||
|
||||
+10
@@ -1,2 +1,12 @@
|
||||
# Release notes
|
||||
|
||||
## 0.6.2
|
||||
- Support generating a plugin's book info dynamically
|
||||
- Improve navigation on dark theme
|
||||
- Improve path normalization when parsing SUMMARY.md
|
||||
|
||||
## 0.6.0
|
||||
- Generate header id the same as github
|
||||
- Custom links can be added at top of sidebar
|
||||
- Summary can now be transformed by plugins
|
||||
- Support importing code snippets
|
||||
|
||||
@@ -92,6 +92,11 @@ Here are the options that can be stored in this file:
|
||||
"issues": null,
|
||||
"contribute": null,
|
||||
|
||||
// Custom links at top of sidebar
|
||||
"custom": {
|
||||
"Custom link name": "https://customlink.com"
|
||||
},
|
||||
|
||||
// Sharing links
|
||||
"sharing": {
|
||||
"google": null,
|
||||
@@ -253,3 +258,14 @@ Plugins can used to extend your book's functionality. Read [GitbookIO/plugin](ht
|
||||
* [Markdown within HTML](https://github.com/mrpotes/gitbook-plugin-nestedmd): Process markdown within HTML blocks - allows custom layout options for individual pages
|
||||
* [Bootstrap JavaScript plugins](https://github.com/mrpotes/gitbook-plugin-bootstrapjs): Use the [Bootstrap JavaScript plugins](http://getbootstrap.com/javascript) in your online GitBook
|
||||
* [Piwik Open Analytics](https://github.com/emmanuel-keller/gitbook-plugin-piwik): Piwik Open Analytics tracking for your book
|
||||
* [Heading Anchors](https://github.com/rlmv/gitbook-plugin-anchors): Add linkable Github-style anchors to headings
|
||||
* [JSBin](https://github.com/jcouyang/gitbook-plugin-jsbin): Ebedded jsbin frame into your book
|
||||
|
||||
#### Debugging
|
||||
|
||||
You can use the environment variable `DEBUG=true` to get better error messages (with stack trace). For example:
|
||||
|
||||
```
|
||||
$ export DEBUG=true
|
||||
$ gitbook build ./
|
||||
```
|
||||
|
||||
+13
-3
@@ -10,9 +10,10 @@ var generators = require("../lib/generate").generators;
|
||||
|
||||
var buildCommand = function(command) {
|
||||
return command
|
||||
.option('-v, --verbose', 'Activate verbose mode, useful for debugging errors')
|
||||
.option('-o, --output <directory>', 'Path to output directory, defaults to ./_book')
|
||||
.option('-f, --format <name>', 'Change generation format, defaults to site, availables are: '+_.keys(generators).join(", "))
|
||||
.option('--config <config file>', 'Configuration file to use, defaults to book.js or book.json')
|
||||
.option('--config <config file>', 'Configuration file to use, defaults to book.js or book.json');
|
||||
};
|
||||
|
||||
|
||||
@@ -26,6 +27,11 @@ var makeBuildFunc = function(converter) {
|
||||
dir = dir || process.cwd();
|
||||
outputDir = options.output;
|
||||
|
||||
// Set debugging
|
||||
if(options.verbose) {
|
||||
process.env.DEBUG = "true";
|
||||
}
|
||||
|
||||
console.log('Starting build ...');
|
||||
return converter(
|
||||
_.extend({}, options || {}, {
|
||||
@@ -38,8 +44,12 @@ var makeBuildFunc = function(converter) {
|
||||
.then(function(output) {
|
||||
console.log("Successfully built!");
|
||||
return output;
|
||||
}, utils.logError)
|
||||
.fail(function() {
|
||||
})
|
||||
.fail(function(err) {
|
||||
// Log error
|
||||
utils.logError(err);
|
||||
|
||||
// Exit process with failure code
|
||||
process.exit(-1);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@ var fs = require('../lib/generate/fs');
|
||||
var utils = require('./utils');
|
||||
var build = require('./build');
|
||||
var Server = require('./server');
|
||||
var platform = require("./platform");
|
||||
|
||||
// General options
|
||||
prog
|
||||
@@ -115,6 +116,22 @@ prog
|
||||
return initDir(dir);
|
||||
});
|
||||
|
||||
prog
|
||||
.command('publish [source_dir]')
|
||||
.description('Publish content to the associated gitbook.io book')
|
||||
.action(function(dir) {
|
||||
dir = dir || process.cwd();
|
||||
return platform.publish(dir);
|
||||
});
|
||||
|
||||
prog
|
||||
.command('git:remote [source_dir] [book_id]')
|
||||
.description('Adds a git remote to a book repository')
|
||||
.action(function(dir, bookId) {
|
||||
dir = dir || process.cwd();
|
||||
return platform.remote(dir, bookId);
|
||||
});
|
||||
|
||||
// Parse and fallback to help if no args
|
||||
if(_.isEmpty(prog.parse(process.argv).args) && process.argv.length === 2) {
|
||||
prog.help();
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
var Q = require("q");
|
||||
var utils = require("./utils");
|
||||
|
||||
var publish = function(folder) {
|
||||
if (!folder) {
|
||||
console.log("Need a repository folder");
|
||||
return process.exit(-1);
|
||||
}
|
||||
|
||||
utils.gitCmd("push", ["gitbook", "master"])
|
||||
.then(function(out) {
|
||||
console.log(out.stdout);
|
||||
}, function(err) {
|
||||
if (err.code == 128) {
|
||||
console.log("No book on gitbook.io is configured with this git repository.");
|
||||
console.log("Run 'gitbook git:remote username/book' to intialize this repository.");
|
||||
} else {
|
||||
console.log(err.message);
|
||||
}
|
||||
process.exit(-1);
|
||||
});
|
||||
};
|
||||
|
||||
var remote = function(folder, bookId) {
|
||||
if (!folder || !bookId) {
|
||||
console.log("Need a repository folder and a book id");
|
||||
return process.exit(-1);
|
||||
}
|
||||
|
||||
var url = "https://push.gitbook.io/"+bookId+".git";
|
||||
var addRemote = function() {
|
||||
return utils.gitCmd("remote", ["add", "gitbook", url]);
|
||||
}
|
||||
|
||||
addRemote()
|
||||
.fail(function(err) {
|
||||
if (err.code == 128) {
|
||||
return utils.gitCmd("remote", ["rm", "gitbook"]).then(addRemote);
|
||||
}
|
||||
return Q.reject(err);
|
||||
})
|
||||
.then(function(out) {
|
||||
console.log("Book remote '"+url+"' added to the folder");
|
||||
}, function(err) {
|
||||
console.log(err.message);
|
||||
process.exit(-1);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
publish: publish,
|
||||
remote: remote
|
||||
};
|
||||
+23
-1
@@ -1,6 +1,7 @@
|
||||
var Q = require('q');
|
||||
var _ = require('lodash');
|
||||
|
||||
var exec = require('child_process').exec;
|
||||
var http = require('http');
|
||||
var send = require('send');
|
||||
|
||||
@@ -36,9 +37,30 @@ function logError(err) {
|
||||
return Q.reject(err);
|
||||
};
|
||||
|
||||
function runGitCommand(command, args) {
|
||||
var d = Q.defer(), child;
|
||||
args = ["git", command].concat(args).join(" ");
|
||||
|
||||
child = exec(args, function (error, stdout, stderr) {
|
||||
if (error !== null) {
|
||||
error.stdout = stdout;
|
||||
error.stderr = stderr;
|
||||
d.reject(error);
|
||||
} else {
|
||||
d.resolve({
|
||||
stdout: stdout,
|
||||
stderr: stderr
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
return d.promise;
|
||||
};
|
||||
|
||||
|
||||
// Exports
|
||||
module.exports = {
|
||||
watch: watch,
|
||||
logError: logError
|
||||
logError: logError,
|
||||
gitCmd: runGitCommand
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
var exec = require('child_process').exec;
|
||||
|
||||
var fs = require("fs");
|
||||
var fs = require('graceful-fs');
|
||||
var parse = require("../../parse");
|
||||
var BaseGenerator = require("../page");
|
||||
var stringUtils = require("../../utils/string");
|
||||
@@ -35,9 +35,12 @@ Generator.prototype.finish = function() {
|
||||
"--title": that.options.title,
|
||||
"--comments": that.options.description,
|
||||
"--authors": that.options.author,
|
||||
"--chapter": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter ')]",
|
||||
"--chapter-mark": "pagebreak",
|
||||
"--level1-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-1 ')]",
|
||||
"--level2-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-2 ')]",
|
||||
"--level3-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-3 ')]"
|
||||
"--level3-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-3 ')]",
|
||||
"--no-chapters-in-toc": true
|
||||
};
|
||||
|
||||
if (format == "pdf") {
|
||||
|
||||
+6
-1
@@ -1,5 +1,5 @@
|
||||
var Q = require("q");
|
||||
var fs = require("fs");
|
||||
var fs = require('graceful-fs');
|
||||
var fsExtra = require("fs-extra");
|
||||
var Ignore = require("fstream-ignore");
|
||||
|
||||
@@ -30,6 +30,11 @@ var getFiles = function(path) {
|
||||
'*.pdf',
|
||||
'*.epub',
|
||||
'*.mobi',
|
||||
|
||||
// Skip config files
|
||||
'.ignore',
|
||||
'.bookignore',
|
||||
'book.json',
|
||||
], '__custom_stuff');
|
||||
|
||||
// Push each file to our list
|
||||
|
||||
@@ -14,13 +14,13 @@ var BaseGenerator = function(options) {
|
||||
};
|
||||
|
||||
BaseGenerator.prototype.callHook = function(name, data) {
|
||||
return this.plugins.hook(name, this, data);
|
||||
return this.plugins.hook(name, data);
|
||||
};
|
||||
|
||||
BaseGenerator.prototype.loadPlugins = function() {
|
||||
var that = this;
|
||||
|
||||
return Plugin.fromList(this.options.plugins, this.options.input)
|
||||
return Plugin.fromList(this.options.plugins, this.options.input, this)
|
||||
.then(function(_plugins) {
|
||||
that.plugins = _plugins;
|
||||
|
||||
|
||||
+33
-8
@@ -224,8 +224,15 @@ var generateBook = function(options) {
|
||||
options.github = null;
|
||||
return null;
|
||||
} else if(options.github) {
|
||||
// Git already specified in options
|
||||
return options.github;
|
||||
// Git already specified in options
|
||||
options.github = (
|
||||
// Support URLs in "github" entry of book.json
|
||||
parse.git.githubID(options.github) ||
|
||||
|
||||
// Fallback
|
||||
options.github
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try auto detecting
|
||||
@@ -264,13 +271,31 @@ var generateBook = function(options) {
|
||||
|
||||
// Get summary
|
||||
.then(function() {
|
||||
return fs.readFile(path.join(options.input, "SUMMARY.md"), "utf-8")
|
||||
.then(function(_summary) {
|
||||
options.summary = parse.summary(_summary);
|
||||
var summary = {
|
||||
path: path.join(options.input, "SUMMARY.md")
|
||||
};
|
||||
|
||||
// Parse navigation
|
||||
options.navigation = parse.navigation(options.summary);
|
||||
});
|
||||
var _callHook = function(name) {
|
||||
return generator.callHook(name, summary)
|
||||
.then(function(_summary) {
|
||||
summary = _summary;
|
||||
return summary;
|
||||
});
|
||||
};
|
||||
|
||||
return fs.readFile(summary.path, "utf-8")
|
||||
.then(function(_content) {
|
||||
summary.content = _content;
|
||||
return _callHook("summary:before");
|
||||
})
|
||||
.then(function() {
|
||||
summary.content = parse.summary(summary.content);
|
||||
return _callHook("summary:after");
|
||||
})
|
||||
.then(function() {
|
||||
options.summary = summary.content;
|
||||
options.navigation = parse.navigation(options.summary);
|
||||
})
|
||||
})
|
||||
|
||||
// Skip processing some files
|
||||
|
||||
+150
-78
@@ -10,11 +10,15 @@ var pkg = require("../../package.json");
|
||||
|
||||
var RESOURCES = ["js", "css"];
|
||||
|
||||
var Plugin = function(name, root) {
|
||||
var Plugin = function(name, root, generator) {
|
||||
this.name = name;
|
||||
this.root = root;
|
||||
this.packageInfos = {};
|
||||
this.infos = {};
|
||||
this.generator = generator;
|
||||
|
||||
// Bind methods
|
||||
_.bindAll(this);
|
||||
|
||||
_.each([
|
||||
"gitbook-plugin-"+name,
|
||||
@@ -43,28 +47,67 @@ Plugin.prototype.load = function(name, baseDir) {
|
||||
}
|
||||
};
|
||||
|
||||
// Return resources
|
||||
Plugin.prototype.getResources = function(resource) {
|
||||
if (!this.infos.book || !this.infos.book[resource]) {
|
||||
return [];
|
||||
Plugin.prototype.normalizeResource = function(resource) {
|
||||
// Parse the resource path
|
||||
var parsed = url.parse(resource);
|
||||
|
||||
// This is a remote resource
|
||||
// so we will simply link to using it's URL
|
||||
if (parsed.protocol) {
|
||||
return {
|
||||
"url": resource
|
||||
};
|
||||
}
|
||||
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();
|
||||
|
||||
// This will be copied over from disk
|
||||
// and shipped with the book's build
|
||||
return { "path": this.name+"/"+resource };
|
||||
};
|
||||
|
||||
// Return resources
|
||||
Plugin.prototype._getResources = function() {
|
||||
var book = this.infos.book;
|
||||
|
||||
// Nothing specified, fallback to default
|
||||
if (!book) {
|
||||
return Q({});
|
||||
}
|
||||
|
||||
// Dynamic function
|
||||
if(typeof book === "function") {
|
||||
// Call giving it the context of our generator
|
||||
return Q().then(book.bind(this.generator));
|
||||
}
|
||||
|
||||
// Plain data object
|
||||
return Q(_.cloneDeep(book));
|
||||
};
|
||||
|
||||
// Normalize resources and return them
|
||||
Plugin.prototype.getResources = function() {
|
||||
var that = this;
|
||||
|
||||
|
||||
|
||||
return this._getResources()
|
||||
.then(function(resources) {
|
||||
|
||||
_.each(RESOURCES, function(resourceType) {
|
||||
resources[resourceType] = (resources[resourceType] || []).map(that.normalizeResource);
|
||||
});
|
||||
|
||||
return resources;
|
||||
});
|
||||
};
|
||||
|
||||
// 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)
|
||||
this.packageInfos &&
|
||||
this.packageInfos.name &&
|
||||
this.packageInfos.engines &&
|
||||
this.packageInfos.engines.gitbook &&
|
||||
semver.satisfies(pkg.version, this.packageInfos.engines.gitbook)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -74,7 +117,10 @@ Plugin.prototype.resolveFile = function(filename) {
|
||||
};
|
||||
|
||||
// Resolve file path
|
||||
Plugin.prototype.callHook = function(name, context, data) {
|
||||
Plugin.prototype.callHook = function(name, data) {
|
||||
// Our generator will be the context to apply
|
||||
var context = this.generator;
|
||||
|
||||
var hookFunc = this.infos.hooks? this.infos.hooks[name] : null;
|
||||
data = data || {};
|
||||
|
||||
@@ -86,18 +132,20 @@ Plugin.prototype.callHook = function(name, context, data) {
|
||||
});
|
||||
};
|
||||
|
||||
// Has assets
|
||||
Plugin.prototype.hasAssets = function(out) {
|
||||
return (this.infos.book && this.infos.book.assets);
|
||||
};
|
||||
|
||||
// Copy plugin assets fodler
|
||||
Plugin.prototype.copyAssets = function(out) {
|
||||
if (!this.hasAssets()) return Q();
|
||||
return fs.copy(
|
||||
this.resolveFile(this.infos.book.assets),
|
||||
out
|
||||
);
|
||||
var that = this;
|
||||
|
||||
return this.getResources().get('assets')
|
||||
.then(function(assets) {
|
||||
// Assets are undefined
|
||||
if(!assets) return false;
|
||||
|
||||
return fs.copy(
|
||||
that.resolveFile(assets),
|
||||
out
|
||||
).then(_.constant(true));
|
||||
}, _.constant(false));
|
||||
};
|
||||
|
||||
|
||||
@@ -114,7 +162,7 @@ Plugin.normalizeNames = function(names) {
|
||||
return name.length > 0 && name[0] == "-";
|
||||
})
|
||||
.map(function(name) {
|
||||
return name.slice(1)
|
||||
return name.slice(1);
|
||||
})
|
||||
.value();
|
||||
|
||||
@@ -133,69 +181,93 @@ Plugin.normalizeNames = function(names) {
|
||||
};
|
||||
|
||||
// Extract data from a list of plugin
|
||||
Plugin.fromList = function(names, root) {
|
||||
Plugin.fromList = function(names, root, generator) {
|
||||
var failed = [];
|
||||
|
||||
// Load plugins
|
||||
var plugins = _.map(names, function(name) {
|
||||
var plugin = new Plugin(name, root);
|
||||
var plugin = new Plugin(name, root, generator);
|
||||
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();
|
||||
resources.html = {}
|
||||
// The raw resources extracted from each plugin
|
||||
var pluginResources;
|
||||
|
||||
_.each(plugins, function(plugin) {
|
||||
if (!plugin.infos.book || !plugin.infos.book.html) return;
|
||||
var html = plugin.infos.book.html || {};
|
||||
_.each(html, function(code, key) {
|
||||
if (!_.isFunction(code)) code = _.constant(code);
|
||||
if (!resources.html[key]) resources.html[key] = [];
|
||||
resources.html[key].push(code);
|
||||
// Get resources of plugins
|
||||
return Q.all(_.map(plugins, function(plugin) {
|
||||
return plugin.getResources();
|
||||
}))
|
||||
// Extract resources out
|
||||
// css, js, etc ...
|
||||
.then(function(resources) {
|
||||
pluginResources = resources;
|
||||
// Group by resource types
|
||||
return _.chain(RESOURCES)
|
||||
.map(function(resourceType) {
|
||||
// Get resources from all the plugins for this current type
|
||||
return [
|
||||
// Key
|
||||
resourceType,
|
||||
// Value
|
||||
_.chain(resources)
|
||||
.pluck(resourceType)
|
||||
.compact()
|
||||
.flatten()
|
||||
.value()
|
||||
];
|
||||
})
|
||||
});
|
||||
|
||||
return Q({
|
||||
'list': plugins,
|
||||
'resources': resources,
|
||||
'hook': function(name, context, data) {
|
||||
return _.reduce(plugins, function(prev, plugin) {
|
||||
return prev.then(function(ret) {
|
||||
return plugin.callHook(name, context, ret);
|
||||
})
|
||||
}, Q(data));
|
||||
},
|
||||
'template': function(name) {
|
||||
var withTpl = _.find(plugins, function(plugin) {
|
||||
return (plugin.infos.templates
|
||||
&& plugin.infos.templates[name]);
|
||||
.object()
|
||||
.value();
|
||||
})
|
||||
// Extract html snippets
|
||||
.then(function(resources) {
|
||||
// Map of html resources by name added by each plugin
|
||||
resources.html = pluginResources.reduce(function(accu, resource) {
|
||||
var html = (resource && resource.html) || {};
|
||||
_.each(html, function(code, key) {
|
||||
// Turn into function if not one already
|
||||
if (!_.isFunction(code)) code = _.constant(code);
|
||||
// Append
|
||||
accu[key] = (accu[key] || []).concat([code]);
|
||||
});
|
||||
|
||||
if (!withTpl) return null;
|
||||
return withTpl.resolveFile(withTpl.infos.templates[name]);
|
||||
},
|
||||
'html': function(tag, context, options) {
|
||||
return _.map(resources.html[tag] || [], function(code) {
|
||||
return code.call(context, options);
|
||||
}).join("\n");
|
||||
}
|
||||
return accu;
|
||||
}, {});
|
||||
|
||||
return resources;
|
||||
})
|
||||
// Return big multi-plugin object
|
||||
.then(function(resources) {
|
||||
return {
|
||||
'list': plugins,
|
||||
'resources': resources,
|
||||
'hook': function(name, data) {
|
||||
return _.reduce(plugins, function(prev, plugin) {
|
||||
return prev.then(function(ret) {
|
||||
return plugin.callHook(name, ret);
|
||||
});
|
||||
}, Q(data));
|
||||
},
|
||||
'template': function(name) {
|
||||
var withTpl = _.find(plugins, function(plugin) {
|
||||
return (
|
||||
plugin.infos.templates &&
|
||||
plugin.infos.templates[name]
|
||||
);
|
||||
});
|
||||
|
||||
if (!withTpl) return null;
|
||||
return withTpl.resolveFile(withTpl.infos.templates[name]);
|
||||
},
|
||||
'html': function(tag, context, options) {
|
||||
return _.map(resources.html[tag] || [], function(code) {
|
||||
return code.call(context, options);
|
||||
}).join("\n");
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -102,6 +102,7 @@ Generator.prototype.convertFile = function(content, _input) {
|
||||
|
||||
var page = {
|
||||
path: _input,
|
||||
rawPath: input, // path to raw md file
|
||||
content: content,
|
||||
progress: parse.progress(this.options.navigation, _input)
|
||||
};
|
||||
@@ -197,16 +198,16 @@ Generator.prototype.copyAssets = function() {
|
||||
.then(function() {
|
||||
return Q.all(
|
||||
_.map(that.plugins.list, function(plugin) {
|
||||
if (!plugin.hasAssets()) return Q();
|
||||
|
||||
var pluginAssets = path.join(that.options.output, "gitbook/plugins/", plugin.name);
|
||||
return plugin.copyAssets(pluginAssets)
|
||||
.then(function() {
|
||||
.then(function(copiedStuff) {
|
||||
// Nothing was copied
|
||||
if(!copiedStuff) { return; }
|
||||
return that.manifest.addFolder(pluginAssets, "gitbook/plugins/"+plugin.name);
|
||||
});
|
||||
})
|
||||
);
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
// Dump search index to disk
|
||||
|
||||
@@ -2,7 +2,7 @@ var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
|
||||
var lunr = require('lunr');
|
||||
var marked = require('marked');
|
||||
var kramed = require('kramed');
|
||||
var textRenderer = require('marked-text-renderer');
|
||||
|
||||
|
||||
@@ -28,14 +28,14 @@ Indexer.prototype.text = function(nodes) {
|
||||
// Copy section
|
||||
var section = _.toArray(nodes);
|
||||
|
||||
// marked's Render expects this, we don't use it yet
|
||||
// kramed's Render expects this, we don't use it yet
|
||||
section.links = {};
|
||||
|
||||
var options = _.extend({}, marked.defaults, {
|
||||
var options = _.extend({}, kramed.defaults, {
|
||||
renderer: this.renderer
|
||||
});
|
||||
|
||||
return marked.parser(section, options);
|
||||
return kramed.parser(section, options);
|
||||
};
|
||||
|
||||
Indexer.prototype.addSection = function(path, section) {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
var fs = require('graceful-fs');
|
||||
var path = require('path');
|
||||
|
||||
module.exports = function(code, folder) {
|
||||
folder = folder || '';
|
||||
|
||||
return code.replace(/{{([\s\S]+?)}}/g, function(match, filename) {
|
||||
// Normalize filename
|
||||
var fname = path.join(folder, filename.trim());
|
||||
|
||||
// Try including snippet from FS
|
||||
try {
|
||||
// Trim trailing newlines/space of imported snippets
|
||||
return fs.readFileSync(fname, 'utf8').trimRight();
|
||||
} catch(err) {}
|
||||
|
||||
// If fails leave content as is
|
||||
return match;
|
||||
});
|
||||
};
|
||||
+1
-1
@@ -28,7 +28,7 @@ function gitURL(path) {
|
||||
// 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;
|
||||
var sliceEnd = _url.slice(-4) === '.git' ? -4 : undefined;
|
||||
|
||||
// Detect HTTPS repos
|
||||
var parsed = url.parse(_url);
|
||||
|
||||
+8
-6
@@ -1,13 +1,14 @@
|
||||
var _ = require("lodash")
|
||||
var parseSummary = require("./summary");
|
||||
var _ = require("lodash");
|
||||
var parseEntries = require("./summary").entries;
|
||||
|
||||
|
||||
var parseLangs = function(content) {
|
||||
var summary = parseSummary(content);
|
||||
var entries = parseEntries(content);
|
||||
|
||||
return {
|
||||
list: _.chain(summary.chapters)
|
||||
list: _.chain(entries)
|
||||
.filter(function(entry) {
|
||||
return entry.path != null;
|
||||
return Boolean(entry.path);
|
||||
})
|
||||
.map(function(entry) {
|
||||
return {
|
||||
@@ -17,7 +18,8 @@ var parseLangs = function(content) {
|
||||
};
|
||||
})
|
||||
.value()
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
module.exports = parseLangs;
|
||||
+6
-2
@@ -1,5 +1,5 @@
|
||||
var _ = require('lodash');
|
||||
var marked = require('marked');
|
||||
var kramed = require('kramed');
|
||||
|
||||
// Split a page up into sections (lesson, exercises, ...)
|
||||
function splitSections(nodes) {
|
||||
@@ -88,7 +88,7 @@ function sectionId(section, idx) {
|
||||
|
||||
function lexPage(src) {
|
||||
// Lex file
|
||||
var nodes = marked.lexer(src);
|
||||
var nodes = kramed.lexer(src);
|
||||
|
||||
return _.chain(splitSections(nodes))
|
||||
.map(function(section, idx) {
|
||||
@@ -118,6 +118,10 @@ function lexPage(src) {
|
||||
|
||||
return sections;
|
||||
}, [])
|
||||
.map(function(section) {
|
||||
section.links = nodes.links;
|
||||
return section;
|
||||
})
|
||||
.value();
|
||||
}
|
||||
|
||||
|
||||
@@ -26,15 +26,9 @@ function navigation(summary, files) {
|
||||
// Support single files as well as list
|
||||
files = _.isArray(files) ? files : (_.isString(files) ? [files] : null);
|
||||
|
||||
// Special README nav
|
||||
var README_NAV = {
|
||||
path: 'README.md',
|
||||
title: 'Introduction',
|
||||
level: '0',
|
||||
};
|
||||
|
||||
// List of all navNodes
|
||||
var navNodes = [README_NAV].concat(flattenChapters(summary.chapters));
|
||||
// Flatten chapters, then add in default README node if ndeeded etc ...
|
||||
var navNodes = flattenChapters(summary.chapters);
|
||||
var prevNodes = [null].concat(navNodes.slice(0, -1));
|
||||
var nextNodes = navNodes.slice(1).concat([null]);
|
||||
|
||||
|
||||
+14
-9
@@ -1,10 +1,11 @@
|
||||
var _ = require('lodash');
|
||||
var marked = require('marked');
|
||||
var kramed = require('kramed');
|
||||
var hljs = require('highlight.js');
|
||||
|
||||
var lex = require('./lex');
|
||||
var renderer = require('./renderer');
|
||||
|
||||
var codeInclude = require('./code_include');
|
||||
var lnormalize = require('../utils/lang').normalize;
|
||||
|
||||
|
||||
@@ -12,13 +13,12 @@ var lnormalize = require('../utils/lang').normalize;
|
||||
// Render a section using our custom renderer
|
||||
function render(section, _options) {
|
||||
// Copy section
|
||||
var links = section.links || {};
|
||||
section = _.toArray(section);
|
||||
|
||||
// marked's Render expects this, we don't use it yet
|
||||
section.links = {};
|
||||
section.links = links;
|
||||
|
||||
// Build options using defaults and our custom renderer
|
||||
var options = _.extend({}, marked.defaults, {
|
||||
var options = _.extend({}, kramed.defaults, {
|
||||
renderer: renderer(null, _options),
|
||||
|
||||
// Synchronous highlighting with highlight.js
|
||||
@@ -36,7 +36,7 @@ function render(section, _options) {
|
||||
}
|
||||
});
|
||||
|
||||
return marked.parser(section, options);
|
||||
return kramed.parser(section, options);
|
||||
}
|
||||
|
||||
function quizQuestion(node) {
|
||||
@@ -74,15 +74,20 @@ function parsePage(src, options) {
|
||||
// Main language
|
||||
var lang = validLangs ? langs[0] : null;
|
||||
|
||||
// codeInclude shortcut
|
||||
var ci = function(code) {
|
||||
return codeInclude(code, options.dir);
|
||||
};
|
||||
|
||||
return {
|
||||
id: section.id,
|
||||
type: section.type,
|
||||
content: render(nonCodeNodes),
|
||||
lang: lang,
|
||||
code: {
|
||||
base: codeNodes[0].text,
|
||||
solution: codeNodes[1].text,
|
||||
validation: codeNodes[2].text,
|
||||
base: ci(codeNodes[0].text),
|
||||
solution: ci(codeNodes[1].text),
|
||||
validation: ci(codeNodes[2].text),
|
||||
// Context is optional
|
||||
context: codeNodes[3] ? codeNodes[3].text : null,
|
||||
}
|
||||
|
||||
@@ -11,9 +11,6 @@ var calculProgress = function(navigation, current) {
|
||||
nav.path = path;
|
||||
return nav;
|
||||
})
|
||||
.sortBy(function(nav) {
|
||||
return parseFloat(nav.level);
|
||||
})
|
||||
.map(function(nav, i) {
|
||||
// Calcul percent
|
||||
nav.percent = (i * 100) / Math.max((n - 1), 1);
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
var _ = require('lodash');
|
||||
var marked = require('marked');
|
||||
var kramed = require('kramed');
|
||||
var textRenderer = require('marked-text-renderer');
|
||||
|
||||
function extractFirstNode(nodes, nType) {
|
||||
@@ -18,7 +18,7 @@ function parseReadme(src) {
|
||||
var renderer = textRenderer();
|
||||
|
||||
// Parse content
|
||||
nodes = marked.lexer(src);
|
||||
nodes = kramed.lexer(src);
|
||||
|
||||
title = extractFirstNode(nodes, "heading") || '';
|
||||
description = extractFirstNode(nodes, "paragraph") || '';
|
||||
@@ -28,7 +28,7 @@ function parseReadme(src) {
|
||||
return _.unescape(text.replace(/(\r\n|\n|\r)/gm, ""));
|
||||
},
|
||||
function(text) {
|
||||
return marked.parse(text, _.extend({}, marked.defaults, {
|
||||
return kramed.parse(text, _.extend({}, kramed.defaults, {
|
||||
renderer: renderer
|
||||
}));
|
||||
}
|
||||
|
||||
+20
-4
@@ -1,11 +1,12 @@
|
||||
var url = require('url');
|
||||
var inherits = require('util').inherits;
|
||||
var links = require('../utils').links;
|
||||
var codeInclude = require('./code_include');
|
||||
|
||||
|
||||
var path = require('path');
|
||||
|
||||
var marked = require('marked');
|
||||
var kramed = require('kramed');
|
||||
|
||||
var rendererId = 0;
|
||||
|
||||
@@ -20,7 +21,7 @@ function GitBookRenderer(options, extra_options) {
|
||||
this.id = rendererId++;
|
||||
this.quizIndex = 0;
|
||||
}
|
||||
inherits(GitBookRenderer, marked.Renderer);
|
||||
inherits(GitBookRenderer, kramed.Renderer);
|
||||
|
||||
GitBookRenderer.prototype._unsanitized = function(href) {
|
||||
var prot = '';
|
||||
@@ -56,7 +57,7 @@ GitBookRenderer.prototype.link = function(href, title, text) {
|
||||
|
||||
// Relative link, rewrite it to point to github repo
|
||||
if(links.isRelative(_href)) {
|
||||
if (path.extname(_href) == ".md") {
|
||||
if (path.extname(parsed.path) == ".md") {
|
||||
_href = links.toAbsolute(_href, o.dir || "./", o.outdir || "./");
|
||||
|
||||
if (o.singleFile) {
|
||||
@@ -125,7 +126,7 @@ GitBookRenderer.prototype._createCheckboxAndRadios = function(text) {
|
||||
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) {
|
||||
return GitBookRenderer.super_.prototype.tablecell(this._createCheckboxAndRadios(content), flags);
|
||||
@@ -135,5 +136,20 @@ GitBookRenderer.prototype.listitem = function(text) {
|
||||
return GitBookRenderer.super_.prototype.listitem(this._createCheckboxAndRadios(text));
|
||||
};
|
||||
|
||||
GitBookRenderer.prototype.code = function(code, lang, escaped) {
|
||||
return GitBookRenderer.super_.prototype.code.call(
|
||||
this,
|
||||
// Import code snippets
|
||||
codeInclude(code, this._extra_options.dir),
|
||||
lang,
|
||||
escaped
|
||||
);
|
||||
};
|
||||
|
||||
GitBookRenderer.prototype.heading = function(text, level, raw) {
|
||||
var id = this.options.headerPrefix + raw.toLowerCase().replace(/[^\w -]+/g, '').replace(/ /g, '-');
|
||||
return '<h' + level + ' id="' + id + '">' + text + '</h' + level + '>\n';
|
||||
};
|
||||
|
||||
// Exports
|
||||
module.exports = GitBookRenderer;
|
||||
|
||||
+46
-15
@@ -1,5 +1,5 @@
|
||||
var _ = require('lodash');
|
||||
var marked = require('marked');
|
||||
var kramed = require('kramed');
|
||||
|
||||
|
||||
// Utility function for splitting a list into groups
|
||||
@@ -43,7 +43,7 @@ function listSplit(nodes, start_type, end_type) {
|
||||
}
|
||||
|
||||
// Get the biggest list
|
||||
// out of a list of marked nodes
|
||||
// out of a list of kramed nodes
|
||||
function filterList(nodes) {
|
||||
return _.chain(nodes)
|
||||
.toArray()
|
||||
@@ -64,7 +64,7 @@ function filterList(nodes) {
|
||||
// supports extracting links
|
||||
function parseTitle(src, nums) {
|
||||
// Check if it's a link
|
||||
var matches = marked.InlineLexer.rules.link.exec(src);
|
||||
var matches = kramed.InlineLexer.rules.link.exec(src);
|
||||
|
||||
var level = nums.join('.');
|
||||
|
||||
@@ -81,12 +81,17 @@ function parseTitle(src, nums) {
|
||||
title: matches[1],
|
||||
level: level,
|
||||
|
||||
// Replace .md references with .html
|
||||
path: matches[2].replace(/\\/g, '/'),
|
||||
// Normalize path
|
||||
// 1. Convert Window's "\" to "/"
|
||||
// 2. Remove leading "/" if exists
|
||||
path: matches[2].replace(/\\/g, '/').replace(/^\/+/, ''),
|
||||
};
|
||||
}
|
||||
|
||||
function parseChapter(nodes, nums) {
|
||||
// Convert single number to an array
|
||||
nums = _.isArray(nums) ? nums : [nums];
|
||||
|
||||
return _.extend(parseTitle(_.first(nodes).text, nums), {
|
||||
articles: _.map(listSplit(filterList(nodes), 'list_item_start', 'list_item_end'), function(nodes, i) {
|
||||
return parseChapter(nodes, nums.concat(i + 1));
|
||||
@@ -94,24 +99,50 @@ function parseChapter(nodes, nums) {
|
||||
});
|
||||
}
|
||||
|
||||
function defaultChapterList(chapterList) {
|
||||
var first = _.first(chapterList);
|
||||
|
||||
// Check if introduction node was specified in SUMMARY.md
|
||||
if (first) {
|
||||
var chapter = parseChapter(first, [0]);
|
||||
|
||||
// Already have README node, we're good to go
|
||||
if(chapter.path === 'README.md') {
|
||||
return chapterList;
|
||||
}
|
||||
}
|
||||
|
||||
// It wasn't specified, so add in default
|
||||
return [
|
||||
[ { type: 'text', text: '[Introduction](README.md)' } ]
|
||||
].concat(chapterList);
|
||||
}
|
||||
|
||||
function listGroups(src) {
|
||||
var nodes = kramed.lexer(src);
|
||||
|
||||
// Get out groups of lists
|
||||
return listSplit(
|
||||
filterList(nodes),
|
||||
'list_item_start', 'list_item_end'
|
||||
);
|
||||
}
|
||||
|
||||
function parseSummary(src) {
|
||||
var nodes = marked.lexer(src);
|
||||
|
||||
// Get out list of chapters
|
||||
var chapterList = filterList(nodes);
|
||||
|
||||
// Split out chapter sections
|
||||
var chapters = _.chain(listSplit(chapterList, 'list_item_start', 'list_item_end'))
|
||||
.map(function(nodes, i) {
|
||||
return parseChapter(nodes, [i + 1]);
|
||||
})
|
||||
.value();
|
||||
var chapters = defaultChapterList(listGroups(src))
|
||||
.map(parseChapter);
|
||||
|
||||
return {
|
||||
chapters: chapters
|
||||
};
|
||||
}
|
||||
|
||||
function parseEntries (src) {
|
||||
return listGroups(src).map(parseChapter);
|
||||
}
|
||||
|
||||
|
||||
// Exports
|
||||
module.exports = parseSummary;
|
||||
module.exports.entries = parseEntries;
|
||||
|
||||
@@ -2,6 +2,7 @@ var MAP = {
|
||||
'py': 'python',
|
||||
'js': 'javascript',
|
||||
'rb': 'ruby',
|
||||
'csharp': 'cs',
|
||||
};
|
||||
|
||||
function normalize(lang) {
|
||||
|
||||
+11
-3
@@ -3,14 +3,22 @@ var path = require('path');
|
||||
|
||||
// Is the link an external link
|
||||
var isExternal = function(href) {
|
||||
return Boolean(url.parse(href).protocol);
|
||||
try {
|
||||
return Boolean(url.parse(href).protocol);
|
||||
} catch(err) { }
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// Return true if the link is relative
|
||||
var isRelative = function(href) {
|
||||
var parsed = url.parse(href);
|
||||
try {
|
||||
var parsed = url.parse(href);
|
||||
|
||||
return !parsed.protocol && parsed.path && parsed.path[0] != '/';
|
||||
return !parsed.protocol && parsed.path && parsed.path[0] != '/';
|
||||
} catch(err) {}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Relative to absolute path
|
||||
|
||||
+5
-4
@@ -1,19 +1,20 @@
|
||||
{
|
||||
"name": "gitbook",
|
||||
"version": "0.5.3",
|
||||
"version": "0.7.0",
|
||||
"homepage": "http://www.gitbook.io/",
|
||||
"description": "Library and cmd utility to generate GitBooks",
|
||||
"main": "lib/index.js",
|
||||
"dependencies": {
|
||||
"q": "1.0.1",
|
||||
"lodash": "2.4.1",
|
||||
"marked": "0.3.2",
|
||||
"marked-text-renderer": "0.0.1",
|
||||
"kramed": "0.4.0",
|
||||
"marked-text-renderer": "0.1.0",
|
||||
"lunr": "0.5.2",
|
||||
"swig": "1.3.2",
|
||||
"send": "0.2.0",
|
||||
"fstream-ignore": "0.0.7",
|
||||
"commander": "2.2.0",
|
||||
"graceful-fs": "3.0.2",
|
||||
"fs-extra": "0.8.1",
|
||||
"highlight.js": "8.0.0",
|
||||
"tmp": "0.0.23",
|
||||
@@ -22,7 +23,7 @@
|
||||
"resolve": "0.6.3",
|
||||
"tiny-lr-fork": "0.0.5",
|
||||
"gitbook-plugin": "0.0.2",
|
||||
"gitbook-plugin-mathjax": "0.0.3",
|
||||
"gitbook-plugin-mathjax": "0.0.4",
|
||||
"gitbook-plugin-livereload": "0.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
# Summary
|
||||
|
||||
* [Custom name for Introduction](README.md)
|
||||
* [Chapter 1](chapter-1/README.md)
|
||||
* [Article 1](chapter-1/ARTICLE1.md)
|
||||
* [Article 2](chapter-1/ARTICLE2.md)
|
||||
* [article 1.2.1](chapter-1/ARTICLE-1-2-1.md)
|
||||
* [article 1.2.2](chapter-1/ARTICLE-1-2-2.md)
|
||||
* [Chapter 2](chapter-2/README.md)
|
||||
* [Chapter 3](chapter-3/README.md)
|
||||
* [Chapter 4](chapter-4/README.md)
|
||||
* Unfinished article
|
||||
* Unfinished Chapter
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
# Beautiful chapter
|
||||
|
||||
Here is a nice included snippet :
|
||||
|
||||
```c
|
||||
{{ included.c }}
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
An exercise using includes
|
||||
|
||||
```c
|
||||
{{ included.c }}
|
||||
|
||||
Remove this extra code at the end
|
||||
```
|
||||
|
||||
```c
|
||||
{{ included.c }}
|
||||
```
|
||||
|
||||
```c
|
||||
{{ included.c }}
|
||||
|
||||
This validation code is wrong but who cares ?
|
||||
```
|
||||
|
||||
----
|
||||
Vendored
+2
-2
@@ -3,8 +3,8 @@
|
||||
* [Chapter 1](chapter-1/README.md)
|
||||
* [Article 1](chapter-1/ARTICLE1.md)
|
||||
* [Article 2](chapter-1/ARTICLE2.md)
|
||||
* [article 1.2.1](chapter-1/ARTICLE-1-2-1.md)
|
||||
* [article 1.2.2](chapter-1/ARTICLE-1-2-2.md)
|
||||
* [article 1.2.1](\chapter-1\ARTICLE-1-2-1.md)
|
||||
* [article 1.2.2](/chapter-1/ARTICLE-1-2-2.md)
|
||||
* [Chapter 2](chapter-2/README.md)
|
||||
* [Chapter 3](chapter-3/README.md)
|
||||
* [Chapter 4](chapter-4/README.md)
|
||||
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
printf("All is well\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var assert = require('assert');
|
||||
|
||||
var page = require('../').parse.page;
|
||||
|
||||
var FIXTURES_DIR = path.join(__dirname, './fixtures/');
|
||||
|
||||
function loadPage (name, options) {
|
||||
var CONTENT = fs.readFileSync(FIXTURES_DIR + name + '.md', 'utf8');
|
||||
return page(CONTENT, options);
|
||||
}
|
||||
|
||||
|
||||
describe('Code includes', function() {
|
||||
|
||||
var LEXED = loadPage('INCLUDES', {
|
||||
'dir': FIXTURES_DIR,
|
||||
});
|
||||
|
||||
var INCLUDED_C = fs.readFileSync(path.join(FIXTURES_DIR, 'included.c'), 'utf8');
|
||||
|
||||
it('should work for snippets', function() {
|
||||
assert.equal(LEXED[0].type, 'normal');
|
||||
// Has replaced include
|
||||
assert.equal(
|
||||
LEXED[0].content.indexOf('{{ included.c }}'),
|
||||
-1
|
||||
);
|
||||
});
|
||||
|
||||
it('should work for exercises', function() {
|
||||
assert.equal(LEXED[1].type, 'exercise');
|
||||
|
||||
// Solution is trimmed version of source
|
||||
assert.equal(LEXED[1].code.solution, INCLUDED_C.trim());
|
||||
});
|
||||
});
|
||||
@@ -7,7 +7,9 @@ var navigation = require('../').parse.navigation;
|
||||
|
||||
|
||||
var CONTENT = fs.readFileSync(path.join(__dirname, './fixtures/SUMMARY.md'), 'utf8');
|
||||
var ALT_CONTENT = fs.readFileSync(path.join(__dirname, './fixtures/ALTERNATIVE_SUMMARY.md'), 'utf8');
|
||||
var LEXED = summary(CONTENT);
|
||||
var ALT_LEXED = summary(ALT_CONTENT);
|
||||
|
||||
|
||||
describe('Summary navigation', function() {
|
||||
@@ -76,6 +78,21 @@ describe('Summary navigation', function() {
|
||||
assert.equal(nav['chapter-3/README.md'].level, '3');
|
||||
});
|
||||
|
||||
it('should have a default README node', function() {
|
||||
var nav = navigation(LEXED);
|
||||
|
||||
assert.equal(nav['README.md'].level, '0');
|
||||
assert.equal(nav['README.md'].title, 'Introduction');
|
||||
});
|
||||
|
||||
it('Should allow README node to be customized', function() {
|
||||
var nav = navigation(ALT_LEXED);
|
||||
|
||||
assert(nav['README.md']);
|
||||
assert.equal(nav['README.md'].level, '0');
|
||||
assert.notEqual(nav['README.md'].title, 'Introduction');
|
||||
});
|
||||
|
||||
it('should not accept null paths', function() {
|
||||
var nav = navigation(LEXED);
|
||||
|
||||
|
||||
+17
-13
@@ -12,13 +12,13 @@ var LEXED = summary(CONTENT);
|
||||
describe('Summary parsing', function () {
|
||||
|
||||
it('should detect chapters', function() {
|
||||
assert.equal(LEXED.chapters.length, 5);
|
||||
assert.equal(LEXED.chapters.length, 6);
|
||||
});
|
||||
|
||||
it('should support articles', function() {
|
||||
assert.equal(LEXED.chapters[0].articles.length, 2);
|
||||
assert.equal(LEXED.chapters[1].articles.length, 0);
|
||||
assert.equal(LEXED.chapters[1].articles.length, 2);
|
||||
assert.equal(LEXED.chapters[2].articles.length, 0);
|
||||
assert.equal(LEXED.chapters[3].articles.length, 0);
|
||||
});
|
||||
|
||||
it('should detect paths and titles', function() {
|
||||
@@ -26,30 +26,34 @@ describe('Summary parsing', function () {
|
||||
assert(LEXED.chapters[1].path);
|
||||
assert(LEXED.chapters[2].path);
|
||||
assert(LEXED.chapters[3].path);
|
||||
assert.equal(LEXED.chapters[4].path, null);
|
||||
assert(LEXED.chapters[4].path);
|
||||
assert.equal(LEXED.chapters[5].path, null);
|
||||
|
||||
assert(LEXED.chapters[0].title);
|
||||
assert(LEXED.chapters[1].title);
|
||||
assert(LEXED.chapters[2].title);
|
||||
assert(LEXED.chapters[3].title);
|
||||
assert(LEXED.chapters[4].title);
|
||||
assert(LEXED.chapters[5].title);
|
||||
});
|
||||
|
||||
it('should normalize paths from .md to .html', function() {
|
||||
assert.equal(LEXED.chapters[0].path,'chapter-1/README.md');
|
||||
assert.equal(LEXED.chapters[1].path,'chapter-2/README.md');
|
||||
assert.equal(LEXED.chapters[2].path,'chapter-3/README.md');
|
||||
assert.equal(LEXED.chapters[0].path,'README.md');
|
||||
assert.equal(LEXED.chapters[1].path,'chapter-1/README.md');
|
||||
assert.equal(LEXED.chapters[2].path,'chapter-2/README.md');
|
||||
assert.equal(LEXED.chapters[3].path,'chapter-3/README.md');
|
||||
});
|
||||
|
||||
it('should detect levels correctly', function() {
|
||||
var c = LEXED.chapters;
|
||||
|
||||
assert.equal(c[0].level, '1');
|
||||
assert.equal(c[1].level, '2');
|
||||
assert.equal(c[2].level, '3');
|
||||
assert.equal(c[0].level, '0');
|
||||
assert.equal(c[1].level, '1');
|
||||
assert.equal(c[2].level, '2');
|
||||
assert.equal(c[3].level, '3');
|
||||
|
||||
assert.equal(c[0].articles[0].level, '1.1');
|
||||
assert.equal(c[0].articles[1].level, '1.2');
|
||||
assert.equal(c[0].articles[1].articles[0].level, '1.2.1');
|
||||
assert.equal(c[1].articles[0].level, '1.1');
|
||||
assert.equal(c[1].articles[1].level, '1.2');
|
||||
assert.equal(c[1].articles[1].articles[0].level, '1.2.1');
|
||||
});
|
||||
});
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
.link-inherit{color:inherit}.link-inherit:hover,.link-inherit:focus{color:inherit}.hljs-comment,.hljs-title{color:#8e908c}.hljs-variable,.hljs-attribute,.hljs-tag,.hljs-regexp,.ruby .hljs-constant,.xml .hljs-tag .hljs-title,.xml .hljs-pi,.xml .hljs-doctype,.html .hljs-doctype,.css .hljs-id,.css .hljs-class,.css .hljs-pseudo{color:#c82829}.hljs-number,.hljs-preprocessor,.hljs-pragma,.hljs-built_in,.hljs-literal,.hljs-params,.hljs-constant{color:#f5871f}.ruby .hljs-class .hljs-title,.css .hljs-rules .hljs-attribute{color:#eab700}.hljs-string,.hljs-value,.hljs-inheritance,.hljs-header,.ruby .hljs-symbol,.xml .hljs-cdata{color:#718c00}.css .hljs-hexcolor{color:#3e999f}.hljs-function,.python .hljs-decorator,.python .hljs-title,.ruby .hljs-function .hljs-title,.ruby .hljs-title .hljs-keyword,.perl .hljs-sub,.javascript .hljs-title,.coffeescript .hljs-title{color:#4271ae}.hljs-keyword,.javascript .hljs-function{color:#8959a8}.hljs{display:block;background:white;color:#4d4d4c;padding:.5em}.coffeescript .javascript,.javascript .xml,.tex .hljs-formula,.xml .javascript,.xml .vbscript,.xml .css,.xml .hljs-cdata{opacity:.5}.book-chapter{display:none}article{page-break-after:always}pre,blockquote{border:1px solid #999;page-break-inside:avoid;background:#f1f1f1;padding:8px}img{max-width:100%!important;page-break-inside:avoid;margin:0 auto}.exercise,.quiz{margin:1cm 0;padding:.4cm;page-break-inside:avoid;border:3px solid #ddd}.exercise .exercise-header,.quiz .exercise-header{margin-bottom:.4cm;padding-bottom:.2cm;border-bottom:1px solid #ddd}.exercise .question,.quiz .question{margin-top:.4cm}
|
||||
.link-inherit{color:inherit}.link-inherit:hover,.link-inherit:focus{color:inherit}.hljs-comment,.hljs-title{color:#8e908c}.hljs-variable,.hljs-attribute,.hljs-tag,.hljs-regexp,.ruby .hljs-constant,.xml .hljs-tag .hljs-title,.xml .hljs-pi,.xml .hljs-doctype,.html .hljs-doctype,.css .hljs-id,.css .hljs-class,.css .hljs-pseudo{color:#c82829}.hljs-number,.hljs-preprocessor,.hljs-pragma,.hljs-built_in,.hljs-literal,.hljs-params,.hljs-constant{color:#f5871f}.ruby .hljs-class .hljs-title,.css .hljs-rules .hljs-attribute{color:#eab700}.hljs-string,.hljs-value,.hljs-inheritance,.hljs-header,.ruby .hljs-symbol,.xml .hljs-cdata{color:#718c00}.css .hljs-hexcolor{color:#3e999f}.hljs-function,.python .hljs-decorator,.python .hljs-title,.ruby .hljs-function .hljs-title,.ruby .hljs-title .hljs-keyword,.perl .hljs-sub,.javascript .hljs-title,.coffeescript .hljs-title{color:#4271ae}.hljs-keyword,.javascript .hljs-function{color:#8959a8}.hljs{display:block;background:white;color:#4d4d4c;padding:.5em}.coffeescript .javascript,.javascript .xml,.tex .hljs-formula,.xml .javascript,.xml .vbscript,.xml .css,.xml .hljs-cdata{opacity:.5}.book-chapter{display:none}pre,blockquote{border:1px solid #999;page-break-inside:avoid;background:#f1f1f1;padding:8px}img{max-width:100%!important;page-break-inside:avoid;margin:0 auto}.exercise,.quiz{margin:1cm 0;padding:.4cm;page-break-inside:avoid;border:3px solid #ddd}.exercise .exercise-header,.quiz .exercise-header{margin-bottom:.4cm;padding-bottom:.2cm;border-bottom:1px solid #ddd}.exercise .question,.quiz .question{margin-top:.4cm}
|
||||
File diff suppressed because one or more lines are too long
@@ -18,6 +18,14 @@ define([
|
||||
editor.setTheme("ace/theme/tomorrow");
|
||||
editor.getSession().setUseWorker(false);
|
||||
editor.getSession().setMode("ace/mode/javascript");
|
||||
|
||||
editor.commands.addCommand({
|
||||
name: "submit",
|
||||
bindKey: "Ctrl-Return|Cmd-Return",
|
||||
exec: function() {
|
||||
$exercise.find(".action-submit").click();
|
||||
}
|
||||
});
|
||||
|
||||
// Submit: test code
|
||||
$exercise.find(".action-submit").click(function(e) {
|
||||
@@ -52,4 +60,4 @@ define([
|
||||
init: init,
|
||||
prepare: prepareExercise
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,6 @@ define([
|
||||
"core/loading"
|
||||
], function($, URL, events, state, progress, exercises, quiz, loading) {
|
||||
var prev, next;
|
||||
var githubCountStars, githubCountWatch;
|
||||
|
||||
var usePushState = (typeof history.pushState !== "undefined");
|
||||
|
||||
@@ -63,11 +62,6 @@ define([
|
||||
}));
|
||||
};
|
||||
|
||||
var updateGitHubCounts = function() {
|
||||
$(".book-header .count-star span").text(githubCountStars);
|
||||
$(".book-header .count-watch span").text(githubCountWatch);
|
||||
};
|
||||
|
||||
var updateNavigationPosition = function() {
|
||||
var bodyInnerWidth, pageWrapperWidth;
|
||||
|
||||
@@ -95,20 +89,6 @@ define([
|
||||
// Focus on content
|
||||
$pageWrapper.focus();
|
||||
|
||||
// Update GitHub count
|
||||
if (state.githubId) {
|
||||
if (githubCountStars) {
|
||||
updateGitHubCounts();
|
||||
} else {
|
||||
$.getJSON("https://api.github.com/repos/"+state.githubId)
|
||||
.done(function(repo) {
|
||||
githubCountStars = repo.stargazers_count;
|
||||
githubCountWatch = repo.subscribers_count;
|
||||
updateGitHubCounts();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Send to mixpanel
|
||||
events.trigger("page.change");
|
||||
};
|
||||
|
||||
@@ -13,6 +13,9 @@ define([
|
||||
},
|
||||
"google-plus": function($el) {
|
||||
window.open("https://plus.google.com/share?url="+encodeURIComponent(url))
|
||||
},
|
||||
"weibo": function($el) {
|
||||
window.open("http://service.weibo.com/share/share.php?content=utf-8&url="+encodeURIComponent(url)+"&title="+encodeURIComponent(title))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -30,4 +33,4 @@ define([
|
||||
return {
|
||||
init: init
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
article {
|
||||
page-break-after: always;
|
||||
}
|
||||
|
||||
pre, blockquote {
|
||||
border: 1px solid #999;
|
||||
|
||||
@@ -103,27 +103,27 @@
|
||||
}
|
||||
|
||||
&.font-size-0 {
|
||||
.book-body {
|
||||
.book-body .page-inner section {
|
||||
font-size:@s-font-size;
|
||||
}
|
||||
}
|
||||
&.font-size-1 {
|
||||
.book-body {
|
||||
.book-body .page-inner section {
|
||||
font-size:@m-font-size;
|
||||
}
|
||||
}
|
||||
&.font-size-2 {
|
||||
.book-body {
|
||||
.book-body .page-inner section {
|
||||
font-size:@l-font-size;
|
||||
}
|
||||
}
|
||||
&.font-size-3 {
|
||||
.book-body {
|
||||
&.font-size-3{
|
||||
.book-body .page-inner section {
|
||||
font-size:@xl-font-size;
|
||||
}
|
||||
}
|
||||
&.font-size-4 {
|
||||
.book-body {
|
||||
.book-body .page-inner section {
|
||||
font-size:@xxl-font-size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,12 +44,16 @@
|
||||
padding-right: 200px;
|
||||
.transition(margin-left 0.5s ease);
|
||||
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
a, a:hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
@media (max-width: 1000px) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,10 +49,14 @@
|
||||
}
|
||||
}
|
||||
.book.color-theme-1 .book-body {
|
||||
.navigation:hover{
|
||||
}
|
||||
|
||||
}
|
||||
.book.color-theme-2 .book-body {
|
||||
.navigation:hover{
|
||||
.navigation {
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
|
||||
&:hover {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,10 +21,8 @@
|
||||
{% if options.links.sharing.twitter !== false %}
|
||||
<a href="#" target="_blank" class="btn pull-right twitter-sharing-link sharing-link" data-sharing="twitter" aria-label="Share on Twitter"><i class="fa fa-twitter"></i></a>
|
||||
{% endif %}
|
||||
|
||||
{% if githubId %}
|
||||
<a href="{{ githubHost }}{{ githubId }}/stargazers" target="_blank" class="btn pull-right count-star hidden-xs"><i class="fa fa-star-o"></i> Star (<span>-</span>)</a>
|
||||
<a href="{{ githubHost }}{{ githubId }}/watchers" target="_blank" class="btn pull-right count-watch hidden-xs"><i class="fa fa-eye"></i> Watch (<span>-</span>)</a>
|
||||
{% if options.links.sharing.weibo === true %}
|
||||
<a href="#" target="_blank" class="btn pull-right twitter-sharing-link sharing-link" data-sharing="weibo" aria-label="Share on Weibo"><i class="fa fa-weibo"></i></a>
|
||||
{% endif %}
|
||||
|
||||
<!-- Title -->
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
{% macro articles(_articles) %}
|
||||
{% for item in _articles %}
|
||||
{% set externalLink = item.path|isExternalLink %}
|
||||
<li class="chapter {% if item._path == _input %}active{% endif %}" data-level="{{ item.level }}" {% if item.path && !externalLink %}data-path="{{ item.path|mdLink }}"{% endif %}>
|
||||
<li class="chapter {% if item.path == _input %}active{% endif %}" data-level="{{ item.level }}" {% if item.path && !externalLink %}data-path="{{ item.path|mdLink }}"{% endif %}>
|
||||
{% if item.path %}
|
||||
{% if !externalLink %}
|
||||
<a href="{{ basePath }}/{{ item.path|mdLink }}">
|
||||
<i class="fa fa-check"></i> <b>{{ item.level }}.</b> {{ item.title }}
|
||||
<i class="fa fa-check"></i>
|
||||
{% if item.level !== "0" %}
|
||||
<b>{{ item.level }}.</b>
|
||||
{% endif %}
|
||||
{{ item.title }}
|
||||
</a>
|
||||
{% else %}
|
||||
<a target="_blank" href="{{ item.path }}">
|
||||
<i class="fa fa-check"></i> <b>{{ item.level }}.</b> {{ item.title }}
|
||||
<i class="fa fa-check"></i>
|
||||
{% if item.level !== "0" %}
|
||||
<b>{{ item.level }}.</b>
|
||||
{% endif %}
|
||||
{{ item.title }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
@@ -40,7 +48,7 @@
|
||||
{% if options.links.issues !== false && (options.links.issues || githubId) %}
|
||||
{% set _divider = true %}
|
||||
<li>
|
||||
<a href="{{ options.links.issues|default(githubHost+githubId+"/issues") }}" target="blank"class="issues-link">Questions and Issues</a>
|
||||
<a href="{{ options.links.issues|default(githubHost+githubId+"/issues") }}" target="blank" class="issues-link">Questions and Issues</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
@@ -51,13 +59,19 @@
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if options.links.custom %}
|
||||
{% for link in options.links.custom %}
|
||||
{% set _divider = true %}
|
||||
<li>
|
||||
<a href="{{ options.links.custom[loop.key] }}" target="blank" class="custom-link">{{ loop.key }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if _divider %}
|
||||
<li class="divider"></li>
|
||||
{% endif %}
|
||||
|
||||
<li data-level="0" data-path="index.html">
|
||||
<a href="{{ basePath }}/"><i class="fa fa-check"></i> Introduction</a>
|
||||
</li>
|
||||
{{ articles(summary.chapters) }}
|
||||
|
||||
{% if options.links.gitbook !== false %}
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
{% set exercise = 1 %}
|
||||
<article>
|
||||
<h1 class="book-chapter book-chapter-1">Exercise Solutions</h1>
|
||||
<h1>Exercise Solutions</h1>
|
||||
|
||||
{% for item in progress.chapters %}
|
||||
{% if pages[item.path] %}
|
||||
|
||||
Reference in New Issue
Block a user