Compare commits

...

81 Commits

Author SHA1 Message Date
Samy Pessé b629b78dba Bump version to 0.7.0 2014-08-01 13:58:36 -07:00
Samy Pessé f62c218735 Fix #278: Add page break between chapters/articles 2014-08-01 13:55:19 -07:00
Aaron O'Mullan 725510028f Use marked-text-renderer@0.1.0 2014-08-01 00:09:45 -07:00
Aaron O'Mullan ad7927242d Add comments to default introduction chapter code
Fixes #388
2014-07-31 08:43:57 -07:00
Aaron O'Mullan 5692bdd229 Merge pull request #386 from codepiano/china-social-network-sharing
add sharing to weibo.com.
2014-07-31 01:41:56 -07:00
codepiano 3402746719 add sharing to weibo.com.
google、facebook and twitter are block by GFW in China,Chinese can
not visit these websites.
add 'weibo' config in option links.sharing,the share link will not be
used by default,users should set the value of 'weibo' to true to turn
on sharing to weibo.com.
2014-07-31 16:31:31 +08:00
Aaron O'Mullan 30a8414b06 Switch to graceful-fs, fix #373 2014-07-30 20:45:33 -07:00
Aaron O'Mullan 9d46899ea7 Replace "marked" with "kramed" 2014-07-30 16:17:47 -07:00
Samy Pessé 58f14fd9ca Fix font settings apply 2014-07-30 11:17:28 -07:00
Samy Pessé b6af14b13e Remove github count stars and watchers 2014-07-30 10:43:10 -07:00
Aaron O'Mullan ba2134c480 Bump version to 0.6.3 2014-07-30 10:23:11 -07:00
Aaron O'Mullan 4705548860 Fix resource normalization in when loading plugins
Fixes #384
2014-07-30 10:17:07 -07:00
Samy Pessé 7d51ae3689 Add changelogs for 0.6.2 2014-07-30 00:26:44 -07:00
Aaron O'Mullan b9360de3e3 Bump version to 0.6.2 2014-07-30 00:22:49 -07:00
Aaron O'Mullan 9f241457bb Fix minor issues with new plugin additions 2014-07-29 23:45:54 -07:00
Aaron O'Mullan 60a7c28e66 Support generating a plugin's book info dynamically 2014-07-29 21:50:26 -07:00
Samy Pessé 5dff9b795e Fix #345: Improve navigation on dark theme 2014-07-29 21:33:47 -07:00
Aaron O'Mullan 0134677e0e Improve path normalization when parsing SUMMARY.md 2014-07-29 20:49:11 -07:00
Aaron O'Mullan 39ef4fa199 Add invalid paths in SUMMARY.md fixture
To test for correct path normalization
2014-07-29 20:46:14 -07:00
Samy Pessé b507a30301 Bump version to 0.6.1 2014-07-23 16:49:57 -07:00
Samy Pessé 539c2d0c57 Update plugin-mathjax@0.0.4 2014-07-23 16:28:38 -07:00
Samy Pessé aa6f3530f9 Fix readme spacing 2014-07-22 23:28:13 -07:00
Samy Pessé 98057af219 Merge pull request #372 from justanotheranonymoususer/patch-1
Update summary.html (fixed an HTML typo)
2014-07-20 14:04:12 -07:00
justanotheranonymoususer bb48c8233c Update summary.html 2014-07-20 23:57:56 +03:00
Aaron O'Mullan 8f14db64df Merge pull request #370 from nightwing/patch-1
Add keyboard shortcut for submitting exercise
2014-07-15 21:33:58 -07:00
Harutyun Amirjanyan 37d40dc66c Add keyboard shortcut for submitting exercise 2014-07-16 08:07:22 +04:00
Samy Pessé 6c96680cca Merge pull request #366 from shaform/ignore
Ignore config files by default in fs.list
2014-07-14 10:08:29 -07:00
Shaform 4e475857d5 Ignore config files by default in fs.list 2014-07-13 08:40:23 -07:00
Samy Pessé f8ff0453dd Merge pull request #359 from jcouyang/patch-1
add jsbin embedded plugin
2014-07-12 07:02:18 -07:00
Samy Pessé 3cc5c47535 Bump version to 0.6.0 2014-07-12 23:28:07 +02:00
Samy Pessé a2046814b4 Change command "git:push" to "publish" 2014-07-12 23:25:40 +02:00
Samy Pessé 82f814fdbd Fix #303: generate header id the same as github 2014-07-12 23:23:03 +02:00
Jichao Ouyang cd382dc8fc add jsbin embedded support 2014-07-12 21:42:52 +08:00
Samy Pessé 3842e5bf4a Merge pull request #358 from GitbookIO/featureplatform
Add base command git:push and git:remote
2014-07-11 17:42:16 -07:00
Samy Pessé f05b138fd6 Add base command git:push and git:remote 2014-07-12 09:23:15 +02:00
Samy Pessé 1b87cec893 Merge pull request #266 from shaform/ref_links
Add reference links support
2014-07-11 17:20:43 -07:00
Samy Pessé 4a894b1e18 Merge pull request #268 from rlmv/master
Fix page.path for plugins
2014-07-11 17:19:26 -07:00
Samy Pessé c7c98009e7 Merge pull request #347 from rlmv/custom_links
Add custom links to TOC
2014-07-11 17:15:37 -07:00
Samy Pessé 356322e0ed Merge pull request #353 from citizenmatt/active_page
Highlight active page in summary
2014-07-11 17:14:36 -07:00
Samy Pessé 7a47049367 Merge pull request #354 from citizenmatt/summary_hooks
Plugin hooks for summary data
2014-07-11 17:13:40 -07:00
Samy Pessé a9f473260c Merge pull request #356 from GitbookIO/feature/includes
Support importing code snippets
2014-07-10 17:59:34 -07:00
Aaron O'Mullan d16577e8bf Add tests for includes 2014-07-10 17:49:50 +02:00
Aaron O'Mullan 15b4053d6b Support code includes for exercises 2014-07-10 17:30:12 +02:00
Aaron O'Mullan 925a9b1388 Support importing code snippets
Allowing to keep code in separate files, which can be cleaner in a
quite a few scenarios
2014-07-11 01:22:07 +02:00
Matt Ellis fc942fdeca Hooks for summary data 2014-07-09 17:17:40 +01:00
Matt Ellis 75a6ecbf52 Highlight active page in summary 2014-07-09 16:06:55 +01:00
Aaron O'Mullan 520045441b Merge pull request #352 from citizenmatt/links_with_fragments
Convert links with fragments from md to html
2014-07-08 21:45:01 +02:00
Matt Ellis 00c3e966d3 Convert links with fragments from md to html 2014-07-08 16:25:39 +01:00
Samy Pessé 8b6ebe86b8 Merge pull request #348 from rlmv/anchors_readme
Add anchor plugin to README
2014-07-06 17:12:57 +02:00
Bo Marchman d5bd3453e1 add heading anchors plugin to readme 2014-07-02 23:49:07 -04:00
Bo Marchman ddacbbe73b update readme with custom links 2014-07-02 23:40:57 -04:00
Bo Marchman f7d52f0eff Add custom link capability
Users can add custom links at the top of the table of contents by
adding a links.custom object to book.json. Issue #304
2014-07-02 23:38:14 -04:00
Robert Marchman 39c50640ea pass 'rawPath' page path to plugins 2014-06-24 12:33:19 -04:00
Samy Pessé 16128c7e60 Bump version to 0.5.6 2014-06-24 12:35:13 +02:00
Samy Pessé 8b6a80cffb Fix error when chapters list is empty
Fix GitbookIO/gitbook.io#101 and GitbookIO/gitbook.io#102
2014-06-24 12:34:29 +02:00
Aaron O'Mullan 86f8a9d8fb Remove useless (and bad) sort on progress
Fixes bad ordering of chapters on gitbook.io
2014-06-20 03:40:47 -07:00
Aaron O'Mullan 790dfd601e Add csharp language highlight mapping
Fixes #327
2014-06-18 11:26:50 -07:00
Aaron O'Mullan 3426428142 Bump version to 0.5.5 2014-06-18 00:25:23 -07:00
Aaron O'Mullan 6c5c8e6eb7 Fix LANGS.md parsing
And cleanup Summary parsing
2014-06-17 16:18:08 -07:00
Aaron O'Mullan 3aefcaea73 Bump version to 0.5.4 2014-06-17 10:56:45 -07:00
Aaron O'Mullan 04c025774d Improve robustness of link utility functions
Fixes #321, fixes #323
2014-06-17 10:56:13 -07:00
Aaron O'Mullan 80aac7f581 Support --verbose flag, fixes #325 2014-06-17 10:46:56 -07:00
Samy Pessé 266f7ca73d Add infos about debugging 2014-06-17 18:45:44 +02:00
Shaform 6bbefad24e Merge remote-tracking branch 'upstream/master' into ref_links 2014-06-17 22:08:46 +08:00
Aaron O'Mullan d6eb8e4c60 Support URLs in "github" config
Fixes GitbookIO/gitbook.io#90
2014-06-16 18:52:36 -07:00
Aaron O'Mullan c87edb155a Better string slicing in parse.git.githubID() 2014-06-16 17:48:18 -07:00
Samy Pessé ef0468ad5d Fix #310: improve display of long title in header 2014-06-16 14:03:28 +02:00
Samy Pessé f03153eb0c Remove page break between articles in ebook
Add title for exercise solutions sections
2014-06-16 13:55:07 +02:00
Samy Pessé 3d8b6e550a Merge pull request #319 from GitbookIO/enchancement/custom_intro
Enchancement/custom intro
2014-06-16 13:02:38 +02:00
Aaron O'Mullan 4c3dbca5ca Normalize intro node in Summary parsing 2014-06-16 00:26:11 -07:00
Aaron O'Mullan a258feea37 Remove special case in navigation
For intro node
2014-06-16 00:25:59 -07:00
Aaron O'Mullan 3a3332d0b9 Adapt summary tests to new summary parsing
We now insert default README.md (introduction) node in parsing the
SUMMARY.md logic
2014-06-16 00:25:33 -07:00
Aaron O'Mullan 4259283f32 Refactor Summary rendering
Introduction is no longer an edge case
2014-06-16 00:09:48 -07:00
Aaron O'Mullan 243d977cd6 Add test for level regarding #318 2014-06-15 23:36:40 -07:00
Aaron O'Mullan 94bbbecedf Support custom intro node, fixes #318 2014-06-15 23:30:43 -07:00
Aaron O'Mullan f49eab42a9 Fix test for #318 2014-06-15 23:29:24 -07:00
Aaron O'Mullan ce6b2bc47d Add extra navigation tests for #318 2014-06-15 23:19:28 -07:00
Aaron O'Mullan 3245d7e430 Drop Travis tests for Node 0.8 2014-06-15 22:49:15 -07:00
Shaform 1f4cf33dd0 Improve code syntax for links assignment 2014-05-28 06:57:50 +08:00
Shaform 7274aa6940 fix links error 2014-05-27 22:24:23 +08:00
Shaform 125e804cd5 Add reference links support
This commit resolves #184
2014-05-27 17:24:47 +08:00
47 changed files with 655 additions and 226 deletions
-1
View File
@@ -2,4 +2,3 @@ language: node_js
node_js:
- "0.11"
- "0.10"
- "0.8"
+10
View File
@@ -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
+16
View File
@@ -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
View File
@@ -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);
});
};
+17
View File
@@ -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();
+53
View File
@@ -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
View File
@@ -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
};
+5 -2
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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");
}
};
});
};
+5 -4
View File
@@ -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
+4 -4
View File
@@ -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) {
+20
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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();
}
+2 -8
View File
@@ -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
View File
@@ -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,
}
-3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+1
View File
@@ -2,6 +2,7 @@ var MAP = {
'py': 'python',
'js': 'javascript',
'rb': 'ruby',
'csharp': 'cs',
};
function normalize(lang) {
+11 -3
View File
@@ -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
View File
@@ -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": {
+13
View File
@@ -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
+29
View File
@@ -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 ?
```
----
+2 -2
View File
@@ -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)
+7
View File
@@ -0,0 +1,7 @@
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("All is well\n");
return 0;
}
+38
View File
@@ -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());
});
});
+17
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -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
+9 -1
View File
@@ -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
};
});
});
-20
View File
@@ -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");
};
+4 -1
View File
@@ -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
};
});
});
-3
View File
@@ -7,9 +7,6 @@
display: none;
}
article {
page-break-after: always;
}
pre, blockquote {
border: 1px solid #999;
+6 -6
View File
@@ -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;
}
}
+5 -1
View File
@@ -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;
}
+7 -3
View File
@@ -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);
}
}
}
+2 -4
View File
@@ -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 -->
+21 -7
View File
@@ -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 %}
+1
View File
@@ -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] %}