mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-27 04:29:05 +00:00
Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 6bbefad24e | |||
| 1f4cf33dd0 | |||
| 7274aa6940 | |||
| 125e804cd5 |
@@ -1,2 +1,7 @@
|
||||
# Release notes
|
||||
|
||||
## 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,6 +258,8 @@ 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
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+24
-6
@@ -271,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
|
||||
|
||||
+134
-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,50 @@ 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(book);
|
||||
};
|
||||
|
||||
// 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 +100,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 +115,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 +145,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 +164,94 @@ Plugin.normalizeNames = function(names) {
|
||||
};
|
||||
|
||||
// Extract data from a list of plugin
|
||||
Plugin.fromList = function(names, root) {
|
||||
Plugin.fromList = function(names, root, generator) {
|
||||
var that = this;
|
||||
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,11 +198,11 @@ 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);
|
||||
});
|
||||
})
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
var fs = require('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;
|
||||
});
|
||||
};
|
||||
@@ -118,6 +118,10 @@ function lexPage(src) {
|
||||
|
||||
return sections;
|
||||
}, [])
|
||||
.map(function(section) {
|
||||
section.links = nodes.links;
|
||||
return section;
|
||||
})
|
||||
.value();
|
||||
}
|
||||
|
||||
|
||||
+11
-6
@@ -5,6 +5,7 @@ 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,10 +13,9 @@ 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, {
|
||||
@@ -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);
|
||||
|
||||
+18
-2
@@ -1,6 +1,7 @@
|
||||
var url = require('url');
|
||||
var inherits = require('util').inherits;
|
||||
var links = require('../utils').links;
|
||||
var codeInclude = require('./code_include');
|
||||
|
||||
|
||||
var path = require('path');
|
||||
@@ -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;
|
||||
|
||||
+10
-6
@@ -81,8 +81,10 @@ 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(/^\/+/, ''),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -100,11 +102,13 @@ function parseChapter(nodes, nums) {
|
||||
function defaultChapterList(chapterList) {
|
||||
var first = _.first(chapterList);
|
||||
|
||||
var chapter = parseChapter(first, [0]);
|
||||
if (first) {
|
||||
var chapter = parseChapter(first, [0]);
|
||||
|
||||
// Already have README node, we're good to go
|
||||
if(chapter.path === 'README.md') {
|
||||
return chapterList;
|
||||
// Already have README node, we're good to go
|
||||
if(chapter.path === 'README.md') {
|
||||
return chapterList;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
@@ -2,6 +2,7 @@ var MAP = {
|
||||
'py': 'python',
|
||||
'js': 'javascript',
|
||||
'rb': 'ruby',
|
||||
'csharp': 'cs',
|
||||
};
|
||||
|
||||
function normalize(lang) {
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gitbook",
|
||||
"version": "0.5.5",
|
||||
"version": "0.6.2",
|
||||
"homepage": "http://www.gitbook.io/",
|
||||
"description": "Library and cmd utility to generate GitBooks",
|
||||
"main": "lib/index.js",
|
||||
@@ -22,7 +22,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
+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());
|
||||
});
|
||||
});
|
||||
+1
-1
File diff suppressed because one or more lines are too long
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
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{% 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 }}">
|
||||
@@ -48,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 %}
|
||||
|
||||
@@ -59,6 +59,15 @@
|
||||
</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 %}
|
||||
|
||||
Reference in New Issue
Block a user