Compare commits

...

48 Commits

Author SHA1 Message Date
Aaron O'Mullan 6b76032bb1 Bump version to 1.5.0 2014-12-17 17:08:09 +01:00
Aaron O'Mullan 85b09eda56 Add 1.4.2's changes 2014-12-17 17:07:10 +01:00
Aaron O'Mullan c4be78a81f Merge pull request #506 from Nijikokun/feature/clarity-midnight
Clarity: Midnight Theme (Night)
2014-12-16 18:53:09 +01:00
Aaron O'Mullan e7309f71d2 Merge pull request #530 from GitbookIO/fix/cli-error-handling
Robust CLI error handling
2014-12-16 17:46:01 +01:00
Aaron O'Mullan 6c979c9419 Robust CLI error handling
Fixes #528, fixes #524, fixes #518
2014-12-16 16:05:50 +01:00
Aaron O'Mullan 7fd7732345 Bump version to 1.4.2 2014-12-10 23:27:01 +01:00
Aaron O'Mullan 960ef4d945 Fix descriptor and me leak with bad write queues
Node.js for some reason queues up writes from fs.writeFile causing
memory and descriptor leaks, preventing building large books. This is
now fixed

Fixes #336, fixes #454
2014-12-10 16:32:49 +01:00
Aaron O'Mullan 92523fb4b5 Force process.exit at end of build 2014-12-09 17:31:23 +01:00
Samy Pessé 66bb46df22 Merge pull request #516 from jr0cket/patch-1
Add gitbook init command to "how to use" section
2014-12-08 00:46:04 +01:00
John Stevenson 0454710d3b Add gitbook init command to "how to use" section
Highlight that Gitbook can generate the files and directories for a book from its SUMMARY.md file.
2014-12-07 23:44:41 +00:00
Samy Pessé c9a31cc987 Add option isbn for books 2014-12-03 23:19:47 +01:00
Samy Pessé f03c316274 Bump version to 1.4.1 2014-11-30 17:01:47 +01:00
Samy Pessé 001a4f91be Fix command 'install' without arguments 2014-11-30 17:00:12 +01:00
Samy Pessé 44a3bfe296 Bump version to 1.4.0 2014-11-30 14:59:08 +01:00
Samy Pessé a9ce6f808d Add doc about gitbook install 2014-11-30 14:54:34 +01:00
Samy Pessé e5c667e0fa Add test for parsing of plugins list with version 2014-11-30 14:50:47 +01:00
Samy Pessé e36f274dbe Handle specific version for plugins 2014-11-30 14:46:21 +01:00
Samy Pessé 2a88427e74 Improve error message when plugins not found 2014-11-30 13:13:00 +01:00
Samy Pessé ed48f5f3ee Add command "install" to install plugins from book.json 2014-11-30 13:04:38 +01:00
Samy Pessé 7b4accb117 Bump version to 1.3.4 2014-11-25 18:55:33 +01:00
Samy Pessé d6437d31d4 Omit "generator" when building file with config file 2014-11-25 18:53:02 +01:00
Samy Pessé 3dd33f1f3c Merge pull request #502 from tonka3000/X-UA-Compatible-tag
add X-UA-Compatible meta tag for IE
2014-11-25 15:20:14 +01:00
Samy Pessé 3a0f00411b Fix #505: add glossary to ebook file 2014-11-24 22:08:19 +01:00
Samy Pessé 5b655bb29a Fx typo 2014-11-24 21:56:33 +01:00
Samy Pessé faf10605e8 Add hook finish:before 2014-11-24 21:41:50 +01:00
Nijiko Yonskai c52aecdbd5 Initial release 2014-11-22 20:37:06 -08:00
Samy Pessé 5c98b3350b Merge pull request #503 from jr0cket/ebook-command-fix
updated eBook generation commands - resolves #432
2014-11-22 16:41:19 +01:00
John Stevenson 6f4e1d10c1 made commands clearer by seperating output filename option 2014-11-22 14:47:12 +00:00
John Stevenson 870886599a updated eBook generation commands 2014-11-22 11:26:08 +00:00
Michael Aigner 1b9801b8a4 add X-UA-Compatible meta tag for IE (https://github.com/GitbookIO/gitbook/issues/501) 2014-11-20 10:16:40 +01:00
Samy Pessé 897463b22e Bump version to 1.3.3 2014-11-14 22:10:19 +01:00
Samy Pessé aa4894e94c Fix parsing of already lexed content 2014-11-14 22:09:30 +01:00
Samy Pessé 8a0180ae6b Bump version to 1.3.2 2014-11-14 18:53:57 +01:00
Samy Pessé d6776b98d3 Fix bundle task 2014-11-14 18:53:48 +01:00
Samy Pessé 6a04ba89a5 Update gitbook bundle 2014-11-14 18:53:39 +01:00
Samy Pessé 05eaeeb0d0 Fix #441: Improve validity of epub output 2014-11-13 08:34:35 +01:00
Samy Pessé 77043464b2 Fix #496: improve replacement of glossary terms in html 2014-11-12 22:10:38 +01:00
Samy Pessé b90f74a821 Merge pull request #494 from cnDelbert/master
Bug fix for issue#493: 2-level or more deep relative links go wrong when in win32
2014-11-12 19:06:33 +01:00
Aaron O'Mullan f804eb42ac Merge pull request #495 from romanlytkin/patch-1
Update README.md
2014-11-07 15:58:18 +01:00
romanlytkin 74fd8c159b Update README.md
Hi, i add 2 new plugins for gitbook, I hope it will be useful to someone.
2014-11-07 17:37:27 +03:00
Delbert a741ec1350 Bug fix for issue #493: 2-level or more deep relative links may go wrong when in win32 2014-11-06 15:50:59 +08:00
Samy Pessé 4b26c31b3b Remove unnecessary option "repo" from tests 2014-11-03 17:43:44 +01:00
Samy Pessé dfd487692a Adapt new page.parse api for tests 2014-11-03 17:40:38 +01:00
Samy Pessé 195374ecee Merge pull request #489 from codepiano/fork
bugfix,init page object's lexed attribute.
2014-11-03 17:29:54 +01:00
Samy Pessé 1659778a77 Bump version to 1.3.1 2014-11-03 16:58:37 +01:00
Samy Pessé 40c4035afb Fix #491: handle correctly url without path for links 2014-11-03 16:57:35 +01:00
codepiano 1502d65286 Merge branch 'master' of github.com:GitbookIO/gitbook into fork
Conflicts:
	lib/parse/page.js
2014-11-02 01:34:28 +08:00
codepiano 9da10e2b25 bugfix,init page object's lexed attribute.
the page object's attribute 'lexed' is not correctly inited.so when
use page.lexed to generate the search index file,nothing will be
indexed.
2014-10-31 18:00:09 +08:00
33 changed files with 560 additions and 210 deletions
+31
View File
@@ -1,5 +1,36 @@
# Release notes
## 1.5.0
- Fix `serve` command, broken by `1.4.2`
- Add nicer `dark` theme :)
## 1.4.2
- Force `process.exit` after builds, to prevent (possibly) lingering plugins
## 1.4.1
- Fix command 'install' without arguments
## 1.4.0
- Add command `gitbook install` to install plugins from book.json
- `package.json` is no longer necessary
## 1.3.4
- Add glossary to ebooks
- Fix autocover with new hook "finish:before"
- Add X-UA-Compatible meta tag for IE
## 1.3.3
- Fix parsing of lexed content using the client library
## 1.3.2
- ePub files are now passing validation from epubcheck
- Fix replacement of multiple glossary terms in a single sentence
- Fix on windows deep relative links
- Fix search indexer
## 1.3.1
- Fix error with links in markdown
## 1.3.0
- Bundle gitbook parsing library as a client side library in `gitbook.js` and `gitbook.min.js`
+1 -1
View File
@@ -118,7 +118,7 @@ module.exports = function (grunt) {
// Bundle the library
grunt.registerTask('bundle', [
'bower-install',
'browserify',
'uglify'
]);
+15 -2
View File
@@ -16,6 +16,11 @@ GitBook can be installed from **NPM** using:
$ npm install gitbook -g
```
Create the directories and files for a book from its [SUMMARY.md](https://github.com/GitbookIO/gitbook#book-format) file using
```
$ gitbook init
```
You can serve a repository as a book using:
```
@@ -52,9 +57,10 @@ Here are the options that can be stored in this file:
// It's not advised this option in the book.json
"generator": "site",
// Book title and description (defaults are extracted from the README)
// Book metadats (somes are extracted from the README by default)
"title": null,
"description": null,
"isbn": null,
// For ebook format, the extension to use for generation (default is detected from output extension)
// "epub", "pdf", "mobi"
@@ -132,7 +138,10 @@ You can publish your books to our index by visiting [GitBook.io](http://www.gitb
GitBook can generate your book in the following formats:
* **Static Website**: This is the default format. It generates a complete interactive static website that can be, for example, hosted on GitHub Pages.
* **eBook**: A complete eBook with exercise solutions at the end of the book. Generate this format using: ```gitbook ebook ./myrepo```. You need to have [ebook-convert](http://manual.calibre-ebook.com/cli/ebook-convert.html) installed. The output format could be **PDF**, **ePub** or **MOBI**.
* **eBook**: A complete eBook with exercise solutions at the end of the book. You need to have [ebook-convert](http://manual.calibre-ebook.com/cli/ebook-convert.html) installed. You can specify the eBook filename with the `-o` option, otherwise `book` will be used.
* Generate a **PDF** using: `gitbook pdf ./myrepo`
* Generate a **ePub** using: `gitbook epub ./myrepo`
* Generate a **MOBI** using: `gitbook mobi ./myrepo`
* **JSON**: This format is used for debugging or extracting metadata from a book. Generate this format using: ```gitbook build ./myrepo -f json```.
## Book Format
@@ -211,6 +220,8 @@ The platform [GitBook.io](https://www.gitbook.io/) is like an "Heroku for books"
Plugins can be used to extend your book's functionality. Read [GitbookIO/plugin](https://github.com/GitbookIO/plugin) for more information about how to build a plugin for GitBook.
Plugins needed to build a book can be installed using: `gitbook install ./`.
##### Official plugins:
| Name | Description |
@@ -236,6 +247,8 @@ Plugins can be used to extend your book's functionality. Read [GitbookIO/plugin]
| [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) | Embedded jsbin frame into your book |
| [gitbook-grvis](https://github.com/romanlytkin/gitbook-grvis) | Gitbook GrViz plugin is used to select from markdown dot and converting it into a picture format svg |
| [gitbook-plantuml](https://github.com/romanlytkin/gitbook-plantuml) | Gitbook PlantUml plugin is used to select from markdown uml and converting it into a picture format svg |
#### Debugging
-7
View File
@@ -44,13 +44,6 @@ var makeBuildFunc = function(converter) {
.then(function(output) {
console.log("Successfully built!");
return output;
})
.fail(function(err) {
// Log error
utils.logError(err);
// Exit process with failure code
process.exit(-1);
});
};
};
+44 -22
View File
@@ -7,11 +7,12 @@ var prog = require('commander');
var tinylr = require('tiny-lr-fork');
var pkg = require('../package.json');
var generators = require("../lib/generate").generators;
var genbook = require("../lib/generate");
var initDir = require("../lib/generate/init");
var fs = require('../lib/generate/fs');
var utils = require('./utils');
var action = utils.action;
var build = require('./build');
var Server = require('./server');
var platform = require("./platform");
@@ -22,13 +23,13 @@ prog
build.command(prog.command('build [source_dir]'))
.description('Build a gitbook from a directory')
.action(build.folder);
.action(action(build.folder));
build.command(prog.command('serve [source_dir]'))
.description('Build then serve a gitbook from a directory')
.option('-p, --port <port>', 'Port for server to listen on', 4000)
.option('--no-watch', 'Disable restart with file watching')
.action(function(dir, options) {
.action(action(function(dir, options) {
var server = new Server();
// init livereload server
@@ -41,9 +42,11 @@ build.command(prog.command('serve [source_dir]'))
});
var generate = function() {
if (server.isRunning()) console.log("Stopping server");
if (server.isRunning()) {
console.log("Stopping server");
}
server.stop()
return server.stop()
.then(function() {
return build.folder(dir, _.extend(options || {}, {
defaultsPlugins: ["livereload"]
@@ -77,59 +80,78 @@ build.command(prog.command('serve [source_dir]'))
console.log('Press CTRL+C to quit ...');
console.log('')
generate();
});
return generate();
}));
build.commandEbook(prog.command('install [source_dir]'))
.description('Install plugins for a book')
.action(action(function(dir, options) {
dir = dir || process.cwd();
console.log("Install plugins in", dir);
return genbook.config.read({
input: dir
})
.then(function(options) {
return genbook.Plugin.install(options);
})
.then(function() {
console.log("Successfully installed plugins!");
});
}));
build.commandEbook(prog.command('pdf [source_dir]'))
.description('Build a gitbook as a PDF')
.action(function(dir, options) {
build.file(dir, _.extend(options, {
.action(action(function(dir, options) {
return build.file(dir, _.extend(options, {
extension: "pdf",
format: "ebook"
}));
});
}));
build.commandEbook(prog.command('epub [source_dir]'))
.description('Build a gitbook as a ePub book')
.action(function(dir, options) {
build.file(dir, _.extend(options, {
.action(action(function(dir, options) {
return build.file(dir, _.extend(options, {
extension: "epub",
format: "ebook"
}));
});
}));
build.commandEbook(prog.command('mobi [source_dir]'))
.description('Build a gitbook as a Mobi book')
.action(function(dir, options) {
build.file(dir, _.extend(options, {
.action(action(function(dir, options) {
return build.file(dir, _.extend(options, {
extension: "mobi",
format: "ebook"
}));
});
}));
prog
.command('init [source_dir]')
.description('Create files and folders based on contents of SUMMARY.md')
.action(function(dir) {
.action(action(function(dir) {
dir = dir || process.cwd();
return initDir(dir);
});
}));
prog
.command('publish [source_dir]')
.description('Publish content to the associated gitbook.io book')
.action(function(dir) {
.action(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) {
.action(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) {
+30
View File
@@ -30,6 +30,34 @@ function watch(dir) {
return d.promise;
}
// exit wraps a promise
// and forcefully quits the program when the promise is resolved
function exit(promise) {
promise
.then(function() {
// Prevent badly behaving plugins
// from making the process hang
process.exit(0);
}, function(err) {
// Log error
logError(err);
// Exit process with failure code
process.exit(-1);
});
}
// CLI action wrapper, calling exit when finished
function action(f) {
return function() {
// Call func and get optional promise
var p = f.apply(null, arguments);
// Exit process
return exit(Q(p));
}
}
function logError(err) {
var message = err.message || err;
if (process.env.DEBUG != null) message = err.stack || message;
@@ -60,6 +88,8 @@ function runGitCommand(command, args) {
// Exports
module.exports = {
exit: exit,
action: action,
watch: watch,
logError: logError,
gitCmd: runGitCommand
+9 -4
View File
@@ -427,8 +427,10 @@ function parsePage(src, options) {
options = options || {};
// Lex if not already lexed
return (_.isArray(src) ? src : lex(include(src, options.includer || function() { return undefined; })))
.map(function(section) {
var parsed = {
lexed: (_.isArray(src) ? src : lex(include(src, options.includer || function() { return undefined; })))
};
parsed.sections = parsed.lexed.map(function(section) {
// Transform given type
if(section.type === 'exercise') {
var nonCodeNodes = _.reject(section, {
@@ -526,6 +528,8 @@ function parsePage(src, options) {
content: render(section, options)
};
});
return parsed;
}
// Exports
@@ -628,6 +632,7 @@ module.exports = parseReadme;
},{"kramed":134,"kramed-text-renderer":133,"lodash":135}],13:[function(require,module,exports){
var url = require('url');
var _ = require('lodash');
var inherits = require('util').inherits;
var links = require('../utils').links;
var kramed = require('kramed');
@@ -677,7 +682,7 @@ GitBookRenderer.prototype.link = function(href, title, text) {
// Parsed version of the url
var parsed = url.parse(href);
var o = this._extra_options;
var extname = _.last(parsed.path.split("."));
var extname = parsed.path? _.last(parsed.path.split(".")) : "";
// Relative link, rewrite it to point to github repo
if(links.isRelative(_href) && extname == "md") {
@@ -768,7 +773,7 @@ GitBookRenderer.prototype.heading = function(text, level, raw) {
// Exports
module.exports = GitBookRenderer;
},{"../utils":15,"kramed":134,"url":25,"util":27}],14:[function(require,module,exports){
},{"../utils":15,"kramed":134,"lodash":135,"url":25,"util":27}],14:[function(require,module,exports){
var _ = require('lodash');
var kramed = require('kramed');
+1 -1
View File
File diff suppressed because one or more lines are too long
+28 -4
View File
@@ -1,3 +1,4 @@
var Q = require('q');
var _ = require('lodash');
var path = require('path');
@@ -16,9 +17,10 @@ var CONFIG = {
// Configuration file to use
"configFile": "book",
// Book title and description (defaults are extracted from the README)
// Book metadats (somes are extracted from the README by default)
"title": null,
"description": null,
"isbn": null,
// For ebook format, the extension to use for generation (default is detected from output extension)
// "epub", "pdf", "mobi"
@@ -96,10 +98,32 @@ var CONFIG = {
}
};
// Return complete configuration
var defaultsConfig = function(options) {
return _.merge(options || {}, CONFIG, _.defaults);
};
// Read configuration from book.json
var readConfig = function(options) {
options = defaultsConfig(options);
return Q()
.then(function() {
try {
var _config = require(path.resolve(options.input, options.configFile));
options = _.merge(options, _.omit(_config, 'input', 'configFile', 'defaultsPlugins', 'generator'));
}
catch(err) {
// No config file: not a big deal
return Q();
}
})
.thenResolve(options);
};
module.exports = {
CONFIG: CONFIG,
defaults: function(options) {
return _.merge(options || {}, CONFIG, _.defaults);
}
defaults: defaultsConfig,
read: readConfig
}
+1
View File
@@ -30,6 +30,7 @@ Generator.prototype.finish = function() {
"--cover": that.options.cover,
"--title": that.options.title,
"--comments": that.options.description,
"--isbn": that.options.isbn,
"--authors": that.options.author,
"--publisher": "GitBook",
"--chapter": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter ')]",
+14 -1
View File
@@ -64,7 +64,20 @@ var getFiles = function(path) {
module.exports = {
list: getFiles,
readFile: Q.denodeify(fs.readFile),
writeFile: Q.denodeify(fs.writeFile),
//writeFile: Q.denodeify(fs.writeFile),
writeFile: function(filename, data, options) {
var d = Q.defer();
try {
fs.writeFileSync(filename, data, options)
} catch(err) {
d.reject(err);
}
d.resolve();
return d.promise;
},
mkdirp: Q.denodeify(fsExtra.mkdirp),
copy: Q.denodeify(fsExtra.copy),
remove: Q.denodeify(fsExtra.remove),
+16 -20
View File
@@ -48,28 +48,18 @@ var loadGenerator = function(options) {
var generate = function(options) {
// Set defaults to options
options = defaultConfig.defaults(options);
// Validate options
if (!options.input) {
return Q.reject(new Error("Need option input (book input directory)"));
}
// Check files to get folder type (book, multilanguage book or neither)
return checkGenerator(options)
// Read config file
.then(function() {
try {
var _config = require(path.resolve(options.input, options.configFile));
return defaultConfig.read(options)
.then(function(_options) {
options = _options;
options = _.merge(options, _.omit(_config, 'input', 'configFile', 'defaultsPlugins'));
}
catch(err) {
// No config file: not a big deal
return Q();
// Validate options
if (!options.input) {
return Q.reject(new Error("Need option input (book input directory)"));
}
// Check files to get folder type (book, multilanguage book or neither)
return checkGenerator(options);
})
// Read readme
@@ -99,7 +89,9 @@ var generate = function(options) {
});
};
/*
* Generate a multilanguage book by generating a book for each folder.
*/
var generateMultiLang = function(options) {
var langsSummary;
options.output = options.output || path.join(options.input, "_book");
@@ -297,6 +289,9 @@ var generateBook = function(options) {
})
// Finish generation
.then(function() {
return generator.callHook("finish:before");
})
.then(function() {
return generator.finish();
})
@@ -375,4 +370,5 @@ module.exports = {
file: generateFile,
book: generateBook,
Plugin: Plugin,
config: defaultConfig
};
+3 -2
View File
@@ -29,8 +29,9 @@ Generator.prototype.convertFile = function(content, input) {
dir: path.dirname(input) || '/'
});
})
.then(function(sections) {
json.sections = sections;
.then(function(parsed) {
json.lexed = parsed.lexed;
json.sections = parsed.sections;
})
.then(function() {
return fs.writeFile(
+9 -1
View File
@@ -26,6 +26,9 @@ Generator.prototype.loadTemplates = function() {
this.summaryTemplate = swig.compileFile(
this.plugins.template("ebook:sumary") || path.resolve(this.options.theme, 'templates/ebook/summary.html')
);
this.glossaryTemplate = swig.compileFile(
this.plugins.template("ebook:glossary") || path.resolve(this.options.theme, 'templates/ebook/glossary.html')
);
};
// Generate table of contents
@@ -46,7 +49,7 @@ Generator.prototype.finish = function() {
var output = path.join(this.options.output, "index.html");
var progress = parse.progress(this.options.navigation, "README.md");
return Q()
// Write table of contents
@@ -54,6 +57,11 @@ Generator.prototype.finish = function() {
return that.writeToc();
})
// Write glossary
.then(function() {
return that.writeGlossary();
})
// Copy cover
.then(function() {
return that.copyCover();
+57 -16
View File
@@ -4,6 +4,7 @@ var semver = require("semver");
var path = require("path");
var url = require("url");
var fs = require("./fs");
var npmi = require('npmi');
var resolve = require('resolve');
var pkg = require("../../package.json");
@@ -152,33 +153,73 @@ Plugin.prototype.copyAssets = function(out, options) {
};
// Normalize a list of plugin name to use
Plugin.normalizeNames = function(names) {
// Install a list of plugin
Plugin.install = function(options) {
// Normalize list of plugins
var plugins = Plugin.normalizeList(options.plugins);
// Install plugins one by one
return _.reduce(plugins, function(prev, plugin) {
return prev.then(function() {
var fullname = "gitbook-plugin-"+plugin.name;
console.log("Install plugin", plugin.name, "from npm ("+fullname+") with version", (plugin.version || "*"));
return Q.nfcall(npmi, {
'name': fullname,
'version': plugin.version,
'path': options.input,
'npmLoad': {
'loglevel': 'silent',
'loaded': false,
'prefix': options.input
}
});
});
}, Q());
};
// Normalize a list of plugins to use
Plugin.normalizeList = function(plugins) {
// Normalize list to an array
names = _.isString(names) ? names.split(",") : (names || []);
plugins = _.isString(plugins) ? plugins.split(",") : (plugins || []);
// Divide as {name, version} to handle format like "myplugin@1.0.0"
plugins = _.map(plugins, function(plugin) {
var parts = plugin.split("@");
return {
'name': parts[0],
'version': parts[1] // optional
}
});
// List plugins to remove
var toremove = _.chain(names)
.filter(function(name) {
return name.length > 0 && name[0] == "-";
var toremove = _.chain(plugins)
.filter(function(plugin) {
return plugin.name.length > 0 && plugin.name[0] == "-";
})
.map(function(name) {
return name.slice(1);
.map(function(plugin) {
return plugin.name.slice(1);
})
.value();
// Merge with defaults
names = _.chain(names)
.concat(Plugin.defaults)
plugins = _.chain(plugins)
.concat(_.map(Plugin.defaults, function(plugin) {
return { 'name': plugin }
}))
.uniq()
.value();
// Remove plugins starting with
names = _.filter(names, function(name) {
return !_.contains(toremove, name) && !(name.length > 0 && name[0] == "-");
// Build final list
plugins = _.filter(plugins, function(plugin) {
return !_.contains(toremove, plugin.name) && !(plugin.name.length > 0 && plugin.name[0] == "-");
});
return names;
return plugins;
};
// Normalize a list of plugin name to use
Plugin.normalizeNames = function(plugins) {
return _.pluck(Plugin.normalizeList(plugins), "name");
};
// Extract data from a list of plugin
@@ -196,7 +237,7 @@ Plugin.fromList = function(names, root, generator, options) {
return plugin;
});
if (_.size(failed) > 0) return Q.reject(new Error("Error loading plugins: "+failed.join(",")));
if (_.size(failed) > 0) return Q.reject(new Error("Error loading plugins: "+failed.join(",")+". Run 'gitbook install' to install plugins from NPM."));
// The raw resources extracted from each plugin
var pluginResources;
@@ -276,7 +317,7 @@ Plugin.fromList = function(names, root, generator, options) {
});
};
// Default plugins
// Default plugins added to each books
Plugin.defaults = [
"mathjax"
];
+9 -3
View File
@@ -134,8 +134,9 @@ Generator.prototype.prepareFile = function(content, _input) {
], path.join, fs.readFileSync)
});
})
.then(function(sections) {
page.sections = sections;
.then(function(parsed) {
page.lexed = parsed.lexed;
page.sections = parsed.sections;
// Use plugin hook
return _callHook("page");
@@ -153,7 +154,12 @@ Generator.prototype.convertFile = function(content, _input) {
if (_output == "README.html") _output = "index.html";
var output = path.join(this.options.output, _output);
var basePath = path.relative(path.dirname(output), this.options.output) || ".";
// Bug fix for issue #493 which would occur when relative-links are 2-level or more deep in win32
if (process.platform === 'win32') {
basePath = basePath.replace(/\\/g, '/');
}
return this.prepareFile(content, _input)
.then(function(page) {
// Index page in search
+6 -2
View File
@@ -51,8 +51,10 @@ function parsePage(src, options) {
options = options || {};
// Lex if not already lexed
return (_.isArray(src) ? src : lex(include(src, options.includer || function() { return undefined; })))
.map(function(section) {
var parsed = {
lexed: (_.isArray(src) ? src : lex(include(src, options.includer || function() { return undefined; })))
};
parsed.sections = parsed.lexed.map(function(section) {
// Transform given type
if(section.type === 'exercise') {
var nonCodeNodes = _.reject(section, {
@@ -150,6 +152,8 @@ function parsePage(src, options) {
content: render(section, options)
};
});
return parsed;
}
// Exports
+1 -1
View File
@@ -49,7 +49,7 @@ GitBookRenderer.prototype.link = function(href, title, text) {
// Parsed version of the url
var parsed = url.parse(href);
var o = this._extra_options;
var extname = _.last(parsed.path.split("."));
var extname = parsed.path? _.last(parsed.path.split(".")) : "";
// Relative link, rewrite it to point to github repo
if(links.isRelative(_href) && extname == "md") {
+2 -1
View File
@@ -1,6 +1,6 @@
{
"name": "gitbook",
"version": "1.3.0",
"version": "1.5.0",
"homepage": "http://www.gitbook.io/",
"description": "Library and cmd utility to generate GitBooks",
"main": "lib/index.js",
@@ -19,6 +19,7 @@
"highlight.js": "8.3.0",
"tmp": "0.0.23",
"semver": "2.2.1",
"npmi": "0.1.1",
"gaze": "~0.5.1",
"resolve": "0.6.3",
"tiny-lr-fork": "0.0.5",
+1 -1
View File
@@ -9,7 +9,7 @@ var FIXTURES_DIR = path.join(__dirname, './fixtures/');
function loadPage (name, options) {
var CONTENT = fs.readFileSync(FIXTURES_DIR + name + '.md', 'utf8');
return page(CONTENT, options);
return page(CONTENT, options).sections;
}
+1 -10
View File
@@ -6,7 +6,7 @@ var page = require('../').parse.page;
function loadPage (name, options) {
var CONTENT = fs.readFileSync(path.join(__dirname, './fixtures/' + name + '.md'), 'utf8');
return page(CONTENT, options);
return page(CONTENT, options).sections;
}
var LEXED = loadPage('PAGE', {
@@ -80,9 +80,6 @@ describe('Page parsing', function() {
describe('Relative links', function() {
it('should replace link to .md by link to .html', function() {
var LEXED = loadPage('MARKDOWN_LINKS', {
// GitHub repo ID
repo: 'GitBookIO/javascript',
// Imaginary folder of markdown file
dir: 'course',
outdir: 'course'
@@ -96,9 +93,6 @@ describe('Relative links', function() {
describe('Relative images', function() {
it('should keep image relative with considering output directory in site format', function() {
var LEXED = loadPage('IMAGES', {
// GitHub repo ID
repo: 'GitBookIO/javascript',
// Imaginary folder of markdown file
dir: 'syntax',
outdir: 'syntax'
@@ -110,9 +104,6 @@ describe('Relative images', function() {
it('should keep image relative with considering output directory in page format', function() {
var LEXED = loadPage('IMAGES', {
// GitHub repo ID
repo: 'GitBookIO/javascript',
// Imaginary folder of markdown file
dir: 'syntax',
outdir: './'
+10 -1
View File
@@ -12,7 +12,7 @@ describe('Plugin validation', function () {
});
});
describe('Plugin list of names', function () {
describe('Plugins list', function () {
var firstDefault = _.first(Plugin.defaults);
it('should convert string to array', function() {
@@ -27,6 +27,15 @@ describe('Plugin list of names', function () {
it('should remove name starting with -', function() {
assert(!_.contains(Plugin.normalizeNames(["-"+firstDefault]), firstDefault));
});
it('should accept version', function() {
var _name = "test@0.3.0,exercises@1.2.0,test2";
var plugins = Plugin.normalizeList(_name);
assert(_.find(plugins, {'name': "test"}).version = "0.3.0");
assert(_.find(plugins, {'name': "exercises"}).version = "1.2.0");
assert(!_.find(plugins, {'name': "test2"}).version);
});
});
describe('Plugin defaults loading', function () {
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+51 -50
View File
@@ -31,21 +31,57 @@ define([
return d.promise();
}
$.fn.replaceText = function( search, replace, text_only ) {
return this.each(function(){
var node = this.firstChild,
val,
new_val,
// Elements to be removed at the end.
remove = [];
// Only continue if firstChild exists.
if ( node ) {
// Loop over all childNodes.
do {
// Only process text nodes.
if ( node.nodeType === 3 ) {
// The original node value.
val = node.nodeValue;
// The new value.
new_val = val.replace( search, replace );
// Only replace text if the new value is actually different!
if ( new_val !== val ) {
if ( !text_only && /</.test( new_val ) ) {
// The new value contains HTML, set it in a slower but far more
// robust way.
$(node).before( new_val );
// Don't remove the node yet, or the loop will lose its place.
remove.push( node );
} else {
// The new value contains no HTML, so it can be set in this
// very fast, simple way.
node.nodeValue = new_val;
}
}
}
} while ( node = node.nextSibling );
}
// Time to remove those elements!
remove.length && $(remove).remove();
});
};
var pregQuote = function( str ) {
// http://kevin.vanzonneveld.net
// + original by: booeyOH
// + improved by: Ates Goral (http://magnetiq.com)
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + bugfixed by: Onno Marsman
// * example 1: preg_quote("$40");
// * returns 1: '\$40'
// * example 2: preg_quote("*RRRING* Hello?");
// * returns 2: '\*RRRING\* Hello\?'
// * example 3: preg_quote("\\.+*?[^]$(){}=!<>|:");
// * returns 3: '\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:'
return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1");
};
@@ -58,48 +94,13 @@ define([
});
};
var matchText = function(node, regex, callback, excludeElements) {
excludeElements || (excludeElements = ['script', 'style', 'iframe', 'canvas']);
var child = node.firstChild;
if (!child) return;
do {
switch (child.nodeType) {
case 1:
if (excludeElements.indexOf(child.tagName.toLowerCase()) > -1) {
continue;
}
matchText(child, regex, callback, excludeElements);
break;
case 3:
child.data.replace(regex, function(all) {
var args = [].slice.call(arguments),
offset = args[args.length - 2],
newTextNode = child.splitText(offset);
newTextNode.data = newTextNode.data.substr(all.length);
callback.apply(window, [child].concat(args));
child = newTextNode;
});
break;
}
} while (child = child.nextSibling);
return node;
};
var replaceTerm = function($el, term) {
var r = new RegExp( "\\b(" + pregQuote(term.name.toLowerCase()) + ")\\b" , 'gi' );
matchText($el.get(0), r, function(node, match, offset) {
var span = document.createElement("span");
span.className = "glossary-term";
span.textContent = match;
span.setAttribute("data-glossary-term", term.id);
span.setAttribute("title", term.description);
node.parentNode.insertBefore(span, node.nextSibling);
$el.find("*").replaceText(r, function(match) {
return "<span class='glossary-term' data-glossary-term='"+term.id+"' title='"+term.description+"'>"+match+"</span>";
});
};
var prepare = function() {
+84 -18
View File
@@ -16,57 +16,123 @@
}
.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal {
color:@page-color-1;
color: @page-color-1;
a {
color: @page-link-color-1;
}
h1,
h2,
h3,
h4,
h5,
h6 {
color: @page-heading-color-1;
}
h1, h2 {
border-color: @page-heading-border-color-1;
}
h6 {
color: @page-h6-color-1;
}
hr {
background-color: @page-hr-color-1;
}
blockquote {
border-color: @page-blockquote-border-color-1;
}
pre, code {
background: #fdf6e3;
color: #657b83;
border-color: darken(#fdf6e3, 15%);
background: @page-pre-background-1;
color: @page-pre-color-1;
border-color: @page-pre-border-color-1;
@import "./highlight/sepia.less";
}
.highlight {
background-color: @page-highlight-background-1;
}
table {
th, td {
border-color: darken(#fdf6e3, 25%);
border-color: @page-table-header-border-color-1;
}
tr {
background: #fdf6e3;
color: inherit;
border-color: #444;
color: @page-table-row-color-1;
background-color: @page-table-row-background-1;
border-color: @page-table-row-border-color-1;
}
tr:nth-child(2n) {
background-color: darken(#fdf6e3, 5%);
background-color: @page-table-row-odd-background-1;
}
}
}
.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal {
color:@page-color-2;
color: @page-color-2;
a {
color: @page-link-color-2;
}
h1,
h2,
h3,
h4,
h5,
h6 {
color: @page-heading-color-2;
}
h1, h2 {
border-color: @page-heading-border-color-2;
}
h6 {
color: @page-h6-color-2;
}
hr {
background-color: @page-hr-color-2;
}
blockquote {
border-color: @page-blockquote-border-color-2;
}
pre, code {
background: black;
color: #eaeaea;
border-color: #000;
color: @page-pre-color-2;
background: @page-pre-background-2;
border-color: @page-pre-border-color-2;
@import "./highlight/night.less";
}
.highlight {
background-color: @page-highlight-background-2;
}
table {
th, td {
border-color: #444;
border-color: @page-table-header-border-color-2;
}
tr {
background-color: black;
color: #eaeaea;
border-color: #444;
color: @page-table-row-color-2;
background-color: @page-table-row-background-2;
border-color: @page-table-row-border-color-2;
}
tr:nth-child(2n) {
background-color: #222;
background-color: @page-table-row-odd-background-2;
}
}
}
+7 -1
View File
@@ -11,7 +11,7 @@
width: @sidebar-width;
color: @sidebar-color;
background: @sidebar-background;
border-right: 1px solid @sidebar-border;
border-right: @sidebar-border-right;
.transition(left @sidebar-transition-duration ease);
@@ -152,6 +152,7 @@
.book-summary {
color: @sidebar-color-1;
background: @sidebar-background-1;
border-right: @sidebar-border-right-1;
.book-search {
background: @sidebar-search-background-1;
@@ -179,11 +180,13 @@
a, span {
color: @sidebar-link-color-1;
background: @sidebar-link-background-1;
font-weight: @sidebar-link-weight-1;
}
&.active > a, a:hover {
color: @sidebar-link-hover-color-1;
background: @sidebar-link-hover-background-1;
font-weight: @sidebar-link-hover-weight-1;
}
}
}
@@ -197,6 +200,7 @@
.book-summary {
color: @sidebar-color-2;
background: @sidebar-background-2;
border-right: @sidebar-border-right-2;
.book-search {
background: @sidebar-search-background-2;
@@ -224,11 +228,13 @@
a, span {
color: @sidebar-link-color-2;
background: @sidebar-link-background-2;
font-weight: @sidebar-link-weight-2;
}
&.active > a, a:hover {
color: @sidebar-link-hover-color-2;
background: @sidebar-link-hover-background-2;
font-weight: @sidebar-link-hover-weight-2;
}
}
}
+78 -35
View File
@@ -31,7 +31,7 @@
@sidebar-breakpoint: 600px;
@sidebar-color: hsl(207, 15%, 25%);
@sidebar-background: #fafafa;
@sidebar-border: @header-border;
@sidebar-border-right: 1px solid @header-border;
@sidebar-nested-padding: 20px;
@@ -40,7 +40,7 @@
@sidebar-search-input-background: transparent;
@sidebar-search-input-border-color: transparent;
@sidebar-divider-color: @sidebar-border;
@sidebar-divider-color: @header-border;
@sidebar-link-color: @sidebar-color;
@sidebar-link-background: transparent;
@@ -103,12 +103,12 @@
@FontPath: '@{staticPath}/fonts';
@fa-font-path: "@{FontPath}/fontawesome";
@s-font-size: 1.2rem;
@m-font-size: 1.4rem;
@l-font-size: 1.6rem;
@xl-font-size: 2.2rem;
@xxl-font-size: 4.0rem;
@default-font-size: @l-font-size;
@s-font-size: 1.2rem;
@m-font-size: 1.4rem;
@l-font-size: 1.6rem;
@xl-font-size: 2.2rem;
@xxl-font-size: 4.0rem;
@default-font-size: @l-font-size;
/*
,--------.,--.
@@ -123,9 +123,9 @@
@header-background-1: transparent;
@header-background-2: transparent;
@header-button-color-1: #AFA790;
@header-button-color-2: #7e888b;
@header-button-color-2: hsl(230, 17%, 28%);
@header-button-hover-color-1: #73553C;
@header-button-hover-color-2: #C9C9C9;
@header-button-hover-color-2: hsl(60, 100%, 98%);
@header-button-hover-background-1: none;
@header-button-hover-background-2: none;
@header-dropdown-background-1: @sidebar-background-1;
@@ -133,14 +133,16 @@
// Body
@body-background-1: #F3EACB;
@body-background-2: #1d1f21;
@body-background-2: hsl(228, 21%, 14%);
// Sidebar
@sidebar-transition-duration: 250ms;
@sidebar-color-1: #AFA790;
@sidebar-color-2: hsl(207, 15%, 80%);
@sidebar-color-2: hsl(226, 20%, 78%);
@sidebar-background-1: #111;
@sidebar-background-2: #111;
@sidebar-background-2: hsl(229, 20%, 22%);
@sidebar-border-right-1: @sidebar-border-right;
@sidebar-border-right-2: none;
@sidebar-search-background-1: transparent;
@sidebar-search-background-2: transparent;
@@ -148,51 +150,92 @@
@sidebar-search-input-border-color-2: transparent;
@sidebar-divider-color-1: @sidebar-divider-color;
@sidebar-divider-color-2: #1d1f21;
@sidebar-divider-color-2: hsl(230, 19%, 19%);
@sidebar-link-color-1: #877F6A;
@sidebar-link-color-2: hsl(207, 15%, 50%);
@sidebar-link-color-2: hsl(226, 22%, 80%);
@sidebar-link-background-1: transparent;
@sidebar-link-background-2: transparent;
@sidebar-link-weight-1: normal;
@sidebar-link-weight-2: 600;
@sidebar-link-hover-color-1: #704214;
@sidebar-link-hover-color-2: hsl(207, 100%, 50%);
@sidebar-link-hover-color-2: hsl(240, 5%, 96%);
@sidebar-link-hover-background-1: transparent;
@sidebar-link-hover-background-2: transparent;
@sidebar-link-hover-background-2: hsl(233, 19%, 18%);
@sidebar-link-hover-weight-1: normal;
@sidebar-link-hover-weight-2: 600;
@sidebar-icon-color-1: hsl(120, 60%, 50%);
@sidebar-icon-color-2: @sidebar-icon-color-1;
@sidebar-link-completed-1: @sidebar-link-color-1;
@sidebar-link-completed-2: @sidebar-link-color-2;
@sidebar-link-completed-2: hsl(227, 13%, 44%);
@sidebar-link-completed-weight-1: normal;
@sidebar-link-completed-weight-2: normal;
@sidebar-link-completed-weight-2: 600;
// Dropdown
@dropdown-divider-color-1: @sidebar-divider-color-1;
@dropdown-background-1: @sidebar-background-1;
@dropdown-button-color-1: @header-button-color-1;
@dropdown-border-color-1: @sidebar-divider-color-1;
@dropdown-button-hover-color-1: @header-button-hover-color-1;
@dropdown-divider-color-2: @sidebar-divider-color-2;
@dropdown-background-2: @sidebar-background-2;
@dropdown-button-color-2: @header-button-color-2;
@dropdown-border-color-1: @sidebar-divider-color-1;
@dropdown-border-color-2: @sidebar-divider-color-2;
@dropdown-button-hover-color-2: @header-button-hover-color-2;
@dropdown-background-1: @sidebar-background-1;
@dropdown-background-2: @sidebar-background-2;
@dropdown-button-color-1: @header-button-color-1;
@dropdown-button-color-2: hsl(228, 13%, 44%);
@dropdown-button-hover-color-1: @header-button-hover-color-1;
@dropdown-button-hover-color-2: hsl(240, 5%, 96%);
// Page
@page-color-1: #704214;
@page-color-2: #a4b1b1;
@page-background-1: #F3EACB;
@page-background-2: #1d1f21;
@page-color-1: #704214;
@page-color-2: hsl(214, 29%, 80%);
@page-background-1: @body-background-1;
@page-background-2: @body-background-2;
@bar-background-1: #F3EACB;
@bar-background-2: #1d1f21;
@page-link-color-1: inherit;
@page-link-color-2: hsl(193, 61%, 53%);
@page-heading-color-1: inherit;
@page-heading-color-2: hsl(60, 100%, 99%);
@page-heading-border-color-1: inherit;
@page-heading-border-color-2: hsl(230, 17%, 26%);
@page-h6-color-1: inherit;
@page-h6-color-2: hsl(230, 17%, 26%);
@page-hr-color-1: inherit;
@page-hr-color-2: hsl(230, 17%, 26%);
@page-blockquote-border-color-1: inherit;
@page-blockquote-border-color-2: hsl(230, 17%, 26%);
@page-pre-color-1: #657b83;
@page-pre-color-2: hsl(206, 43%, 73%);
@page-pre-background-1: #fdf6e3;
@page-pre-background-2: hsl(229, 20%, 22%);
@page-pre-border-color-1: darken(#fdf6e3, 15%);
@page-pre-border-color-2: hsl(229, 20%, 22%);
@page-highlight-background-1: inherit;
@page-highlight-background-2: hsl(233, 18%, 19%);
@page-table-header-border-color-1: darken(#fdf6e3, 25%);
@page-table-header-border-color-2: hsl(230, 17%, 28%);
@page-table-row-border-color-1: #444;
@page-table-row-border-color-2: hsl(230, 17%, 28%);
@page-table-row-color-1: inherit;
@page-table-row-color-2: hsl(216, 24%, 77%);
@page-table-row-background-1: #fdf6e3;
@page-table-row-background-2: hsl(229, 19%, 22%);
@page-table-row-odd-background-1: darken(#fdf6e3, 5%);
@page-table-row-odd-background-2: hsl(229, 17%, 25%);
// Page Bar
@bar-background-1: @page-background-1;
@bar-background-2: @page-background-2;
// Navigation
@navigation-color-1: @header-button-color-1;
@navigation-color-2: @header-button-color-2;
@navigation-color-2: hsl(224, 19%, 27%);
@navigation-hover-color-1: @header-button-hover-color-1;
@navigation-hover-color-2: @header-button-hover-color-2;
@navigation-hover-color-2: hsl(60, 100%, 98%);
// Basics of a navbar
@navbar-default-border-1: #d5d5d5;
+15
View File
@@ -0,0 +1,15 @@
{% extends "./layout.html" %}
{% block title %}Glossary | {{ title }}{% endblock %}
{% block content %}
<div class="page page-toc">
<h1>Glossary</h1>
{% for item in glossaryIndex %}
<section class="normal glossary" id="{{ item.id }}">
<h2><a href="#{{ item.id }}">{{ item.name }}</a></h2>
<p>{{ item.description }}</p>
</section>
{% endfor %}
</div>
{% endblock %}
+22
View File
@@ -0,0 +1,22 @@
<!DOCTYPE HTML>
<html lang="en-US" {% block htmlTag %}{% endblock %}>
{{ htmlSnippet("html:start")|default("") }}
<head>
{{ htmlSnippet("head:start")|default("") }}
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="{% block description %}{% endblock %}">
<meta name="generator" content="GitBook {{ gitbook.version }}">
{% block head %}{% endblock %}
{{ htmlSnippet("head:end")|default("") }}
</head>
<body>
{{ htmlSnippet("body:start")|default("") }}
{% block style %}{% endblock %}
{% block content %}{% endblock %}
{% block javascript %}{% endblock %}
{{ htmlSnippet("body:end")|default("") }}
</body>
{{ htmlSnippet("html:end")|default("") }}
</html>
+3 -3
View File
@@ -1,4 +1,4 @@
{% extends "../layout.html" %}
{% extends "./layout.html" %}
{% block title %}{{ progress.current.title }} | {{ title }}{% endblock %}
@@ -17,7 +17,7 @@
<div class="page">
<h1 class="book-chapter book-chapter-{{ progress.current.level|lvl }}">{{ progress.current.title }}</h1>
{% for section in content %}
<section class="{{ section.type }}" id="section-{{ section.id }}">
<div class="{{ section.type }}" id="section-{{ section.id }}">
{% if section.type == "normal" %}
{% autoescape false %}{{ section.content }}{% endautoescape %}
{% elif section.type == "exercise" %}
@@ -25,7 +25,7 @@
{% elif section.type == "quiz" %}
{% include "./includes/quiz.html" with {section: section} %}
{% endif %}
</section>
</div>
{% endfor %}
</div>
{% endblock %}
+5 -1
View File
@@ -1,4 +1,4 @@
{% extends "../layout.html" %}
{% extends "./layout.html" %}
{% block title %}Table of Contents | {{ title }}{% endblock %}
@@ -40,6 +40,10 @@
<h1>Table of Contents</h1>
<ol>
{{ articles(summary.chapters) }}
{% if glossary.length > 0 %}
<li><a href="{{ basePath }}/GLOSSARY.html">Glossary</a></li>
{% endif %}
</ol>
</div>
{% endblock %}
+4
View File
@@ -4,6 +4,7 @@
<head>
{{ htmlSnippet("head:start")|default("") }}
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=11; IE=10; IE=9; IE=8; IE=7; IE=EDGE" />
<title>{% block title %}{% endblock %}</title>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="{% block description %}{% endblock %}">
@@ -14,6 +15,9 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="{{ staticBase }}/images/apple-touch-icon-precomposed-152.png">
<link rel="shortcut icon" href="{{ staticBase }}/images/favicon.ico" type="image/x-icon">
{% if options.isbn %}
<meta name="identifier" content="{{ options.isbn }}" scheme="ISBN">
{% endif %}
{% block head %}{% endblock %}
{{ htmlSnippet("head:end")|default("") }}
</head>