Compare commits

...

129 Commits

Author SHA1 Message Date
Aaron O'Mullan 799815d85a Bump version to 0.4.2 2014-05-01 16:17:05 -07:00
Samy Pessé 2946246834 Fix #191: remove log for changes in files 2014-05-01 18:55:45 +02:00
Samy Pessé 6d276d1e79 Add option to remove home link 2014-05-01 18:53:49 +02:00
Samy Pessé 6a3a369b3e Add options to remove sharing links 2014-05-01 18:37:30 +02:00
Samy Pessé 5d8afa2135 Merge pull request #196 from mrpotes/new-plugins
New plugins
2014-05-01 18:33:30 +02:00
Samy Pessé ab3e6aa8ce Fix font-size for titles in markdown content 2014-05-01 18:29:42 +02:00
James Phillpotts 822092736e New plugins 2014-05-01 17:00:38 +01:00
Samy Pessé 7497acd2e3 Center navigation button on mobile on start page 2014-05-01 17:37:38 +02:00
Samy Pessé ae137885b8 Fix book content padding on mobile 2014-05-01 17:27:03 +02:00
Samy Pessé b964cc7009 Fix #162: hide watch/star buttons on mobile 2014-05-01 17:16:09 +02:00
Samy Pessé c2ea126c5f Fix #182: Replace all book body when page changes 2014-05-01 14:05:14 +02:00
Samy Pessé b668dba479 Update gaze version to 0.5.1 2014-05-01 13:26:50 +02:00
Samy Pessé 2cae5a0310 Secure .book position 2014-05-01 13:26:19 +02:00
Samy Pessé b6a0f3ff67 Fix #164: use position absolute instead of fixed 2014-04-30 17:01:25 +02:00
Samy Pessé fa00b52fb5 Fix #127: add configuration for changing links in sidebar 2014-04-30 16:46:24 +02:00
Samy Pessé 2999d90907 Merge pull request #188 from snipe/feature/json-doc
Added quotes for JSON example, per #127
2014-04-30 12:48:28 +02:00
snipe b6696bcad2 Added quotes for JSON example, per #127 2014-04-30 06:46:28 -04:00
Samy Pessé 728c87c525 Merge pull request #187 from snipe/feature/typo-fixes
Fixed typos
2014-04-30 12:05:12 +02:00
snipe ace394ee2d Fixed typos 2014-04-30 05:28:18 -04:00
Samy Pessé 076ee98f64 Fix #186: Plugins that don't have assets should be skipped 2014-04-30 11:15:01 +02:00
Samy Pessé 2e472654ca Fix error when calling hooks in site generation 2014-04-30 10:54:45 +02:00
Aaron O'Mullan d46bd14678 0.4.1 2014-04-29 10:23:54 -07:00
Samy Pessé b3e5b3d106 Fix #183: normalize hooks and transformation of page (content, progress, ..° 2014-04-29 17:45:54 +02:00
Samy Pessé b830ef5dca Merge pull request #179 from erixtekila/master
Update README.md
2014-04-29 08:23:33 +02:00
Eric Priou 45326c4ad2 Update README.md 2014-04-29 03:45:03 +02:00
Aaron O'Mullan 603b61e8c8 Bump version to 0.4.0 2014-04-28 13:26:40 -07:00
Samy Pessé efb066c8e3 Update doc about how to configure gitbook 2014-04-28 20:45:10 +02:00
Samy Pessé c30db427b1 Remove some options from command line (moved to book.json) 2014-04-28 20:33:25 +02:00
Samy Pessé 23056fc85d Use a global configuration file for a book (default to book.json) 2014-04-28 20:29:36 +02:00
Samy Pessé 5f1bfc0280 Fix #163: Move logic of git infos extraction to lib 2014-04-28 20:16:30 +02:00
Samy Pessé b715901f92 Add hooks "page:before" and "page:after" 2014-04-28 19:56:40 +02:00
Samy Pessé 38fea06cf9 Use hook to allow plugins to edit page content during generation (Fixes #176) 2014-04-28 17:21:06 +02:00
Aaron O'Mullan 4481b5f556 Bump version to 0.3.2 2014-04-27 13:46:20 -07:00
Samy Pessé 0e0d051f7c Merge pull request #173 from erixtekila/master
Update README.md
2014-04-27 22:26:56 +02:00
Eric Priou a1b649a279 Update README.md 2014-04-27 22:24:37 +02:00
Eric Priou e6bc0a0fb7 Update README.md
3rd party plugins done in the weekend
2014-04-27 22:18:09 +02:00
Samy Pessé 93e4016c09 Add link to disqus plugin 2014-04-27 21:55:50 +02:00
Samy Pessé 20c741a248 Fix stupid typo thanks to @erixtekila 2014-04-27 21:21:48 +02:00
Samy Pessé 54f530c7c3 Merge pull request #171 from GitbookIO/feature/offlineCache
Improve pages loading
2014-04-27 18:05:13 +02:00
Samy Pessé 9fdc5da22d Update page when new version of application cache 2014-04-27 17:57:34 +02:00
Samy Pessé 99ce48b146 Add base loading indicator for navigation
#161
2014-04-27 17:36:12 +02:00
Samy Pessé 6a33ad9ef7 Add base cache manifest 2014-04-27 17:14:57 +02:00
Samy Pessé ca94055adf Adapt tests for plugins 2014-04-27 15:45:16 +02:00
Samy Pessé 36a67e4c91 Fix #169: fix resolve of external plugins 2014-04-27 15:41:02 +02:00
Samy Pessé 69431e1bbb Fix #165: replace separator from ":" to "," 2014-04-27 09:14:39 +02:00
Samy Pessé a3fc4ee2b0 Fix display of code element in sepia/night 2014-04-25 18:14:41 +02:00
Samy Pessé e505fd6d54 Improve summary divider for theme sepia and night 2014-04-25 18:12:39 +02:00
Samy Pessé 93a3785e4c Improve highlight theme for sepia and night themes 2014-04-25 18:05:09 +02:00
Samy Pessé a02de84812 Change highlight theme to tomorrow 2014-04-25 17:56:48 +02:00
Samy Pessé c0521f0ec7 Improve display of pre in markdown content 2014-04-25 17:48:31 +02:00
Samy Pessé 83d8c16853 Add file watcher in "gitbook serve" command (Fix #47, #89, #140) 2014-04-25 16:23:13 +02:00
Samy Pessé 47e9506397 Fix #153 and Improve pushState navigation 2014-04-25 15:12:19 +02:00
Samy Pessé b8a3d0e254 Merge pull request #144 from attomos/master
Make quiz easier to click by surround it with label.
2014-04-25 14:29:04 +02:00
Samy Pessé 001d7ec44e Merge pull request #155 from pborreli/patch-1
Fixed typo
2014-04-24 22:44:36 +02:00
Pascal Borreli 5cb0ec447a Fixed typo 2014-04-24 21:43:32 +01:00
Aaron O'Mullan 67e7d77302 Bump version to 0.3.1 2014-04-24 10:45:55 -07:00
Aaron O'Mullan 8cbd8b2b48 Reorder plugin names
Improves #149 and improves GitBookIO/editor#12
2014-04-24 01:38:42 -07:00
Aaron O'Mullan 7c404dbf96 Fix plugin tests 2014-04-24 01:34:35 -07:00
Aaron O'Mullan c4a67b6f2f Require plugin names to contain gitbook-*
Fixes #149, fixes GitBookIO/editor#12
2014-04-24 01:28:04 -07:00
Samy Pessé 3b70347625 Fix #146: complete license 2014-04-24 09:00:15 +02:00
Aaron O'Mullan bb0a7935e9 Merge pull request #141 from frodsan/niceties
Niceties
2014-04-23 11:34:08 -07:00
Nattaphoom Ch 8f4b799d3c Make quiz easier to click by surround it with label. 2014-04-24 01:19:13 +07:00
Francesco Rodriguez 931e0c66c9 Update color themes for highlight.js link. 2014-04-23 12:30:05 -03:00
Francesco Rodriguez 0212084862 Fix typo. 2014-04-23 12:28:44 -03:00
Samy Pessé 9abbabbce3 Merge pull request #138 from Hermanya/patch-1
Add @Hermanya as a contributor
2014-04-22 23:47:13 +02:00
Hermanya ac610487e8 Add @Hermanya as a contributor 2014-04-22 17:45:41 -04:00
Aaron O'Mullan a9a0f89892 Bump version to 0.3.0 2014-04-22 14:05:52 -07:00
Samy Pessé 305f221077 Fix #136: replace links to .md by links to .html
Add more tests for links in page
2014-04-22 17:45:12 +02:00
Samy Pessé 53514f2602 Improve sepia theme sidebar 2014-04-22 14:27:48 +02:00
Samy Pessé 00327778cb Improve font-settings dropdown design 2014-04-22 14:12:53 +02:00
Samy Pessé 37fadec787 Merge branch 'master' of https://github.com/Hermanya/gitbook into Hermanya-master 2014-04-22 14:02:19 +02:00
Samy Pessé ada11d0b59 Add section in readme about plugins 2014-04-22 12:51:10 +02:00
Aaron O'Mullan 4c90fdb7b2 Merge pull request #123 from GitbookIO/feature/plugins
Fix #65: Plugins Architecture
2014-04-21 14:26:09 -07:00
Samy Pessé 98c133df3c Change plugins separator to "," 2014-04-21 23:23:53 +02:00
Samy Pessé f86c27b090 Add option --pluginsConfig 2014-04-21 22:58:51 +02:00
Samy Pessé b5b8ed72a5 Update default addons 2014-04-21 22:39:23 +02:00
Samy Pessé 5beff86548 Change js interface for plugins and add configuration 2014-04-21 22:20:41 +02:00
Samy Pessé dd395edae1 Use mathjax plugin version 0.0.2 2014-04-21 16:30:02 +02:00
Aaron O'Mullan d1c1d7b75b Bump version to 0.2.4 2014-04-20 16:41:09 -07:00
Samy Pessé 4a380e9279 Fix #36: add mathjax plugin as default 2014-04-20 15:20:21 +02:00
Samy Pessé 878c105def Use complete assets folder for plugins 2014-04-20 02:04:15 +02:00
Samy Pessé 74d628676b Add option to change templates from plugin 2014-04-20 00:10:46 +02:00
Samy Pessé 6fdd98ec3a Trigger event on document when ready 2014-04-19 20:39:58 +02:00
Samy Pessé 2ed1be3533 Add global js interface for plugins and remove mixpanel 2014-04-19 20:10:03 +02:00
Samy Pessé 0ac04e1584 Add call for hooks "init" and "finish" 2014-04-19 19:50:32 +02:00
Herman Starikov 2a38d33d8a Font settings, closes #102 2014-04-19 13:46:14 -04:00
Samy Pessé fb14caf5b3 Add resources to site build 2014-04-19 19:31:02 +02:00
Samy Pessé cf51772f54 Add command line option "plugins" 2014-04-19 18:00:24 +02:00
Samy Pessé cc8516df83 Load plugins during generation 2014-04-19 17:57:40 +02:00
Samy Pessé c6a624bc6b Add method to load a list of plugins from generator options 2014-04-19 17:54:15 +02:00
Samy Pessé 8be8b825ee Add base loading of default addons
Add mixpanel as default addon
2014-04-19 17:28:44 +02:00
Samy Pessé 56eb4a2897 Add base plugin object and test validation of plugin 2014-04-19 15:41:29 +02:00
Samy Pessé 7a461eaa61 Remove theme readme
Add semver dependency
2014-04-19 14:40:46 +02:00
Samy Pessé fd64c05809 Fix #118: fix single page generation when file from summary doesn't exists 2014-04-18 23:42:02 +02:00
Aaron O'Mullan 6bce93d893 Bump version to 0.2.3 2014-04-18 11:53:18 -07:00
Aaron O'Mullan c9db068227 Refactor navigation generation to be functional
This makes it a lot less error prone and far more robust. As well as a
lot easier to understand
2014-04-18 11:49:55 -07:00
Samy Pessé 8a6d36ac4d Improve stability of mixpanel tracking 2014-04-18 20:42:18 +02:00
Samy Pessé 64622c5945 Load css before content 2014-04-18 20:19:45 +02:00
Samy Pessé 837813edb0 Add warning about theme 2014-04-18 20:16:21 +02:00
Aaron O'Mullan 0d9d62f1a9 Merge pull request #117 from GitbookIO/fix/99
Fix #99
2014-04-18 10:17:20 -07:00
Samy Pessé f7e9abf984 Fix quiz interractivity 2014-04-18 19:10:13 +02:00
Samy Pessé dd0579b60d Update tests for relative images 2014-04-18 17:23:33 +02:00
Samy Pessé b5bb953e47 Fix image path calcul 2014-04-18 17:15:03 +02:00
Samy Pessé ef4a8ea1b6 Fix #116: update url before updating html content 2014-04-18 15:49:53 +02:00
Samy Pessé 49cde96ee7 Add contributors to package.json and change homepage 2014-04-18 15:00:40 +02:00
Samy Pessé 627be20e48 Add editor and twitter links in the readme 2014-04-18 14:48:03 +02:00
Samy Pessé e45bb84b02 Fix navigation on chrome for iOS: disable pushState 2014-04-18 13:09:16 +02:00
Aaron O'Mullan 4e8876c725 Update README.md 2014-04-17 16:39:06 -07:00
Aaron O'Mullan 6d5be15ec9 Bump version to 0.2.2 2014-04-17 15:31:57 -07:00
Samy Pessé 1a8db646f2 Improve styling for pdf and ebook 2014-04-17 12:15:28 +02:00
Samy Pessé d3aba2c906 Fix state update after navigation 2014-04-17 11:48:04 +02:00
Samy Pessé 457a83282f Improve style for pdf and ebook 2014-04-17 11:27:21 +02:00
Samy Pessé 48e94c937d Remove useless caracter from page format 2014-04-17 11:17:40 +02:00
Samy Pessé 6e613e9f71 Fix #107: Render all the page in single page format 2014-04-17 11:16:18 +02:00
Aaron O'Mullan b698d43d7d Bump version to 0.2.1 2014-04-17 01:10:11 -07:00
Aaron O'Mullan a36b9a6088 Normalize code language before highlighting 2014-04-17 01:09:26 -07:00
Samy Pessé 20acd89c8a Merge pull request #104 from GitbookIO/fix/auto_highlighting
Disable automatic highlighting
2014-04-17 09:24:56 +02:00
Aaron O'Mullan 7bed9a257d Merge pull request #105 from ptagell/master
Removed duplicate line of readme
2014-04-16 14:38:49 -07:00
Paul Tagell 19c83aa5a8 Removed duplicate line of readme 2014-04-17 07:35:28 +10:00
Aaron O'Mullan 52610d7d61 Disable automatic highlighting
Fallback to no highlighting rather that potentially funky highlighting
2014-04-16 13:15:35 -07:00
Aaron O'Mullan 9e4b393b7a In parse/page remove global marked settings
Highlighter settings were global, moved them to the render function
(where they should have been)
2014-04-16 13:07:35 -07:00
Samy Pessé a0ae34a3b7 Fix #100: fix section test isQuiz 2014-04-16 17:16:18 +02:00
Samy Pessé 413dc5d004 Add more test for quiz (related to #100) 2014-04-16 17:15:45 +02:00
Aaron O'Mullan c9eb27bfe7 Do not pass SUMMARY.md to generator, fixes #93
We want to parse SUMMARY.md but not generate a file from it, thus it
should be removed from the list of files to be processed by the
generator
2014-04-16 00:18:35 -07:00
Aaron O'Mullan b78505c290 Ignore .gitignore file by default in fs.list
Partial fix of #93
2014-04-16 00:11:54 -07:00
Samy Pessé 95f9ae83d0 Fix #97: keep scroll position during page changement 2014-04-15 17:42:46 +02:00
Samy Pessé 9304289315 Fix progress display when page change 2014-04-14 23:00:40 +02:00
Samy Pessé 1b726ca293 Update preview 2014-04-14 22:35:10 +02:00
Samy Pessé 656ca18929 Improve languages list design 2014-04-14 21:29:26 +02:00
68 changed files with 2348 additions and 488 deletions
+1
View File
@@ -15,3 +15,4 @@ Contributors
------------
- Nijiko Yonskai
- Herman Starikov
+1 -5
View File
@@ -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'
}
+2 -2
View File
@@ -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.
+69 -7
View File
@@ -3,7 +3,7 @@ GitBook
[![Build Status](https://travis-ci.org/GitbookIO/gitbook.png?branch=master)](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.
![Image](https://raw.github.com/GitbookIO/gitbook/master/preview.png)
@@ -30,16 +30,59 @@ $ gitbook build ./repository --output=./outputFolder
Options for commands `build` and `serve` are:
```
-t, --title <name> Name of the book to generate, defaults to repo name
-i, --intro <intro> Description of the book to generate
-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
--config <config file> Configuration file to use, defaults to book.json
```
GitBook load the default configuration from a `book.json` file in the repository if it exists.
Here are the options that can be stored in this file:
```
{
// Folders to use for output (caution: it override the value from the command line)
"output": null,
// Generator to use for building (caution: it override the value from the command line)
"generator": "site",
// Book title and description (defaults are extracted from the README)
"title": null,
"description": null,
// GitHub informations (defaults are extracted using git)
"github": null,
"githubHost": "https://github.com/",
// Plugins list, can contain "-name" for removing default plugins
"plugins": [],
// Global configuration for plugins
"pluginsConfig": {},
// Links in template (null: default, false: remove, string: new value)
"links": {
// Link to home in the top-left corner
"home": null,
// Links in top of sidebar
"about": null,
"issues": null,
"edit": null,
// Sharing links
"sharing": {
"google": null,
"facebook": null,
"twitter": null
}
}
}
```
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 +175,22 @@ 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's functionality. Read [GitbookIO/plugin](https://github.com/GitbookIO/plugin) for more information 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
* [Disqus](https://github.com/GitbookIO/plugin-disqus): Disqus comments integration in your book
* [Transform annoted quotes to notes](https://github.com/erixtekila/gitbook-plugin-richquotes): Allow extra markdown markup to render blockquotes as nice notes
* [Send code to console](https://github.com/erixtekila/gitbook-plugin-toconsole): Evaluate javascript blockin the browser inspector's console
* [Revealable sections](https://github.com/mrpotes/gitbook-plugin-reveal): Reveal sections of the page using buttons made from the first title in each section
* [Markdown within HTML](https://github.com/mrpotes/gitbook-plugin-nestedmd): Process markdown within HTML blocks - allows custom layout options for individual pages
* [Bootstrap JavaScript plugins](https://github.com/mrpotes/gitbook-plugin-bootstrapjs): Use the [Bootstrap JavaScript plugins](http://getbootstrap.com/javascript) in your online GitBook
+21 -27
View File
@@ -1,50 +1,44 @@
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('--config <config file>', 'Configuration file to use, defualt to book.json')
};
var makeBuildFunc = function(converter) {
return function(dir, options) {
dir = dir || process.cwd();
outputDir = options.output
outputDir = options.output;
console.log('Starting build ...');
// Get repo's URL
return utils.gitURL(dir)
.then(function(url) {
// Get ID of repo
return utils.githubID(url);
}, function(err) {
return null;
})
.then(function(repoID) {
return converter(
_.extend(options || {}, {
input: dir,
output: outputDir,
title: options.title,
description: options.intro,
github: options.github || repoID,
githubHost: options.githubHost,
generator: options.format,
theme: options.theme
})
);
})
return converter(
_.extend({}, options || {}, {
input: dir,
output: outputDir,
generator: options.format,
configFile: options.config
})
)
.then(function(output) {
console.log("Successfuly built !");
return output;
}, utils.logError);
};
}
};
module.exports = {
folder: makeBuildFunc(generate.folder),
file: makeBuildFunc(generate.file)
file: makeBuildFunc(generate.file),
command: buildCommand
};
+36 -26
View File
@@ -12,45 +12,55 @@ var fs = require('../lib/generate/fs');
var utils = require('./utils');
var build = require('./build');
var Server = require('./server');
// General options
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)
.option('--no-watch', 'Disable restart with file watching')
.action(function(dir, options) {
build.folder(dir, options || {})
.then(function(_options) {
console.log();
console.log('Starting server ...');
return utils.serveDir(_options.output, options.port)
var server = new Server();
var generate = function() {
if (server.isRunning()) console.log("Stopping server");
server.stop()
.then(function() {
return build.folder(dir, options);
})
.then(function(_options) {
console.log();
console.log('Starting server ...');
return server.start(_options.output, options.port)
.then(function() {
console.log('Serving book on http://localhost:'+options.port);
if (!options.watch) return;
return utils.watch(_options.input)
.then(function(filepath) {
console.log("Restart after change in files");
console.log('');
return generate();
})
})
})
.fail(utils.logError);
})
.then(function() {
console.log('Serving book on http://localhost:'+options.port);
console.log();
console.log('Press CTRL+C to quit ...');
});
};
console.log('Press CTRL+C to quit ...');
console.log('')
generate();
});
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 +70,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) {
+96
View File
@@ -0,0 +1,96 @@
var Q = require('q');
var _ = require('lodash');
var events = require('events');
var http = require('http');
var send = require('send');
var util = require('util');
var url = require('url');
var Server = function() {
this.running = null;
this.dir = null;
this.port = 0;
this.sockets = [];
};
util.inherits(Server, events.EventEmitter);
// Return true if the server is running
Server.prototype.isRunning = function() {
return this.running != null;
};
// Stop the server
Server.prototype.stop = function() {
var that = this;
if (!this.isRunning()) return Q();
var d = Q.defer();
this.running.close(function(err) {
that.running = null;
that.emit("state", false);
if (err) d.reject(err);
else d.resolve();
});
for (var i = 0; i < this.sockets.length; i++) {
this.sockets[i].destroy();
}
return d.promise;
};
Server.prototype.start = function(dir, port) {
var that = this, pre = Q();
port = port || 8004;
if (that.isRunning()) pre = this.stop();
return pre
.then(function() {
var d = Q.defer();
that.running = http.createServer(function(req, res){
// Render error
function error(err) {
res.statusCode = err.status || 500;
res.end(err.message);
}
// Redirect to directory's index.html
function redirect() {
res.statusCode = 301;
res.setHeader('Location', req.url + '/');
res.end('Redirecting to ' + req.url + '/');
}
// Send file
send(req, url.parse(req.url).pathname)
.root(dir)
.on('error', error)
.on('directory', redirect)
.pipe(res);
});
that.running.on('connection', function (socket) {
that.sockets.push(socket);
socket.setTimeout(4000);
socket.on('close', function () {
that.sockets.splice(that.sockets.indexOf(socket), 1);
});
});
that.running.listen(port, function(err) {
if (err) return d.reject(err);
that.port = port;
that.dir = dir;
that.emit("state", true);
d.resolve();
});
return d.promise;
});
}
module.exports = Server;
+15 -74
View File
@@ -4,83 +4,27 @@ var _ = require('lodash');
var http = require('http');
var send = require('send');
var url = require('url');
var cp = require('child_process');
var path = require('path');
var Gaze = require('gaze').Gaze;
// Get the remote of a given repo
function gitURL(path) {
function watch(dir) {
var d = Q.defer();
dir = path.resolve(dir);
cp.exec("git config --get remote.origin.url", {
cwd: path,
env: process.env,
}, function(err, stdout, stderr) {
if(err) {
return d.reject(err);
}
return d.resolve(stdout);
var gaze = new Gaze("**/*.md", {
cwd: dir
});
return d.promise
.then(function(output) {
return output.replace(/(\r\n|\n|\r)/gm, "");
gaze.once("all", function(e, filepath) {
gaze.close();
d.resolve(filepath);
});
}
gaze.once("error", function(err) {
gaze.close();
// Poorman's parsing
// Parse a git URL to a github ID : username/reponame
function githubID(_url) {
// Remove .git if it's in _url
var sliceEnd = _url.slice(-4) === '.git' ? -4 : _url.length;
// Detect HTTPS repos
var parsed = url.parse(_url);
if(parsed.protocol === 'https:' && parsed.host === 'github.com') {
return parsed.path.slice(1, sliceEnd);
}
// Detect SSH repos
if(_url.indexOf('git@') === 0) {
return _url.split(':', 2)[1].slice(0, sliceEnd);
}
// None found
return null;
}
function titleCase(str)
{
return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
}
function serveDir(dir, port) {
var d = Q.defer();
var server = http.createServer(function(req, res){
// Render error
function error(err) {
res.statusCode = err.status || 500;
res.end(err.message);
}
// Redirect to directory's index.html
function redirect() {
res.statusCode = 301;
res.setHeader('Location', req.url + '/');
res.end('Redirecting to ' + req.url + '/');
}
// Send file
send(req, url.parse(req.url).pathname)
.root(dir)
.on('error', error)
.on('directory', redirect)
.pipe(res);
}).listen(port);
d.resolve(server);
d.reject(err);
});
return d.promise;
}
@@ -93,9 +37,6 @@ function logError(err) {
// Exports
module.exports = {
gitURL: gitURL,
githubID: githubID,
titleCase: titleCase,
serveDir: serveDir,
watch: watch,
logError: logError
};
+3 -1
View File
@@ -16,7 +16,9 @@ var getFiles = function(path) {
// Add extra rules to ignore common folders
ig.addIgnoreRules([
'.git/'
'.git/',
'.gitignore',
'.DS_Store'
], '__custom_stuff');
// Push each file to our list
+21
View File
@@ -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, data) {
return this.plugins.hook(name, this, data);
};
BaseGenerator.prototype.loadPlugins = function() {
var that = this;
return Plugin.fromList(this.options.plugins, this.options.input)
.then(function(_plugins) {
that.plugins = _plugins;
return that.callHook("init");
});
};
BaseGenerator.prototype.convertFile = function(content, input) {
+69 -13
View File
@@ -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"),
@@ -27,22 +28,34 @@ var generate = function(options) {
options = _.defaults(options || {}, {
// Folders to use
input: null,
output: null,
"input": null,
"output": null,
// Config file (relative to input)
"configFile": "book.json",
// Output generator
generator: "site",
"generator": "site",
// Book title, keyword, description
title: null,
description: null,
"title": null,
"description": null,
// Origin github repository id
github: null,
githubHost: 'https://github.com/',
"github": null,
"githubHost": 'https://github.com/',
// Theming
theme: path.resolve(__dirname, '../../theme')
"theme": path.resolve(__dirname, '../../theme'),
// Plugins
"plugins": [],
"pluginsConfig": {},
// Links
"links": {
"about": null
}
});
if (!options.input) {
@@ -58,6 +71,35 @@ var generate = function(options) {
// Clean output folder
return fs.remove(options.output)
// Read config file
.then(function() {
return fs.readFile(path.resolve(options.input, options.configFile))
.then(function(_config) {
// Extend current config
_config = JSON.parse(_config);
_config = _.omit(_config, 'input', 'configFile');
_.extend(options, _config);
}, function() {
// No config file: not a big deal
return Q();
})
})
// Get repo's URL
.then(function() {
return parse.git.url(options.input)
.then(function(_url) {
// Get ID of repo
return parse.git.githubID(_url);
}, function(err) {
return null;
})
.then(function(repoId) {
options.github = options.github || repoId;
});
})
.then(function() {
return fs.mkdirp(options.output);
})
@@ -70,9 +112,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 +172,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(
@@ -151,9 +203,12 @@ var generate = function(options) {
);
})
// Finish gneration
// Finish generation
.then(function() {
return generator.finish();
})
.then(function() {
return generator.callHook("finish");
});
}
})
@@ -177,7 +232,7 @@ var generateFile = function(options) {
return Q.nfcall(tmp.dir)
.then(function(tmpDir) {
return generate(
_.extend({},
_.extend({},
options,
{
output: tmpDir
@@ -195,7 +250,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 +280,6 @@ var generateFile = function(options) {
module.exports = {
generators: generators,
folder: generate,
file: generateFile
file: generateFile,
Plugin: Plugin
};
+72
View File
@@ -0,0 +1,72 @@
var _ = require('lodash');
var path = require('path');
var Q = require('q');
var fs = require("./fs");
var extsToIgnore = [".gz"]
var Manifest = function() {
this.revision = 0;
this.clear(Date.now());
};
// Regenerate manifest
Manifest.prototype.clear = function(revision) {
if (revision) this.revision = revision;
this.sections = {
'CACHE': {},
'NETWORK': {},
'FALLBACK': {}
};
return Q(this);
};
// Add a resource
Manifest.prototype.add = function(category, resource, value) {
if (_.isArray(resource)) {
_.each(resource, function(subres) {
this.add(category, subres, value);
}, this);
return;
}
this.sections[category][resource] = value;
};
// Add a directory in cache
Manifest.prototype.addFolder = function(folder, root, except) {
var that = this;
root = root || "/";
return fs.list(folder)
.then(function(files) {
_.each(
// Ignore diretcories
_.filter(files, function(file) {
return file.substr(-1) != "/" && !_.contains(except, path.join(root, file)) && !_.contains(extsToIgnore, path.extname(file));
}),
function(file) {
that.add("CACHE", path.join(root, file));
}
);
})
};
// Get manifest content
Manifest.prototype.dump = function() {
var lines = [
"CACHE MANIFEST",
"# Revision "+this.revision
];
_.each(this.sections, function(content, section) {
if (_.size(content) == 0) return;
lines.push("");
lines.push(section+":");
lines = lines.concat(_.keys(content));
}, this);
return Q(lines.join("\n"));
};
module.exports = Manifest;
+8 -3
View File
@@ -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) {
+192
View File
@@ -0,0 +1,192 @@
var _ = require("lodash");
var Q = require("q");
var semver = require("semver");
var path = require("path");
var url = require("url");
var fs = require("./fs");
var resolve = require('resolve');
var pkg = require("../../package.json");
var RESOURCES = ["js", "css"];
var Plugin = function(name, root) {
this.name = name;
this.root = root;
this.packageInfos = {};
this.infos = {};
_.each([
"gitbook-plugin-"+name,
"gitbook-theme-"+name,
"gitbook-"+name,
name,
], function(_name) {
if (this.load(_name, __dirname)) return false;
if (this.load(_name, path.resolve(root))) return false;
}, this);
};
// Load from a name
Plugin.prototype.load = function(name, baseDir) {
try {
var res = resolve.sync(name+"/package.json", { basedir: baseDir });
this.baseDir = path.dirname(res);
this.packageInfos = require(res);
this.infos = require(resolve.sync(name, { basedir: baseDir }));
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(this.baseDir, filename);
};
// Resolve file path
Plugin.prototype.callHook = function(name, context, data) {
var hookFunc = this.infos.hooks? this.infos.hooks[name] : null;
data = data || {};
if (!hookFunc) return Q(data);
return Q()
.then(function() {
return hookFunc.apply(context, [data]);
});
};
// Has assets
Plugin.prototype.hasAssets = function(out) {
return (this.infos.book && this.infos.book.assets);
};
// Copy plugin assets fodler
Plugin.prototype.copyAssets = function(out) {
if (!this.hasAssets()) return Q();
return fs.copy(
this.resolveFile(this.infos.book.assets),
out
);
};
// 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, root) {
var failed = [];
// Load plugins
var plugins = _.map(names, function(name) {
var plugin = new Plugin(name, root);
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, context, data) {
return _.reduce(plugins, function(prev, plugin) {
return prev.then(function(ret) {
return plugin.callHook(name, context, ret);
})
}, Q(data));
},
'template': function(name) {
var withTpl = _.find(plugins, function(plugin) {
return (plugin.infos.templates
&& plugin.infos.templates[name]);
});
if (!withTpl) return null;
return withTpl.resolveFile(withTpl.infos.templates[name]);
}
});
};
// Default plugins
Plugin.defaults = [
"mixpanel",
"mathjax"
];
module.exports = Plugin;
+102 -13
View File
@@ -9,6 +9,7 @@ var parse = require("../../parse");
var BaseGenerator = require("../generator");
var indexer = require('./search_indexer');
var Manifest = require('../manifest');
// Swig filter for returning the count of lines in a code section
swig.setFilter('lines', function(content) {
@@ -30,17 +31,39 @@ 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'));
this.manifest = new Manifest(Date.now());
this.manifest.add("NETWORK", [
'*'
]);
};
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) {
Generator.prototype._writeTemplate = function(tpl, options, output, interpolate) {
var that = this;
interpolate = interpolate || _.identity;
return Q()
.then(function(sections) {
return tpl(_.extend({
@@ -54,9 +77,15 @@ 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: that.options
}, options));
})
.then(interpolate)
.then(function(html) {
return fs.writeFile(
output,
@@ -73,7 +102,6 @@ Generator.prototype.indexPage = function(lexed, pagePath) {
// Convert a markdown file to html
Generator.prototype.convertFile = function(content, _input) {
var that = this;
var progress = parse.progress(this.options.navigation, _input);
_output = _input.replace(".md", ".html");
if (_output == "README.html") _output = "index.html";
@@ -82,10 +110,28 @@ Generator.prototype.convertFile = function(content, _input) {
var output = path.join(this.options.output, _output);
var basePath = path.relative(path.dirname(output), this.options.output) || ".";
var page = {
path: _input,
content: content,
progress: parse.progress(this.options.navigation, _input)
};
var _callHook = function(name) {
return that.callHook(name, page)
.then(function(_page) {
page = _page;
return page;
});
};
return Q()
.then(function() {
// Send content to plugins
return _callHook("page:before");
})
.then(function() {
// Lex page
return parse.lex(content);
return parse.lex(page.content);
})
.then(function(lexed) {
// Index page in search
@@ -101,15 +147,27 @@ Generator.prototype.convertFile = function(content, _input) {
});
})
.then(function(sections) {
page.sections = sections;
// Use plugin hook
return _callHook("page");
})
.then(function() {
that.manifest.add("CACHE", _output);
return that._writeTemplate(that.template, {
progress: progress,
progress: page.progress,
_input: _input,
content: sections,
content: page.sections,
basePath: basePath,
staticBase: path.join(basePath, "gitbook"),
}, output);
}, output, function(html) {
page.content = html;
return _callHook("page:after").get("content")
});
});
};
@@ -134,10 +192,31 @@ 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")
);
)
// Add to cach manifest
.then(function() {
return that.manifest.addFolder(path.join(that.options.output, "gitbook"), "gitbook");
})
// Copy plugins assets
.then(function() {
return Q.all(
_.map(that.plugins.list, function(plugin) {
if (!plugin.hasAssets()) return Q();
var pluginAssets = path.join(that.options.output, "gitbook/plugins/", plugin.name);
return plugin.copyAssets(pluginAssets)
.then(function() {
return that.manifest.addFolder(pluginAssets, "gitbook/plugins/"+plugin.name);
});
})
);
})
};
// Dump search index to disk
@@ -148,9 +227,19 @@ Generator.prototype.writeSearchIndex = function() {
);
};
// Add cache manifest
Generator.prototype.writeCacheManifest = function() {
return fs.writeFile(
path.join(this.options.output, 'manifest.appcache'),
this.manifest.dump()
);
};
Generator.prototype.finish = function() {
return this.copyAssets()
.then(this.writeSearchIndex);
.then(this.writeSearchIndex)
.then(this.writeCacheManifest);
};
module.exports = Generator;
+56
View File
@@ -0,0 +1,56 @@
var Q = require('q');
var _ = require('lodash');
var cp = require('child_process');
var url = require('url');
// Get the remote of a given repo
function gitURL(path) {
var d = Q.defer();
cp.exec("git config --get remote.origin.url", {
cwd: path,
env: process.env,
}, function(err, stdout, stderr) {
if(err) {
return d.reject(err);
}
return d.resolve(stdout);
});
return d.promise
.then(function(output) {
return output.replace(/(\r\n|\n|\r)/gm, "");
});
}
// Poorman's parsing
// Parse a git URL to a github ID : username/reponame
function githubID(_url) {
// Remove .git if it's in _url
var sliceEnd = _url.slice(-4) === '.git' ? -4 : _url.length;
// Detect HTTPS repos
var parsed = url.parse(_url);
if(parsed.protocol === 'https:' && parsed.host === 'github.com') {
return parsed.path.slice(1, sliceEnd);
}
// Detect SSH repos
if(_url.indexOf('git@') === 0) {
return _url.split(':', 2)[1].slice(0, sliceEnd);
}
// None found
return null;
}
function titleCase(str) {
return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
}
module.exports = {
url: gitURL,
githubID: githubID,
titleCase: titleCase
};
+2 -1
View File
@@ -5,5 +5,6 @@ module.exports = {
lex: require('./lex'),
progress: require('./progress'),
navigation: require('./navigation'),
readme: require('./readme')
readme: require('./readme'),
git: require('./git')
};
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+34 -10
View File
@@ -1,5 +1,7 @@
var url = require('url');
var inherits = require('util').inherits;
var links = require('../utils').links;
var path = require('path');
@@ -16,6 +18,7 @@ function GitBookRenderer(options, extra_options) {
this._extra_options = extra_options;
this.quizRowId = 0;
this.id = rendererId++;
this.quizIndex = 0;
}
inherits(GitBookRenderer, marked.Renderer);
@@ -38,23 +41,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 +96,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);
@@ -97,10 +117,14 @@ GitBookRenderer.prototype._createCheckboxAndRadios = function(text) {
if (!match) {
return text;
}
var field = "<input name='quiz-row-" + this.id + "-" + this.quizRowId + "' type='";
var quizIdentifier = 'quiz-row-' + this.id + '-' + this.quizRowId + '-' + this.quizIndex++;
var field = "<input name='" + quizIdentifier + "' id='" + quizIdentifier + "' type='";
field += match[1] === '(' ? "radio" : "checkbox";
field += match[2] === 'x' ? "' checked/>" : "'/>";
return text.replace(fieldRegex, field);
var splittedText = text.split(fieldRegex);
var length = splittedText.length;
var label = '<label class="quiz-label" for="' + quizIdentifier + '">' + splittedText[length - 1] + '</label>';
return text.replace(fieldRegex, field).replace(splittedText[length - 1], label);
}
GitBookRenderer.prototype.tablecell = function(content, flags) {
+1
View File
@@ -1,3 +1,4 @@
module.exports = {
lang: require('./lang'),
links: require('./links')
};
+29
View File
@@ -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
};
+22 -4
View File
@@ -1,6 +1,7 @@
{
"name": "gitbook",
"version": "0.2.0",
"version": "0.4.2",
"homepage": "http://www.gitbook.io/",
"description": "Library and cmd utility to generate GitBooks",
"main": "lib/index.js",
"dependencies": {
@@ -15,7 +16,14 @@
"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",
"gaze": "~0.5.1",
"resolve": "0.6.3",
"gitbook-plugin": "0.0.2",
"gitbook-plugin-mixpanel": "0.0.2",
"gitbook-plugin-mathjax": "0.0.3"
},
"devDependencies": {
"mocha": "1.18.2",
@@ -39,9 +47,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
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 164 KiB

+12
View File
@@ -0,0 +1,12 @@
# Images Test
This is a test for images path calculation. It supposed this fiel is in a syntax/ folder
### #
[![Screen](./preview.png)](./preview.png)
### 2
[![Screen](../preview2.png)](./preview2.png)
+5
View File
@@ -0,0 +1,5 @@
# Nice course
Check out this other chapter [Test](test.md)
Check out this other chapter [Test](../before.md)
+15
View File
@@ -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
View File
@@ -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);
});
});
+49
View File
@@ -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", __dirname);
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, __dirname)
.then(function(_r) {
ret = _r;
}, function(err) {
ret = null;
})
.fin(done);
});
it('should load defaults addons', function() {
assert(ret != null);
});
});
-5
View File
@@ -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
View File
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
+7
View File
@@ -0,0 +1,7 @@
define([
"jQuery"
], function($) {
var events = $({});
return events;
});
+3 -3
View File
@@ -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);
+115
View File
@@ -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
}
});
+18
View File
@@ -0,0 +1,18 @@
define([
"jQuery"
], function($) {
var showLoading = function(p) {
var $book = $(".book");
$book.addClass("is-loading");
p.always(function() {
$book.removeClass("is-loading");
});
return p;
};
return {
show: showLoading
};
});
+32 -22
View File
@@ -1,34 +1,42 @@
define([
"jQuery",
"utils/analytic",
"utils/path",
"core/events",
"core/state",
"core/search",
"core/progress",
"core/exercise",
"core/quiz"
], function($, analytic, state, search, progress, exercises, quiz) {
"core/quiz",
"core/loading"
], function($, path, events, state, search, progress, exercises, quiz, loading) {
var prev, next;
var githubCountStars, githubCountWatch;
var updateHistory = function(url, title) {
history.pushState({ path: url }, title, url);
};
var usePushState = (typeof history.pushState !== "undefined");
var handleNavigation = function(url, push) {
if (typeof history.pushState === "undefined") {
var handleNavigation = function(relativeUrl, push) {
var url = path.isAbsolute(relativeUrl) ? relativeUrl : path.join(path.dirname(window.location.pathname), relativeUrl);
console.log("navigate to ", url, "baseurl="+relativeUrl);
if (!usePushState) {
// Refresh the page to the new URL if pushState not supported
location.href = url;
location.href = relativeUrl;
return
}
return $.get(url)
return loading.show($.get(url)
.done(function (html) {
// Push url to history
if (push) history.pushState({ path: url }, null, url);
// Replace html content
html = html.replace( /<(\/?)(html|head|body)([^>]*)>/ig, function(a,b,c,d){
return '<' + b + 'div' + ( b ? '' : ' data-element="' + c + '"' ) + d + '>';
});
var $page = $(html);
var $pageHead = $page.find("[data-element=head]");
var $pageBody = $page.find('.book');
// Merge heads
var headContent = $pageHead.html()
@@ -38,17 +46,20 @@ define([
});
$("head").html(headContent);
// Update header, body and summary
$('.book-header').html($page.find('.book-header').html());
$('.book-body').html($page.find('.book-body').html());
$('.book-summary').html($page.find('.book-summary').html());
// Merge body
var scrollPosition = $('.book-summary .summary').scrollTop();
$pageBody.toggleClass("with-summary", $(".book").hasClass("with-summary"))
if (push) updateHistory(url, null);
$(".book").replaceWith($pageBody);
$('.book-summary .summary').scrollTop(scrollPosition);
// Update state
state.update($("html"));
preparePage();
})
.fail(function () {
location.href = url;
});
.fail(function (e) {
location.href = relativeUrl;
}));
};
var updateGitHubCounts = function() {
@@ -101,7 +112,7 @@ define([
}
// Send to mixpanel
analytic.track("page.view");
events.trigger("page.change");
};
var handlePagination = function (e) {
@@ -122,7 +133,7 @@ define([
if (url) handleNavigation(url, true);
};
var init = function() {
// Prevent cache so that using the back button works
@@ -139,14 +150,13 @@ define([
if (event.state === null) {
return;
}
return handleNavigation(event.state.path, false);
};
$(document).on('click', ".navigation-prev", handlePagination);
$(document).on('click', ".navigation-next", handlePagination);
$(document).on('click', ".summary [data-path] a", handlePagination);
$(window).resize(updateNavigationPosition);
// Prepare current page
+8 -5
View File
@@ -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) {
+12 -7
View File
@@ -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,19 @@
require([
define([
"jQuery",
"utils/storage",
"utils/analytic",
"utils/sharing",
"utils/appcache",
"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, appCache, sharing, events, fontSettings, state, keyboard, navigation, progress, sidebar, search){
var start = function(config) {
var $book;
$book = state.$book;
@@ -29,10 +31,23 @@ require([
// Init keyboard
keyboard.init();
// Init appcache
appCache.init();
// Bind sharing button
sharing.init();
// Init navigation
navigation.init();
});
//Init font settings
fontSettings.init();
events.trigger("start", config);
}
return {
start: start,
events: events
};
});
-33
View File
@@ -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
};
});
+15
View File
@@ -0,0 +1,15 @@
define([], function() {
var isAvailable = (typeof applicationCache !== "undefined");
var init = function() {
if (!isAvailable) return;
window.applicationCache.addEventListener('updateready', function() {
window.location.reload();
}, false);
};
return {
init: init
};
});
+47
View File
@@ -0,0 +1,47 @@
define([], function() {
// Joins path segments. Preserves initial "/" and resolves ".." and "."
// Does not support using ".." to go above/outside the root.
// This means that join("foo", "../../bar") will not resolve to "../bar"
function join(/* path segments */) {
// Split the inputs into a list of path commands.
var parts = [];
for (var i = 0, l = arguments.length; i < l; i++) {
parts = parts.concat(arguments[i].split("/"));
}
// Interpret the path commands to get the new resolved path.
var newParts = [];
for (i = 0, l = parts.length; i < l; i++) {
var part = parts[i];
// Remove leading and trailing slashes
// Also remove "." segments
if (!part || part === ".") continue;
// Interpret ".." to pop the last segment
if (part === "..") newParts.pop();
// Push new path segments.
else newParts.push(part);
}
// Preserve the initial slash if there was one.
if (parts[0] === "") newParts.unshift("");
// Turn back into a single string path.
return newParts.join("/") || (newParts.length ? "/" : ".");
}
// A simple function to get the dirname of a path
// Trailing slashes are ignored. Leading slash is preserved.
function dirname(path) {
return join(path, "..");
}
// test if a path or url is absolute
function isAbsolute(path) {
if (!path) return false;
return (path[0] == "/" || path.indexOf("http://") == 0 || path.indexOf("https://") == 0);
}
return {
dirname: dirname,
join: join,
isAbsolute: isAbsolute
};
})
-2
View File
@@ -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||[]);
+54 -4
View File
@@ -1,11 +1,16 @@
.book {
position: relative;
width: 100%;
height: 100%;
.book-body {
position: fixed;
position: absolute;
top: @header-height;
right: 0px;
left: 0px;
bottom: 0px;
color: @page-color;
background: @body-background;
.transition(left 0.5s ease);
@@ -23,7 +28,6 @@
outline: none;
.page-inner {
font-family: @font-family-serif;
max-width: 800px;
margin: 0px auto;
@@ -33,8 +37,6 @@
background: @page-background;
border-radius: 2px;
font-size: 16px;
line-height: 1.5em;
}
@@ -57,6 +59,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 +96,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;
}
}
}
}
}
}
+159
View File
@@ -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;
}
}
}
}
}
}
+43 -2
View File
@@ -2,8 +2,8 @@
.book-header {
font-family: @font-family-sans;
position: fixed;
overflow: hidden;
position: absolute;
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;
@@ -51,9 +52,23 @@
@media (max-width: 800px) {
display: none;
}
i {
display: none;
}
}
}
&.is-loading {
.book-header h1 {
i {
display: inline-block;
}
a {
display: none;
}
}
}
&.with-summary {
.book-header h1 {
margin-left: 250px;
@@ -64,4 +79,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;
}
}
}
}
}
@@ -0,0 +1,92 @@
/* Tomorrow Night Bright Theme */
/* Original theme - https://github.com/chriskempson/tomorrow-theme */
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
/* Tomorrow Comment */
.hljs-comment,
.hljs-title {
color: #969896;
}
/* 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: #d54e53;
}
/* Tomorrow Orange */
.hljs-number,
.hljs-preprocessor,
.hljs-pragma,
.hljs-built_in,
.hljs-literal,
.hljs-params,
.hljs-constant {
color: #e78c45;
}
/* Tomorrow Yellow */
.ruby .hljs-class .hljs-title,
.css .hljs-rules .hljs-attribute {
color: #e7c547;
}
/* Tomorrow Green */
.hljs-string,
.hljs-value,
.hljs-inheritance,
.hljs-header,
.ruby .hljs-symbol,
.xml .hljs-cdata {
color: #b9ca4a;
}
/* Tomorrow Aqua */
.css .hljs-hexcolor {
color: #70c0b1;
}
/* 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: #7aa6da;
}
/* Tomorrow Purple */
.hljs-keyword,
.javascript .hljs-function {
color: #c397d8;
}
.hljs {
display: block;
background: black;
color: #eaeaea;
padding: 0.5em;
}
.coffeescript .javascript,
.javascript .xml,
.tex .hljs-formula,
.xml .javascript,
.xml .vbscript,
.xml .css,
.xml .hljs-cdata {
opacity: 0.5;
}
+107
View File
@@ -0,0 +1,107 @@
/*
Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull <sourdrums@gmail.com>
*/
.hljs {
display: block;
padding: 0.5em;
background: #fdf6e3;
color: #657b83;
}
.hljs-comment,
.hljs-template_comment,
.diff .hljs-header,
.hljs-doctype,
.hljs-pi,
.lisp .hljs-string,
.hljs-javadoc {
color: #93a1a1;
}
/* Solarized Green */
.hljs-keyword,
.hljs-winutils,
.method,
.hljs-addition,
.css .hljs-tag,
.hljs-request,
.hljs-status,
.nginx .hljs-title {
color: #859900;
}
/* Solarized Cyan */
.hljs-number,
.hljs-command,
.hljs-string,
.hljs-tag .hljs-value,
.hljs-rules .hljs-value,
.hljs-phpdoc,
.tex .hljs-formula,
.hljs-regexp,
.hljs-hexcolor,
.hljs-link_url {
color: #2aa198;
}
/* Solarized Blue */
.hljs-title,
.hljs-localvars,
.hljs-chunk,
.hljs-decorator,
.hljs-built_in,
.hljs-identifier,
.vhdl .hljs-literal,
.hljs-id,
.css .hljs-function {
color: #268bd2;
}
/* Solarized Yellow */
.hljs-attribute,
.hljs-variable,
.lisp .hljs-body,
.smalltalk .hljs-number,
.hljs-constant,
.hljs-class .hljs-title,
.hljs-parent,
.haskell .hljs-type,
.hljs-link_reference {
color: #b58900;
}
/* Solarized Orange */
.hljs-preprocessor,
.hljs-preprocessor .hljs-keyword,
.hljs-pragma,
.hljs-shebang,
.hljs-symbol,
.hljs-symbol .hljs-string,
.diff .hljs-change,
.hljs-special,
.hljs-attr_selector,
.hljs-subst,
.hljs-cdata,
.clojure .hljs-title,
.css .hljs-pseudo,
.hljs-header {
color: #cb4b16;
}
/* Solarized Red */
.hljs-deletion,
.hljs-important {
color: #dc322f;
}
/* Solarized Violet */
.hljs-link_label {
color: #6c71c4;
}
.tex .hljs-formula {
background: #eee8d5;
}
@@ -0,0 +1,90 @@
/* http://jmblog.github.com/color-themes-for-google-code-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;
}
-2
View File
@@ -37,8 +37,6 @@
padding: 10px 5px;
font-size: 16px;
border-bottom: 1px solid #eee;
a {
}
+35 -29
View File
@@ -1,8 +1,29 @@
.book .book-body .page-wrapper .page-inner section.normal {
padding: 25px;
padding-top: 15px;
background-color: white;
.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal {
color:@page-color-1;
pre, code {
background: #fdf6e3;
color: #657b83;
border-color: darken(#fdf6e3, 15%);
@import "./highlight/sepia.less";
}
}
.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal {
color:@page-color-2;
pre, code {
background: black;
color: #eaeaea;
border-color: #000;
@import "./highlight/night.less";
}
}
.book .book-body .page-wrapper .page-inner section.normal {
padding-bottom: 25px;
padding-top: 15px;
color:@page-color;
& > *:first-child {
margin-top: 0 !important; }
@@ -51,26 +72,25 @@
font-size: inherit; }
h1 {
font-size: 28px;
color: black; }
font-size: 2.5em;
}
h2 {
font-size: 24px;
font-size: 2em;
border-bottom: 1px solid #eee;
color: black; }
}
h3 {
font-size: 18px; }
font-size: 1.5em; }
h4 {
font-size: 16px; }
font-size: 1.2em; }
h5 {
font-size: 14px; }
font-size: 1em; }
h6 {
color: #777777;
font-size: 14px; }
font-size: 1em; }
p, blockquote, ul, ol, dl, table, pre {
margin: 15px 0; }
@@ -272,29 +292,15 @@
white-space: pre;
border: none;
background: transparent;
}
.highlight pre {
color: hsl(204, 40%, 80%);
background-color: hsl(204, 30%, 10%);
border: 1px solid hsl(204, 30%, 10%);
font-size: 16px;
line-height: 1.5em;
overflow: auto;
padding: 20px;
margin: 0 -20px;
border-radius: 3px;
@import "./highlight/white.less";
}
pre {
color: hsl(204, 40%, 80%);
background-color: hsl(204, 30%, 10%);
border: 1px solid hsl(204, 30%, 10%);
font-size: 16px;
font-size: inherit;
line-height: 1.5em;
overflow: auto;
padding: 20px;
margin: 0 -20px;
border-radius: 3px;
}
+15
View File
@@ -40,6 +40,21 @@
width: 50%;
display: inline-block;
float: left;
&.navigation-unique {
max-width: 100%;
width: 100%;
}
}
}
}
.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;
}
}
+33 -2
View File
@@ -16,8 +16,7 @@
width: 100%;
position: relative;
background: #fff;
margin-bottom: 20px;
z-index: 10;
margin-bottom: 10px;
.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;
}
}
}
+6 -2
View File
@@ -29,8 +29,12 @@
.question-inner {
padding: 15px;
.quiz-label {
font-weight: normal;
cursor: pointer;
}
}
table {
margin-bottom: 10px;
width: 100%;
@@ -46,4 +50,4 @@
}
}
}
}
}
+91 -1
View File
@@ -2,7 +2,7 @@
.book-summary {
font-family: @font-family-sans;
position: fixed;
position: absolute;
top: @header-height;
left: -@sidebar-width;
bottom: 0px;
@@ -141,3 +141,93 @@
}
}
}
/*
* 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;
box-shadow: none;
}
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;
box-shadow: none;
}
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;
}
}
}
}
}
+1 -6
View File
@@ -5,11 +5,10 @@
@import "variables.less";
@import "fonts.less";
@import "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 +24,6 @@
-webkit-font-smoothing: antialiased;
}
.form-control {
.box-shadow(none);
}
html, body {
height: 100%;
}
+12 -12
View File
@@ -1,8 +1,9 @@
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
/* http://jmblog.github.io/color-themes-for-highlightjs */
/* Tomorrow Comment */
.hljs-comment {
color: hsl(207, 35%, 35%);
.hljs-comment,
.hljs-title {
color: #8e908c;
}
/* Tomorrow Red */
@@ -27,9 +28,9 @@
.hljs-pragma,
.hljs-built_in,
.hljs-literal,
.hljs-constant,
.hljs-function .hljs-title {
color: hsl(50, 100%, 60%);
.hljs-params,
.hljs-constant {
color: #f5871f;
}
/* Tomorrow Yellow */
@@ -45,7 +46,7 @@
.hljs-header,
.ruby .hljs-symbol,
.xml .hljs-cdata {
color: hsl(0, 100%, 70%);
color: #718c00;
}
/* Tomorrow Aqua */
@@ -54,7 +55,7 @@
}
/* Tomorrow Blue */
.hljs-function .keyword,
.hljs-function,
.python .hljs-decorator,
.python .hljs-title,
.ruby .hljs-function .hljs-title,
@@ -62,13 +63,13 @@
.perl .hljs-sub,
.javascript .hljs-title,
.coffeescript .hljs-title {
color: hsl(207, 70%, 60%);
color: #4271ae;
}
/* Tomorrow Purple */
.hljs-keyword,
.javascript .hljs-function {
color: hsl(207, 95%, 70%);
color: #8959a8;
}
.hljs {
@@ -76,7 +77,6 @@
background: white;
color: #4d4d4c;
padding: 0.5em;
font-family: "Anonymous Pro", "Inconsolata", "Monaco", monospace;
}
.coffeescript .javascript,
@@ -87,4 +87,4 @@
.xml .css,
.xml .hljs-cdata {
opacity: 0.5;
}
}
+55 -21
View File
@@ -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);
}
}
+90
View File
@@ -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>
+19 -5
View File
@@ -1,21 +1,35 @@
<div class="book-header">
<!-- Actions Left -->
{% if githubId %}
<a href="{{ githubHost }}{{ githubId }}" target="_blank" class="btn pull-left"><i class="fa fa-github-alt"></i></a>
{% if options.links.home !== false and (options.links.home != null or githubId) %}
<a href="{{ options.links.home|default(githubHost+githubId) }}" target="_blank" class="btn pull-left"><i class="fa fa-bookmark-o"></i></a>
{% 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 -->
{% if options.links.sharing.google !== false %}
<a href="#" target="_blank" class="btn pull-right" data-sharing="google-plus"><i class="fa fa-google-plus"></i></a>
{% endif %}
{% if options.links.sharing.facebook !== false %}
<a href="#" target="_blank" class="btn pull-right" data-sharing="facebook"><i class="fa fa-facebook"></i></a>
{% endif %}
{% if options.links.sharing.twitter !== false %}
<a href="#" target="_blank" class="btn pull-right" data-sharing="twitter"><i class="fa fa-twitter"></i></a>
{% endif %}
{% if githubId %}
<a href="{{ githubHost }}{{ githubId }}/stargazers" target="_blank" class="btn pull-right count-star"><i class="fa fa-star-o"></i> Star (<span>-</span>)</a>
<a href="{{ githubHost }}{{ githubId }}/watchers" target="_blank" class="btn pull-right count-watch"><i class="fa fa-eye"></i> Watch (<span>-</span>)</a>
<a href="{{ githubHost }}{{ githubId }}/stargazers" target="_blank" class="btn pull-right count-star hidden-xs"><i class="fa fa-star-o"></i> Star (<span>-</span>)</a>
<a href="{{ githubHost }}{{ githubId }}/watchers" target="_blank" class="btn pull-right count-watch hidden-xs"><i class="fa fa-eye"></i> Watch (<span>-</span>)</a>
{% endif %}
<!-- Title -->
<h1><a href="{{ basePath }}/" >{{ title }}</a></h1>
<h1>
<i class="fa fa-spinner fa-spin"></i>
<a href="{{ basePath }}/" >{{ title }}</a>
</h1>
</div>
+21 -4
View File
@@ -3,18 +3,28 @@
<input type="text" placeholder="Search" class="form-control" />
</div>
<ul class="summary">
{% if githubId %}
{% if options.links.about !== false && (options.links.about != null || githubId) %}
<li>
<a href="{{ githubHost }}{{ githubAuthor }}" target="blank">About the author</a>
<a href="{{ options.links.about|default(githubHost+githubAuthor) }}" target="blank">About the author</a>
</li>
{% endif %}
{% if options.links.issues !== false && (options.links.issues != null || githubId) %}
<li>
<a href="{{ githubHost }}{{ githubId }}/issues" target="blank">Questions and Issues</a>
<a href="{{ options.links.issues|default(githubHost+githubId+"/issues") }}" target="blank">Questions and Issues</a>
</li>
{% endif %}
{% if options.links.contribute !== false && (options.links.contribute != null || githubId) %}
<li>
<a href="{{ githubHost }}{{ githubId }}/edit/master/{{ _input }}" target="blank">Edit and Contribute</a>
<a href="{{ options.links.contribute|default(githubHost+githubId+"/edit/master/"+_input) }}" target="blank">Edit and Contribute</a>
</li>
{% endif %}
{% if options.links.contribute || options.links.issues || options.links.about || githubId %}
<li class="divider"></li>
{% endif %}
<li data-level="0" data-path="index.html">
<a href="{{ basePath }}/"><i class="fa fa-check"></i> Introduction</a>
</li>
@@ -44,5 +54,12 @@
{% endif %}
</li>
{% endfor %}
{% if options.links.gitbook !== false %}
<li class="divider"></li>
<li>
<a href="http://www.gitbook.io/" target="blank">Generated using GitBook</a>
</li>
{% endif %}
</ul>
</div>
+2 -2
View File
@@ -1,5 +1,5 @@
<!DOCTYPE HTML>
<html lang="en-US">
<html lang="en-US" {% block htmlTag %}{% endblock %}>
<head prefix="og: http://ogp.me/ns# book: http://ogp.me/ns/book#">
{% block head %}
<meta charset="UTF-8">
@@ -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
View File
@@ -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 %}
+31 -2
View File
@@ -1,5 +1,6 @@
{% extends "layout.html" %}
{% block htmlTag %}manifest="{{ basePath }}/manifest.appcache"{% endblock %}
{% block title %}{{ progress.current.title }}{% parent %}{% endblock %}
{% block content %}
<div class="book" {% if githubId %}data-github="{{ githubId }}"{% endif %} data-level="{{ progress.current.level }}" data-basepath="{{ basePath }}" data-revision="{{ revision }}">
@@ -27,11 +28,39 @@
</div>
{% if progress.current.prev and progress.current.prev.path %}
<a href="{{ basePath }}/{{ progress.current.prev.path|mdLink }}" class="navigation navigation-prev"><i class="fa fa-angle-left"></i></a>
<a href="{{ basePath }}/{{ progress.current.prev.path|mdLink }}" class="navigation navigation-prev {% if !progress.current.next or !progress.current.next.path %}navigation-unique{% endif %}"><i class="fa fa-angle-left"></i></a>
{% endif %}
{% if progress.current.next and progress.current.next.path %}
<a href="{{ basePath }}/{{ progress.current.next.path|mdLink }}" class="navigation navigation-next"><i class="fa fa-angle-right"></i></a>
<a href="{{ basePath }}/{{ progress.current.next.path|mdLink }}" class="navigation navigation-next {% if !progress.current.prev or !progress.current.prev.path %}navigation-unique{% endif %}"><i class="fa fa-angle-right"></i></a>
{% endif %}
</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 %}