mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-28 21:18:57 +00:00
Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 67e7d77302 | |||
| 8cbd8b2b48 | |||
| 7c404dbf96 | |||
| c4a67b6f2f | |||
| 3b70347625 | |||
| bb0a7935e9 | |||
| 931e0c66c9 | |||
| 0212084862 | |||
| 9abbabbce3 | |||
| ac610487e8 | |||
| a9a0f89892 | |||
| 305f221077 | |||
| 53514f2602 | |||
| 00327778cb | |||
| 37fadec787 | |||
| ada11d0b59 | |||
| 4c90fdb7b2 | |||
| 98c133df3c | |||
| f86c27b090 | |||
| b5b8ed72a5 | |||
| 5beff86548 | |||
| dd395edae1 | |||
| d1c1d7b75b | |||
| 4a380e9279 | |||
| 878c105def | |||
| 74d628676b | |||
| 6fdd98ec3a | |||
| 2ed1be3533 | |||
| 0ac04e1584 | |||
| 2a38d33d8a | |||
| fb14caf5b3 | |||
| cf51772f54 | |||
| cc8516df83 | |||
| c6a624bc6b | |||
| 8be8b825ee | |||
| 56eb4a2897 | |||
| 7a461eaa61 | |||
| fd64c05809 | |||
| 6bce93d893 | |||
| c9db068227 | |||
| 8a6d36ac4d | |||
| 64622c5945 | |||
| 837813edb0 | |||
| 0d9d62f1a9 | |||
| f7e9abf984 | |||
| dd0579b60d | |||
| b5bb953e47 | |||
| ef4a8ea1b6 | |||
| 49cde96ee7 | |||
| 627be20e48 | |||
| e45bb84b02 | |||
| 4e8876c725 | |||
| 6d5be15ec9 | |||
| 1a8db646f2 | |||
| d3aba2c906 | |||
| 457a83282f | |||
| 48e94c937d | |||
| 6e613e9f71 | |||
| b698d43d7d | |||
| a36b9a6088 | |||
| 20acd89c8a | |||
| 7bed9a257d | |||
| 19c83aa5a8 | |||
| 52610d7d61 | |||
| 9e4b393b7a | |||
| a0ae34a3b7 | |||
| 413dc5d004 | |||
| c9eb27bfe7 | |||
| b78505c290 | |||
| 95f9ae83d0 | |||
| 9304289315 | |||
| 1b726ca293 | |||
| 656ca18929 |
@@ -15,3 +15,4 @@ Contributors
|
||||
------------
|
||||
|
||||
- Nijiko Yonskai
|
||||
- Herman Starikov
|
||||
|
||||
+1
-5
@@ -24,7 +24,7 @@ module.exports = function (grunt) {
|
||||
requirejs: {
|
||||
compile: {
|
||||
options: {
|
||||
name: "app",
|
||||
name: "gitbook",
|
||||
baseUrl: "theme/javascript/",
|
||||
out: "theme/assets/app.js",
|
||||
preserveLicenseComments: false,
|
||||
@@ -35,7 +35,6 @@ module.exports = function (grunt) {
|
||||
"lodash": 'vendors/lodash',
|
||||
"requireLib": 'vendors/require',
|
||||
"Mousetrap": 'vendors/mousetrap',
|
||||
"mixpanel": 'vendors/mixpanel',
|
||||
"lunr": path.join(__dirname, "node_modules/lunr/lunr")
|
||||
},
|
||||
shim: {
|
||||
@@ -48,9 +47,6 @@ module.exports = function (grunt) {
|
||||
'Mousetrap': {
|
||||
exports: 'Mousetrap'
|
||||
},
|
||||
'mixpanel': {
|
||||
exports: 'mixpanel'
|
||||
},
|
||||
'lunr': {
|
||||
exports: 'lunr'
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ Apache License
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
Copyright 2014 FriendCode Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -198,4 +198,4 @@ Apache License
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
limitations under the License.
|
||||
|
||||
@@ -3,7 +3,7 @@ GitBook
|
||||
|
||||
[](https://travis-ci.org/GitbookIO/gitbook)
|
||||
|
||||
GitBook is a command line tool (and Node.js library) for building beautiful programming books and exercises using GitHub/Git and Markdown. You can see an example: [Learn Javascript](http://gitbookio.github.io/javascript/).
|
||||
GitBook is a command line tool (and Node.js library) for building beautiful programming books and exercises using GitHub/Git and Markdown. You can see an example: [Learn Javascript](http://gitbookio.github.io/javascript/). An [editor](https://github.com/GitbookIO/editor) is available for Windows, Mac and Linux. You can follow [@GitBookIO](https://twitter.com/GitBookIO) on Twitter.
|
||||
|
||||

|
||||
|
||||
@@ -35,11 +35,13 @@ Options for commands `build` and `serve` are:
|
||||
-g, --github <repo_path> ID of github repo like : username/repo
|
||||
-o, --output <directory> Path to output directory, defaults to ./_book
|
||||
-f, --format <name> Change generation format, defaults to site, availables are: site, page, pdf, json
|
||||
-i, --intro <intro> Description of the book to generate
|
||||
--githubHost <url> The url of the github host (defaults to https://github.com/)
|
||||
--theme <path> Path to theme directory
|
||||
--plugins <plugins> List of plugins to use separated by ","
|
||||
--pluginsConfig <json file> JSON File containing plugins configuration
|
||||
```
|
||||
|
||||
You can publish your books to our index by visiting [GitBook.io](http://www.gitbook.io)
|
||||
|
||||
## Output Formats
|
||||
|
||||
GitBook can generate your book in the following formats:
|
||||
@@ -132,3 +134,16 @@ You can see a complete example with the [Learn Git](https://github.com/GitbookIO
|
||||
#### Ignoring files & folders
|
||||
|
||||
GitBook will read the `.gitignore`, `.bookignore` and `.ignore` files to get a list of files and folders to skip. (The format inside those files, follows the same convention as `.gitignore`)
|
||||
|
||||
#### Plugins
|
||||
|
||||
Plugins can used to extend your book functionnalities. Read [GitbookIO/plugin](https://github.com/GitbookIO/plugin) for more informations about how to build a plugin for gitbook.
|
||||
|
||||
##### Default plugins:
|
||||
|
||||
* [mathjax](https://github.com/GitbookIO/plugin-mathjax): displays mathematical notation in the book.
|
||||
* [mixpanel](https://github.com/GitbookIO/plugin-mixpanel): Mixpanel tracking for your book
|
||||
|
||||
##### Other plugins:
|
||||
|
||||
* [Google Analytics](https://github.com/GitbookIO/plugin-ga): Google Analytics tracking for your book
|
||||
|
||||
+27
-6
@@ -1,18 +1,37 @@
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var _ = require('lodash');
|
||||
var fs = require('fs');
|
||||
|
||||
var utils = require('./utils');
|
||||
|
||||
var generate = require("../lib/generate");
|
||||
var parse = require("../lib/parse");
|
||||
var fs = require('../lib/generate/fs');
|
||||
var generators = require("../lib/generate").generators;
|
||||
|
||||
var buildCommand = function(command) {
|
||||
return command
|
||||
.option('-o, --output <directory>', 'Path to output directory, defaults to ./_book')
|
||||
.option('-f, --format <name>', 'Change generation format, defaults to site, availables are: '+_.keys(generators).join(", "))
|
||||
.option('-t, --title <name>', 'Name of the book to generate, default is extracted from readme')
|
||||
.option('-i, --intro <intro>', 'Description of the book to generate, default is extracted from readme')
|
||||
.option('--plugins <plugins>', 'List of plugins to use separated by ","')
|
||||
.option('--pluginsConfig <json file>', 'JSON File containing plugins configuration')
|
||||
.option('-g, --github <repo_path>', 'ID of github repo like : username/repo')
|
||||
.option('--githubHost <url>', 'The url of the github host (defaults to https://github.com/');
|
||||
};
|
||||
|
||||
|
||||
var makeBuildFunc = function(converter) {
|
||||
return function(dir, options) {
|
||||
dir = dir || process.cwd();
|
||||
outputDir = options.output
|
||||
outputDir = options.output;
|
||||
|
||||
// Read plugins config
|
||||
var pluginsConfig = {};
|
||||
if (options.pluginsConfig) {
|
||||
pluginsConfig = JSON.parse(fs.readFileSync(options.pluginsConfig))
|
||||
}
|
||||
|
||||
|
||||
console.log('Starting build ...');
|
||||
// Get repo's URL
|
||||
@@ -33,7 +52,8 @@ var makeBuildFunc = function(converter) {
|
||||
github: options.github || repoID,
|
||||
githubHost: options.githubHost,
|
||||
generator: options.format,
|
||||
theme: options.theme
|
||||
plugins: options.plugins,
|
||||
pluginsConfig: pluginsConfig
|
||||
})
|
||||
);
|
||||
})
|
||||
@@ -42,9 +62,10 @@ var makeBuildFunc = function(converter) {
|
||||
return output;
|
||||
}, utils.logError);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
folder: makeBuildFunc(generate.folder),
|
||||
file: makeBuildFunc(generate.file)
|
||||
file: makeBuildFunc(generate.file),
|
||||
command: buildCommand
|
||||
};
|
||||
|
||||
+4
-15
@@ -17,22 +17,11 @@ var build = require('./build');
|
||||
prog
|
||||
.version(pkg.version);
|
||||
|
||||
var buildCommand = function(command) {
|
||||
return command
|
||||
.option('-o, --output <directory>', 'Path to output directory, defaults to ./_book')
|
||||
.option('-f, --format <name>', 'Change generation format, defaults to site, availables are: '+_.keys(generators).join(", "))
|
||||
.option('-t, --title <name>', 'Name of the book to generate, default is extracted from readme')
|
||||
.option('-i, --intro <intro>', 'Description of the book to generate, default is extracted from readme')
|
||||
.option('-g, --github <repo_path>', 'ID of github repo like : username/repo')
|
||||
.option('--githubHost <url>', 'The url of the github host (defaults to https://github.com/')
|
||||
.option('--theme <path>', 'Path to theme directory');
|
||||
};
|
||||
|
||||
buildCommand(prog.command('build [source_dir]'))
|
||||
build.command(prog.command('build [source_dir]'))
|
||||
.description('Build a gitbook from a directory')
|
||||
.action(build.folder);
|
||||
|
||||
buildCommand(prog.command('serve [source_dir]'))
|
||||
build.command(prog.command('serve [source_dir]'))
|
||||
.description('Build then serve a gitbook from a directory')
|
||||
.option('-p, --port <port>', 'Port for server to listen on', 4000)
|
||||
.action(function(dir, options) {
|
||||
@@ -50,7 +39,7 @@ buildCommand(prog.command('serve [source_dir]'))
|
||||
});
|
||||
});
|
||||
|
||||
buildCommand(prog.command('pdf [source_dir]'))
|
||||
build.command(prog.command('pdf [source_dir]'))
|
||||
.description('Build a gitbook as a PDF')
|
||||
.option('-pf, --paperformat <format>', 'PDF paper format (default is A4): "5in*7.5in", "10cm*20cm", "A4", "Letter"')
|
||||
.action(function(dir, options) {
|
||||
@@ -60,7 +49,7 @@ buildCommand(prog.command('pdf [source_dir]'))
|
||||
}));
|
||||
});
|
||||
|
||||
buildCommand(prog.command('ebook [source_dir]'))
|
||||
build.command(prog.command('ebook [source_dir]'))
|
||||
.description('Build a gitbook as a eBook')
|
||||
.option('-c, --cover <path>', 'Cover image, default is cover.png if exists')
|
||||
.action(function(dir, options) {
|
||||
|
||||
+2
-1
@@ -16,7 +16,8 @@ var getFiles = function(path) {
|
||||
|
||||
// Add extra rules to ignore common folders
|
||||
ig.addIgnoreRules([
|
||||
'.git/'
|
||||
'.git/',
|
||||
'.gitignore',
|
||||
], '__custom_stuff');
|
||||
|
||||
// Push each file to our list
|
||||
|
||||
@@ -1,9 +1,30 @@
|
||||
var _ = require("lodash");
|
||||
var path = require("path");
|
||||
var Q = require("q");
|
||||
var fs = require("./fs");
|
||||
|
||||
var Plugin = require("./plugin");
|
||||
|
||||
var BaseGenerator = function(options) {
|
||||
this.options = options;
|
||||
|
||||
this.options.plugins = Plugin.normalizeNames(this.options.plugins);
|
||||
this.plugins = [];
|
||||
};
|
||||
|
||||
BaseGenerator.prototype.callHook = function(name) {
|
||||
return this.plugins.hook(name, this);
|
||||
};
|
||||
|
||||
BaseGenerator.prototype.loadPlugins = function() {
|
||||
var that = this;
|
||||
|
||||
return Plugin.fromList(this.options.plugins)
|
||||
.then(function(_plugins) {
|
||||
that.plugins = _plugins;
|
||||
|
||||
return that.callHook("init");
|
||||
});
|
||||
};
|
||||
|
||||
BaseGenerator.prototype.convertFile = function(content, input) {
|
||||
|
||||
+24
-5
@@ -6,6 +6,7 @@ var tmp = require('tmp');
|
||||
|
||||
var fs = require("./fs");
|
||||
var parse = require("../parse");
|
||||
var Plugin = require("./plugin");
|
||||
|
||||
var generators = {
|
||||
"site": require("./site"),
|
||||
@@ -42,7 +43,11 @@ var generate = function(options) {
|
||||
githubHost: 'https://github.com/',
|
||||
|
||||
// Theming
|
||||
theme: path.resolve(__dirname, '../../theme')
|
||||
theme: path.resolve(__dirname, '../../theme'),
|
||||
|
||||
// Plugins
|
||||
plugins: [],
|
||||
pluginsConfig: {}
|
||||
});
|
||||
|
||||
if (!options.input) {
|
||||
@@ -70,9 +75,10 @@ var generate = function(options) {
|
||||
})
|
||||
})
|
||||
|
||||
// Create the generator
|
||||
// Create the generator and load plugins
|
||||
.then(function() {
|
||||
generator = new generators[options.generator](options);
|
||||
return generator.loadPlugins();
|
||||
})
|
||||
|
||||
// Detect multi-languages book
|
||||
@@ -129,6 +135,15 @@ var generate = function(options) {
|
||||
});
|
||||
})
|
||||
|
||||
// Skip processing some files
|
||||
.then(function() {
|
||||
files = _.filter(files, function (file) {
|
||||
return !(
|
||||
file === 'SUMMARY.md'
|
||||
);
|
||||
});
|
||||
})
|
||||
|
||||
// Copy file and replace markdown file
|
||||
.then(function() {
|
||||
return Q.all(
|
||||
@@ -154,6 +169,9 @@ var generate = function(options) {
|
||||
// Finish gneration
|
||||
.then(function() {
|
||||
return generator.finish();
|
||||
})
|
||||
.then(function() {
|
||||
return generator.callHook("finish");
|
||||
});
|
||||
}
|
||||
})
|
||||
@@ -177,7 +195,7 @@ var generateFile = function(options) {
|
||||
return Q.nfcall(tmp.dir)
|
||||
.then(function(tmpDir) {
|
||||
return generate(
|
||||
_.extend({},
|
||||
_.extend({},
|
||||
options,
|
||||
{
|
||||
output: tmpDir
|
||||
@@ -195,7 +213,7 @@ var generateFile = function(options) {
|
||||
_outputFile = _outputFile.slice(0, -path.extname(_outputFile).length)+"_"+lang+path.extname(_outputFile);
|
||||
_tmpDir = path.join(_tmpDir, lang);
|
||||
}
|
||||
|
||||
|
||||
return fs.copy(
|
||||
path.join(_tmpDir, "index."+ext),
|
||||
_outputFile
|
||||
@@ -225,5 +243,6 @@ var generateFile = function(options) {
|
||||
module.exports = {
|
||||
generators: generators,
|
||||
folder: generate,
|
||||
file: generateFile
|
||||
file: generateFile,
|
||||
Plugin: Plugin
|
||||
};
|
||||
|
||||
@@ -25,14 +25,18 @@ swig.setFilter('code', function(code, lang) {
|
||||
var Generator = function() {
|
||||
BaseGenerator.apply(this, arguments);
|
||||
|
||||
// Load base template
|
||||
this.template = swig.compileFile(path.resolve(this.options.theme, 'templates/page.html'));
|
||||
|
||||
// List of pages content
|
||||
this.pages = {};
|
||||
};
|
||||
util.inherits(Generator, BaseGenerator);
|
||||
|
||||
// Load all templates
|
||||
Generator.prototype.loadTemplates = function() {
|
||||
this.template = swig.compileFile(
|
||||
this.plugins.template("page") || path.resolve(this.options.theme, 'templates/page.html')
|
||||
);
|
||||
};
|
||||
|
||||
Generator.prototype.convertFile = function(content, input) {
|
||||
var that = this;
|
||||
var json = {
|
||||
@@ -46,6 +50,7 @@ Generator.prototype.convertFile = function(content, input) {
|
||||
repo: that.options.githubId,
|
||||
dir: path.dirname(input) || '/',
|
||||
outdir: './',
|
||||
singleFile: true
|
||||
});
|
||||
})
|
||||
.then(function(sections) {
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
var _ = require("lodash");
|
||||
var Q = require("q");
|
||||
var semver = require("semver");
|
||||
var path = require("path");
|
||||
var url = require("url");
|
||||
var fs = require("./fs");
|
||||
|
||||
var pkg = require("../../package.json");
|
||||
|
||||
var RESOURCES = ["js", "css"];
|
||||
|
||||
var Plugin = function(name) {
|
||||
this.name = name;
|
||||
this.packageInfos = {};
|
||||
this.infos = {};
|
||||
|
||||
_.each([
|
||||
"gitbook-plugin-"+name,
|
||||
"gitbook-theme-"+name,
|
||||
"gitbook-"+name,
|
||||
name,
|
||||
], function(_name) {
|
||||
if (this.load(_name)) return false;
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
// Load from a name
|
||||
Plugin.prototype.load = function(name) {
|
||||
try {
|
||||
this.packageInfos = require(name+"/package.json");
|
||||
this.infos = require(name);
|
||||
this.name = name;
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Return resources
|
||||
Plugin.prototype.getResources = function(resource) {
|
||||
if (!this.infos.book || !this.infos.book[resource]) {
|
||||
return [];
|
||||
}
|
||||
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();
|
||||
};
|
||||
|
||||
// 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)
|
||||
);
|
||||
};
|
||||
|
||||
// Resolve file path
|
||||
Plugin.prototype.resolveFile = function(filename) {
|
||||
return path.resolve(path.dirname(require.resolve(this.name)), filename);
|
||||
};
|
||||
|
||||
// Resolve file path
|
||||
Plugin.prototype.callHook = function(name, args) {
|
||||
var hookFunc = this.infos.hooks? this.infos.hooks[name] : null;
|
||||
args = _.isArray(args) ? args : [args];
|
||||
|
||||
if (!hookFunc) return Q();
|
||||
|
||||
return Q()
|
||||
.then(function() {
|
||||
return hookFunc.apply(null, args);
|
||||
});
|
||||
};
|
||||
|
||||
// Copy plugin assets fodler
|
||||
Plugin.prototype.copyAssets = function(out) {
|
||||
if (!this.infos.book || !this.infos.book.assets) return Q();
|
||||
return fs.copy(
|
||||
this.resolveFile(this.infos.book.assets),
|
||||
out
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// Normalize a list of plugin name to use
|
||||
Plugin.normalizeNames = function(names) {
|
||||
// Normalize list to an array
|
||||
names = _.isString(names) ? names.split(":") : (names || []);
|
||||
|
||||
// List plugins to remove
|
||||
var toremove = _.chain(names)
|
||||
.filter(function(name) {
|
||||
return name.length > 0 && name[0] == "-";
|
||||
})
|
||||
.map(function(name) {
|
||||
return name.slice(1)
|
||||
})
|
||||
.value();
|
||||
|
||||
// Merge with defaults
|
||||
names = _.chain(names)
|
||||
.concat(Plugin.defaults)
|
||||
.uniq()
|
||||
.value();
|
||||
|
||||
// Remove plugins starting with
|
||||
names = _.filter(names, function(name) {
|
||||
return !_.contains(toremove, name) && !(name.length > 0 && name[0] == "-");
|
||||
});
|
||||
|
||||
return names;
|
||||
};
|
||||
|
||||
// Extract data from a list of plugin
|
||||
Plugin.fromList = function(names) {
|
||||
var failed = [];
|
||||
|
||||
// Load plugins
|
||||
var plugins = _.map(names, function(name) {
|
||||
var plugin = new Plugin(name);
|
||||
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();
|
||||
|
||||
return Q({
|
||||
'list': plugins,
|
||||
'resources': resources,
|
||||
'hook': function(name, args) {
|
||||
return _.reduce(plugins, function(prev, plugin) {
|
||||
return prev.then(function() {
|
||||
return plugin.callHook(name, args);
|
||||
})
|
||||
}, Q());
|
||||
},
|
||||
'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]);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Default plugins
|
||||
Plugin.defaults = [
|
||||
"mixpanel",
|
||||
"mathjax"
|
||||
];
|
||||
|
||||
module.exports = Plugin;
|
||||
@@ -30,13 +30,28 @@ var Generator = function() {
|
||||
|
||||
this.revision = Date.now();
|
||||
this.indexer = indexer();
|
||||
|
||||
// Load base template
|
||||
this.template = swig.compileFile(path.resolve(this.options.theme, 'templates/site.html'));
|
||||
this.langsTemplate = swig.compileFile(path.resolve(this.options.theme, 'templates/langs.html'));
|
||||
};
|
||||
util.inherits(Generator, BaseGenerator);
|
||||
|
||||
// Load all templates
|
||||
Generator.prototype.loadTemplates = function() {
|
||||
this.template = swig.compileFile(
|
||||
this.plugins.template("site") || path.resolve(this.options.theme, 'templates/site.html')
|
||||
);
|
||||
this.langsTemplate = swig.compileFile(
|
||||
this.plugins.template("langs") || path.resolve(this.options.theme, 'templates/langs.html')
|
||||
);
|
||||
};
|
||||
|
||||
// Load plugins
|
||||
Generator.prototype.loadPlugins = function() {
|
||||
var that = this;
|
||||
|
||||
return BaseGenerator.prototype.loadPlugins.apply(this)
|
||||
.then(function() {
|
||||
return that.loadTemplates();
|
||||
});
|
||||
};
|
||||
|
||||
// Generate a template
|
||||
Generator.prototype._writeTemplate = function(tpl, options, output) {
|
||||
@@ -54,7 +69,10 @@ Generator.prototype._writeTemplate = function(tpl, options, output) {
|
||||
githubHost: that.options.githubHost,
|
||||
|
||||
summary: that.options.summary,
|
||||
allNavigation: that.options.navigation
|
||||
allNavigation: that.options.navigation,
|
||||
|
||||
plugins: that.plugins,
|
||||
pluginsConfig: JSON.stringify(that.options.pluginsConfig)
|
||||
}, options));
|
||||
})
|
||||
.then(function(html) {
|
||||
@@ -134,10 +152,19 @@ Generator.prototype.langsIndex = function(langs) {
|
||||
Generator.prototype.copyAssets = function() {
|
||||
var that = this;
|
||||
|
||||
// Copy gitbook assets
|
||||
return fs.copy(
|
||||
path.join(that.options.theme, "assets"),
|
||||
path.join(that.options.output, "gitbook")
|
||||
);
|
||||
)
|
||||
// Copy plugins assets
|
||||
.then(function() {
|
||||
return Q.all(
|
||||
_.map(that.plugins.list, function(plugin) {
|
||||
return plugin.copyAssets(path.join(that.options.output, "gitbook/plugins/", plugin.name))
|
||||
})
|
||||
);
|
||||
})
|
||||
};
|
||||
|
||||
// Dump search index to disk
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ function isQuiz(nodes) {
|
||||
|
||||
if(
|
||||
// List of questions
|
||||
listIdx !== -1 && isQuizNode(quizNodes[listIdx + 1]) &&
|
||||
listIdx !== -1 && isQuizNode(quizNodes[listIdx + 1]) ||
|
||||
|
||||
// Table of questions
|
||||
(
|
||||
|
||||
+30
-49
@@ -6,6 +6,12 @@ function clean(obj) {
|
||||
return obj && _.omit(obj, ['articles']);
|
||||
}
|
||||
|
||||
function flattenChapters(chapters) {
|
||||
return _.reduce(chapters, function(accu, chapter) {
|
||||
return accu.concat([clean(chapter)].concat(chapter.articles));
|
||||
}, []);
|
||||
}
|
||||
|
||||
// Returns from a summary a map of
|
||||
/*
|
||||
{
|
||||
@@ -20,62 +26,37 @@ function navigation(summary, files) {
|
||||
// Support single files as well as list
|
||||
files = _.isArray(files) ? files : (_.isString(files) ? [files] : null);
|
||||
|
||||
// Mapping of prev/next for a give path
|
||||
var mapping = {};
|
||||
|
||||
// Special README nav
|
||||
var README_NAV = {
|
||||
path: 'README.md',
|
||||
title: 'Introduction',
|
||||
};
|
||||
|
||||
// Walk the chapter/article tree and create navigation entires
|
||||
_.each(summary.chapters, function(chapter, idx, chapters) {
|
||||
// Skip if no path
|
||||
if(!chapter.path) return;
|
||||
|
||||
var currentChapter = clean(chapter);
|
||||
var prevChapter = (idx-1 < 0) ? README_NAV : chapters[idx-1];
|
||||
var nextChapter = (idx+1 >= chapters.length) ? null : chapters[idx+1];
|
||||
|
||||
var prev = (!prevChapter || _.isEmpty(prevChapter.articles)) ?
|
||||
prevChapter : _.last(prevChapter.articles);
|
||||
var next = (!chapter || _.isEmpty(chapter.articles)) ?
|
||||
nextChapter : _.first(chapter.articles);
|
||||
|
||||
// Add chapter mapping
|
||||
mapping[chapter.path] = {
|
||||
title: chapter.title,
|
||||
prev: clean(prev),
|
||||
next: clean(next),
|
||||
level: (idx+1).toString(),
|
||||
};
|
||||
|
||||
// Check a chapter's articles
|
||||
_.each(chapter.articles, function(article, _idx, articles) {
|
||||
// Skip if no path
|
||||
if(!article.path) return;
|
||||
|
||||
var prev = (_idx-1 < 0) ? currentChapter : clean(articles[_idx-1]);
|
||||
var next = (_idx+1 >= articles.length) ? nextChapter : clean(articles[_idx+1]);
|
||||
|
||||
mapping[article.path] = {
|
||||
title: article.title,
|
||||
prev: clean(prev),
|
||||
next: clean(next),
|
||||
level: [idx+1, _idx+1].join('.'),
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
// Hack for README.html
|
||||
mapping['README.md'] = {
|
||||
title: README_NAV.title,
|
||||
prev: null,
|
||||
next: clean(summary.chapters[0]),
|
||||
level: '0',
|
||||
};
|
||||
|
||||
// List of all navNodes
|
||||
var navNodes = [README_NAV].concat(flattenChapters(summary.chapters));
|
||||
var prevNodes = [null].concat(navNodes.slice(0, -1));
|
||||
var nextNodes = navNodes.slice(1).concat([null]);
|
||||
|
||||
// Mapping of prev/next for a give path
|
||||
var mapping = _.chain(_.zip(navNodes, prevNodes, nextNodes))
|
||||
.map(function(nodes) {
|
||||
var current = nodes[0], prev = nodes[1], next = nodes[2];
|
||||
|
||||
// Skip if no path
|
||||
if(!current.path) return null;
|
||||
|
||||
return [current.path, {
|
||||
title: current.title,
|
||||
prev: prev,
|
||||
next: next,
|
||||
level: current.level,
|
||||
}];
|
||||
})
|
||||
.filter()
|
||||
.object()
|
||||
.value();
|
||||
|
||||
// Filter for only files we want
|
||||
if(files) {
|
||||
return _.pick(mapping, files);
|
||||
|
||||
+15
-12
@@ -8,17 +8,6 @@ var renderer = require('./renderer');
|
||||
var lnormalize = require('../utils/lang').normalize;
|
||||
|
||||
|
||||
// Synchronous highlighting with highlight.js
|
||||
marked.setOptions({
|
||||
highlight: function (code, lang) {
|
||||
try {
|
||||
return hljs.highlight(lang, code).value;
|
||||
} catch(e) {
|
||||
return hljs.highlightAuto(code).value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Render a section using our custom renderer
|
||||
function render(section, _options) {
|
||||
@@ -30,7 +19,21 @@ function render(section, _options) {
|
||||
|
||||
// Build options using defaults and our custom renderer
|
||||
var options = _.extend({}, marked.defaults, {
|
||||
renderer: renderer(null, _options)
|
||||
renderer: renderer(null, _options),
|
||||
|
||||
// Synchronous highlighting with highlight.js
|
||||
highlight: function (code, lang) {
|
||||
if(!lang) return code;
|
||||
|
||||
// Normalize lang
|
||||
lang = lnormalize(lang);
|
||||
|
||||
try {
|
||||
return hljs.highlight(lang, code).value;
|
||||
} catch(e) { }
|
||||
|
||||
return code;
|
||||
}
|
||||
});
|
||||
|
||||
return marked.parser(section, options);
|
||||
|
||||
+27
-8
@@ -1,5 +1,7 @@
|
||||
var url = require('url');
|
||||
var inherits = require('util').inherits;
|
||||
var links = require('../utils').links;
|
||||
|
||||
|
||||
var path = require('path');
|
||||
|
||||
@@ -38,23 +40,38 @@ GitBookRenderer.prototype._unsanitized = function(href) {
|
||||
};
|
||||
|
||||
GitBookRenderer.prototype.link = function(href, title, text) {
|
||||
// Our "fixed" href
|
||||
var _href = href;
|
||||
|
||||
// Don't build if it looks malicious
|
||||
if (this.options.sanitize && this._unsanitized(href)) {
|
||||
return '';
|
||||
return text;
|
||||
}
|
||||
|
||||
// Parsed version of the url
|
||||
var parsed = url.parse(href);
|
||||
|
||||
var o = this._extra_options;
|
||||
|
||||
// Relative link, rewrite it to point to github repo
|
||||
if(!parsed.protocol && parsed.path && parsed.path[0] != '/' && o && o.repo && o.dir) {
|
||||
href = url.resolve('https://github.com/' + o.repo + '/blob/', [o.dir, href].join("/"));
|
||||
parsed = url.parse(href);
|
||||
if(links.isRelative(_href)) {
|
||||
if (path.extname(_href) == ".md") {
|
||||
_href = links.toAbsolute(_href, o.dir || "./", o.outdir || "./");
|
||||
|
||||
if (o.singleFile) {
|
||||
_href = "#"+_href;
|
||||
} else {
|
||||
_href = _href.replace(".md", ".html");
|
||||
}
|
||||
} else if (o && o.repo && o.dir) {
|
||||
href = url.resolve('https://github.com/' + o.repo + '/blob/', [o.dir, _href].join("/"));
|
||||
parsed = url.parse(href);
|
||||
_href = parsed.href;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate HTML for link
|
||||
var out = '<a href="' + parsed.href + '"';
|
||||
var out = '<a href="' + _href + '"';
|
||||
// Title if no null
|
||||
if (title) {
|
||||
out += ' title="' + title + '"';
|
||||
@@ -78,9 +95,11 @@ GitBookRenderer.prototype.image = function(href, title, text) {
|
||||
var o = this._extra_options;
|
||||
|
||||
// Relative image, rewrite it depending output
|
||||
if(!parsed.protocol && parsed.path && parsed.path[0] != '/' && o && o.dir && o.outdir) {
|
||||
var outdir = o.outdir.charAt(o.outdir.length - 1) === '/' ? o.outdir : o.outdir + '/';
|
||||
_href = url.resolve(outdir, [o.dir, href].join('/'));
|
||||
if(links.isRelative(href) && o && o.dir && o.outdir) {
|
||||
// o.dir: directory parent of the file currently in rendering process
|
||||
// o.outdir: directory parent from the html output
|
||||
|
||||
_href = links.toAbsolute(_href, o.dir, o.outdir);
|
||||
}
|
||||
|
||||
return GitBookRenderer.super_.prototype.image.call(this, _href, title, text);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
module.exports = {
|
||||
lang: require('./lang'),
|
||||
links: require('./links')
|
||||
};
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
var url = require('url');
|
||||
var path = require('path');
|
||||
|
||||
// Return true if the link is relative
|
||||
var isRelative = function(href) {
|
||||
var parsed = url.parse(href);
|
||||
|
||||
return !parsed.protocol && parsed.path && parsed.path[0] != '/';
|
||||
};
|
||||
|
||||
// Relative to absolute path
|
||||
// dir: directory parent of the file currently in rendering process
|
||||
// outdir: directory parent from the html output
|
||||
|
||||
var toAbsolute = function(_href, dir, outdir) {
|
||||
// Absolute file in source
|
||||
_href = path.join(dir, _href);
|
||||
|
||||
// make it relative to output
|
||||
_href = path.relative(outdir, _href);
|
||||
|
||||
return _href;
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
isRelative: isRelative,
|
||||
toAbsolute: toAbsolute
|
||||
};
|
||||
+20
-4
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "gitbook",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.1",
|
||||
"homepage": "http://www.gitbook.io/",
|
||||
"description": "Library and cmd utility to generate GitBooks",
|
||||
"main": "lib/index.js",
|
||||
"dependencies": {
|
||||
@@ -15,7 +16,12 @@
|
||||
"commander": "2.2.0",
|
||||
"fs-extra": "0.8.1",
|
||||
"highlight.js": "8.0.0",
|
||||
"tmp": "0.0.23"
|
||||
"tmp": "0.0.23",
|
||||
"semver": "2.2.1",
|
||||
|
||||
"gitbook-plugin": "0.0.2",
|
||||
"gitbook-plugin-mixpanel": "0.0.2",
|
||||
"gitbook-plugin-mathjax": "0.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "1.18.2",
|
||||
@@ -39,9 +45,19 @@
|
||||
"book",
|
||||
"gitbook"
|
||||
],
|
||||
"author": "Aaron O'Mullan <aaron.omullan@friendco.de>",
|
||||
"author": "FriendCode <contact@friendco.de>",
|
||||
"license": "Apache 2",
|
||||
"bugs": {
|
||||
"url": "https://github.com/GitbookIO/gitbook/issues"
|
||||
}
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Aaron O'Mullan",
|
||||
"email": "aaron.omullan@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Samy Pessé",
|
||||
"email": "samypesse@gmail.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 164 KiB |
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
# Images Test
|
||||
|
||||
This is a test for images path calculation. It supposed this fiel is in a syntax/ folder
|
||||
|
||||
### #
|
||||
|
||||
[](./preview.png)
|
||||
|
||||
### 2
|
||||
|
||||
[](./preview2.png)
|
||||
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
# Nice course
|
||||
|
||||
Check out this other chapter [Test](test.md)
|
||||
|
||||
Check out this other chapter [Test](../before.md)
|
||||
Vendored
+15
@@ -31,3 +31,18 @@ Some more nice content ....
|
||||
[Cool stuff](http://gitbook.io)
|
||||
|
||||
[Link to another Markdown file](./xyz/file.md)
|
||||
|
||||
---
|
||||
|
||||
Quiz test 2: What does Gitbook support?
|
||||
- [x] Table-based questions with radio buttons
|
||||
- [x] Table-based questions with checkboxes
|
||||
- [ ] Telepathy
|
||||
- [x] List-based questions with checkboxes
|
||||
- [x] List-based questions with radio buttons
|
||||
- [ ] Moon-on-a-stick
|
||||
|
||||
> Gitbook supports table and list based quiz questions using either radio buttons or checkboxes.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+62
-5
@@ -28,6 +28,7 @@ describe('Page parsing', function() {
|
||||
assert.equal(QUIZ_LEXED[0].type, 'normal');
|
||||
assert.equal(QUIZ_LEXED[1].type, 'quiz');
|
||||
assert.equal(QUIZ_LEXED[2].type, 'normal');
|
||||
assert.equal(QUIZ_LEXED[3].type, 'quiz');
|
||||
});
|
||||
|
||||
it('should gen content for normal sections', function() {
|
||||
@@ -35,10 +36,6 @@ describe('Page parsing', function() {
|
||||
assert(LEXED[2].content);
|
||||
});
|
||||
|
||||
it('should make image URLs relative', function() {
|
||||
assert(LEXED[2].content.indexOf('_book/assets/my-pretty-picture.png') !== -1);
|
||||
});
|
||||
|
||||
it('should gen code and content for exercise sections', function() {
|
||||
assert(LEXED[1].content);
|
||||
assert(LEXED[1].code);
|
||||
@@ -87,9 +84,69 @@ describe('Relative links', function() {
|
||||
repo: 'GitBookIO/javascript',
|
||||
|
||||
// Imaginary folder of markdown file
|
||||
dir: 'course'
|
||||
dir: 'course',
|
||||
outdir: './'
|
||||
});
|
||||
|
||||
assert(LEXED[0].content.indexOf('https://github.com/GitBookIO/javascript/blob/src/something.cpp') !== -1);
|
||||
});
|
||||
|
||||
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'
|
||||
});
|
||||
|
||||
assert(LEXED[0].content.indexOf('test.html') !== -1);
|
||||
assert(LEXED[0].content.indexOf('../before.html') !== -1);
|
||||
});
|
||||
|
||||
it('should replace link to .md by link to page in format single page', function() {
|
||||
var LEXED = loadPage('MARKDOWN_LINKS', {
|
||||
// GitHub repo ID
|
||||
repo: 'GitBookIO/javascript',
|
||||
|
||||
// Imaginary folder of markdown file
|
||||
dir: 'course',
|
||||
outdir: './',
|
||||
singleFile: true
|
||||
});
|
||||
|
||||
assert(LEXED[0].content.indexOf('#course/test.md') !== -1);
|
||||
assert(LEXED[0].content.indexOf('#before.md') !== -1);
|
||||
});
|
||||
});
|
||||
|
||||
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'
|
||||
});
|
||||
|
||||
assert(LEXED[0].content.indexOf('"preview.png"') !== -1);
|
||||
assert(LEXED[0].content.indexOf('"../preview2.png"') !== -1);
|
||||
});
|
||||
|
||||
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: './'
|
||||
});
|
||||
|
||||
assert(LEXED[0].content.indexOf('"syntax/preview.png"') !== -1);
|
||||
assert(LEXED[0].content.indexOf('"preview2.png"') !== -1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
var _ = require('lodash');
|
||||
var path = require('path');
|
||||
var assert = require('assert');
|
||||
|
||||
var Plugin = require('../').generate.Plugin;
|
||||
|
||||
describe('Plugin validation', function () {
|
||||
var plugin = new Plugin("plugin");
|
||||
|
||||
it('should be valid', function() {
|
||||
assert(plugin.isValid());
|
||||
});
|
||||
});
|
||||
|
||||
describe('Plugin list of names', function () {
|
||||
var firstDefault = _.first(Plugin.defaults);
|
||||
|
||||
it('should convert string to array', function() {
|
||||
var _name = "test";
|
||||
assert(_.contains(Plugin.normalizeNames(_name), _name));
|
||||
});
|
||||
|
||||
it('should contains default plugins', function() {
|
||||
assert(_.contains(Plugin.normalizeNames([]), firstDefault));
|
||||
});
|
||||
|
||||
it('should remove name starting with -', function() {
|
||||
assert(!_.contains(Plugin.normalizeNames(["-"+firstDefault]), firstDefault));
|
||||
});
|
||||
});
|
||||
|
||||
describe('Plugin defaults loading', function () {
|
||||
var ret = true;
|
||||
|
||||
beforeEach(function(done){
|
||||
Plugin.fromList(Plugin.defaults)
|
||||
.then(function(_r) {
|
||||
ret = _r;
|
||||
}, function(err) {
|
||||
ret = null;
|
||||
})
|
||||
.fin(done);
|
||||
});
|
||||
|
||||
|
||||
it('should load defaults addons', function() {
|
||||
assert(ret != null);
|
||||
});
|
||||
});
|
||||
@@ -1,5 +0,0 @@
|
||||
# GitBook Theme
|
||||
|
||||
For creating a theme for GitBook, just copy this folder. The theme folder should at least contains two folders: `assets` and `templates`.
|
||||
|
||||
The theme can be changed using the option ```--theme=<path>```
|
||||
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
||||
define([
|
||||
"jQuery"
|
||||
], function($) {
|
||||
var events = $({});
|
||||
|
||||
return events;
|
||||
});
|
||||
@@ -1,9 +1,9 @@
|
||||
define([
|
||||
"jQuery",
|
||||
"utils/execute",
|
||||
"utils/analytic",
|
||||
"core/events",
|
||||
"core/state"
|
||||
], function($, execute, analytic, state){
|
||||
], function($, execute, events, state){
|
||||
// Bind an exercise
|
||||
var prepareExercise = function($exercise) {
|
||||
var codeSolution = $exercise.find(".code-solution").text();
|
||||
@@ -19,7 +19,7 @@ define([
|
||||
$exercise.find(".action-submit").click(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
analytic.track("exercise.submit", {type: "code"});
|
||||
events.trigger("exercise.submit", {type: "code"});
|
||||
|
||||
execute("javascript", editor.getValue(), codeValidation, codeContext, function(err, result) {
|
||||
$exercise.toggleClass("return-error", err != null);
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
define([
|
||||
"jQuery",
|
||||
"utils/storage"
|
||||
], function($, storage) {
|
||||
var fontState;
|
||||
|
||||
var togglePopover = function(e) {
|
||||
var $dropdown = $("#font-settings-wrapper .dropdown-menu");
|
||||
|
||||
$dropdown.toggleClass("open");
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
var closePopover = function(e) {
|
||||
var $dropdown = $("#font-settings-wrapper .dropdown-menu");
|
||||
|
||||
$dropdown.removeClass("open");
|
||||
};
|
||||
|
||||
var enlargeFontSize = function(e){
|
||||
var $bookBody = $(".book-body");
|
||||
|
||||
if (fontState.size < 4){
|
||||
$bookBody.toggleClass("font-size-"+fontState.size, false);
|
||||
fontState.size++;
|
||||
|
||||
$bookBody.toggleClass("font-size-"+fontState.size, true);
|
||||
fontState.save();
|
||||
}
|
||||
};
|
||||
|
||||
var reduceFontSize = function(e){
|
||||
var $bookBody = $(".book-body");
|
||||
|
||||
if (fontState.size > 0){
|
||||
$bookBody.toggleClass("font-size-"+fontState.size);
|
||||
fontState.size--;
|
||||
|
||||
$bookBody.toggleClass("font-size-"+fontState.size);
|
||||
fontState.save();
|
||||
}
|
||||
};
|
||||
|
||||
var changeFontFamily = function(){
|
||||
var $bookBody = $(".book-body");
|
||||
var index = $(this).data("font");
|
||||
|
||||
$bookBody.toggleClass("font-family-"+fontState.family);
|
||||
$(".font-settings .font-family-list li:nth-child("+(fontState.family+1)+")")
|
||||
.removeClass("active");
|
||||
|
||||
fontState.family = index;
|
||||
$bookBody.toggleClass("font-family-"+fontState.family);
|
||||
$(this).addClass("active");
|
||||
fontState.save();
|
||||
};
|
||||
|
||||
var changeColorTheme = function(){
|
||||
var $book = $(".book");
|
||||
var index = $(this).data("theme");
|
||||
|
||||
if (fontState.theme !== 0)
|
||||
$book.removeClass("color-theme-"+fontState.theme);
|
||||
|
||||
fontState.theme = index;
|
||||
if (fontState.theme !== 0)
|
||||
$book.addClass("color-theme-"+fontState.theme);
|
||||
|
||||
fontState.save();
|
||||
};
|
||||
|
||||
var init = function() {
|
||||
var $toggle, $bookBody, $dropdown, $book;
|
||||
|
||||
//Find DOM elements.
|
||||
$book = $(".book");
|
||||
$toggle = $(".book-header .toggle-font-settings");
|
||||
$dropdown = $("#font-settings-wrapper .dropdown-menu");
|
||||
$bookBody = $(".book-body");
|
||||
|
||||
//Instantiate font state object
|
||||
fontState = storage.get("fontState", {
|
||||
size:1,
|
||||
family:0,
|
||||
theme:0
|
||||
});
|
||||
fontState.save = function(){
|
||||
storage.set("fontState",fontState);
|
||||
};
|
||||
|
||||
$bookBody.addClass("font-size-"+fontState.size);
|
||||
$bookBody.addClass("font-family-"+fontState.family);
|
||||
|
||||
$(".font-settings .font-family-list li:nth-child("+(fontState.family+1)+")").addClass("active");
|
||||
|
||||
if(fontState.theme !== 0)
|
||||
$book.addClass("color-theme-"+fontState.theme);
|
||||
|
||||
//Add event listeners
|
||||
$(document).on('click', "#enlarge-font-size", enlargeFontSize);
|
||||
$(document).on('click', "#reduce-font-size", reduceFontSize);
|
||||
|
||||
$(document).on('click', "#font-settings-wrapper .font-family-list li", changeFontFamily);
|
||||
$(document).on('click', "#font-settings-wrapper .color-theme-list button", changeColorTheme);
|
||||
|
||||
$(document).on('click', ".book-header .toggle-font-settings", togglePopover);
|
||||
$(document).on('click', "#font-settings-wrapper .dropdown-menu", function(e){ e.stopPropagation(); });
|
||||
$(document).on("click", closePopover);
|
||||
};
|
||||
|
||||
return {
|
||||
init: init
|
||||
}
|
||||
});
|
||||
@@ -1,21 +1,23 @@
|
||||
define([
|
||||
"jQuery",
|
||||
"utils/analytic",
|
||||
"core/events",
|
||||
"core/state",
|
||||
"core/search",
|
||||
"core/progress",
|
||||
"core/exercise",
|
||||
"core/quiz"
|
||||
], function($, analytic, state, search, progress, exercises, quiz) {
|
||||
], function($, events, state, search, progress, exercises, quiz) {
|
||||
var prev, next;
|
||||
var githubCountStars, githubCountWatch;
|
||||
|
||||
var usePushState = !navigator.userAgent.match('CriOS') && (typeof history.pushState !== "undefined");
|
||||
|
||||
var updateHistory = function(url, title) {
|
||||
history.pushState({ path: url }, title, url);
|
||||
};
|
||||
|
||||
var handleNavigation = function(url, push) {
|
||||
if (typeof history.pushState === "undefined") {
|
||||
if (!usePushState) {
|
||||
// Refresh the page to the new URL if pushState not supported
|
||||
location.href = url;
|
||||
return
|
||||
@@ -23,6 +25,8 @@ define([
|
||||
|
||||
return $.get(url)
|
||||
.done(function (html) {
|
||||
if (push) updateHistory(url, null);
|
||||
|
||||
html = html.replace( /<(\/?)(html|head|body)([^>]*)>/ig, function(a,b,c,d){
|
||||
return '<' + b + 'div' + ( b ? '' : ' data-element="' + c + '"' ) + d + '>';
|
||||
});
|
||||
@@ -38,12 +42,17 @@ define([
|
||||
});
|
||||
$("head").html(headContent);
|
||||
|
||||
// Update header, body and summary
|
||||
// Update header, body
|
||||
$('.book-header').html($page.find('.book-header').html());
|
||||
$('.book-body').html($page.find('.book-body').html());
|
||||
$('.book-summary').html($page.find('.book-summary').html());
|
||||
|
||||
if (push) updateHistory(url, null);
|
||||
// Update summary
|
||||
var scrollPosition = $('.book-summary .summary').scrollTop();
|
||||
$('.book-summary').html($page.find('.book-summary').html());
|
||||
$('.book-summary .summary').scrollTop(scrollPosition);
|
||||
|
||||
// Update state
|
||||
state.update($("html"));
|
||||
preparePage();
|
||||
})
|
||||
.fail(function () {
|
||||
@@ -101,7 +110,7 @@ define([
|
||||
}
|
||||
|
||||
// Send to mixpanel
|
||||
analytic.track("page.view");
|
||||
events.trigger("page.change");
|
||||
};
|
||||
|
||||
var handlePagination = function (e) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
define([
|
||||
"jQuery",
|
||||
"utils/execute",
|
||||
"utils/analytic",
|
||||
"core/events",
|
||||
"core/state"
|
||||
], function($, execute, analytic, state){
|
||||
], function($, execute, events, state){
|
||||
// Bind an exercise
|
||||
var prepareQuiz = function($quiz) {
|
||||
|
||||
@@ -14,14 +14,17 @@ define([
|
||||
// Submit: test code
|
||||
$quiz.find(".action-submit").click(function(e) {
|
||||
e.preventDefault();
|
||||
analytic.track("exercise.submit", {type: "quiz"});
|
||||
events.trigger("exercise.submit", {type: "quiz"});
|
||||
$quiz.find("tr.alert-danger,li.alert-danger").removeClass("alert-danger");
|
||||
$quiz.find(".alert-success,.alert-danger").addClass("hidden");
|
||||
|
||||
$quiz.find(".question").each(function(q) {
|
||||
var result = true;
|
||||
var $answers = $quiz.find(".question-answers").slice(q).find("input[type=radio], input[type=checkbox]");
|
||||
$(this).find("input[type=radio],input[type=checkbox]").each(function(i) {
|
||||
|
||||
var $questions = $(this).find(".question-content").find("input[type=radio], input[type=checkbox]");
|
||||
var $answers = $(this).find(".question-answers").find("input[type=radio], input[type=checkbox]");
|
||||
|
||||
$questions.each(function(i) {
|
||||
var correct = $(this).is(":checked") === $answers.slice(i).first().is(":checked");
|
||||
result = result && correct;
|
||||
if (!correct) {
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
define([
|
||||
"jQuery"
|
||||
], function() {
|
||||
var $book = $(".book");
|
||||
var state = {};
|
||||
|
||||
return {
|
||||
'$book': $book,
|
||||
state.update = function(dom) {
|
||||
var $book = $(dom.find(".book"));
|
||||
|
||||
'githubId': $book.data("github"),
|
||||
'level': $book.data("level"),
|
||||
'basePath': $book.data("basepath"),
|
||||
'revision': $book.data("revision")
|
||||
state.$book = $book;
|
||||
state.githubId = $book.data("github");
|
||||
state.level = $book.data("level");
|
||||
state.basePath = $book.data("basepath");
|
||||
state.revision = $book.data("revision");
|
||||
};
|
||||
|
||||
state.update($);
|
||||
|
||||
return state;
|
||||
});
|
||||
@@ -1,17 +1,18 @@
|
||||
require([
|
||||
define([
|
||||
"jQuery",
|
||||
"utils/storage",
|
||||
"utils/analytic",
|
||||
"utils/sharing",
|
||||
|
||||
"core/events",
|
||||
"core/font-settings",
|
||||
"core/state",
|
||||
"core/keyboard",
|
||||
"core/navigation",
|
||||
"core/progress",
|
||||
"core/sidebar",
|
||||
"core/search"
|
||||
], function($, storage, analytic, sharing, state, keyboard, navigation, progress, sidebar, search){
|
||||
$(document).ready(function() {
|
||||
], function($, storage, sharing, events, fontSettings, state, keyboard, navigation, progress, sidebar, search){
|
||||
var start = function(config) {
|
||||
var $book;
|
||||
$book = state.$book;
|
||||
|
||||
@@ -34,5 +35,15 @@ require([
|
||||
|
||||
// Init navigation
|
||||
navigation.init();
|
||||
});
|
||||
|
||||
//Init font settings
|
||||
fontSettings.init();
|
||||
|
||||
events.trigger("start", config);
|
||||
}
|
||||
|
||||
return {
|
||||
start: start,
|
||||
events: events
|
||||
};
|
||||
});
|
||||
@@ -1,33 +0,0 @@
|
||||
define([
|
||||
"lodash",
|
||||
"mixpanel"
|
||||
], function(_, mixpanel) {
|
||||
mixpanel.init("01eb2b950ae09a5fdb15a98dcc5ff20e", {
|
||||
loaded: function() {
|
||||
track("page.start");
|
||||
}
|
||||
});
|
||||
|
||||
var isAvailable = function() {
|
||||
return (
|
||||
typeof mixpanel !== "undefined" &&
|
||||
typeof mixpanel.track === "function"
|
||||
);
|
||||
};
|
||||
|
||||
var track = function(e, data) {
|
||||
if (!isAvailable()) {
|
||||
console.warn("tracking not available!");
|
||||
return;
|
||||
}
|
||||
console.log("track", e);
|
||||
mixpanel.track(e, _.extend(data || {}, {
|
||||
'domain': window.location.host
|
||||
}));
|
||||
};
|
||||
|
||||
return {
|
||||
isAvailable: isAvailable,
|
||||
track: track
|
||||
};
|
||||
});
|
||||
Vendored
-2
@@ -1,2 +0,0 @@
|
||||
(function(e,b){if(!b.__SV){var a,f,i,g;window.mixpanel=b;b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!==typeof d?c=b[d]=[]:d="mixpanel";c.people=c.people||[];c.toString=function(b){var a="mixpanel";"mixpanel"!==d&&(a+="."+d);b||(a+=" (stub)");return a};c.people.toString=function(){return c.toString(1)+".people (stub)"};i="disable track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.track_charge people.clear_charges people.delete_user".split(" ");
|
||||
for(g=0;g<i.length;g++)f(c,i[g]);b._i.push([a,e,d])};b.__SV=1.2;a=e.createElement("script");a.type="text/javascript";a.async=!0;a.src=("https:"===e.location.protocol?"https:":"http:")+'//cdn.mxpnl.com/libs/mixpanel-2.2.min.js';f=e.getElementsByTagName("script")[0];f.parentNode.insertBefore(a,f)}})(document,window.mixpanel||[]);
|
||||
@@ -6,6 +6,7 @@
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
|
||||
color: @page-color;
|
||||
background: @body-background;
|
||||
.transition(left 0.5s ease);
|
||||
|
||||
@@ -23,7 +24,6 @@
|
||||
outline: none;
|
||||
|
||||
.page-inner {
|
||||
font-family: @font-family-serif;
|
||||
max-width: 800px;
|
||||
margin: 0px auto;
|
||||
|
||||
@@ -33,8 +33,6 @@
|
||||
|
||||
background: @page-background;
|
||||
border-radius: 2px;
|
||||
|
||||
font-size: 16px;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
@@ -57,6 +55,28 @@
|
||||
min-height: calc(~"100% - 57px")
|
||||
}
|
||||
}
|
||||
&.font-size-0{
|
||||
font-size:@s-font-size;
|
||||
}
|
||||
&.font-size-1{
|
||||
font-size:@m-font-size;
|
||||
}
|
||||
&.font-size-2{
|
||||
font-size:@l-font-size;
|
||||
}
|
||||
&.font-size-3{
|
||||
font-size:@xl-font-size;
|
||||
}
|
||||
&.font-size-4{
|
||||
font-size:@xxl-font-size;
|
||||
}
|
||||
|
||||
&.font-family-0{
|
||||
font-family: @font-family-serif;
|
||||
}
|
||||
&.font-family-1{
|
||||
font-family: @font-family-sans;
|
||||
}
|
||||
}
|
||||
|
||||
&.with-summary {
|
||||
@@ -72,4 +92,30 @@
|
||||
.transition(none) !important;
|
||||
}
|
||||
}
|
||||
&.color-theme-1{
|
||||
.book-body {
|
||||
color: @page-color-1;
|
||||
background: @body-background-1;
|
||||
.page-wrapper {
|
||||
.page-inner {
|
||||
section{
|
||||
background: @page-background-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.color-theme-2{
|
||||
.book-body {
|
||||
color: @page-color-2;
|
||||
background: @body-background-2;
|
||||
.page-wrapper {
|
||||
.page-inner {
|
||||
section{
|
||||
background: @page-background-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
.book-header{
|
||||
#font-settings-wrapper{
|
||||
position:relative;
|
||||
.dropdown-menu{
|
||||
background-color:@header-background;
|
||||
border-color:@sidebar-divider-color;
|
||||
padding: 0px;
|
||||
|
||||
.dropdown-caret{
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
left: -8px;
|
||||
width: 10px;
|
||||
height: 18px;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
.caret-outer{
|
||||
position: absolute;
|
||||
border-bottom: 9px solid transparent;
|
||||
border-top: 9px solid transparent;
|
||||
border-right: 9px solid rgba(0,0,0,0.1);
|
||||
height: auto;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: auto;
|
||||
display: inline-block;
|
||||
margin-left: -1px;
|
||||
}
|
||||
.caret-inner{
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
margin-left: -1px;
|
||||
top: 0;
|
||||
left: 1px;
|
||||
border-bottom: 9px solid transparent;
|
||||
border-top: 9px solid transparent;
|
||||
border-right: 9px solid @header-background;
|
||||
}
|
||||
}
|
||||
button{
|
||||
border: 0;
|
||||
background-color:transparent;
|
||||
color:@header-button-color;
|
||||
&:hover{
|
||||
color:@header-button-hover-color;
|
||||
background-color:@header-button-hover-background;
|
||||
}
|
||||
}
|
||||
#enlarge-font-size{
|
||||
width: 50%;
|
||||
font-size:1.4em;
|
||||
}
|
||||
#reduce-font-size{
|
||||
width: 50.5%;
|
||||
font-size:1em;
|
||||
}
|
||||
.btn-group-xs{
|
||||
.btn{
|
||||
width: 33.7%;
|
||||
padding: initial;
|
||||
}
|
||||
}
|
||||
.list-group{
|
||||
margin: 0px 0;
|
||||
|
||||
.list-group-item{
|
||||
cursor:pointer;
|
||||
background-color:transparent;
|
||||
border-color: @sidebar-divider-color;
|
||||
border-width: 1px 0 !important;
|
||||
|
||||
&:hover{
|
||||
color:@header-button-hover-color;
|
||||
background-color:@header-button-hover-background !important;
|
||||
}
|
||||
&.active{
|
||||
color:@header-button-hover-color;
|
||||
background-color:@header-button-hover-background !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.open{
|
||||
display:block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Theme 1
|
||||
*/
|
||||
|
||||
.color-theme-1{
|
||||
#font-settings-wrapper{
|
||||
.dropdown-menu{
|
||||
background-color:@sidebar-background-1;
|
||||
border-color:@sidebar-divider-color-1;
|
||||
.dropdown-caret .caret-inner{
|
||||
border-right: 9px solid @sidebar-background-1;
|
||||
}
|
||||
button{
|
||||
color:@header-button-color-1;
|
||||
&:hover{
|
||||
color:@header-button-hover-color-1;
|
||||
background-color:@header-button-hover-background-1;
|
||||
}
|
||||
}
|
||||
.list-group{
|
||||
.list-group-item{
|
||||
border-color:@sidebar-divider-color-1;
|
||||
&:hover{
|
||||
color:@header-button-hover-color-1;
|
||||
background-color:@header-button-hover-background-1 !important;
|
||||
}
|
||||
&.active{
|
||||
color:@header-button-hover-color-1;
|
||||
background-color:@header-button-hover-background-1 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Theme 2
|
||||
*/
|
||||
|
||||
.color-theme-2{
|
||||
#font-settings-wrapper{
|
||||
.dropdown-menu{
|
||||
background-color:@sidebar-background-2;
|
||||
border-color:@sidebar-divider-color-2;
|
||||
.dropdown-caret .caret-inner{
|
||||
border-right: 9px solid @sidebar-background-2;
|
||||
}
|
||||
button{
|
||||
color:@header-button-color-2;
|
||||
&:hover{
|
||||
color:@header-button-hover-color-2;
|
||||
background-color:@header-button-hover-background-2;
|
||||
}
|
||||
}
|
||||
.list-group{
|
||||
.list-group-item{
|
||||
border-color:@sidebar-divider-color-2;
|
||||
&:hover{
|
||||
color:@header-button-hover-color-2;
|
||||
background-color:@header-button-hover-background-2 !important;
|
||||
}
|
||||
&.active{
|
||||
color:@header-button-hover-color-2;
|
||||
background-color:@header-button-hover-background-2 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
font-family: @font-family-sans;
|
||||
|
||||
position: fixed;
|
||||
overflow: hidden;
|
||||
overflow: visible;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
left: 0px;
|
||||
@@ -24,6 +24,7 @@
|
||||
text-transform: uppercase;
|
||||
line-height: @header-height;
|
||||
.box-shadow(none) !important;
|
||||
position:relative;
|
||||
|
||||
&:hover {
|
||||
position: relative;
|
||||
@@ -64,4 +65,30 @@
|
||||
.transition(none) !important;
|
||||
}
|
||||
}
|
||||
&.color-theme-1{
|
||||
.book-header{
|
||||
color: @header-color-1;
|
||||
background: @header-background-1;
|
||||
.btn {
|
||||
color: @header-button-color-1;
|
||||
&:hover {
|
||||
color: @header-button-hover-color-1;
|
||||
background: @header-button-hover-background-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.color-theme-2{
|
||||
.book-header{
|
||||
color: @header-color-2;
|
||||
background: @header-background-2;
|
||||
.btn {
|
||||
color: @header-button-color-2;
|
||||
&:hover {
|
||||
color: @header-button-hover-color-2;
|
||||
background: @header-button-hover-background-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Executable → Regular
+2
-2
@@ -1,4 +1,4 @@
|
||||
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
|
||||
/* http://jmblog.github.io/color-themes-for-highlightjs */
|
||||
|
||||
/* Tomorrow Comment */
|
||||
.hljs-comment {
|
||||
@@ -87,4 +87,4 @@
|
||||
.xml .css,
|
||||
.xml .hljs-cdata {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
@@ -37,8 +37,6 @@
|
||||
padding: 10px 5px;
|
||||
font-size: 16px;
|
||||
|
||||
border-bottom: 1px solid #eee;
|
||||
|
||||
a {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal {
|
||||
color:@page-color-1;
|
||||
}
|
||||
.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal {
|
||||
color:@page-color-2;
|
||||
}
|
||||
.book .book-body .page-wrapper .page-inner section.normal {
|
||||
padding: 25px;
|
||||
padding-top: 15px;
|
||||
background-color: white;
|
||||
|
||||
color:@page-color;
|
||||
|
||||
& > *:first-child {
|
||||
margin-top: 0 !important; }
|
||||
@@ -52,12 +57,12 @@
|
||||
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
color: black; }
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
border-bottom: 1px solid #eee;
|
||||
color: black; }
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 18px; }
|
||||
@@ -69,7 +74,6 @@
|
||||
font-size: 14px; }
|
||||
|
||||
h6 {
|
||||
color: #777777;
|
||||
font-size: 14px; }
|
||||
|
||||
p, blockquote, ul, ol, dl, table, pre {
|
||||
|
||||
@@ -42,4 +42,14 @@
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
.book.color-theme-1 .book-body {
|
||||
.navigation:hover{
|
||||
background-color: @body-pagination-background-1;
|
||||
}
|
||||
}
|
||||
.book.color-theme-2 .book-body {
|
||||
.navigation:hover{
|
||||
background-color: @body-pagination-background-2;
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
position: relative;
|
||||
background: #fff;
|
||||
margin-bottom: 20px;
|
||||
z-index: 10;
|
||||
|
||||
.bar {
|
||||
height: @bar-height;
|
||||
@@ -81,4 +80,36 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.book.color-theme-1 .book-body {
|
||||
.book-progress {
|
||||
.bar {
|
||||
background: @bar-background-1;
|
||||
.inner {
|
||||
background: @bar-progress-background-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.chapters .chapter{
|
||||
background: @bar-background-1;
|
||||
&.done{
|
||||
background: @bar-progress-background-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.book.color-theme-2 .book-body {
|
||||
.book-progress {
|
||||
.bar {
|
||||
background: @bar-background-2;
|
||||
.inner {
|
||||
background: @bar-progress-background-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
.chapters .chapter{
|
||||
background: @bar-background-2;
|
||||
&.done{
|
||||
background: @bar-progress-background-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,3 +141,91 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Theme 1
|
||||
*/
|
||||
.book.color-theme-1 {
|
||||
.book-summary {
|
||||
color: @sidebar-color-1;
|
||||
background: @sidebar-background-1;
|
||||
|
||||
.book-search {
|
||||
background: @sidebar-search-background-1;
|
||||
|
||||
input, input:focus {
|
||||
border: 1px solid @sidebar-search-input-border-color-1;
|
||||
}
|
||||
}
|
||||
|
||||
ul.summary {
|
||||
li {
|
||||
&.divider {
|
||||
background: @sidebar-divider-color-1;
|
||||
}
|
||||
|
||||
i.fa-check {
|
||||
color: @sidebar-icon-color-1;
|
||||
}
|
||||
|
||||
&.done > a {
|
||||
color: @sidebar-link-completed-1;
|
||||
}
|
||||
|
||||
a, span {
|
||||
color: @sidebar-link-color-1;
|
||||
background: @sidebar-link-background-1;
|
||||
}
|
||||
|
||||
&.active > a, a:hover {
|
||||
color: @sidebar-link-hover-color-1;
|
||||
background: @sidebar-link-hover-background-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Theme 2
|
||||
*/
|
||||
.book.color-theme-2{
|
||||
.book-summary {
|
||||
color: @sidebar-color-2;
|
||||
background: @sidebar-background-2;
|
||||
|
||||
.book-search {
|
||||
background: @sidebar-search-background-2;
|
||||
|
||||
input, input:focus {
|
||||
border: 1px solid @sidebar-search-input-border-color-2;
|
||||
}
|
||||
}
|
||||
|
||||
ul.summary {
|
||||
li {
|
||||
&.divider {
|
||||
background: @sidebar-divider-color-2;
|
||||
}
|
||||
|
||||
i.fa-check {
|
||||
color: @sidebar-icon-color-2;
|
||||
}
|
||||
|
||||
&.done > a {
|
||||
color: @sidebar-link-completed-2;
|
||||
}
|
||||
|
||||
a, span {
|
||||
color: @sidebar-link-color-2;
|
||||
background: @sidebar-link-background-2;
|
||||
}
|
||||
|
||||
&.active > a, a:hover {
|
||||
color: @sidebar-link-hover-color-2;
|
||||
background: @sidebar-link-hover-background-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
@import "variables.less";
|
||||
@import "fonts.less";
|
||||
|
||||
@import "highlight.less";
|
||||
|
||||
@import "book/highlight.less";
|
||||
@import "book/languages.less";
|
||||
@import "book/header.less";
|
||||
@import "book/summary.less";
|
||||
@import "book/font-settings.less";
|
||||
@import "book/body.less";
|
||||
@import "book/exercise.less";
|
||||
@import "book/quiz.less";
|
||||
@@ -25,10 +25,6 @@
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
.box-shadow(none);
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/* http://jmblog.github.io/color-themes-for-highlightjs */
|
||||
|
||||
/* Tomorrow Comment */
|
||||
.hljs-comment,
|
||||
.hljs-title {
|
||||
color: #8e908c;
|
||||
}
|
||||
|
||||
/* Tomorrow Red */
|
||||
.hljs-variable,
|
||||
.hljs-attribute,
|
||||
.hljs-tag,
|
||||
.hljs-regexp,
|
||||
.ruby .hljs-constant,
|
||||
.xml .hljs-tag .hljs-title,
|
||||
.xml .hljs-pi,
|
||||
.xml .hljs-doctype,
|
||||
.html .hljs-doctype,
|
||||
.css .hljs-id,
|
||||
.css .hljs-class,
|
||||
.css .hljs-pseudo {
|
||||
color: #c82829;
|
||||
}
|
||||
|
||||
/* Tomorrow Orange */
|
||||
.hljs-number,
|
||||
.hljs-preprocessor,
|
||||
.hljs-pragma,
|
||||
.hljs-built_in,
|
||||
.hljs-literal,
|
||||
.hljs-params,
|
||||
.hljs-constant {
|
||||
color: #f5871f;
|
||||
}
|
||||
|
||||
/* Tomorrow Yellow */
|
||||
.ruby .hljs-class .hljs-title,
|
||||
.css .hljs-rules .hljs-attribute {
|
||||
color: #eab700;
|
||||
}
|
||||
|
||||
/* Tomorrow Green */
|
||||
.hljs-string,
|
||||
.hljs-value,
|
||||
.hljs-inheritance,
|
||||
.hljs-header,
|
||||
.ruby .hljs-symbol,
|
||||
.xml .hljs-cdata {
|
||||
color: #718c00;
|
||||
}
|
||||
|
||||
/* Tomorrow Aqua */
|
||||
.css .hljs-hexcolor {
|
||||
color: #3e999f;
|
||||
}
|
||||
|
||||
/* Tomorrow Blue */
|
||||
.hljs-function,
|
||||
.python .hljs-decorator,
|
||||
.python .hljs-title,
|
||||
.ruby .hljs-function .hljs-title,
|
||||
.ruby .hljs-title .hljs-keyword,
|
||||
.perl .hljs-sub,
|
||||
.javascript .hljs-title,
|
||||
.coffeescript .hljs-title {
|
||||
color: #4271ae;
|
||||
}
|
||||
|
||||
/* Tomorrow Purple */
|
||||
.hljs-keyword,
|
||||
.javascript .hljs-function {
|
||||
color: #8959a8;
|
||||
}
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
background: white;
|
||||
color: #4d4d4c;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.coffeescript .javascript,
|
||||
.javascript .xml,
|
||||
.tex .hljs-formula,
|
||||
.xml .javascript,
|
||||
.xml .vbscript,
|
||||
.xml .css,
|
||||
.xml .hljs-cdata {
|
||||
opacity: 0.5;
|
||||
}
|
||||
@@ -1,8 +1,47 @@
|
||||
h1, h2 {
|
||||
@import "vendors/bootstrap/bootstrap.less";
|
||||
@import "vendors/fontawesome/font-awesome.less";
|
||||
|
||||
@import "mixins.less";
|
||||
@import "variables.less";
|
||||
@import "fonts.less";
|
||||
|
||||
@import "page/highlight.less";
|
||||
|
||||
|
||||
@font-size-base: 13px;
|
||||
|
||||
|
||||
* {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
-webkit-text-size-adjust: none;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
text-rendering: optimizeLegibility;
|
||||
font-smoothing: antialiased;
|
||||
font-family: @font-family-base;
|
||||
}
|
||||
|
||||
|
||||
h1, h2, h3 {
|
||||
page-break-after: avoid;
|
||||
page-break-before: auto;
|
||||
}
|
||||
|
||||
h1 { font-size: floor(@font-size-base * 2.15); }
|
||||
h2 { font-size: floor(@font-size-base * 1.70); }
|
||||
h3 { font-size: ceil(@font-size-base * 1.25); }
|
||||
h4 { font-size: ceil(@font-size-base * 1); }
|
||||
h5 { font-size: ceil(@font-size-base * 0.85); }
|
||||
h6 { font-size: ceil(@font-size-base * 0.65); }
|
||||
|
||||
pre, blockquote {
|
||||
border: 1px solid #999;
|
||||
page-break-inside: avoid;
|
||||
@@ -26,41 +65,36 @@ section {
|
||||
}
|
||||
|
||||
&#summary {
|
||||
text-align: center;
|
||||
margin: 1.5cm;
|
||||
|
||||
ul {
|
||||
font-size: 0.5cm;
|
||||
line-height: 1.8em;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
list-style: none;
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
> ul {
|
||||
> li {
|
||||
margin-bottom: 1cm;
|
||||
ol {
|
||||
list-style: none;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
margin-left: 1cm;
|
||||
}
|
||||
|
||||
> a {
|
||||
font-size: 0.6cm;
|
||||
}
|
||||
> ol {
|
||||
> li {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
article {
|
||||
margin: 1.5cm;
|
||||
|
||||
/* Big centered title */
|
||||
margin-bottom: 1.5cm;
|
||||
|
||||
&.new-chapter {
|
||||
page-break-after: always;
|
||||
font-size: 0.6cm;
|
||||
text-align: center;
|
||||
padding: 3cm 0cm;
|
||||
|
||||
border-top: 1px solid #ccc;
|
||||
|
||||
@media print {
|
||||
border: none;
|
||||
h1 {
|
||||
font-size: floor(@font-size-base * 2.7);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
@sidebar-link-completed-weight: normal;
|
||||
|
||||
// Page
|
||||
@page-color: black;
|
||||
@page-background: white;
|
||||
|
||||
// Progress Bar
|
||||
@@ -79,5 +80,94 @@
|
||||
@font-family-sans: "Open Sans", "Clear Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
@font-family-base: @font-family-serif;
|
||||
|
||||
|
||||
@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;
|
||||
|
||||
/*
|
||||
,--------.,--.
|
||||
'--. .--'| ,---. ,---. ,--,--,--. ,---. ,---. .--.
|
||||
| | | .-. || .-. :| || .-. :( .-' '--'
|
||||
| | | | | |\ --.| | | |\ --..-' `).--.
|
||||
`--' `--' `--' `----'`--`--`--' `----'`----' '--'
|
||||
*/
|
||||
// Header
|
||||
@header-color-1: #AFA790;
|
||||
@header-color-2: #7e888b;
|
||||
@header-background-1: #ECE3C4;
|
||||
@header-background-2: #1d1f21;
|
||||
@header-button-color-1: #AFA790;
|
||||
@header-button-color-2: #7e888b;
|
||||
@header-button-hover-color-1: #73553C;
|
||||
@header-button-hover-color-2: #C9C9C9;
|
||||
@header-button-hover-background-1: #E2DABE;
|
||||
@header-button-hover-background-2: #0B0D0E;
|
||||
|
||||
// Body
|
||||
@body-background-1: #F3EACB;
|
||||
@body-background-2: #1d1f21;
|
||||
@body-pagination-background-1: #FFFAEA;
|
||||
@body-pagination-background-2: #33404D;
|
||||
|
||||
// Sidebar
|
||||
@sidebar-color-1: #AFA790;
|
||||
@sidebar-color-2: hsl(207, 15%, 80%);
|
||||
@sidebar-background-1: #F3EACB;
|
||||
@sidebar-background-2: #111111;
|
||||
|
||||
@sidebar-search-background-1: transparent;
|
||||
@sidebar-search-background-2: transparent;
|
||||
@sidebar-search-input-border-color-1: #D6CFBA;
|
||||
@sidebar-search-input-border-color-2: #c4cdd4;
|
||||
|
||||
@sidebar-divider-color-1: hsl(207, 15%, 85%);
|
||||
@sidebar-divider-color-2: #1d1f21;
|
||||
|
||||
@sidebar-link-color-1: #877F6A;
|
||||
@sidebar-link-color-2: hsl(207, 15%, 50%);
|
||||
@sidebar-link-background-1: transparent;
|
||||
@sidebar-link-background-2: transparent;
|
||||
@sidebar-link-hover-color-1: #704214;
|
||||
@sidebar-link-hover-color-2: hsl(207, 100%, 50%);
|
||||
@sidebar-link-hover-background-1: transparent;
|
||||
@sidebar-link-hover-background-2: transparent;
|
||||
|
||||
@sidebar-icon-color-1: @bar-progress-background;
|
||||
@sidebar-icon-color-2: @bar-progress-background;
|
||||
@sidebar-link-completed-1: hsl(207, 15%, 25%);
|
||||
@sidebar-link-completed-2: hsl(207, 15%, 25%);
|
||||
@sidebar-link-completed-weight-1: normal;
|
||||
@sidebar-link-completed-weight-2: normal;
|
||||
|
||||
// Page
|
||||
@page-color-1: #704214;
|
||||
@page-color-2: #a4b1b1;
|
||||
@page-background-1: #F3EACB;
|
||||
@page-background-2: #1d1f21;
|
||||
|
||||
@bar-background-1: #F3EACB;
|
||||
@bar-background-2: #1d1f21;
|
||||
@bar-progress-background-1: #704214;
|
||||
@bar-progress-background-2: hsl(120, 60%, 50%);
|
||||
|
||||
// Basics of a navbar
|
||||
@navbar-default-border-1: #d5d5d5;
|
||||
@navbar-default-border-2: #d5d5d5;
|
||||
|
||||
// Navbar brand label
|
||||
@navbar-default-color-1: #333;
|
||||
@navbar-default-color-2: #333;
|
||||
@navbar-default-link-color-1: #333;
|
||||
@navbar-default-link-color-2: #333;
|
||||
@navbar-default-brand-color-1: @navbar-default-link-color;
|
||||
@navbar-default-brand-color-2: @navbar-default-link-color;
|
||||
@navbar-default-brand-hover-color-1: @navbar-default-link-color;
|
||||
@navbar-default-brand-hover-color-2: @navbar-default-link-color;
|
||||
@navbar-default-brand-hover-bg-1: transparent;
|
||||
@navbar-default-brand-hover-bg-2: transparent;
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<div class="dropdown-menu font-settings">
|
||||
<div class="dropdown-caret">
|
||||
<span class="caret-outer"></span>
|
||||
<span class="caret-inner"></span>
|
||||
</div>
|
||||
|
||||
<div class="btn-group btn-block">
|
||||
<button id="reduce-font-size" class="btn btn-default">A</button>
|
||||
<button id="enlarge-font-size" class="btn btn-default">A</button>
|
||||
</div>
|
||||
|
||||
<ul class="list-group font-family-list">
|
||||
<li class="list-group-item" data-font="0">Serif</li>
|
||||
<li class="list-group-item" data-font="1">Sans</li>
|
||||
</ul>
|
||||
|
||||
<div class="btn-group btn-group-xs btn-block color-theme-list">
|
||||
<button type="button" class="btn btn-default" id="color-theme-preview-0" data-theme="0">White</button>
|
||||
<button type="button" class="btn btn-default" id="color-theme-preview-1" data-theme="1">Sepia</button>
|
||||
<button type="button" class="btn btn-default" id="color-theme-preview-2" data-theme="2">Night</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -5,6 +5,11 @@
|
||||
{% endif %}
|
||||
<a href="#" class="btn pull-left toggle-summary"><i class="fa fa-align-justify"></i></a>
|
||||
<a href="#" class="btn pull-left toggle-search"><i class="fa fa-search"></i></a>
|
||||
<span id="font-settings-wrapper">
|
||||
<a href="#" class="btn pull-left toggle-font-settings"><i class="fa fa-font"></i>
|
||||
</a>
|
||||
{% include "./font-settings.html" %}
|
||||
</span>
|
||||
|
||||
<!-- Actions Right -->
|
||||
<a href="#" target="_blank" class="btn pull-right" data-sharing="google-plus"><i class="fa fa-google-plus"></i></a>
|
||||
|
||||
@@ -35,10 +35,10 @@
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
{% block content %}{% endblock %}
|
||||
{% block style %}
|
||||
<link rel="stylesheet" href="{{ staticBase }}/style.css">
|
||||
{% endblock %}
|
||||
{% block content %}{% endblock %}
|
||||
{% block javascript %}
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/ace.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/mode-javascript.js"></script>
|
||||
|
||||
+49
-56
@@ -3,10 +3,32 @@
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block style %}
|
||||
{% parent %}
|
||||
<link rel="stylesheet" href="{{ staticBase }}/print.css">
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% macro articleContent(content) %}
|
||||
{% for section in content %}
|
||||
{% if section.type == "normal" %}
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
{% elif section.type == "exercise" %}
|
||||
<div class="exercise">
|
||||
<div class="exercise-header">Exercise #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
<pre><code>{% autoescape false %}{{ section.code.base|code }}{% endautoescape %}</code></pre>
|
||||
{% set exercise = exercise + 1 %}
|
||||
</div>
|
||||
{% elif section.type == "quiz" %}
|
||||
<div class="quiz">
|
||||
<div class="exercise-header">Exercise #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
{% autoescape false %}{{ section.quiz.base }}{% endautoescape %}
|
||||
{% set exercise = exercise + 1 %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
|
||||
{% set exercise = 1 %}
|
||||
{% block content %}
|
||||
{# Cover #}
|
||||
@@ -19,87 +41,58 @@
|
||||
|
||||
{# Summary #}
|
||||
<section id="summary">
|
||||
<h1>Summary</h1>
|
||||
|
||||
<ul class="summary">
|
||||
<h1>Table of Contents</h1>
|
||||
<ol>
|
||||
<li>
|
||||
<a href="#README.md">Introduction</a>
|
||||
<a href="#README.md">
|
||||
<h2>Introduction</h2>
|
||||
</a>
|
||||
</li>
|
||||
{% for item in summary.chapters %}
|
||||
<li>
|
||||
<a href="#{{ item.path }}">{{ item.level }}) {{ item.title }}</a>
|
||||
<h2><span>{{ item.level }}.</span> <a href="#{{ item.path }}">{{ item.title }}</a></h2>
|
||||
{% if item.articles.length > 0 %}
|
||||
<ul>
|
||||
<ol>
|
||||
{% for article in item.articles %}
|
||||
<li>
|
||||
<a href="#{{ article.path }}">{{ article.level }}) {{ article.title }}</a>
|
||||
<span>{{ article.level }}</span> <a href="#{{ article.path }}">{{ article.title }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</ol>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
{# Pages content #}
|
||||
<section>
|
||||
<article id="README.md">
|
||||
{% for section in pages["README.md"].content %}
|
||||
{% if section.type == "normal" %}
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
{% elif section.type == "exercise" %}
|
||||
<div class="exercise">
|
||||
<div class="exercise-header">Exercise #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
<pre><code>{% autoescape false %}{{ section.code.base|code }}{% endautoescape %}</code></pre>
|
||||
{% set exercise = exercise + 1 %}
|
||||
</div>
|
||||
{% elif section.type == "quiz" %}
|
||||
<div class="quiz">
|
||||
<div class="exercise-header">Exercise #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
{% autoescape false %}{{ section.quiz.base }}{% endautoescape %}
|
||||
{% set exercise = exercise + 1 %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{{ articleContent(pages["README.md"].content) }}
|
||||
</article>
|
||||
</section>
|
||||
{% for item in summary.chapters %}
|
||||
{% if item.articles.length > 0 %}
|
||||
<section>
|
||||
<article id="{{ item.path }}" class="new-chapter">
|
||||
<h1>{{ item.title }}</h1>
|
||||
</article>
|
||||
|
||||
{% for article in item.articles %}
|
||||
{% if pages[article.path] %}
|
||||
<article id="{{ article.path }}">
|
||||
{% for section in pages[article.path].content %}
|
||||
{% if section.type == "normal" %}
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
{% elif section.type == "exercise" %}
|
||||
<div class="exercise">
|
||||
<div class="exercise-header">Exercise #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
<pre><code>{% autoescape false %}{{ section.code.base|code }}{% endautoescape %}</code></pre>
|
||||
{% set exercise = exercise + 1 %}
|
||||
</div>
|
||||
{% elif section.type == "quiz" %}
|
||||
<div class="quiz">
|
||||
<div class="exercise-header">Exercise #{{ exercise }}</div>
|
||||
{% autoescape false %}{{ section.content }}{% endautoescape %}
|
||||
{% autoescape false %}{{ section.quiz.base }}{% endautoescape %}
|
||||
{% set exercise = exercise + 1 %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</article>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if pages[item.path] %}
|
||||
<article>
|
||||
{{ articleContent(pages[item.path].content) }}
|
||||
</article>
|
||||
{% endif %}
|
||||
|
||||
{% if item.articles.length > 0 %}
|
||||
{% for article in item.articles %}
|
||||
{% if pages[article.path] %}
|
||||
<article id="{{ article.path }}">
|
||||
{{ articleContent(pages[article.path].content) }}
|
||||
</article>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
|
||||
@@ -35,3 +35,31 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block javascript %}
|
||||
{% parent %}
|
||||
{% for resource in plugins.resources.js %}
|
||||
{% if resource.url %}
|
||||
<script src="{{ resource.url }}"></script>
|
||||
{% else %}
|
||||
<script src="{{ staticBase }}/plugins/{{ resource.path }}"></script>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<script>
|
||||
require(["gitbook"], function(gitbook) {
|
||||
var config = {% autoescape false %}{{ pluginsConfig }}{% endautoescape %};
|
||||
gitbook.start(config);
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block style %}
|
||||
{% parent %}
|
||||
{% for resource in plugins.resources.css %}
|
||||
{% if resource.url %}
|
||||
<link rel="stylesheet" href="{{ resource.url }}">
|
||||
{% else %}
|
||||
<link rel="stylesheet" href="{{ staticBase }}/plugins/{{ resource.path }}">
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user