Compare commits

...

139 Commits

Author SHA1 Message Date
Samy Pessé 21cf5fcaf4 Bump version to 2.6.2 2015-11-25 11:56:29 +01:00
Samy Pessé 8c7509759b Update nunjucks@2.2.0 2015-11-25 11:54:19 +01:00
Samy Pessé dc9ac0d0ff Compile js 2015-11-25 11:52:50 +01:00
Samy Pessé 93b401ca9f Add trailing slash to gitbook.state.root, fix value of gitbook.state.bookRoot 2015-11-25 11:49:55 +01:00
Samy Pessé b137d8270a Bump version to 2.6.1 2015-11-25 11:02:15 +01:00
Samy Pessé 6db7757451 Fix gitbook.state.innerLanguage: use camel case 2015-11-25 11:01:17 +01:00
Samy Pessé 43fd42b757 Bump version to 2.6.0 2015-11-25 10:54:09 +01:00
Samy Pessé 5d1c4d2b9f Fix #926: use zh-hans instead of zh-cn and ja instead of jp 2015-11-25 10:52:36 +01:00
Samy Pessé 425acd8bf6 Follow github style: change color of h6 headings 2015-11-25 10:42:36 +01:00
Samy Pessé 414160e0df Merge pull request #1027 from GitbookIO/revert-1020-remove-font-smooth
Revert "Fix small font rendering issue on Macs"
2015-11-25 10:32:28 +01:00
Samy Pessé 117c6aefe8 Revert "Fix small font rendering issue on Macs" 2015-11-25 10:32:17 +01:00
Samy Pessé 13d584d39a Merge pull request #1020 from dylang/remove-font-smooth
Fix small font rendering issue on Macs
2015-11-25 10:28:23 +01:00
Samy Pessé 9bc89c24f5 Fix #1023: close sidebar after clicking a link on mobile 2015-11-25 10:25:58 +01:00
Samy Pessé 5a8c4d5a4e Cleanup lib/generators/json.js 2015-11-25 10:14:34 +01:00
Samy Pessé 98da83a552 Fix urijs dependency on linux 2015-11-25 10:08:24 +01:00
Samy Pessé 5344a30a3b Generate css 2015-11-25 10:05:16 +01:00
Samy Pessé d5d6b3673e Add bookRoot to gitbook.state 2015-11-25 10:05:05 +01:00
Samy Pessé d2a2fe88d1 Update urijs@1.17.0 and tiny-lr 2015-11-25 10:04:44 +01:00
Samy Pesse ced44c86ce Fix #1022: fix bottom margin for lists 2015-11-24 23:38:26 +01:00
Samy Pessé ee402ad444 Merge pull request #1018 from atilacamurca/patch-1
Update README.md
2015-11-19 12:24:42 +01:00
Dylan Greene 3d2155eb66 Make fonts nicer on Macs
Removed the `antialias` setting which tells Chrome to not use native font rendering and instead use something from the late 2010's that isn't subpixel-friendly and looks very blocky. This used to be required when OS X browsers didn't antialias by default because it made fonts look bolder than on Windows. This has since been fixed and we are advised to not use `-webkit-font-smooth`.

https://developer.mozilla.org/en-US/docs/Web/CSS/font-smooth
2015-11-17 16:15:33 -05:00
Samy Pessé bd24b03548 Add @buren to AUTHORS 2015-11-16 22:04:41 +01:00
Samy Pessé 5cb55a0f37 Merge pull request #1019 from buren/sv-locale
Add Swedish translation
2015-11-16 22:02:49 +01:00
Jacob Burenstam 163242f80f Add Swedish translation 2015-11-16 22:00:16 +01:00
Átila Camurça Alves 266bfcb8bf Update README.md
There is an extra space in the command line example that enable debug mode.
2015-11-16 16:45:33 -03:00
Samy Pessé d82fa02669 Update plugin-search@1.1.0 2015-11-16 13:09:05 +01:00
Samy Pessé 3a5207a0d2 Merge pull request #1016 from GitbookIO/fix/999
Fix #999: detection of structure should be case incensitive
2015-11-16 10:39:29 +01:00
Samy Pessé e38f3bb884 Improve book.findFile to be case incensitive 2015-11-16 10:31:24 +01:00
Samy Pessé 751cdd9b3b Add test for #999 2015-11-16 10:30:10 +01:00
Samy Pessé 8df6bdca89 Update node version for travis and appveyor tests 2015-11-16 10:18:52 +01:00
Samy Pessé f9df7545ad Merge pull request #1014 from GitbookIO/fix/1013
Fix #1013: prioritize structure defined in book.json
2015-11-16 10:16:52 +01:00
Samy Pessé 9b7a90afef Test first extension of structure itself in book.findFile 2015-11-16 10:06:31 +01:00
Samy Pessé 2ae83b3247 Add failing test for #1013 2015-11-16 09:55:45 +01:00
Samy Pessé 5d907f234e Merge pull request #1005 from mjanda/master
Czech translation
2015-11-07 21:56:28 +01:00
Marek Janda 3a005c4f13 Czech translation 2015-11-07 21:51:27 +01:00
Marek Janda 6c2f50470a Czech translation 2015-11-07 21:00:45 +01:00
Samy Pessé d233783942 Fix bottom margin of blockquote 2015-11-05 11:33:21 +01:00
Samy Pessé 3d9e4c80a2 Bump version to 2.5.2 2015-11-03 09:32:35 +01:00
Samy Pessé a7f49015c1 Fix custom styles for ebook 2015-11-03 09:25:51 +01:00
Samy Pessé 68ed2631dc Correctly show space between toolbar icon and text in buttons 2015-11-02 17:16:43 +01:00
Samy Pessé 2814fea67e Trigger "start" before "page.change" 2015-11-02 16:57:08 +01:00
Samy Pessé 3388be9db6 Update app.js 2015-10-27 23:11:55 +01:00
Samy Pessé 9b89319820 Bump version to 2.5.1 2015-10-27 23:11:31 +01:00
Samy Pessé ad34089df5 Merge changelog for 2.5.0 2015-10-27 23:10:29 +01:00
Samy Pessé 7a4c6bc326 Fix calcul of root in gitbook.state when serving index.html as / 2015-10-27 18:44:04 +01:00
Samy Pessé 6ab386ae35 Bump version to 2.5.0 2015-10-27 13:25:52 +01:00
Samy Pessé 0abbfde912 Add chapterTitle to app build 2015-10-27 12:19:54 +01:00
Samy Pessé 0521defb63 Add 'chapterTitle' to gitbook.state api 2015-10-27 12:18:04 +01:00
Samy Pessé ea4d96610f Compatibility for require of plugins 2015-10-24 21:36:17 +02:00
Samy Pessé 4bc56564b4 Comment gitbook.state api 2015-10-22 16:12:21 +02:00
Samy Pessé 013f7f09da Add gitbook.state.filepath
Fix gitbook.state.root
2015-10-22 16:11:05 +02:00
Samy Pessé f082de1f92 Bump version to 2.5.0-beta.7 2015-10-22 13:52:19 +02:00
Samy Pessé ad39dc8c0c Update app.js 2015-10-22 13:50:05 +02:00
Samy Pessé 481a76319f Provide gitbook.state.root to plugins 2015-10-22 13:49:28 +02:00
Samy Pessé 27a43d5aea Merge pull request #986 from GitbookIO/fix/heading_ids
Normalize ID for headings
2015-10-21 18:05:40 +02:00
Samy Pessé df1d5a0680 Correctly generate id if none found 2015-10-21 17:58:06 +02:00
Samy Pessé 722109faf0 Update gitbook-parsers@0.8.7 2015-10-21 16:22:47 +02:00
Samy Pessé d4dca7736a Update gitbook-parsers@0.8.6 2015-10-21 16:15:56 +02:00
Samy Pessé 4e90becd52 Use Arial as default font 2015-10-21 15:54:45 +02:00
Samy Pessé 26890e94eb Bump version to 2.5.0-beta.6 2015-10-20 18:20:44 +02:00
Samy Pessé 55c83f9a59 Use helvetica by default for pdf 2015-10-20 18:09:51 +02:00
Samy Pessé b837d82878 Fix default style for pdf footer and header 2015-10-20 18:02:23 +02:00
Samy Pessé cbdcdb1e0f Fix #974: correctly handle query in image urls 2015-10-20 17:22:01 +02:00
Samy Pessé 4fea85289e Merge pull request #983 from GitbookIO/fix/982
Fix #982: correctly handle templating in imported content
2015-10-20 17:09:15 +02:00
Samy Pessé ba5f9132c9 Fix #982: fix shortcuts for included templates 2015-10-20 17:08:44 +02:00
Samy Pessé 991681daa2 Add failing test for #982 2015-10-20 16:38:48 +02:00
Samy Pessé 2faf53e16b Bump version to 2.5.0-beta.5 2015-10-16 13:48:27 +02:00
Samy Pessé 5430ed99de Update gitbook-parsers@0.8.5 2015-10-16 13:47:09 +02:00
Samy Pessé fef4dfd05e Fix position of sidebar toggle button 2015-10-14 13:35:03 +02:00
Samy Pessé 05943db3b5 Bump version to 2.5.0-beta.4 2015-10-14 13:16:49 +02:00
Samy Pessé 682fabd1ef Fix position of glossary icon relative to sidebar toggler 2015-10-14 13:15:36 +02:00
Samy Pessé ee8ccc6d7e Fix legacy properties book.options.input, ... 2015-10-14 12:31:37 +02:00
Samy Pessé 8ee0bcac2a Bump version to 2.5.0-beta.3 2015-10-14 11:54:58 +02:00
Samy Pessé 0c4ad74788 Fix resolve of plugins for prereleases 2015-10-14 11:54:12 +02:00
Samy Pessé 7d944e4f1f Fix instruction for local developement 2015-10-14 11:48:27 +02:00
Samy Pessé 67b2394c16 Bump version to 2.5.0-beta.2 2015-10-14 11:39:23 +02:00
Samy Pessé 269d08a35f Correctly handle pre-version for plugins checks 2015-10-13 15:50:54 +02:00
Samy Pessé f626e83e48 Bump version to 2.5.0-beta.1 2015-10-13 15:21:20 +02:00
Samy Pessé 8ba18cd587 Merge pull request #979 from GitbookIO/feature/plugins_config
Improvements for plugin configurations
2015-10-13 15:14:26 +02:00
Samy Pessé 54c12fea89 Set default config for all plugins 2015-10-13 15:05:46 +02:00
Samy Pessé afb5ce71ce Update gitbook-plugin-sharing@1.0.1 2015-10-13 14:56:59 +02:00
Samy Pessé 6fef6acf10 Correctly apply defaults config to pluginsConfig 2015-10-13 14:55:39 +02:00
Samy Pessé a27411401b Update gitbook-plugin-fontsettings@1.0.2 2015-10-13 14:48:09 +02:00
Samy Pessé 3718fc319b Fix error in accessing plugins config 2015-10-13 14:47:33 +02:00
Samy Pessé c6c9463f8c Update gitbook-plugin-search@1.0.2 2015-10-13 14:38:54 +02:00
Samy Pessé b410685318 Update gitbook-plugin-search@1.0.1 2015-10-13 14:34:29 +02:00
Samy Pessé b742b29259 Add tests for plugins config validation 2015-10-13 14:31:11 +02:00
Samy Pessé df5d27ab61 Use jsonschema to valid and default plugins config 2015-10-13 13:56:46 +02:00
Samy Pessé 2870155c0b Fix js error 2015-10-12 10:45:13 +02:00
Samy Pessé 9ba1075f48 Add hook "config" to transform configuration before generation 2015-10-12 10:43:14 +02:00
Samy Pessé 4ae5b2e050 Merge pull request #959 from GitbookIO/improvements/ebooks
Improvements for ebooks
2015-10-08 20:58:57 +02:00
Samy Pessé 4369bd8f5e Fix copy of assets and js path in gulpfile 2015-10-06 14:23:10 +02:00
Samy Pessé 321b24a7b9 Don't copy assets for ebook to website bundle 2015-10-06 14:21:02 +02:00
Samy Pessé 22e0c1fb54 Fix css being used for pdf footer and header 2015-10-06 13:35:04 +02:00
Samy Pessé ba65e3c020 Adapt tests 2015-10-06 13:26:32 +02:00
Samy Pessé c19ab952e3 Only add page count on pdf/epub 2015-10-06 13:20:20 +02:00
Samy Pessé ecc29087b5 Fix summary page counter for epub low resolution 2015-10-06 13:10:51 +02:00
Samy Pessé 87e056de64 Change markdown -> markup in less mixins 2015-10-06 13:05:32 +02:00
Samy Pessé 761200529f Adapt glossary and summary for pdf/epub to new markup style 2015-10-06 13:03:52 +02:00
Samy Pessé 770351916e Cleaner markup css 2015-10-06 12:45:25 +02:00
Samy Pessé 4b7d957b47 Use black links in mobi files 2015-10-06 10:38:09 +02:00
Samy Pessé a1c03c18a5 Improve support of glossary page in pdf 2015-10-06 10:00:49 +02:00
Samy Pesse 5d376b054f Handle correctly rtl for toc in ebooks 2015-10-06 09:47:41 +02:00
Samy Pesse 277d13c2bb Center title for table of content 2015-10-06 09:47:25 +02:00
Samy Pesse 4a9389d80a Improve display of table of contents for ebooks 2015-10-06 09:47:15 +02:00
Samy Pesse 33593c13ae Inline css for footer/header templates 2015-10-06 09:47:04 +02:00
Samy Pesse eef82c18c2 Remove bottom border on h1 and h2 2015-10-06 09:45:31 +02:00
Samy Pessé 7363580095 Fix glossary page using a "section" instead of a "div" 2015-10-06 09:45:22 +02:00
Samy Pessé 23a46b0597 Remove margin on first header of a page (ebook)
Avoid breaking in code blocks
2015-10-06 09:45:06 +02:00
Samy Pessé 51c79f294c Add css to footer/header for pdf 2015-10-06 09:44:54 +02:00
Samy Pessé ce1e0e1873 Merge pull request #964 from GitbookIO/features/plugin_api
Better APIs for Plugins
2015-10-06 09:42:49 +02:00
Samy Pessé c2775b3b63 Export jQuery for plugins compatibility 2015-10-06 09:37:01 +02:00
Samy Pessé bcb224d1cb Add more node.js versions to travis-ci 2015-10-05 23:06:46 +02:00
Samy Pessé 8bc5f22866 Add search plugin to dependencies 2015-10-05 17:22:47 +02:00
Samy Pessé c3f2ef02c4 Adapt tests for new default plugins 2015-10-05 17:16:37 +02:00
Samy Pessé 08e2c90d9d Don't fail when can't write to localstorage 2015-10-05 17:11:55 +02:00
Samy Pessé d2bb2ba454 Remove style for search 2015-10-05 17:07:25 +02:00
Samy Pessé ba912318cd Tweak some details for browserify compatibility 2015-10-05 16:49:37 +02:00
Samy Pessé 3d1f057373 Switch to gulp/browserify 2015-10-05 16:47:19 +02:00
Samy Pessé 0679807517 Define specific action for toggling summary 2015-10-05 16:02:03 +02:00
Samy Pessé 7f0e4803e2 Start removing search and lunr from core 2015-10-05 15:55:39 +02:00
Samy Pessé fa76029a17 Adapt tests for new default plugins 2015-10-05 14:24:55 +02:00
Samy Pessé 376c68957f Remove sharing 2015-10-05 13:53:19 +02:00
Samy Pessé 222ec8d4b9 Correctly reset buttons when page changed 2015-10-05 12:13:54 +02:00
Samy Pessé 90715c1944 Remove fontsettings from core and add plugin as default 2015-10-05 12:07:19 +02:00
Samy Pessé 801e1b6059 Add api to extend toolbar 2015-10-05 12:07:02 +02:00
Samy Pessé 14bb659b75 Update lodash for client side code 2015-10-05 10:43:21 +02:00
Samy Pessé eab3c50288 Add sharing plugin as default 2015-10-05 10:32:43 +02:00
Samy Pessé 74396e7240 Add method book.formatString 2015-10-05 10:07:04 +02:00
Samy Pessé 6c2539e886 Move Book.init in another file 2015-10-05 09:54:40 +02:00
Samy Pessé aba25d8521 Add eslint instead of jshint 2015-10-05 09:54:28 +02:00
Samy Pessé f0c97756de Bump version to 2.4.3 2015-10-01 14:07:32 +02:00
Samy Pessé f14c57d4c2 Merge pull request #955 from mstade/patch-1
Change nunjucks dependency to v2.1.0
2015-10-01 12:03:21 +02:00
Marcus Stade b6f60cc8ad Change nunjucks dependency to v2.1.0
Looks like mozilla/nunjucks#504 was merged and released in v2.1.0, so this changes `package.json` to no longer depend on a specific commit. Incidentally, this also solves a problem where corporate firewalls blocks npm from pulling in straight from github repos.
2015-10-01 12:01:52 +02:00
Samy Pessé 170516d343 Add ukranian translation 2015-09-28 21:01:28 +02:00
Samy Pessé c931e91bef Fix #952: add configuration for index maximum size 2015-09-28 20:54:45 +02:00
Samy Pessé db9151ae36 Update gitbook-parsers@0.8.3 2015-09-28 20:40:02 +02:00
Samy Pessé f2d86b6d69 Add slack button in readme 2015-09-23 12:19:04 +02:00
Samy Pessé 534cdd091b Add link to slack community channel 2015-09-23 12:16:22 +02:00
135 changed files with 27891 additions and 3173 deletions
+1
View File
@@ -0,0 +1 @@
theme/**/*
+19
View File
@@ -0,0 +1,19 @@
{
"rules": {
"indent": [ 2, 4 ],
"quotes": [ 2, "single" ],
"linebreak-style": [ 2, "unix" ],
"semi": [ 2, "always" ],
"no-unused-vars": [ 2, {
"vars": "all",
"args": "none"
} ],
"spaced-comment": [ 2, "always" ]
},
"env": {
"node": true,
"mocha": true,
"browser": true
},
"extends": "eslint:recommended"
}
-3
View File
@@ -24,8 +24,5 @@ build/Release
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
node_modules node_modules
theme/javascript/vendors
theme/vendors
# vim swapfile # vim swapfile
*.swp *.swp
-7
View File
@@ -1,7 +0,0 @@
{
"strict": false,
"browser": false,
"quotmark": true,
"node": true,
"mocha": true
}
+4 -3
View File
@@ -1,7 +1,8 @@
sudo: false sudo: false
language: node_js language: node_js
node_js: node_js:
- "0.11" - "stable"
- "0.10" - "4.1"
- "0.12"
before_install: before_install:
- npm install svgexport -g - npm install svgexport -g
+6
View File
@@ -55,3 +55,9 @@ Translators
- Hong Nguyen (@nghong) - Hong Nguyen (@nghong)
- Hebrew - Hebrew
- @a-moses - @a-moses
- Ukrainian
- @Karnaukhov
- Czech
- @mjanda
- Swedish
- Jacob Burenstam (@buren)
+48
View File
@@ -2,6 +2,54 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/). This project adheres to [Semantic Versioning](http://semver.org/).
## 2.6.2
- Fix `gitbook.state.bookRoot`, `gitbook.state.root` now has a trailing slash
- Update nunjucks to 2.2.0
## 2.6.1
- Use CamelCase for `gitbook.state.innerLanguage`
## 2.6.0
- Close sidebar after clicking a link on mobile
- Add root for multilingual books: `gitbook.state.bookRoot`
- Fix color of h6 headings
- Fix bottom margin of lists and blockquotes
- Add Swedish translation (`sv`)
- Add Czech translation (`cs`)
- Fix locale names for `zh-hans` and `jp`
- Update plugin `search` to fix crashes
- Aceept case-incensitive structure files (README, GLOSSARY, etc)
## 2.5.2
- Fix custom stylesheets for ebook generation
- Trigger event `start` before `page.change`
## 2.5.1
- Fix calcul of `gitbook.state.root` when serving HTML index as `/`
## 2.5.0
- Font settings, sharing and search are externalized as default plugins
- Plugins can define a configuration schema in the manifest, this schema will be used to validate configuration during build
- New Node.js API for plugin: `book.formatString(type, content)`
- New client side API for website plugins: `gitbook.toolbar.createButton(opts)`
- Better header/footer for PDF, CSS wil be inlined to easily style the header/footer
- Cleaner table of contents for ebooks
- Support for RTL in ebook's table of contents
- Better colors for mobi (links and code blocks)
- Fix installation of plugins when using a pre-release
- Fix support for pre-releases
- Update asciidoc parser to remove git dependency
- Fix templating in imported content
- Fix querystring in image urls
- Normalize heading IDs like GitHub
- Use Arial as default font for PDFs
- Add `root`, `chapterTitle` and `filepath` to `gitbook.state` JS API
## 2.4.3
- Add ukrainian translation (`uk`)
- Add `book.json` configuration for maximum size of search index
- Improve reliability of summary parser
## 2.4.2 ## 2.4.2
- Default plugins should not be installed by `gitbook install` - Default plugins should not be installed by `gitbook install`
- Limit search index size to avoid crash during generation - Limit search index size to avoid crash during generation
-96
View File
@@ -1,96 +0,0 @@
module.exports = function (grunt) {
var path = require("path");
// Load NPM tasks
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-requirejs');
grunt.loadNpmTasks("grunt-bower-install-simple");
// Init GRUNT configuraton
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
'bower-install-simple': {
options: {
color: true,
production: false,
directory: "theme/vendors"
}
},
less: {
development: {
options: {
compress: true,
yuicompress: true,
optimization: 2
},
files: {
"theme/assets/style.css": "theme/stylesheets/website.less",
"theme/assets/print.css": "theme/stylesheets/ebook.less"
}
}
},
requirejs: {
compile: {
options: {
name: "gitbook",
baseUrl: "theme/javascript/",
out: "theme/assets/app.js",
preserveLicenseComments: false,
optimize: "uglify",
include: ["requireLib"],
paths: {
"jQuery": '../vendors/jquery/dist/jquery',
"lodash": '../vendors/lodash/dist/lodash',
"requireLib": '../vendors/requirejs/require',
"Mousetrap": '../vendors/mousetrap/mousetrap',
"lunr": '../vendors/lunr.js/lunr',
"URIjs": '../vendors/URIjs/src/',
"ace": '../vendors/ace-builds/src-noconflict/'
},
shim: {
'jQuery': {
exports: '$'
},
'lodash': {
exports: '_'
},
'Mousetrap': {
exports: 'Mousetrap'
},
'lunr': {
exports: 'lunr'
}
}
}
}
},
copy: {
vendors: {
files: [
{
expand: true,
cwd: 'theme/vendors/fontawesome/fonts/',
src: ['**'],
dest: 'theme/assets/fonts/fontawesome/',
filter: 'isFile'
}
]
}
}
});
grunt.registerTask("bower-install", [ "bower-install-simple" ]);
// Build
grunt.registerTask('build', [
'bower-install',
'less',
'requirejs',
'copy:vendors'
]);
grunt.registerTask('default', [
'build'
]);
};
+6 -5
View File
@@ -4,12 +4,13 @@ GitBook
[![NPM version](https://badge.fury.io/js/gitbook.svg)](http://badge.fury.io/js/gitbook) [![NPM version](https://badge.fury.io/js/gitbook.svg)](http://badge.fury.io/js/gitbook)
[![Linux Build Status](https://travis-ci.org/GitbookIO/gitbook.png?branch=master)](https://travis-ci.org/GitbookIO/gitbook) [![Linux Build Status](https://travis-ci.org/GitbookIO/gitbook.png?branch=master)](https://travis-ci.org/GitbookIO/gitbook)
[![Windows Build status](https://ci.appveyor.com/api/projects/status/63nlflxcwmb2pue6?svg=true)](https://ci.appveyor.com/project/GitBook/gitbook) [![Windows Build status](https://ci.appveyor.com/api/projects/status/63nlflxcwmb2pue6?svg=true)](https://ci.appveyor.com/project/GitBook/gitbook)
[![Slack Status](https://slack.gitbook.com/badge.svg)](https://slack.gitbook.com)
GitBook is a command line tool (and Node.js library) for building beautiful books using GitHub/Git and Markdown (or AsciiDoc). Here is an example: [Learn Javascript](https://www.gitbook.com/book/GitBookIO/javascript). GitBook is a command line tool (and Node.js library) for building beautiful books using GitHub/Git and Markdown (or AsciiDoc). Here is an example: [Learn Javascript](https://www.gitbook.com/book/GitBookIO/javascript).
You can publish and host book easily online using [gitbook.com](https://www.gitbook.com), a desktop editor is [also available](https://www.gitbook.com/editor). You can publish and host book easily online using [gitbook.com](https://www.gitbook.com), a desktop editor is [also available](https://www.gitbook.com/editor).
Stay updated by following [@GitBookIO](https://twitter.com/GitBookIO) on Twitter or [GitBook](https://www.facebook.com/gitbookcom) on Facebook. Check out the [GitBook Community Slack Channel](https://slack.gitbook.com), Stay updated by following [@GitBookIO](https://twitter.com/GitBookIO) on Twitter or [GitBook](https://www.facebook.com/gitbookcom) on Facebook.
Complete documentation is available at [help.gitbook.com](http://help.gitbook.com/). Complete documentation is available at [help.gitbook.com](http://help.gitbook.com/).
@@ -212,7 +213,7 @@ Plugins needed to build a book can be installed using: `gitbook install ./`. You
You can use the options `--log=debug` and `--debug` to get better error messages (with stack trace). For example: You can use the options `--log=debug` and `--debug` to get better error messages (with stack trace). For example:
``` ```
$ gitbook build ./ -- log=debug --debug $ gitbook build ./ --log=debug --debug
``` ```
#### How to use the latest commit from GitBook in gitbook-cli #### How to use the latest commit from GitBook in gitbook-cli
@@ -221,9 +222,9 @@ To use the latest commit from `GitBook/gitbook` with `gitbook-cli`:
``` ```
$ git clone https://github.com/GitbookIO/gitbook.git ./gitbook $ git clone https://github.com/GitbookIO/gitbook.git ./gitbook
$ gitbook versions:link 2.0.0 ./gitbook $ gitbook versions:link ./gitbook
``` ```
Now the version tag `2.0.0` wil be associated with the `./gitbook` folder. Now `gitbook-cli` will be using the `./gitbook` folder.
You can uninstall it using: `gitbook versions:uninstall 2.0.0`. You can uninstall it using: `gitbook versions:uninstall latest`.
+2 -2
View File
@@ -5,8 +5,8 @@ init:
# Test against these versions of Node.js. # Test against these versions of Node.js.
environment: environment:
matrix: matrix:
- nodejs_version: "0.10" - nodejs_version: "0.12"
- nodejs_version: "0.11" - nodejs_version: "4.1"
# Install scripts. (runs after repo cloning) # Install scripts. (runs after repo cloning)
install: install:
+3 -3
View File
@@ -2,6 +2,6 @@
var color = require('bash-color'); var color = require('bash-color');
console.log(color.red("You need to install 'gitbook-cli' to have access to the gitbook command anywhere on your system.")); console.log(color.red('You need to install "gitbook-cli" to have access to the gitbook command anywhere on your system.'));
console.log(color.red("If you've installed this package globally, you need to uninstall it.")); console.log(color.red('If you\'ve installed this package globally, you need to uninstall it.'));
console.log(color.red(">> Run 'npm uninstall -g gitbook' then 'npm install -g gitbook-cli'")); console.log(color.red('>> Run "npm uninstall -g gitbook" then "npm install -g gitbook-cli"'));
-13
View File
@@ -1,13 +0,0 @@
{
"name": "GitBook",
"version": "1.0.0",
"dependencies": {
"jquery": "2.1.1",
"lodash": "2.4.1",
"requirejs": "2.1.11",
"URIjs": "1.13.1",
"mousetrap": "1.4.6",
"lunr.js": "0.5.2",
"fontawesome": "4.1.0"
}
}
+47
View File
@@ -0,0 +1,47 @@
var _ = require('lodash');
var gulp = require('gulp');
var gutil = require('gulp-util');
var less = require('gulp-less');
var rename = require('gulp-rename');
var minifyCSS = require('gulp-minify-css');
var browserify = require('browserify');
var mergeStream = require('merge-stream');
var source = require('vinyl-source-stream');
gulp.task('css', function() {
var merged = mergeStream();
_.each({
'ebook.less': 'ebook/ebook.css',
'pdf.less': 'ebook/pdf.css',
'mobi.less': 'ebook/mobi.css',
'epub.less': 'ebook/epub.css',
'website.less': 'website/style.css'
}, function(out, input) {
gutil.log('compiling', input, 'into', out);
merged.add(gulp.src('theme/stylesheets/'+input)
.pipe(less())
.pipe(minifyCSS())
.pipe(rename(out))
.pipe(gulp.dest('theme/assets/')));
});
return merged;
});
gulp.task('js', function() {
return browserify('./theme/javascript/index.js')
.bundle()
.pipe(source('app.js'))
.pipe(gulp.dest('./theme/assets/website'));
});
gulp.task('assets', function() {
return gulp.src('./node_modules/font-awesome/fonts/*')
.pipe(gulp.dest('theme/assets/website/fonts/fontawesome/'));
});
gulp.task('default', ['css', 'js', 'assets'], function() {
});
+149 -223
View File
@@ -1,25 +1,22 @@
var Q = require("q"); var Q = require('q');
var _ = require("lodash"); var _ = require('lodash');
var path = require("path"); var path = require('path');
var lunr = require("lunr"); var parsers = require('gitbook-parsers');
var parsers = require("gitbook-parsers");
var fs = require("./utils/fs"); var fs = require('./utils/fs');
var parseNavigation = require("./utils/navigation"); var parseNavigation = require('./utils/navigation');
var parseProgress = require("./utils/progress"); var parseProgress = require('./utils/progress');
var pageUtil = require("./utils/page"); var pageUtil = require('./utils/page');
var pathUtil = require("./utils/path"); var pathUtil = require('./utils/path');
var links = require("./utils/links"); var links = require('./utils/links');
var i18n = require("./utils/i18n"); var i18n = require('./utils/i18n');
var logger = require("./utils/logger"); var logger = require('./utils/logger');
var Configuration = require("./configuration"); var Configuration = require('./configuration');
var TemplateEngine = require("./template"); var TemplateEngine = require('./template');
var PluginsList = require("./pluginslist"); var PluginsList = require('./pluginslist');
var generators = require("./generators"); var generators = require('./generators');
var SEARCHINDEX_MAXSIZE = 1000000;
var Book = function(root, context, parent) { var Book = function(root, context, parent) {
this.context = _.defaults(context || {}, { this.context = _.defaults(context || {}, {
@@ -32,7 +29,7 @@ var Book = function(root, context, parent) {
}, },
// Log level // Log level
logLevel: "info" logLevel: 'info'
}); });
// Log // Log
@@ -46,7 +43,7 @@ var Book = function(root, context, parent) {
// Configuration // Configuration
this.config = new Configuration(this, this.context.config); this.config = new Configuration(this, this.context.config);
Object.defineProperty(this, "options", { Object.defineProperty(this, 'options', {
get: function () { get: function () {
return this.config.options; return this.config.options;
} }
@@ -80,23 +77,13 @@ var Book = function(root, context, parent) {
this.readmeFile = null; this.readmeFile = null;
this.langsFile = null; this.langsFile = null;
// Search Index
this.searchIndexEnabled = true;
this.searchIndexSize = 0;
this.searchIndex = lunr(function () {
this.ref("url");
this.field("title", { boost: 10 });
this.field("body");
});
// Bind methods // Bind methods
_.bindAll(this); _.bindAll(this);
}; };
// Return string representation // Return string representation
Book.prototype.toString = function() { Book.prototype.toString = function() {
return "[Book "+this.root+"]"; return '[Book '+this.root+']';
}; };
// Initialize and parse the book: config, summary, glossary // Initialize and parse the book: config, summary, glossary
@@ -114,17 +101,17 @@ Book.prototype.parse = function() {
return that.parseLangs() return that.parseLangs()
.then(function() { .then(function() {
multilingual = that.langs.length > 0; multilingual = that.langs.length > 0;
if (multilingual) that.log.info.ln("Parsing multilingual book, with", that.langs.length, "languages"); if (multilingual) that.log.info.ln('Parsing multilingual book, with', that.langs.length, 'languages');
// Sub-books that inherit from the current book configuration // Sub-books that inherit from the current book configuration
that.books = _.map(that.langs, function(lang) { that.books = _.map(that.langs, function(lang) {
that.log.info.ln("Preparing language book", lang.lang); that.log.info.ln('Preparing language book', lang.lang);
return new Book( return new Book(
path.join(that.root, lang.path), path.join(that.root, lang.path),
_.merge({}, that.context, { _.merge({}, that.context, {
config: _.extend({}, that.options, { config: _.extend({}, that.options, {
"output": path.join(that.options.output, lang.lang), 'output': path.join(that.options.output, lang.lang),
"language": lang.lang 'language': lang.lang
}) })
}), }),
that that
@@ -167,15 +154,15 @@ Book.prototype.generate = function(generator) {
var that = this; var that = this;
that.options.generator = generator || that.options.generator; that.options.generator = generator || that.options.generator;
that.log.info.ln("start generation with", that.options.generator, "generator"); that.log.info.ln('start generation with', that.options.generator, 'generator');
return Q() return Q()
// Clean output folder // Clean output folder
.then(function() { .then(function() {
that.log.info("clean", that.options.generator, "generator"); that.log.info('clean', that.options.generator, 'generator');
return fs.clean(that.options.output) return fs.clean(that.options.output)
.progress(function(p) { .progress(function(p) {
that.log.debug.ln("remove", p.file, "("+p.i+"/"+p.count+")"); that.log.debug.ln('remove', p.file, '('+p.i+'/'+p.count+')');
}) })
.then(function() { .then(function() {
that.log.info.ok(); that.log.info.ok();
@@ -185,12 +172,20 @@ Book.prototype.generate = function(generator) {
// Create generator // Create generator
.then(function() { .then(function() {
var Generator = generators[generator]; var Generator = generators[generator];
if (!Generator) throw "Generator \""+that.options.generator+"\" doesn't exist"; if (!Generator) throw 'Generator \''+that.options.generator+'\' doesn\'t exist';
generator = new Generator(that); generator = new Generator(that);
return generator.prepare(); return generator.prepare();
}) })
// Transform configuration
.then(function() {
return that.callHook('config', that.config.dump())
.then(function(newConfig) {
that.config.replace(newConfig);
});
})
// Generate content // Generate content
.then(function() { .then(function() {
if (that.isMultilingual()) { if (that.isMultilingual()) {
@@ -198,43 +193,43 @@ Book.prototype.generate = function(generator) {
} else { } else {
// Separate list of files into the different operations needed // Separate list of files into the different operations needed
var ops = _.groupBy(that.files, function(file) { var ops = _.groupBy(that.files, function(file) {
if (file[file.length -1] == "/") { if (file[file.length -1] == '/') {
return "directories"; return 'directories';
} else if (_.contains(parsers.extensions, path.extname(file)) && that.navigation[file]) { } else if (_.contains(parsers.extensions, path.extname(file)) && that.navigation[file]) {
return "content"; return 'content';
} else { } else {
return "files"; return 'files';
} }
}); });
return Q() return Q()
// First, let"s create folder // First, let's create folder
.then(function() { .then(function() {
return _.reduce(ops.directories || [], function(prev, folder) { return _.reduce(ops.directories || [], function(prev, folder) {
return prev.then(function() { return prev.then(function() {
that.log.debug.ln("transferring folder", folder); that.log.debug.ln('transferring folder', folder);
return Q(generator.transferFolder(folder)); return Q(generator.transferFolder(folder));
}); });
}, Q()); }, Q());
}) })
// Then, let"s copy other files // Then, let's copy other files
.then(function() { .then(function() {
return Q.all(_.map(ops.files || [], function(file) { return Q.all(_.map(ops.files || [], function(file) {
that.log.debug.ln("transferring file", file); that.log.debug.ln('transferring file', file);
return Q(generator.transferFile(file)); return Q(generator.transferFile(file));
})); }));
}) })
// Finally let"s generate content // Finally let's generate content
.then(function() { .then(function() {
var nFiles = (ops.content || []).length; var nFiles = (ops.content || []).length;
return _.reduce(ops.content || [], function(prev, file, i) { return _.reduce(ops.content || [], function(prev, file, i) {
return prev.then(function() { return prev.then(function() {
var p = ((i*100)/nFiles).toFixed(0)+"%"; var p = ((i*100)/nFiles).toFixed(0)+'%';
that.log.debug.ln("processing", file, p); that.log.debug.ln('processing', file, p);
return Q(generator.convertFile(file)) return Q(generator.convertFile(file))
.fail(function(err) { .fail(function(err) {
@@ -251,16 +246,16 @@ Book.prototype.generate = function(generator) {
// Finish generation // Finish generation
.then(function() { .then(function() {
return generator.callHook("finish:before"); return that.callHook('finish:before');
}) })
.then(function() { .then(function() {
return generator.finish(); return generator.finish();
}) })
.then(function() { .then(function() {
return generator.callHook("finish"); return that.callHook('finish');
}) })
.then(function() { .then(function() {
that.log.info.ln("generation is finished"); that.log.info.ln('generation is finished');
}); });
}; };
@@ -286,7 +281,7 @@ Book.prototype.generateFile = function(output, options) {
options = _.defaults(options || {}, { options = _.defaults(options || {}, {
ebookFormat: path.extname(output).slice(1) ebookFormat: path.extname(output).slice(1)
}); });
output = output || path.resolve(book.root, "book."+options.ebookFormat); output = output || path.resolve(book.root, 'book.'+options.ebookFormat);
return fs.tmp.dir() return fs.tmp.dir()
.then(function(tmpDir) { .then(function(tmpDir) {
@@ -299,13 +294,13 @@ Book.prototype.generateFile = function(output, options) {
var _tmpDir = tmpDir; var _tmpDir = tmpDir;
if (lang) { if (lang) {
_outputFile = _outputFile.slice(0, -path.extname(_outputFile).length)+"_"+lang+path.extname(_outputFile); _outputFile = _outputFile.slice(0, -path.extname(_outputFile).length)+'_'+lang+path.extname(_outputFile);
_tmpDir = path.join(_tmpDir, lang); _tmpDir = path.join(_tmpDir, lang);
} }
book.log.debug.ln("copy ebook to", _outputFile); book.log.debug.ln('copy ebook to', _outputFile);
return fs.copy( return fs.copy(
path.join(_tmpDir, "index."+options.ebookFormat), path.join(_tmpDir, 'index.'+options.ebookFormat),
_outputFile _outputFile
); );
}; };
@@ -325,7 +320,7 @@ Book.prototype.generateFile = function(output, options) {
} }
}) })
.then(function(n) { .then(function(n) {
book.log.info.ok(n+" file(s) generated"); book.log.info.ok(n+' file(s) generated');
return fs.remove(tmpDir); return fs.remove(tmpDir);
}); });
@@ -337,7 +332,7 @@ Book.prototype.generateFile = function(output, options) {
Book.prototype.parseConfig = function() { Book.prototype.parseConfig = function() {
var that = this; var that = this;
that.log.info("loading book configuration...."); that.log.info('loading book configuration....');
return that.config.load() return that.config.load()
.then(function() { .then(function() {
that.log.info.ok(); that.log.info.ok();
@@ -351,34 +346,34 @@ Book.prototype.parsePlugins = function() {
// Load plugins // Load plugins
return that.plugins.load(that.options.plugins) return that.plugins.load(that.options.plugins)
.then(function() { .then(function() {
if (_.size(that.plugins.failed) > 0) return Q.reject(new Error("Error loading plugins: "+that.plugins.failed.join(",")+". Run \"gitbook install\" to install plugins from NPM.")); if (_.size(that.plugins.failed) > 0) return Q.reject(new Error('Error loading plugins: '+that.plugins.failed.join(',')+'. Run \'gitbook install\' to install plugins from NPM.'));
that.log.info.ok(that.plugins.count()+" plugins loaded"); that.log.info.ok(that.plugins.count()+' plugins loaded');
that.log.debug.ln("normalize plugins list"); that.log.debug.ln('normalize plugins list');
}); });
}; };
// Parse readme to extract defaults title and description // Parse readme to extract defaults title and description
Book.prototype.parseReadme = function() { Book.prototype.parseReadme = function() {
var that = this; var that = this;
var structure = that.config.getStructure("readme"); var filename = that.config.getStructure('readme', true);
that.log.debug.ln("start parsing readme:", structure); that.log.debug.ln('start parsing readme:', filename);
return that.findFile(structure) return that.findFile(filename)
.then(function(readme) { .then(function(readme) {
if (!readme) throw "No README file"; if (!readme) throw 'No README file';
if (!_.contains(that.files, readme.path)) throw "README file is ignored"; if (!_.contains(that.files, readme.path)) throw 'README file is ignored';
that.readmeFile = readme.path; that.readmeFile = readme.path;
that._defaultsStructure(that.readmeFile); that._defaultsStructure(that.readmeFile);
that.log.debug.ln("readme located at", that.readmeFile); that.log.debug.ln('readme located at', that.readmeFile);
return that.template.renderFile(that.readmeFile) return that.template.renderFile(that.readmeFile)
.then(function(content) { .then(function(content) {
return readme.parser.readme(content) return readme.parser.readme(content)
.fail(function(err) { .fail(function(err) {
throw that.normError(err, { throw that.normError(err, {
name: err.name || "Readme Parse Error", name: err.name || 'Readme Parse Error',
fileName: that.readmeFile fileName: that.readmeFile
}); });
}); });
@@ -395,23 +390,23 @@ Book.prototype.parseReadme = function() {
Book.prototype.parseLangs = function() { Book.prototype.parseLangs = function() {
var that = this; var that = this;
var structure = that.config.getStructure("langs"); var filename = that.config.getStructure('langs', true);
that.log.debug.ln("start parsing languages index:", structure); that.log.debug.ln('start parsing languages index:', filename);
return that.findFile(structure) return that.findFile(filename)
.then(function(langs) { .then(function(langs) {
if (!langs) return []; if (!langs) return [];
that.langsFile = langs.path; that.langsFile = langs.path;
that._defaultsStructure(that.langsFile); that._defaultsStructure(that.langsFile);
that.log.debug.ln("languages index located at", that.langsFile); that.log.debug.ln('languages index located at', that.langsFile);
return that.template.renderFile(that.langsFile) return that.template.renderFile(that.langsFile)
.then(function(content) { .then(function(content) {
return langs.parser.langs(content) return langs.parser.langs(content)
.fail(function(err) { .fail(function(err) {
throw that.normError(err, { throw that.normError(err, {
name: err.name || "Langs Parse Error", name: err.name || 'Langs Parse Error',
fileName: that.langsFile fileName: that.langsFile
}); });
}); });
@@ -426,29 +421,29 @@ Book.prototype.parseLangs = function() {
Book.prototype.parseSummary = function() { Book.prototype.parseSummary = function() {
var that = this; var that = this;
var structure = that.config.getStructure("summary"); var filename = that.config.getStructure('summary', true);
that.log.debug.ln("start parsing summary:", structure); that.log.debug.ln('start parsing summary:', filename);
return that.findFile(structure) return that.findFile(filename)
.then(function(summary) { .then(function(summary) {
if (!summary) throw "No SUMMARY file"; if (!summary) throw 'No SUMMARY file';
// Remove the summary from the list of files to parse // Remove the summary from the list of files to parse
that.summaryFile = summary.path; that.summaryFile = summary.path;
that._defaultsStructure(that.summaryFile); that._defaultsStructure(that.summaryFile);
that.files = _.without(that.files, that.summaryFile); that.files = _.without(that.files, that.summaryFile);
that.log.debug.ln("summary located at", that.summaryFile); that.log.debug.ln('summary located at', that.summaryFile);
return that.template.renderFile(that.summaryFile) return that.template.renderFile(that.summaryFile)
.then(function(content) { .then(function(content) {
return summary.parser.summary(content, { return summary.parser.summary(content, {
entryPoint: that.readmeFile, entryPoint: that.readmeFile,
entryPointTitle: that.i18n("SUMMARY_INTRODUCTION"), entryPointTitle: that.i18n('SUMMARY_INTRODUCTION'),
files: that.files files: that.files
}) })
.fail(function(err) { .fail(function(err) {
throw that.normError(err, { throw that.normError(err, {
name: err.name || "Summary Parse Error", name: err.name || 'Summary Parse Error',
fileName: that.summaryFile fileName: that.summaryFile
}); });
}); });
@@ -464,10 +459,10 @@ Book.prototype.parseSummary = function() {
Book.prototype.parseGlossary = function() { Book.prototype.parseGlossary = function() {
var that = this; var that = this;
var structure = that.config.getStructure("glossary"); var filename = that.config.getStructure('glossary', true);
that.log.debug.ln("start parsing glossary: ", structure); that.log.debug.ln('start parsing glossary: ', filename);
return that.findFile(structure) return that.findFile(filename)
.then(function(glossary) { .then(function(glossary) {
if (!glossary) return []; if (!glossary) return [];
@@ -476,13 +471,13 @@ Book.prototype.parseGlossary = function() {
that._defaultsStructure(that.glossaryFile); that._defaultsStructure(that.glossaryFile);
that.files = _.without(that.files, that.glossaryFile); that.files = _.without(that.files, that.glossaryFile);
that.log.debug.ln("glossary located at", that.glossaryFile); that.log.debug.ln('glossary located at', that.glossaryFile);
return that.template.renderFile(that.glossaryFile) return that.template.renderFile(that.glossaryFile)
.then(function(content) { .then(function(content) {
return glossary.parser.glossary(content) return glossary.parser.glossary(content)
.fail(function(err) { .fail(function(err) {
throw that.normError(err, { throw that.normError(err, {
name: err.name || "Glossary Parse Error", name: err.name || 'Glossary Parse Error',
fileName: that.glossaryFile fileName: that.glossaryFile
}); });
}); });
@@ -514,12 +509,12 @@ Book.prototype.parsePage = function(filename, options) {
}); });
}; };
that.log.debug.ln("start parsing file", filename); that.log.debug.ln('start parsing file', filename);
var extension = path.extname(filename); var extension = path.extname(filename);
var filetype = parsers.get(extension); var filetype = parsers.get(extension);
if (!filetype) return Q.reject(new Error("Can't parse file: "+filename)); if (!filetype) return Q.reject(new Error('Can\'t parse file: '+filename));
// Type of parser used // Type of parser used
page.type = filetype.name; page.type = filetype.name;
@@ -533,7 +528,7 @@ Book.prototype.parsePage = function(filename, options) {
// Progress in the book // Progress in the book
page.progress = parseProgress(that.navigation, filename); page.progress = parseProgress(that.navigation, filename);
that.log.debug.ln("render template", filename); that.log.debug.ln('render template', filename);
// Read file content // Read file content
return that.readFile(page.path) return that.readFile(page.path)
@@ -560,7 +555,7 @@ Book.prototype.parsePage = function(filename, options) {
.then(function(content) { .then(function(content) {
page.content = content; page.content = content;
that.log.debug.ln("use file parser", filetype.name, "for", filename); that.log.debug.ln('use file parser', filetype.name, 'for', filename);
return filetype.page(page.content); return filetype.page(page.content);
}) })
@@ -568,7 +563,7 @@ Book.prototype.parsePage = function(filename, options) {
.then(function(_page) { .then(function(_page) {
return _.reduce(_page.sections, function(prev, section) { return _.reduce(_page.sections, function(prev, section) {
return prev.then(function(_sections) { return prev.then(function(_sections) {
return that.template.postProcess(section.content || "") return that.template.postProcess(section.content || '')
.then(function(content) { .then(function(content) {
section.content = content; section.content = content;
return _sections.concat([section]); return _sections.concat([section]);
@@ -584,8 +579,8 @@ Book.prototype.parsePage = function(filename, options) {
convertImages: options.convertImages, convertImages: options.convertImages,
input: filename, input: filename,
navigation: that.navigation, navigation: that.navigation,
base: path.dirname(filename) || "./", base: path.dirname(filename) || './',
output: path.dirname(filename) || "./", output: path.dirname(filename) || './',
glossary: that.glossary glossary: that.glossary
}); });
}) })
@@ -597,7 +592,6 @@ Book.prototype.parsePage = function(filename, options) {
}) })
.then(function() { .then(function() {
that.indexPage(page);
return page; return page;
}); });
}; };
@@ -606,25 +600,51 @@ Book.prototype.parsePage = function(filename, options) {
Book.prototype.findFile = function(filename) { Book.prototype.findFile = function(filename) {
var that = this; var that = this;
return _.reduce(parsers.extensions, function(prev, ext) { var ext = path.extname(filename);
var basename = path.basename(filename, ext);
// Ordered list of extensions to test
var exts = parsers.extensions;
if (ext) exts = _.uniq([ext].concat(exts));
return _.reduce(exts, function(prev, ext) {
return prev.then(function(output) { return prev.then(function(output) {
// Stop if already find a parser // Stop if already find a parser
if (output) return output; if (output) return output;
var filepath = filename+ext; var filepath = basename+ext;
return fs.findFile(that.root, filepath)
.then(function(realFilepath) {
if (!realFilepath) return null;
return that.fileExists(filepath)
.then(function(exists) {
if (!exists) return null;
return { return {
parser: parsers.get(ext), parser: parsers.get(ext),
path: filepath path: realFilepath
}; };
}); });
}); });
}, Q(null)); }, Q(null));
}; };
// Format a string using a specific markup language
Book.prototype.formatString = function(extension, content) {
return Q()
.then(function() {
var filetype = parsers.get(extension);
if (!filetype) throw new Error('Filetype doesn\'t exist: '+filetype);
return filetype.page(content);
})
// Merge sections
.then(function(page) {
return _.reduce(page.sections, function(content, section) {
return content + section.content;
}, '');
});
};
// Check if a file exists in the book // Check if a file exists in the book
Book.prototype.fileExists = function(filename) { Book.prototype.fileExists = function(filename) {
return fs.exists( return fs.exists(
@@ -641,7 +661,7 @@ Book.prototype.fileIsInBook = function(filename) {
Book.prototype.readFile = function(filename) { Book.prototype.readFile = function(filename) {
return fs.readFile( return fs.readFile(
this.resolve(filename), this.resolve(filename),
{ encoding: "utf8" } { encoding: 'utf8' }
); );
}; };
@@ -655,28 +675,28 @@ Book.prototype.listAllFiles = function() {
var that = this; var that = this;
return fs.list(this.root, { return fs.list(this.root, {
ignoreFiles: [".ignore", ".gitignore", ".bookignore"], ignoreFiles: ['.ignore', '.gitignore', '.bookignore'],
ignoreRules: [ ignoreRules: [
// Skip Git stuff // Skip Git stuff
".git/", '.git/',
".gitignore", '.gitignore',
// Skip OS X meta data // Skip OS X meta data
".DS_Store", '.DS_Store',
// Skip stuff installed by plugins // Skip stuff installed by plugins
"node_modules", 'node_modules',
// Skip book outputs // Skip book outputs
"_book", '_book',
"*.pdf", '*.pdf',
"*.epub", '*.epub',
"*.mobi", '*.mobi',
// Skip config files // Skip config files
".ignore", '.ignore',
".bookignore", '.bookignore',
"book.json", 'book.json',
] ]
}) })
.then(function(_files) { .then(function(_files) {
@@ -695,7 +715,7 @@ Book.prototype.parentRoot = function() {
return this.root; return this.root;
}; };
// Return true if it"s a sub-book // Return true if it's a sub-book
Book.prototype.isSubBook = function() { Book.prototype.isSubBook = function() {
return !!this.parent; return !!this.parent;
}; };
@@ -724,13 +744,13 @@ Book.prototype.relative = function(p) {
// Normalize a path to .html and convert README -> index // Normalize a path to .html and convert README -> index
Book.prototype.contentPath = function(link) { Book.prototype.contentPath = function(link) {
if ( if (
path.basename(link, path.extname(link)) == "README" || path.basename(link, path.extname(link)) == 'README' ||
link == this.readmeFile link == this.readmeFile
) { ) {
link = path.join(path.dirname(link), "index"+path.extname(link)); link = path.join(path.dirname(link), 'index'+path.extname(link));
} }
link = links.changeExtension(link, ".html"); link = links.changeExtension(link, '.html');
return link; return link;
}; };
@@ -739,40 +759,15 @@ Book.prototype.contentLink = function(link) {
return links.normalize(this.contentPath(link)); return links.normalize(this.contentPath(link));
}; };
// Index a page into the search index
Book.prototype.indexPage = function(page) {
var nav = this.navigation[page.path];
if (!nav || !this.searchIndexEnabled) return;
this.log.debug.ln("index page", page.path);
// Extract text from the page
var text = pageUtil.extractText(page.sections);
// Limit size of index (to avoid #941)
this.searchIndexSize = this.searchIndexSize + text.length;
if (this.searchIndexSize > SEARCHINDEX_MAXSIZE) {
this.log.warn.ln("search index is too big, indexing is now disabled");
this.searchIndexEnabled = false;
return;
}
this.searchIndex.add({
url: this.contentLink(page.path),
title: nav.title,
body: text
});
};
// Default structure paths to an extension // Default structure paths to an extension
Book.prototype._defaultsStructure = function(filename) { Book.prototype._defaultsStructure = function(filename) {
var that = this; var that = this;
var extension = path.extname(filename); var extension = path.extname(filename);
that.readmeFile = that.readmeFile || that.config.getStructure("readme")+extension; that.readmeFile = that.readmeFile || that.config.getStructure('readme')+extension;
that.summaryFile = that.summaryFile || that.config.getStructure("summary")+extension; that.summaryFile = that.summaryFile || that.config.getStructure('summary')+extension;
that.glossaryFile = that.glossaryFile || that.config.getStructure("glossary")+extension; that.glossaryFile = that.glossaryFile || that.config.getStructure('glossary')+extension;
that.langsFile = that.langsFile || that.config.getStructure("langs")+extension; that.langsFile = that.langsFile || that.config.getStructure('langs')+extension;
}; };
// Change output path // Change output path
@@ -805,87 +800,18 @@ Book.prototype.normError = function(err, opts, defs) {
err.toString = function() { err.toString = function() {
var attributes = []; var attributes = [];
if (this.fileName) attributes.push("In file \""+this.fileName+"\""); if (this.fileName) attributes.push('In file \''+this.fileName+'\'');
if (this.lineNumber) attributes.push("Line "+this.lineNumber); if (this.lineNumber) attributes.push('Line '+this.lineNumber);
if (this.columnNumber) attributes.push("Column "+this.columnNumber); if (this.columnNumber) attributes.push('Column '+this.columnNumber);
return (this.name || "Error")+": "+this.message+((attributes.length > 0)? " ("+attributes.join(", ")+")" : ""); return (this.name || 'Error')+': '+this.message+((attributes.length > 0)? ' ('+attributes.join(', ')+')' : '');
}; };
return err; return err;
}; };
// Init and return a book // Call a hook in plugins
Book.init = function(root, opts) { Book.prototype.callHook = function(name, data) {
var book = new Book(root, opts); return this.plugins.hook(name, data);
var extensionToUse = ".md";
var chaptersPaths = function(chapters) {
return _.reduce(chapters || [], function(accu, chapter) {
var o = {
title: chapter.title
};
if (chapter.path) o.path = chapter.path;
return accu.concat(
[o].concat(chaptersPaths(chapter.articles))
);
}, []);
};
book.log.info.ln("init book at", root);
return fs.mkdirp(root)
.then(function() {
book.log.info.ln("detect structure from SUMMARY (if it exists)");
return book.parseSummary();
})
.fail(function() {
return Q();
})
.then(function() {
var summary = book.summaryFile || "SUMMARY.md";
var chapters = book.summary.chapters || [];
extensionToUse = path.extname(summary);
if (chapters.length === 0) {
chapters = [
{
title: "Summary",
path: "SUMMARY"+extensionToUse
},
{
title: "Introduction",
path: "README"+extensionToUse
}
];
}
return Q(chaptersPaths(chapters));
})
.then(function(chapters) {
// Create files that don"t exist
return Q.all(_.map(chapters, function(chapter) {
if (!chapter.path) return Q();
var absolutePath = path.resolve(book.root, chapter.path);
return fs.exists(absolutePath)
.then(function(exists) {
if(exists) {
book.log.info.ln("found", chapter.path);
return;
} else {
book.log.info.ln("create", chapter.path);
}
return fs.mkdirp(path.dirname(absolutePath))
.then(function() {
return fs.writeFile(absolutePath, "# "+chapter.title+"\n");
});
});
}));
})
.then(function() {
book.log.info.ln("initialization is finished");
});
}; };
module.exports= Book; module.exports= Book;
+109
View File
@@ -0,0 +1,109 @@
var path = require('path');
module.exports = {
// Options that can't be extend
'configFile': 'book',
'generator': 'website',
'extension': null,
// Book metadats (somes are extracted from the README by default)
'title': null,
'description': null,
'isbn': null,
'language': 'en',
'direction': null,
'author': null,
// version of gitbook to use
'gitbook': '*',
// Structure
'structure': {
'langs': 'LANGS.md',
'readme': 'README.md',
'glossary': 'GLOSSARY.md',
'summary': 'SUMMARY.md'
},
// CSS Styles
'styles': {
'website': 'styles/website.css',
'print': 'styles/print.css',
'ebook': 'styles/ebook.css',
'pdf': 'styles/pdf.css',
'mobi': 'styles/mobi.css',
'epub': 'styles/epub.css'
},
// Plugins list, can contain '-name' for removing default plugins
'plugins': [],
// Global configuration for plugins
'pluginsConfig': {},
// Variables for templating
'variables': {},
// Set another theme with your own layout
// It's recommended to use plugins or add more options for default theme, though
// See https://github.com/GitbookIO/gitbook/issues/209
'theme': path.resolve(__dirname, '../theme'),
// Links in template (null: default, false: remove, string: new value)
'links': {
// Custom links at top of sidebar
'sidebar': {
// 'Custom link name': 'https://customlink.com'
},
// Sharing links
'sharing': {
'google': null,
'facebook': null,
'twitter': null,
'weibo': null,
'all': null
}
},
// Options for PDF generation
'pdf': {
// Add toc at the end of the file
'toc': true,
// Add page numbers to the bottom of every page
'pageNumbers': false,
// Font for the file content
'fontSize': 12,
'fontFamily': 'Arial',
// Paper size for the pdf
// Choices are [u’a0’, u’a1’, u’a2’, u’a3’, u’a4’, u’a5’, u’a6’, u’b0’, u’b1’, u’b2’, u’b3’, u’b4’, u’b5’, u’b6’, u’legal’, u’letter’]
'paperSize': 'a4',
// How to mark detected chapters.
// Choices are “pagebreak”, “rule”, 'both' or “none”.
'chapterMark' : 'pagebreak',
// An XPath expression. Page breaks are inserted before the specified elements.
// To disable use the expression: '/'
'pageBreaksBefore': '/',
// Margin (in pts)
// Note: 72 pts equals 1 inch
'margin': {
'right': 62,
'left': 62,
'top': 56,
'bottom': 56
},
// Header HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
'headerTemplate': null,
// Footer HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
'footerTemplate': null
}
};
+75 -165
View File
@@ -1,13 +1,16 @@
var _ = require("lodash"); var _ = require('lodash');
var Q = require("q"); var Q = require('q');
var path = require("path"); var path = require('path');
var semver = require("semver"); var semver = require('semver');
var pkg = require("../package.json"); var pkg = require('../package.json');
var i18n = require("./utils/i18n"); var i18n = require('./utils/i18n');
var version = require('./version');
var DEFAULT_CONFIG = require('./config_default');
// Default plugins added to each books // Default plugins added to each books
var DEFAULT_PLUGINS = ["highlight"]; var DEFAULT_PLUGINS = ['highlight', 'search', 'sharing', 'fontsettings'];
// Check if a plugin is a default plugin // Check if a plugin is a default plugin
// Plugin should be in the list // Plugin should be in the list
@@ -26,29 +29,29 @@ function isDefaultPlugin(name, version) {
// Normalize a list of plugins to use // Normalize a list of plugins to use
function normalizePluginsList(plugins, addDefaults) { function normalizePluginsList(plugins, addDefaults) {
// Normalize list to an array // Normalize list to an array
plugins = _.isString(plugins) ? plugins.split(",") : (plugins || []); plugins = _.isString(plugins) ? plugins.split(',') : (plugins || []);
// Remove empty parts // Remove empty parts
plugins = _.compact(plugins); plugins = _.compact(plugins);
// Divide as {name, version} to handle format like "myplugin@1.0.0" // Divide as {name, version} to handle format like 'myplugin@1.0.0'
plugins = _.map(plugins, function(plugin) { plugins = _.map(plugins, function(plugin) {
if (plugin.name) return plugin; if (plugin.name) return plugin;
var parts = plugin.split("@"); var parts = plugin.split('@');
var name = parts[0]; var name = parts[0];
var version = parts[1]; var version = parts[1];
return { return {
"name": name, 'name': name,
"version": version, // optional 'version': version, // optional
"isDefault": isDefaultPlugin(name, version) 'isDefault': isDefaultPlugin(name, version)
}; };
}); });
// List plugins to remove // List plugins to remove
var toremove = _.chain(plugins) var toremove = _.chain(plugins)
.filter(function(plugin) { .filter(function(plugin) {
return plugin.name.length > 0 && plugin.name[0] == "-"; return plugin.name.length > 0 && plugin.name[0] == '-';
}) })
.map(function(plugin) { .map(function(plugin) {
return plugin.name.slice(1); return plugin.name.slice(1);
@@ -63,15 +66,15 @@ function normalizePluginsList(plugins, addDefaults) {
} }
plugins.push({ plugins.push({
"name": plugin, 'name': plugin,
"isDefault": true 'isDefault': true
}); });
}); });
} }
// Remove plugin that start with '-' // Remove plugin that start with '-'
plugins = _.filter(plugins, function(plugin) { plugins = _.filter(plugins, function(plugin) {
return !_.contains(toremove, plugin.name) && !(plugin.name.length > 0 && plugin.name[0] == "-"); return !_.contains(toremove, plugin.name) && !(plugin.name.length > 0 && plugin.name[0] == '-');
}); });
// Remove duplicates // Remove duplicates
@@ -81,33 +84,8 @@ function normalizePluginsList(plugins, addDefaults) {
} }
var Configuration = function(book, options) { var Configuration = function(book, options) {
var that = this;
this.book = book; this.book = book;
this.replace(options);
this.options = _.cloneDeep(Configuration.DEFAULT);
this.options = _.merge(this.options, options || {});
// options.input == book.root
Object.defineProperty(this.options, "input", {
get: function () {
return that.book.root;
}
});
// options.originalInput == book.parent.root
Object.defineProperty(this.options, "originalInput", {
get: function () {
return that.book.parent? that.book.parent.root : undefined;
}
});
// options.originalOutput == book.parent.options.output
Object.defineProperty(this.options, "originalOutput", {
get: function () {
return that.book.parent? that.book.parent.options.output : undefined;
}
});
}; };
// Read and parse the configuration // Read and parse the configuration
@@ -129,7 +107,7 @@ Configuration.prototype.load = function() {
_config = require(configPath); _config = require(configPath);
that.options = _.merge( that.options = _.merge(
that.options, that.options,
_.omit(_config, "configFile", "defaultsPlugins", "generator", "extension") _.omit(_config, 'configFile', 'defaultsPlugins', 'generator', 'extension')
); );
} }
catch(err) { catch(err) {
@@ -139,19 +117,19 @@ Configuration.prototype.load = function() {
}) })
.then(function() { .then(function() {
if (!that.book.isSubBook()) { if (!that.book.isSubBook()) {
if (!semver.satisfies(pkg.version, that.options.gitbook)) { if (!version.satisfies(that.options.gitbook)) {
throw new Error("GitBook version doesn't satisfy version required by the book: "+that.options.gitbook); throw new Error('GitBook version doesn\'t satisfy version required by the book: '+that.options.gitbook);
} }
if (that.options.gitbook != "*" && !semver.satisfies(semver.inc(pkg.version, "patch"), that.options.gitbook)) { if (that.options.gitbook != '*' && !semver.satisfies(semver.inc(pkg.version, 'patch'), that.options.gitbook)) {
that.book.log.warn.ln("gitbook version specified in your book.json might be too strict for future patches, \""+(_.first(pkg.version.split("."))+".x.x")+"\" is more adequate"); that.book.log.warn.ln('gitbook version specified in your book.json might be too strict for future patches, \''+(_.first(pkg.version.split('.'))+'.x.x')+'\' is more adequate');
} }
} }
that.options.output = path.resolve(that.options.output || that.book.resolve("_book")); that.options.output = path.resolve(that.options.output || that.book.resolve('_book'));
that.options.plugins = normalizePluginsList(that.options.plugins); that.options.plugins = normalizePluginsList(that.options.plugins);
that.options.defaultsPlugins = normalizePluginsList(that.options.defaultsPlugins || "", false); that.options.defaultsPlugins = normalizePluginsList(that.options.defaultsPlugins || '', false);
that.options.plugins = _.union(that.options.plugins, that.options.defaultsPlugins); that.options.plugins = _.union(that.options.plugins, that.options.defaultsPlugins);
that.options.plugins = _.uniq(that.options.plugins, "name"); that.options.plugins = _.uniq(that.options.plugins, 'name');
// Default value for text direction (from language) // Default value for text direction (from language)
if (!that.options.direction) { if (!that.options.direction) {
@@ -168,9 +146,47 @@ Configuration.prototype.extend = function(options) {
_.extend(this.options, options); _.extend(this.options, options);
}; };
// Replace the whole configuration
Configuration.prototype.replace = function(options) {
var that = this;
this.options = _.cloneDeep(DEFAULT_CONFIG);
this.options = _.merge(this.options, options || {});
// options.input == book.root
Object.defineProperty(this.options, 'input', {
get: function () {
return that.book.root;
}
});
// options.originalInput == book.parent.root
Object.defineProperty(this.options, 'originalInput', {
get: function () {
return that.book.parent? that.book.parent.root : undefined;
}
});
// options.originalOutput == book.parent.options.output
Object.defineProperty(this.options, 'originalOutput', {
get: function () {
return that.book.parent? that.book.parent.options.output : undefined;
}
});
};
// Dump configuration as json object
Configuration.prototype.dump = function() {
return _.cloneDeep(this.options);
};
// Get structure file // Get structure file
Configuration.prototype.getStructure = function(name) { Configuration.prototype.getStructure = function(name, dontStripExt) {
return this.options.structure[name].split(".").slice(0, -1).join("."); var filename = this.options.structure[name];
if (dontStripExt) return filename;
filename = filename.split('.').slice(0, -1).join('.');
return filename;
}; };
// Return normalized language // Return normalized language
@@ -183,118 +199,12 @@ Configuration.prototype.get = function(key, def) {
return _.get(this.options, key, def); return _.get(this.options, key, def);
}; };
// Default configuration // Update a configuration
Configuration.DEFAULT = { Configuration.prototype.set = function(key, value) {
// Options that can"t be extend return _.set(this.options, key, value);
"configFile": "book",
"generator": "website",
"extension": null,
// Book metadats (somes are extracted from the README by default)
"title": null,
"description": null,
"isbn": null,
"language": "en",
"direction": null,
"author": null,
// version of gitbook to use
"gitbook": "*",
// Structure
"structure": {
"langs": "LANGS.md",
"readme": "README.md",
"glossary": "GLOSSARY.md",
"summary": "SUMMARY.md"
},
// CSS Styles
"styles": {
"website": "styles/website.css",
"print": "styles/print.css",
"ebook": "styles/ebook.css",
"pdf": "styles/pdf.css",
"mobi": "styles/mobi.css",
"epub": "styles/epub.css"
},
// Plugins list, can contain "-name" for removing default plugins
"plugins": [],
// Global configuration for plugins
"pluginsConfig": {
"fontSettings": {
"theme": null, //"sepia", "night" or "white",
"family": "sans",// "serif" or "sans",
"size": 2 // 1 - 4
}
},
// Variables for templating
"variables": {},
// Set another theme with your own layout
// It"s recommended to use plugins or add more options for default theme, though
// See https://github.com/GitbookIO/gitbook/issues/209
"theme": path.resolve(__dirname, "../theme"),
// Links in template (null: default, false: remove, string: new value)
"links": {
// Custom links at top of sidebar
"sidebar": {
//"Custom link name": "https://customlink.com"
},
// Sharing links
"sharing": {
"google": null,
"facebook": null,
"twitter": null,
"weibo": null,
"all": null
}
},
// Options for PDF generation
"pdf": {
// Add toc at the end of the file
"toc": true,
// Add page numbers to the bottom of every page
"pageNumbers": false,
// Font size for the file content
"fontSize": 12,
// Paper size for the pdf
// Choices are [u’a0’, u’a1’, u’a2’, u’a3’, u’a4’, u’a5’, u’a6’, u’b0’, u’b1’, u’b2’, u’b3’, u’b4’, u’b5’, u’b6’, u’legal’, u’letter’]
"paperSize": "a4",
// How to mark detected chapters.
// Choices are “pagebreak”, “rule”, "both" or “none”.
"chapterMark" : "pagebreak",
// An XPath expression. Page breaks are inserted before the specified elements.
// To disable use the expression: "/"
"pageBreaksBefore": "/",
// Margin (in pts)
// Note: 72 pts equals 1 inch
"margin": {
"right": 62,
"left": 62,
"top": 56,
"bottom": 56
},
//Header HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
"headerTemplate": "",
//Footer HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
"footerTemplate": ""
}
}; };
// Default configuration
Configuration.DEFAULT = DEFAULT_CONFIG;
module.exports= Configuration; module.exports= Configuration;
+20 -10
View File
@@ -1,15 +1,19 @@
var path = require("path"); var _ = require('lodash');
var nunjucks = require("nunjucks"); var path = require('path');
var nunjucks = require('nunjucks');
var git = require("./utils/git"); var git = require('./utils/git');
var fs = require("./utils/fs"); var fs = require('./utils/fs');
var pathUtil = require("./utils/path"); var pathUtil = require('./utils/path');
// The loader should handle relative and git url // The loader should handle relative and git url
var BookLoader = nunjucks.Loader.extend({ var BookLoader = nunjucks.Loader.extend({
async: true, async: true,
init: function(book) { init: function(book, opts) {
this.opts = _.defaults(opts || {}, {
interpolate: _.identity
});
this.book = book; this.book = book;
}, },
@@ -20,14 +24,20 @@ var BookLoader = nunjucks.Loader.extend({
.then(function(filepath) { .then(function(filepath) {
// Is local file // Is local file
if (!filepath) filepath = path.resolve(fileurl); if (!filepath) filepath = path.resolve(fileurl);
else that.book.log.debug.ln("resolve from git", fileurl, "to", filepath); else that.book.log.debug.ln('resolve from git', fileurl, 'to', filepath);
// Read file from absolute path // Read file from absolute path
return fs.readFile(filepath) return fs.readFile(filepath)
.then(function(source) {
return that.opts.interpolate(filepath, source.toString());
})
.then(function(source) { .then(function(source) {
return { return {
src: source.toString(), src: source,
path: filepath path: filepath,
// We disable cache sincde content is modified (shortcuts, ...)
noCache: true
}; };
}); });
}) })
@@ -53,7 +63,7 @@ var BookLoader = nunjucks.Loader.extend({
return path.resolve(path.dirname(from), to); return path.resolve(path.dirname(from), to);
}, },
// Handle all files as relative, so that nunjucks pass responsability to "resolve" // Handle all files as relative, so that nunjucks pass responsability to 'resolve'
// Only git urls are considered as absolute // Only git urls are considered as absolute
isRelative: function(filename) { isRelative: function(filename) {
return !git.checkUrl(filename); return !git.checkUrl(filename);
+13 -13
View File
@@ -1,12 +1,12 @@
var _ = require("lodash"); var _ = require('lodash');
var path = require("path"); var path = require('path');
var Q = require("q"); var Q = require('q');
var fs = require("./utils/fs"); var fs = require('./utils/fs');
var BaseGenerator = function(book) { var BaseGenerator = function(book) {
this.book = book; this.book = book;
Object.defineProperty(this, "options", { Object.defineProperty(this, 'options', {
get: function () { get: function () {
return this.book.options; return this.book.options;
} }
@@ -16,19 +16,19 @@ var BaseGenerator = function(book) {
}; };
BaseGenerator.prototype.callHook = function(name, data) { BaseGenerator.prototype.callHook = function(name, data) {
return this.book.plugins.hook(name, data); return this.book.callHook(name, data);
}; };
// Prepare the genertor // Prepare the genertor
BaseGenerator.prototype.prepare = function() { BaseGenerator.prototype.prepare = function() {
var that = this; var that = this;
return that.callHook("init"); return that.callHook('init');
}; };
// Write a parsed file to the output // Write a parsed file to the output
BaseGenerator.prototype.convertFile = function(input) { BaseGenerator.prototype.convertFile = function(input) {
return Q.reject(new Error("Could not convert "+input)); return Q.reject(new Error('Could not convert '+input));
}; };
// Copy file to the output (non parsable) // Copy file to the output (non parsable)
@@ -51,16 +51,16 @@ BaseGenerator.prototype.copyCover = function() {
var that = this; var that = this;
return Q.all([ return Q.all([
fs.copy(that.book.resolve("cover.jpg"), path.join(that.options.output, "cover.jpg")), fs.copy(that.book.resolve('cover.jpg'), path.join(that.options.output, 'cover.jpg')),
fs.copy(that.book.resolve("cover_small.jpg"), path.join(that.options.output, "cover_small.jpg")) fs.copy(that.book.resolve('cover_small.jpg'), path.join(that.options.output, 'cover_small.jpg'))
]) ])
.fail(function() { .fail(function() {
// If orignaly from multi-lang, try copy from parent // If orignaly from multi-lang, try copy from parent
if (!that.book.isSubBook()) return; if (!that.book.isSubBook()) return;
return Q.all([ return Q.all([
fs.copy(path.join(that.book.parentRoot(), "cover.jpg"), path.join(that.options.output, "cover.jpg")), fs.copy(path.join(that.book.parentRoot(), 'cover.jpg'), path.join(that.options.output, 'cover.jpg')),
fs.copy(path.join(that.book.parentRoot(), "cover_small.jpg"), path.join(that.options.output, "cover_small.jpg")) fs.copy(path.join(that.book.parentRoot(), 'cover_small.jpg'), path.join(that.options.output, 'cover_small.jpg'))
]); ]);
}) })
.fail(function() { .fail(function() {
@@ -70,7 +70,7 @@ BaseGenerator.prototype.copyCover = function() {
// At teh end of the generation // At teh end of the generation
BaseGenerator.prototype.finish = function() { BaseGenerator.prototype.finish = function() {
return Q.reject(new Error("Could not finish generation")); return Q.reject(new Error('Could not finish generation'));
}; };
module.exports = BaseGenerator; module.exports = BaseGenerator;
+89 -56
View File
@@ -1,12 +1,13 @@
var util = require("util"); var util = require('util');
var path = require("path"); var path = require('path');
var Q = require("q"); var Q = require('q');
var _ = require("lodash"); var _ = require('lodash');
var exec = require("child_process").exec; var juice = require('juice');
var exec = require('child_process').exec;
var fs = require("../utils/fs"); var fs = require('../utils/fs');
var stringUtils = require("../utils/string"); var stringUtils = require('../utils/string');
var BaseGenerator = require("./website"); var BaseGenerator = require('./website');
var Generator = function(book, format) { var Generator = function(book, format) {
BaseGenerator.apply(this, arguments); BaseGenerator.apply(this, arguments);
@@ -15,10 +16,10 @@ var Generator = function(book, format) {
this.ebookFormat = format; this.ebookFormat = format;
// Resources namespace // Resources namespace
this.namespace = "ebook"; this.namespace = 'ebook';
// Styles to use // Styles to use
this.styles = _.compact(["print", "ebook", this.ebookFormat]); this.styles = _.compact(['print', 'ebook', this.ebookFormat]);
// Convert images (svg -> png) // Convert images (svg -> png)
this.convertImages = true; this.convertImages = true;
@@ -26,9 +27,9 @@ var Generator = function(book, format) {
util.inherits(Generator, BaseGenerator); util.inherits(Generator, BaseGenerator);
Generator.prototype.prepareTemplates = function() { Generator.prototype.prepareTemplates = function() {
this.templates.page = this.book.plugins.template("ebook:page") || path.resolve(this.options.theme, "templates/ebook/page.html"); this.templates.page = this.book.plugins.template('ebook:page') || path.resolve(this.options.theme, 'templates/ebook/page.html');
this.templates.summary = this.book.plugins.template("ebook:summary") || path.resolve(this.options.theme, "templates/ebook/summary.html"); this.templates.summary = this.book.plugins.template('ebook:summary') || path.resolve(this.options.theme, 'templates/ebook/summary.html');
this.templates.glossary = this.book.plugins.template("ebook:glossary") || path.resolve(this.options.theme, "templates/ebook/glossary.html"); this.templates.glossary = this.book.plugins.template('ebook:glossary') || path.resolve(this.options.theme, 'templates/ebook/glossary.html');
return Q(); return Q();
}; };
@@ -37,8 +38,39 @@ Generator.prototype.prepareTemplates = function() {
Generator.prototype.writeSummary = function() { Generator.prototype.writeSummary = function() {
var that = this; var that = this;
that.book.log.info.ln("write SUMMARY.html"); that.book.log.info.ln('write SUMMARY.html');
return this._writeTemplate(this.templates.summary, {}, path.join(this.options.output, "SUMMARY.html")); return this._writeTemplate(this.templates.summary, {}, path.join(this.options.output, 'SUMMARY.html'));
};
// Return template for footer/header with inlined css
Generator.prototype.getPDFTemplate = function(id) {
var tpl = this.options.pdf[id+'Template'];
var defaultTpl = path.resolve(this.options.theme, 'templates/ebook/'+id+'.html');
var defaultCSS = path.resolve(this.options.theme, 'assets/ebook/pdf.css');
// Default template from theme
if (!tpl && fs.existsSync(defaultTpl)) {
tpl = fs.readFileSync(defaultTpl, { encoding: 'utf-8' });
}
// Inline CSS using juice
var stylesheets = [];
// From theme
if (fs.existsSync(defaultCSS)) {
stylesheets.push(fs.readFileSync(defaultCSS, { encoding: 'utf-8' }));
}
// Custom PDF style
if (this.styles.pdf) {
stylesheets.push(fs.readFileSync(this.book.resolve(this.styles.pdf), { encoding: 'utf-8' }));
}
tpl = juice(tpl, {
extraCss: stylesheets.join('\n\n')
});
return tpl;
}; };
Generator.prototype.finish = function() { Generator.prototype.finish = function() {
@@ -52,70 +84,71 @@ Generator.prototype.finish = function() {
.then(function() { .then(function() {
if (!that.ebookFormat) return Q(); if (!that.ebookFormat) return Q();
if (!that.options.cover && fs.existsSync(path.join(that.options.output, "cover.jpg"))) { if (!that.options.cover && fs.existsSync(path.join(that.options.output, 'cover.jpg'))) {
that.options.cover = path.join(that.options.output, "cover.jpg"); that.options.cover = path.join(that.options.output, 'cover.jpg');
} }
var d = Q.defer(); var d = Q.defer();
var _options = { var _options = {
"--cover": that.options.cover, '--cover': that.options.cover,
"--title": that.options.title, '--title': that.options.title,
"--comments": that.options.description, '--comments': that.options.description,
"--isbn": that.options.isbn, '--isbn': that.options.isbn,
"--authors": that.options.author, '--authors': that.options.author,
"--language": that.options.language, '--language': that.options.language,
"--book-producer": "GitBook", '--book-producer': 'GitBook',
"--publisher": "GitBook", '--publisher': 'GitBook',
"--chapter": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter ')]", '--chapter': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter \')]',
"--level1-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-1 ')]", '--level1-toc': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter-1 \')]',
"--level2-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-2 ')]", '--level2-toc': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter-2 \')]',
"--level3-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-3 ')]", '--level3-toc': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter-3 \')]',
"--no-chapters-in-toc": true, '--no-chapters-in-toc': true,
"--max-levels": "1", '--max-levels': '1',
"--breadth-first": true '--breadth-first': true
}; };
if (that.ebookFormat == "pdf") { if (that.ebookFormat == 'pdf') {
var pdfOptions = that.options.pdf; var pdfOptions = that.options.pdf;
_.extend(_options, { _.extend(_options, {
"--chapter-mark": String(pdfOptions.chapterMark), '--chapter-mark': String(pdfOptions.chapterMark),
"--page-breaks-before": String(pdfOptions.pageBreaksBefore), '--page-breaks-before': String(pdfOptions.pageBreaksBefore),
"--margin-left": String(pdfOptions.margin.left), '--margin-left': String(pdfOptions.margin.left),
"--margin-right": String(pdfOptions.margin.right), '--margin-right': String(pdfOptions.margin.right),
"--margin-top": String(pdfOptions.margin.top), '--margin-top': String(pdfOptions.margin.top),
"--margin-bottom": String(pdfOptions.margin.bottom), '--margin-bottom': String(pdfOptions.margin.bottom),
"--pdf-default-font-size": String(pdfOptions.fontSize), '--pdf-default-font-size': String(pdfOptions.fontSize),
"--pdf-mono-font-size": String(pdfOptions.fontSize), '--pdf-mono-font-size': String(pdfOptions.fontSize),
"--paper-size": String(pdfOptions.paperSize), '--paper-size': String(pdfOptions.paperSize),
"--pdf-page-numbers": Boolean(pdfOptions.pageNumbers), '--pdf-page-numbers': Boolean(pdfOptions.pageNumbers),
"--pdf-header-template": String(pdfOptions.headerTemplate) || "<p class='header'><span>"+that.options.title+"</span></p>", '--pdf-header-template': that.getPDFTemplate('header'),
"--pdf-footer-template": String(pdfOptions.footerTemplate) || "<p class='footer'><span>_SECTION_</span> <span style='float:right;'>_PAGENUM_</span></p>" '--pdf-footer-template': that.getPDFTemplate('footer'),
'--pdf-sans-family': String(pdfOptions.fontFamily)
}); });
} else if (that.ebookFormat == "epub") { } else if (that.ebookFormat == 'epub') {
_.extend(_options, { _.extend(_options, {
"--dont-split-on-page-breaks": true '--dont-split-on-page-breaks': true
}); });
} }
var command = [ var command = [
"ebook-convert", 'ebook-convert',
path.join(that.options.output, "SUMMARY.html"), path.join(that.options.output, 'SUMMARY.html'),
path.join(that.options.output, "index."+that.ebookFormat), path.join(that.options.output, 'index.'+that.ebookFormat),
stringUtils.optionsToShellArgs(_options) stringUtils.optionsToShellArgs(_options)
].join(" "); ].join(' ');
that.book.log.info("start conversion to", that.ebookFormat, "...."); that.book.log.info('start conversion to', that.ebookFormat, '....');
var child = exec(command, function (error, stdout) { var child = exec(command, function (error, stdout) {
if (error) { if (error) {
that.book.log.info.fail(); that.book.log.info.fail();
if (error.code == 127) { if (error.code == 127) {
error.message = "Need to install ebook-convert from Calibre"; error.message = 'Need to install ebook-convert from Calibre';
} else { } else {
error.message = error.message + " "+stdout; error.message = error.message + ' '+stdout;
} }
return d.reject(error); return d.reject(error);
} }
@@ -124,11 +157,11 @@ Generator.prototype.finish = function() {
d.resolve(); d.resolve();
}); });
child.stdout.on("data", function (data) { child.stdout.on('data', function (data) {
that.book.log.debug(data); that.book.log.debug(data);
}); });
child.stderr.on("data", function (data) { child.stderr.on('data', function (data) {
that.book.log.debug(data); that.book.log.debug(data);
}); });
+10 -10
View File
@@ -1,11 +1,11 @@
var util = require("util"); var util = require('util');
var path = require("path"); var path = require('path');
var Q = require("q"); var Q = require('q');
var _ = require("lodash"); var _ = require('lodash');
var fs = require("../utils/fs"); var fs = require('../utils/fs');
var BaseGenerator = require("../generator"); var BaseGenerator = require('../generator');
var links = require("../utils/links"); var links = require('../utils/links');
var Generator = function() { var Generator = function() {
BaseGenerator.apply(this, arguments); BaseGenerator.apply(this, arguments);
@@ -26,7 +26,7 @@ Generator.prototype.convertFile = function(input) {
sections: page.sections sections: page.sections
}; };
var output = links.changeExtension(page.path, ".json"); var output = links.changeExtension(page.path, '.json');
output = path.join(that.options.output, output); output = path.join(that.options.output, output);
return fs.writeFile( return fs.writeFile(
@@ -51,7 +51,7 @@ Generator.prototype.writeReadme = function() {
langs = that.book.langs; langs = that.book.langs;
mainLang = langs.length > 0? _.first(langs).lang : null; mainLang = langs.length > 0? _.first(langs).lang : null;
readme = links.changeExtension(that.book.readmeFile, ".json"); readme = links.changeExtension(that.book.readmeFile, '.json');
// Read readme from main language // Read readme from main language
return fs.readFile( return fs.readFile(
@@ -67,7 +67,7 @@ Generator.prototype.writeReadme = function() {
// Write it as README.json // Write it as README.json
return fs.writeFile( return fs.writeFile(
path.join(that.options.output, "README.json"), path.join(that.options.output, 'README.json'),
JSON.stringify(json, null, 4) JSON.stringify(json, null, 4)
); );
}); });
+39 -47
View File
@@ -1,18 +1,18 @@
var util = require("util"); var util = require('util');
var path = require("path"); var path = require('path');
var Q = require("q"); var Q = require('q');
var _ = require("lodash"); var _ = require('lodash');
var nunjucks = require("nunjucks"); var nunjucks = require('nunjucks');
var AutoEscapeExtension = require("nunjucks-autoescape")(nunjucks); var AutoEscapeExtension = require('nunjucks-autoescape')(nunjucks);
var FilterExtension = require("nunjucks-filter")(nunjucks); var FilterExtension = require('nunjucks-filter')(nunjucks);
var fs = require("../utils/fs"); var fs = require('../utils/fs');
var BaseGenerator = require("../generator"); var BaseGenerator = require('../generator');
var links = require("../utils/links"); var links = require('../utils/links');
var i18n = require("../utils/i18n"); var i18n = require('../utils/i18n');
var pkg = require("../../package.json"); var pkg = require('../../package.json');
var Generator = function() { var Generator = function() {
BaseGenerator.apply(this, arguments); BaseGenerator.apply(this, arguments);
@@ -21,10 +21,10 @@ var Generator = function() {
this.revision = new Date(); this.revision = new Date();
// Resources namespace // Resources namespace
this.namespace = "website"; this.namespace = 'website';
// Style to integrates in the output // Style to integrates in the output
this.styles = ["website"]; this.styles = ['website'];
// Convert images (svg -> png) // Convert images (svg -> png)
this.convertImages = false; this.convertImages = false;
@@ -63,9 +63,9 @@ Generator.prototype.prepareStyles = function() {
// Prepare templates // Prepare templates
Generator.prototype.prepareTemplates = function() { Generator.prototype.prepareTemplates = function() {
this.templates.page = this.book.plugins.template("site:page") || path.resolve(this.options.theme, "templates/website/page.html"); this.templates.page = this.book.plugins.template('site:page') || path.resolve(this.options.theme, 'templates/website/page.html');
this.templates.langs = this.book.plugins.template("site:langs") || path.resolve(this.options.theme, "templates/website/langs.html"); this.templates.langs = this.book.plugins.template('site:langs') || path.resolve(this.options.theme, 'templates/website/langs.html');
this.templates.glossary = this.book.plugins.template("site:glossary") || path.resolve(this.options.theme, "templates/website/glossary.html"); this.templates.glossary = this.book.plugins.template('site:glossary') || path.resolve(this.options.theme, 'templates/website/glossary.html');
return Q(); return Q();
}; };
@@ -79,7 +79,7 @@ Generator.prototype.prepareTemplateEngine = function() {
var language = that.book.config.normalizeLanguage(); var language = that.book.config.normalizeLanguage();
if (!i18n.hasLocale(language)) { if (!i18n.hasLocale(language)) {
that.book.log.warn.ln("Language '"+language+"' is not available as a layout locales (en, "+i18n.getLocales().join(", ")+")"); that.book.log.warn.ln('Language "'+language+'" is not available as a layout locales (en, '+i18n.getLocales().join(', ')+')');
} }
var folders = _.chain(that.templates) var folders = _.chain(that.templates)
@@ -96,14 +96,14 @@ Generator.prototype.prepareTemplateEngine = function() {
); );
// Add filter // Add filter
that.env.addFilter("contentLink", that.book.contentLink.bind(that.book)); that.env.addFilter('contentLink', that.book.contentLink.bind(that.book));
that.env.addFilter("lvl", function(lvl) { that.env.addFilter('lvl', function(lvl) {
return lvl.split(".").length; return lvl.split('.').length;
}); });
// Add extension // Add extension
that.env.addExtension("AutoEscapeExtension", new AutoEscapeExtension(that.env)); that.env.addExtension('AutoEscapeExtension', new AutoEscapeExtension(that.env));
that.env.addExtension("FilterExtension", new FilterExtension(that.env)); that.env.addExtension('FilterExtension', new FilterExtension(that.env));
}); });
}; };
@@ -112,7 +112,6 @@ Generator.prototype.finish = function() {
return this.copyAssets() return this.copyAssets()
.then(this.copyCover) .then(this.copyCover)
.then(this.writeGlossary) .then(this.writeGlossary)
.then(this.writeSearchIndex)
.then(this.writeLangsIndex); .then(this.writeLangsIndex);
}; };
@@ -123,20 +122,20 @@ Generator.prototype.convertFile = function(input) {
return that.book.parsePage(input, { return that.book.parsePage(input, {
convertImages: that.convertImages, convertImages: that.convertImages,
interpolateTemplate: function(page) { interpolateTemplate: function(page) {
return that.callHook("page:before", page); return that.callHook('page:before', page);
}, },
interpolateContent: function(page) { interpolateContent: function(page) {
return that.callHook("page", page); return that.callHook('page', page);
} }
}) })
.then(function(page) { .then(function(page) {
var relativeOutput = that.book.contentPath(page.path); var relativeOutput = that.book.contentPath(page.path);
var output = path.join(that.options.output, relativeOutput); var output = path.join(that.options.output, relativeOutput);
var basePath = path.relative(path.dirname(output), that.options.output) || "."; var basePath = path.relative(path.dirname(output), that.options.output) || '.';
if (process.platform === "win32") basePath = basePath.replace(/\\/g, "/"); if (process.platform === 'win32') basePath = basePath.replace(/\\/g, '/');
that.book.log.debug.ln("write parsed file", page.path, "to", relativeOutput); that.book.log.debug.ln('write parsed file', page.path, 'to', relativeOutput);
return that._writeTemplate(that.templates.page, { return that._writeTemplate(that.templates.page, {
progress: page.progress, progress: page.progress,
@@ -145,7 +144,7 @@ Generator.prototype.convertFile = function(input) {
content: page.sections, content: page.sections,
basePath: basePath, basePath: basePath,
staticBase: links.join(basePath, "gitbook") staticBase: links.join(basePath, 'gitbook')
}, output); }, output);
}); });
}; };
@@ -156,7 +155,7 @@ Generator.prototype.writeLangsIndex = function() {
return this._writeTemplate(this.templates.langs, { return this._writeTemplate(this.templates.langs, {
langs: this.book.langs langs: this.book.langs
}, path.join(this.options.output, "index.html")); }, path.join(this.options.output, 'index.html'));
}; };
// Write glossary // Write glossary
@@ -164,15 +163,7 @@ Generator.prototype.writeGlossary = function() {
// No glossary // No glossary
if (this.book.glossary.length === 0) return Q(); if (this.book.glossary.length === 0) return Q();
return this._writeTemplate(this.templates.glossary, {}, path.join(this.options.output, "GLOSSARY.html")); return this._writeTemplate(this.templates.glossary, {}, path.join(this.options.output, 'GLOSSARY.html'));
};
// Write the search index
Generator.prototype.writeSearchIndex = function() {
return fs.writeFile(
path.join(this.options.output, "search_index.json"),
JSON.stringify(this.book.searchIndex)
);
}; };
// Convert a page into a normalized data set // Convert a page into a normalized data set
@@ -189,7 +180,7 @@ Generator.prototype.normalizePage = function(page) {
return Q() return Q()
.then(function() { .then(function() {
return _callHook("page"); return _callHook('page');
}) })
.then(function() { .then(function() {
return page; return page;
@@ -217,6 +208,7 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate)
title: that.options.title, title: that.options.title,
description: that.options.description, description: that.options.description,
language: that.book.config.normalizeLanguage(), language: that.book.config.normalizeLanguage(),
innerlanguage: that.book.isSubBook()? that.book.config.get('language') : null,
glossary: that.book.glossary, glossary: that.book.glossary,
@@ -231,10 +223,10 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate)
options: that.options, options: that.options,
basePath: ".", basePath: '.',
staticBase: path.join(".", "gitbook"), staticBase: path.join('.', 'gitbook'),
"__": that.book.i18n.bind(that.book) '__': that.book.i18n.bind(that.book)
}, options) }, options)
); );
}) })
@@ -253,15 +245,15 @@ Generator.prototype.copyAssets = function() {
// Copy gitbook assets // Copy gitbook assets
return fs.copy( return fs.copy(
path.join(that.options.theme, "assets"), path.join(that.options.theme, 'assets/'+this.namespace),
path.join(that.options.output, "gitbook") path.join(that.options.output, 'gitbook')
) )
// Copy plugins assets // Copy plugins assets
.then(function() { .then(function() {
return Q.all( return Q.all(
_.map(that.book.plugins.list, function(plugin) { _.map(that.book.plugins.list, function(plugin) {
var pluginAssets = path.join(that.options.output, "gitbook/plugins/", plugin.name); var pluginAssets = path.join(that.options.output, 'gitbook/plugins/', plugin.name);
return plugin.copyAssets(pluginAssets, that.namespace); return plugin.copyAssets(pluginAssets, that.namespace);
}) })
); );
+65 -59
View File
@@ -1,50 +1,56 @@
var Q = require("q"); /* eslint no-console: 0 */
var _ = require("lodash");
var path = require("path");
var tinylr = require("tiny-lr");
var color = require("bash-color");
var Book = require("./book"); var Q = require('q');
var Server = require("./utils/server"); var _ = require('lodash');
var stringUtils = require("./utils/string"); var path = require('path');
var watch = require("./utils/watch"); var tinylr = require('tiny-lr');
var logger = require("./utils/logger"); var color = require('bash-color');
var Book = require('./book');
var initBook = require('./init');
var Server = require('./utils/server');
var stringUtils = require('./utils/string');
var watch = require('./utils/watch');
var logger = require('./utils/logger');
var LOG_OPTION = { var LOG_OPTION = {
name: "log", name: 'log',
description: "Minimum log level to display", description: 'Minimum log level to display',
values: _.chain(logger.LEVELS).keys().map(stringUtils.toLowerCase).value(), values: _.chain(logger.LEVELS).keys().map(stringUtils.toLowerCase).value(),
defaults: "info" defaults: 'info'
}; };
var FORMAT_OPTION = { var FORMAT_OPTION = {
name: "format", name: 'format',
description: "Format to build to", description: 'Format to build to',
values: ["website", "json", "ebook"], values: ['website', 'json', 'ebook'],
defaults: "website" defaults: 'website'
}; };
// Export init to gitbook library
Book.init = initBook;
module.exports = { module.exports = {
Book: Book, Book: Book,
LOG_LEVELS: logger.LEVELS, LOG_LEVELS: logger.LEVELS,
commands: _.flatten([ commands: _.flatten([
{ {
name: "build [book] [output]", name: 'build [book] [output]',
description: "build a book", description: 'build a book',
options: [ options: [
FORMAT_OPTION, FORMAT_OPTION,
LOG_OPTION LOG_OPTION
], ],
exec: function(args, kwargs) { exec: function(args, kwargs) {
var input = args[0] || process.cwd(); var input = args[0] || process.cwd();
var output = args[1] || path.join(input, "_book"); var output = args[1] || path.join(input, '_book');
var book = new Book(input, _.extend({}, { var book = new Book(input, _.extend({}, {
"config": { 'config': {
"output": output 'output': output
}, },
"logLevel": kwargs.log 'logLevel': kwargs.log
})); }));
return book.parse() return book.parse()
@@ -52,16 +58,16 @@ module.exports = {
return book.generate(kwargs.format); return book.generate(kwargs.format);
}) })
.then(function(){ .then(function(){
console.log(""); console.log('');
console.log(color.green("Done, without error")); console.log(color.green('Done, without error'));
}); });
} }
}, },
_.map(["pdf", "epub", "mobi"], function(ebookType) { _.map(['pdf', 'epub', 'mobi'], function(ebookType) {
return { return {
name: ebookType+" [book] [output]", name: ebookType+' [book] [output]',
description: "build a book to "+ebookType, description: 'build a book to '+ebookType,
options: [ options: [
LOG_OPTION LOG_OPTION
], ],
@@ -70,7 +76,7 @@ module.exports = {
var output = args[1]; var output = args[1];
var book = new Book(input, _.extend({}, { var book = new Book(input, _.extend({}, {
"logLevel": kwargs.log 'logLevel': kwargs.log
})); }));
return book.parse() return book.parse()
@@ -80,30 +86,30 @@ module.exports = {
}); });
}) })
.then(function(){ .then(function(){
console.log(""); console.log('');
console.log(color.green("Done, without error")); console.log(color.green('Done, without error'));
}); });
} }
}; };
}), }),
{ {
name: "serve [book]", name: 'serve [book]',
description: "Build then serve a gitbook from a directory", description: 'Build then serve a gitbook from a directory',
options: [ options: [
{ {
name: "port", name: 'port',
description: "Port for server to listen on", description: 'Port for server to listen on',
defaults: 4000 defaults: 4000
}, },
{ {
name: "lrport", name: 'lrport',
description: "Port for livereload server to listen on", description: 'Port for livereload server to listen on',
defaults: 35729 defaults: 35729
}, },
{ {
name: "watch", name: 'watch',
description: "Enable/disable file watcher", description: 'Enable/disable file watcher',
defaults: true defaults: true
}, },
FORMAT_OPTION, FORMAT_OPTION,
@@ -118,15 +124,15 @@ module.exports = {
var lrPath; var lrPath;
var generate = function() { var generate = function() {
if (server.isRunning()) console.log("Stopping server"); if (server.isRunning()) console.log('Stopping server');
return server.stop() return server.stop()
.then(function() { .then(function() {
var book = new Book(input, _.extend({}, { var book = new Book(input, _.extend({}, {
"config": { 'config': {
"defaultsPlugins": ["livereload"] 'defaultsPlugins': ['livereload']
}, },
"logLevel": kwargs.log 'logLevel': kwargs.log
})); }));
return book.parse() return book.parse()
@@ -137,10 +143,10 @@ module.exports = {
}) })
.then(function(book) { .then(function(book) {
console.log(); console.log();
console.log("Starting server ..."); console.log('Starting server ...');
return server.start(book.options.output, kwargs.port) return server.start(book.options.output, kwargs.port)
.then(function() { .then(function() {
console.log("Serving book on http://localhost:"+kwargs.port); console.log('Serving book on http://localhost:'+kwargs.port);
if (lrPath) { if (lrPath) {
// trigger livereload // trigger livereload
@@ -157,8 +163,8 @@ module.exports = {
.then(function(filepath) { .then(function(filepath) {
// set livereload path // set livereload path
lrPath = filepath; lrPath = filepath;
console.log("Restart after change in file", filepath); console.log('Restart after change in file', filepath);
console.log(""); console.log('');
return generate(); return generate();
}); });
}); });
@@ -167,17 +173,17 @@ module.exports = {
return Q.nfcall(lrServer.listen.bind(lrServer), kwargs.lrport) return Q.nfcall(lrServer.listen.bind(lrServer), kwargs.lrport)
.then(function() { .then(function() {
console.log("Live reload server started on port:", kwargs.lrport); console.log('Live reload server started on port:', kwargs.lrport);
console.log("Press CTRL+C to quit ..."); console.log('Press CTRL+C to quit ...');
console.log(""); console.log('');
return generate(); return generate();
}); });
} }
}, },
{ {
name: "install [book]", name: 'install [book]',
description: "install plugins dependencies", description: 'install plugins dependencies',
exec: function(args) { exec: function(args) {
var input = args[0] || process.cwd(); var input = args[0] || process.cwd();
@@ -188,20 +194,20 @@ module.exports = {
return book.plugins.install(); return book.plugins.install();
}) })
.then(function(){ .then(function(){
console.log(""); console.log('');
console.log(color.green("Done, without error")); console.log(color.green('Done, without error'));
}); });
} }
}, },
{ {
name: "init [directory]", name: 'init [directory]',
description: "create files and folders based on contents of SUMMARY.md", description: 'create files and folders based on contents of SUMMARY.md',
exec: function(args) { exec: function(args) {
return Book.init(args[0] || process.cwd()) return initBook(args[0] || process.cwd())
.then(function(){ .then(function(){
console.log(""); console.log('');
console.log(color.green("Done, without error")); console.log(color.green('Done, without error'));
}); });
} }
} }
+83
View File
@@ -0,0 +1,83 @@
var _ = require('lodash');
var Q = require('q');
var path = require('path');
var Book = require('./book');
var fs = require('./utils/fs');
// Initialize folder structure for a book
// Read SUMMARY to created the right chapter
function initBook(root, opts) {
var book = new Book(root, opts);
var extensionToUse = '.md';
var chaptersPaths = function(chapters) {
return _.reduce(chapters || [], function(accu, chapter) {
var o = {
title: chapter.title
};
if (chapter.path) o.path = chapter.path;
return accu.concat(
[o].concat(chaptersPaths(chapter.articles))
);
}, []);
};
book.log.info.ln('init book at', root);
return fs.mkdirp(root)
.then(function() {
book.log.info.ln('detect structure from SUMMARY (if it exists)');
return book.parseSummary();
})
.fail(function() {
return Q();
})
.then(function() {
var summary = book.summaryFile || 'SUMMARY.md';
var chapters = book.summary.chapters || [];
extensionToUse = path.extname(summary);
if (chapters.length === 0) {
chapters = [
{
title: 'Summary',
path: 'SUMMARY'+extensionToUse
},
{
title: 'Introduction',
path: 'README'+extensionToUse
}
];
}
return Q(chaptersPaths(chapters));
})
.then(function(chapters) {
// Create files that don't exist
return Q.all(_.map(chapters, function(chapter) {
if (!chapter.path) return Q();
var absolutePath = path.resolve(book.root, chapter.path);
return fs.exists(absolutePath)
.then(function(exists) {
if(exists) {
book.log.info.ln('found', chapter.path);
return;
} else {
book.log.info.ln('create', chapter.path);
}
return fs.mkdirp(path.dirname(absolutePath))
.then(function() {
return fs.writeFile(absolutePath, '# '+chapter.title+'\n');
});
});
}));
})
.then(function() {
book.log.info.ln('initialization is finished');
});
}
module.exports = initBook;
+83 -25
View File
@@ -1,16 +1,27 @@
var _ = require("lodash"); var _ = require('lodash');
var Q = require("q"); var Q = require('q');
var semver = require("semver"); var path = require('path');
var path = require("path"); var url = require('url');
var url = require("url"); var fs = require('./utils/fs');
var fs = require("./utils/fs"); var resolve = require('resolve');
var resolve = require("resolve"); var mergeDefaults = require('merge-defaults');
var jsonschema = require('jsonschema');
var jsonSchemaDefaults = require('json-schema-defaults');
var version = require('./version');
var PLUGIN_PREFIX = 'gitbook-plugin-';
// Return an absolute name for the plugin (the one on NPM)
function absoluteName(name) {
if (name.indexOf(PLUGIN_PREFIX) === 0) return name;
return [PLUGIN_PREFIX, name].join('');
}
var pkg = require("../package.json");
var Plugin = function(book, name) { var Plugin = function(book, name) {
this.book = book; this.book = book;
this.name = name; this.name = absoluteName(name);
this.packageInfos = {}; this.packageInfos = {};
this.infos = {}; this.infos = {};
@@ -18,9 +29,8 @@ var Plugin = function(book, name) {
_.bindAll(this); _.bindAll(this);
_.each([ _.each([
"gitbook-plugin-"+name, absoluteName(name),
"gitbook-"+name, name
name,
], function(_name) { ], function(_name) {
// Load from the book // Load from the book
if (this.load(_name, book.root)) return false; if (this.load(_name, book.root)) return false;
@@ -31,20 +41,27 @@ var Plugin = function(book, name) {
}; };
// Type of plugins resources // Type of plugins resources
Plugin.RESOURCES = ["js", "css"]; Plugin.RESOURCES = ['js', 'css'];
Plugin.HOOKS = [ Plugin.HOOKS = [
"init", "finish", "finish:before", "page", "page:before" 'init', 'finish', 'finish:before', 'config', 'page', 'page:before'
]; ];
// Return the reduce name for the plugin
// "gitbook-plugin-test" -> "test"
// Return a relative name for the plugin (the one on GitBook)
Plugin.prototype.reducedName = function() {
return this.name.replace(PLUGIN_PREFIX, '');
};
// Load from a name // Load from a name
Plugin.prototype.load = function(name, baseDir) { Plugin.prototype.load = function(name, baseDir) {
try { try {
var res = resolve.sync(name+"/package.json", { basedir: baseDir }); var res = resolve.sync(name+'/package.json', { basedir: baseDir });
this.baseDir = path.dirname(res); this.baseDir = path.dirname(res);
this.packageInfos = require(res); this.packageInfos = require(res);
this.infos = require(resolve.sync(name, { basedir: baseDir })); this.infos = require(resolve.sync(name, { basedir: baseDir }));
this.name = name; this.name = this.packageInfos.name;
return true; return true;
} catch (e) { } catch (e) {
@@ -62,13 +79,13 @@ Plugin.prototype.normalizeResource = function(resource) {
// so we will simply link to using it's URL // so we will simply link to using it's URL
if (parsed.protocol) { if (parsed.protocol) {
return { return {
"url": resource 'url': resource
}; };
} }
// This will be copied over from disk // This will be copied over from disk
// and shipped with the book's build // and shipped with the book's build
return { "path": this.name+"/"+resource }; return { 'path': this.name+'/'+resource };
}; };
// Return resources // Return resources
@@ -77,7 +94,7 @@ Plugin.prototype._getResources = function(base) {
var book = this.infos[base]; var book = this.infos[base];
// Compatibility with version 1.x.x // Compatibility with version 1.x.x
if (base == "website") book = book || this.infos.book; if (base == 'website') book = book || this.infos.book;
// Nothing specified, fallback to default // Nothing specified, fallback to default
if (!book) { if (!book) {
@@ -85,7 +102,7 @@ Plugin.prototype._getResources = function(base) {
} }
// Dynamic function // Dynamic function
if(typeof book === "function") { if(typeof book === 'function') {
// Call giving it the context of our book // Call giving it the context of our book
return Q().then(book.bind(this.book)); return Q().then(book.bind(this.book));
} }
@@ -127,18 +144,49 @@ Plugin.prototype.isValid = function() {
this.packageInfos.name && this.packageInfos.name &&
this.packageInfos.engines && this.packageInfos.engines &&
this.packageInfos.engines.gitbook && this.packageInfos.engines.gitbook &&
semver.satisfies(pkg.version, this.packageInfos.engines.gitbook) version.satisfies(this.packageInfos.engines.gitbook)
); );
// Valid hooks // Valid hooks
_.each(this.infos.hooks, function(hook, hookName) { _.each(this.infos.hooks, function(hook, hookName) {
if (_.contains(Plugin.HOOKS, hookName)) return; if (_.contains(Plugin.HOOKS, hookName)) return;
that.book.log.warn.ln("Hook '"+hookName+" 'used by plugin '"+that.packageInfos.name+"' has been removed or is deprecated"); that.book.log.warn.ln('Hook "'+hookName+'"" used by plugin "'+that.packageInfos.name+'" has been removed or is deprecated');
}); });
return isValid; return isValid;
}; };
// Normalize, validate configuration for this plugin using its schema
// Throw an error when shcema is not respected
Plugin.prototype.validateConfig = function(config) {
var that = this;
return Q()
.then(function() {
var schema = that.packageInfos.gitbook || {};
if (!schema) return config;
// Normalize schema
schema.id = '/pluginsConfig.'+that.reducedName();
schema.type = 'object';
// Validate and throw if invalid
var v = new jsonschema.Validator();
var result = v.validate(config, schema, {
propertyName: 'pluginsConfig.'+that.reducedName()
});
// Throw error
if (result.errors.length > 0) {
throw new Error('Configuration Error: '+result.errors[0].stack);
}
// Insert default values
var defaults = jsonSchemaDefaults(schema);
return mergeDefaults(config, defaults);
});
};
// Resolve file path // Resolve file path
Plugin.prototype.resolveFile = function(filename) { Plugin.prototype.resolveFile = function(filename) {
return path.resolve(this.baseDir, filename); return path.resolve(this.baseDir, filename);
@@ -154,8 +202,8 @@ Plugin.prototype.callHook = function(name, data) {
if (!hookFunc) return Q(data); if (!hookFunc) return Q(data);
this.book.log.debug.ln("call hook", name); this.book.log.debug.ln('call hook', name);
if (!_.contains(Plugin.HOOKS, name)) this.book.log.warn.ln("hook '"+name+"' used by plugin '"+this.name+"' is deprecated, and will be removed in the coming versions"); if (!_.contains(Plugin.HOOKS, name)) this.book.log.warn.ln('hook "'+name+'" used by plugin "'+this.name+'" is deprecated, and will be removed in the coming versions');
return Q() return Q()
.then(function() { .then(function() {
@@ -168,7 +216,7 @@ Plugin.prototype.copyAssets = function(out, base) {
var that = this; var that = this;
return this.getResources(base) return this.getResources(base)
.get("assets") .get('assets')
.then(function(assets) { .then(function(assets) {
// Assets are undefined // Assets are undefined
if(!assets) return false; if(!assets) return false;
@@ -180,4 +228,14 @@ Plugin.prototype.copyAssets = function(out, base) {
}, _.constant(false)); }, _.constant(false));
}; };
// Get config from book
Plugin.prototype.getConfig = function() {
return this.book.config.get('pluginsConfig.'+this.reducedName(), {});
};
// Set configuration for this plugin
Plugin.prototype.setConfig = function(values) {
return this.book.config.set('pluginsConfig.'+this.reducedName(), values);
};
module.exports = Plugin; module.exports = Plugin;
+60 -48
View File
@@ -1,14 +1,14 @@
var _ = require("lodash"); var _ = require('lodash');
var Q = require("q"); var Q = require('q');
var npmi = require("npmi"); var npmi = require('npmi');
var npm = require("npm"); var npm = require('npm');
var semver = require("semver"); var semver = require('semver');
var Plugin = require("./plugin"); var Plugin = require('./plugin');
var pkg = require("../package.json"); var version = require('./version');
var initNPM = _.memoize(function() { var initNPM = _.memoize(function() {
return Q.nfcall(npm.load, { silent: true, loglevel: "silent" }); return Q.nfcall(npm.load, { silent: true, loglevel: 'silent' });
}); });
@@ -23,7 +23,7 @@ var PluginsList = function(book, plugins) {
this.failed = []; this.failed = [];
// Namespaces // Namespaces
this.namespaces = _.chain(["website", "ebook"]) this.namespaces = _.chain(['website', 'ebook'])
.map(function(namespace) { .map(function(namespace) {
return [ return [
namespace, namespace,
@@ -66,7 +66,7 @@ PluginsList.prototype.load = function(plugin) {
if (_.isObject(plugin) && !(plugin instanceof Plugin)) plugin = plugin.name; if (_.isObject(plugin) && !(plugin instanceof Plugin)) plugin = plugin.name;
if (_.isString(plugin)) plugin = new Plugin(this.book, plugin); if (_.isString(plugin)) plugin = new Plugin(this.book, plugin);
that.log.info("load plugin", plugin.name, "...."); that.log.info('load plugin', plugin.name, '....');
if (!plugin.isValid()) { if (!plugin.isValid()) {
that.log.info.fail(); that.log.info.fail();
that.failed.push(plugin.name); that.failed.push(plugin.name);
@@ -78,34 +78,46 @@ PluginsList.prototype.load = function(plugin) {
that.list.push(plugin); that.list.push(plugin);
} }
// Extract filters return Q()
that.book.template.addFilters(plugin.getFilters());
// Extract blocks // Validate and normalize configuration
that.book.template.addBlocks(plugin.getBlocks()); .then(function() {
var config = plugin.getConfig();
return plugin.validateConfig(config);
})
.then(function(config) {
// Update configuration
plugin.setConfig(config);
return _.reduce(_.keys(that.namespaces), function(prev, namespaceName) { // Extract filters
return prev.then(function() { that.book.template.addFilters(plugin.getFilters());
return plugin.getResources(namespaceName)
.then(function(plResources) {
var namespace = that.namespaces[namespaceName];
// Extract js and css // Extract blocks
_.each(Plugin.RESOURCES, function(resourceType) { that.book.template.addBlocks(plugin.getBlocks());
namespace.resources[resourceType] = (namespace.resources[resourceType] || []).concat(plResources[resourceType] || []);
});
// Map of html resources by name added by each plugin return _.reduce(_.keys(that.namespaces), function(prev, namespaceName) {
_.each(plResources.html || {}, function(value, tag) { return prev.then(function() {
// Turn into function if not one already return plugin.getResources(namespaceName)
if (!_.isFunction(value)) value = _.constant(value); .then(function(plResources) {
var namespace = that.namespaces[namespaceName];
namespace.html[tag] = namespace.html[tag] || []; // Extract js and css
namespace.html[tag].push(value); _.each(Plugin.RESOURCES, function(resourceType) {
namespace.resources[resourceType] = (namespace.resources[resourceType] || []).concat(plResources[resourceType] || []);
});
// Map of html resources by name added by each plugin
_.each(plResources.html || {}, function(value, tag) {
// Turn into function if not one already
if (!_.isFunction(value)) value = _.constant(value);
namespace.html[tag] = namespace.html[tag] || [];
namespace.html[tag].push(value);
});
}); });
}); });
}); }, Q());
}, Q()); });
}; };
// Call a hook // Call a hook
@@ -135,7 +147,7 @@ PluginsList.prototype.html = function(namespace, tag, context, options) {
var htmlSnippets = this.namespaces[namespace].html[tag]; var htmlSnippets = this.namespaces[namespace].html[tag];
return _.map(htmlSnippets || [], function(code) { return _.map(htmlSnippets || [], function(code) {
return code.call(context, options); return code.call(context, options);
}).join("\n"); }).join('\n');
}; };
// Return a resources map for a namespace // Return a resources map for a namespace
@@ -153,10 +165,10 @@ PluginsList.prototype.install = function() {
}); });
// Install plugins one by one // Install plugins one by one
that.book.log.info.ln(plugins.length+" plugins to install"); that.book.log.info.ln(plugins.length+' plugins to install');
return _.reduce(plugins, function(prev, plugin) { return _.reduce(plugins, function(prev, plugin) {
return prev.then(function() { return prev.then(function() {
var fullname = "gitbook-plugin-"+plugin.name; var fullname = 'gitbook-plugin-'+plugin.name;
return Q() return Q()
@@ -164,10 +176,10 @@ PluginsList.prototype.install = function() {
.then(function() { .then(function() {
if (plugin.version) return plugin.version; if (plugin.version) return plugin.version;
that.book.log.info.ln("No version specified, resolve plugin", plugin.name); that.book.log.info.ln('No version specified, resolve plugin', plugin.name);
return initNPM() return initNPM()
.then(function() { .then(function() {
return Q.nfcall(npm.commands.view, [fullname+"@*", "engines"], true); return Q.nfcall(npm.commands.view, [fullname+'@*', 'engines'], true);
}) })
.then(function(versions) { .then(function(versions) {
return _.chain(versions) return _.chain(versions)
@@ -179,12 +191,12 @@ PluginsList.prototype.install = function() {
}; };
}) })
.filter(function(v) { .filter(function(v) {
return v.gitbook && semver.satisfies(pkg.version, v.gitbook); return v.gitbook && version.satisfies(v.gitbook);
}) })
.sort(function(v1, v2) { .sort(function(v1, v2) {
return semver.lt(v1.version, v2.version)? 1 : -1; return semver.lt(v1.version, v2.version)? 1 : -1;
}) })
.pluck("version") .pluck('version')
.first() .first()
.value(); .value();
}); });
@@ -193,23 +205,23 @@ PluginsList.prototype.install = function() {
// Install the plugin with the resolved version // Install the plugin with the resolved version
.then(function(version) { .then(function(version) {
if (!version) { if (!version) {
throw "Found no satisfactory version for plugin "+plugin.name; throw 'Found no satisfactory version for plugin '+plugin.name;
} }
that.book.log.info.ln("install plugin", plugin.name, "from npm ("+fullname+") with version", version); that.book.log.info.ln('install plugin', plugin.name, 'from npm ('+fullname+') with version', version);
return Q.nfcall(npmi, { return Q.nfcall(npmi, {
"name": fullname, 'name': fullname,
"version": version, 'version': version,
"path": that.book.root, 'path': that.book.root,
"npmLoad": { 'npmLoad': {
"loglevel": "silent", 'loglevel': 'silent',
"loaded": true, 'loaded': true,
"prefix": that.book.root 'prefix': that.book.root
} }
}); });
}) })
.then(function() { .then(function() {
that.book.log.info.ok("plugin", plugin.name, "installed with success"); that.book.log.info.ok('plugin', plugin.name, 'installed with success');
}); });
}); });
}, Q()); }, Q());
+60 -34
View File
@@ -1,12 +1,14 @@
var _ = require("lodash"); var _ = require('lodash');
var Q = require("q"); var Q = require('q');
var nunjucks = require("nunjucks"); var path = require('path');
var escapeStringRegexp = require("escape-string-regexp"); var nunjucks = require('nunjucks');
var parsers = require('gitbook-parsers');
var escapeStringRegexp = require('escape-string-regexp');
var batch = require("./utils/batch"); var batch = require('./utils/batch');
var pkg = require("../package.json"); var pkg = require('../package.json');
var defaultBlocks = require("./blocks"); var defaultBlocks = require('./blocks');
var BookLoader = require("./conrefs_loader"); var BookLoader = require('./conrefs_loader');
// Normalize result from a block // Normalize result from a block
function normBlockResult(blk) { function normBlockResult(blk) {
@@ -16,24 +18,37 @@ function normBlockResult(blk) {
var TemplateEngine = function(book) { var TemplateEngine = function(book) {
var that = this;
this.book = book; this.book = book;
this.log = this.book.log; this.log = this.book.log;
// Template loader
this.loader = new BookLoader(book, {
// Replace shortcuts in imported files
interpolate: function(filepath, source) {
var parser = parsers.get(path.extname(filepath));
var type = parser? parser.name : null;
return that.applyShortcuts(type, source);
}
});
// Nunjucks env // Nunjucks env
this.env = new nunjucks.Environment( this.env = new nunjucks.Environment(
new BookLoader(book), this.loader,
{ {
// Escaping is done after by the markdown parser // Escaping is done after by the markdown parser
autoescape: false, autoescape: false,
// Tags // Tags
tags: { tags: {
blockStart: "{%", blockStart: '{%',
blockEnd: "%}", blockEnd: '%}',
variableStart: "{{", variableStart: '{{',
variableEnd: "}}", variableEnd: '}}',
commentStart: "{###", commentStart: '{###',
commentEnd: "###}" commentEnd: '###}'
} }
} }
); );
@@ -60,20 +75,20 @@ TemplateEngine.prototype.processBlock = function(blk) {
parse: false, parse: false,
post: undefined post: undefined
}); });
blk.id = _.uniqueId("blk"); blk.id = _.uniqueId('blk');
var toAdd = (!blk.parse) || (blk.post !== undefined); var toAdd = (!blk.parse) || (blk.post !== undefined);
// Add to global map // Add to global map
if (toAdd) this.blockBodies[blk.id] = blk; if (toAdd) this.blockBodies[blk.id] = blk;
//Parsable block, just return it // Parsable block, just return it
if (blk.parse) { if (blk.parse) {
return blk.body; return blk.body;
} }
// Return it as a position marker // Return it as a position marker
return "@%@"+blk.id+"@%@"; return '@%@'+blk.id+'@%@';
}; };
// Replace position markers of blocks by body after processing // Replace position markers of blocks by body after processing
@@ -110,11 +125,13 @@ TemplateEngine.prototype.bindContext = function(func) {
TemplateEngine.prototype.addFilter = function(filterName, func) { TemplateEngine.prototype.addFilter = function(filterName, func) {
try { try {
this.env.getFilter(filterName); this.env.getFilter(filterName);
this.log.warn.ln("conflict in filters, \""+filterName+"\" is already set"); this.log.warn.ln('conflict in filters, \''+filterName+'\' is already set');
return false; return false;
} catch(e) {} } catch(e) {
// Filter doesn't exist
}
this.log.debug.ln("add filter \""+filterName+"\""); this.log.debug.ln('add filter \''+filterName+'\'');
this.env.addFilter(filterName, this.bindContext(function() { this.env.addFilter(filterName, this.bindContext(function() {
var ctx = this; var ctx = this;
var args = Array.prototype.slice.apply(arguments); var args = Array.prototype.slice.apply(arguments);
@@ -138,7 +155,7 @@ TemplateEngine.prototype.addFilters = function(filters) {
// Return nunjucks extension name of a block // Return nunjucks extension name of a block
TemplateEngine.prototype.blockExtName = function(name) { TemplateEngine.prototype.blockExtName = function(name) {
return "Block"+name+"Extension"; return 'Block'+name+'Extension';
}; };
// Test if a block is defined // Test if a block is defined
@@ -167,7 +184,7 @@ TemplateEngine.prototype.addBlock = function(name, block) {
block = _.defaults(block || {}, { block = _.defaults(block || {}, {
shortcuts: [], shortcuts: [],
end: "end"+name, end: 'end'+name,
process: _.identity, process: _.identity,
blocks: [] blocks: []
}); });
@@ -175,13 +192,13 @@ TemplateEngine.prototype.addBlock = function(name, block) {
extName = this.blockExtName(name); extName = this.blockExtName(name);
if (this.hasBlock(name) && !defaultBlocks[name]) { if (this.hasBlock(name) && !defaultBlocks[name]) {
this.log.warn.ln("conflict in blocks, \""+name+"\" is already defined"); this.log.warn.ln('conflict in blocks, \''+name+'\' is already defined');
} }
// Cleanup previous block // Cleanup previous block
this.removeBlock(name); this.removeBlock(name);
this.log.debug.ln("add block \""+name+"\""); this.log.debug.ln('add block \''+name+'\'');
this.blocks[name] = block; this.blocks[name] = block;
Ext = function () { Ext = function () {
@@ -238,7 +255,7 @@ TemplateEngine.prototype.addBlock = function(name, block) {
bodies.push(subbodies[blockName][0].body); bodies.push(subbodies[blockName][0].body);
}); });
return new nodes.CallExtensionAsync(this, "run", args, bodies); return new nodes.CallExtensionAsync(this, 'run', args, bodies);
}; };
this.run = function(context) { this.run = function(context) {
@@ -287,7 +304,7 @@ TemplateEngine.prototype.addBlock = function(name, block) {
// Add shortcuts // Add shortcuts
if (!_.isArray(block.shortcuts)) block.shortcuts = [block.shortcuts]; if (!_.isArray(block.shortcuts)) block.shortcuts = [block.shortcuts];
_.each(block.shortcuts, function(shortcut) { _.each(block.shortcuts, function(shortcut) {
this.log.debug.ln("add template shortcut from \""+shortcut.start+"\" to block \""+name+"\" for parsers ", shortcut.parsers); this.log.debug.ln('add template shortcut from \''+shortcut.start+'\' to block \''+name+'\' for parsers ', shortcut.parsers);
this.shortcuts.push({ this.shortcuts.push({
block: name, block: name,
parsers: shortcut.parsers, parsers: shortcut.parsers,
@@ -314,7 +331,7 @@ TemplateEngine.prototype.applyBlock = function(name, blk, ctx) {
var func, block, r; var func, block, r;
block = this.blocks[name]; block = this.blocks[name];
if (!block) throw new Error("Block not found \""+name+"\""); if (!block) throw new Error('Block not found \''+name+'\'');
if (_.isString(blk)) { if (_.isString(blk)) {
blk = { blk = {
body: blk body: blk
@@ -339,14 +356,19 @@ TemplateEngine.prototype.applyBlock = function(name, blk, ctx) {
TemplateEngine.prototype._applyShortcut = function(parser, content, shortcut) { TemplateEngine.prototype._applyShortcut = function(parser, content, shortcut) {
if (!_.contains(shortcut.parsers, parser)) return content; if (!_.contains(shortcut.parsers, parser)) return content;
var regex = new RegExp( var regex = new RegExp(
escapeStringRegexp(shortcut.start) + "([\\s\\S]*?[^\\$])" + escapeStringRegexp(shortcut.end), escapeStringRegexp(shortcut.start) + '([\\s\\S]*?[^\\$])' + escapeStringRegexp(shortcut.end),
"g" 'g'
); );
return content.replace(regex, function(all, match) { return content.replace(regex, function(all, match) {
return "{% "+shortcut.tag.start+" %}"+ match + "{% "+shortcut.tag.end+" %}"; return '{% '+shortcut.tag.start+' %}'+ match + '{% '+shortcut.tag.end+' %}';
}); });
}; };
// Apply all shortcuts to some template string
TemplateEngine.prototype.applyShortcuts = function(type, content) {
return _.reduce(this.shortcuts, _.partial(this._applyShortcut.bind(this), type), content);
};
// Render a string from the book // Render a string from the book
TemplateEngine.prototype.renderString = function(content, context, options) { TemplateEngine.prototype.renderString = function(content, context, options) {
context = _.extend({}, context, { context = _.extend({}, context, {
@@ -367,14 +389,18 @@ TemplateEngine.prototype.renderString = function(content, context, options) {
type: null type: null
}); });
if (options.path) options.path = this.book.resolve(options.path); if (options.path) options.path = this.book.resolve(options.path);
if (!options.type && options.path) {
var parser = parsers.get(path.extname(options.path));
options.type = parser? parser.name : null;
}
// Replace shortcuts // Replace shortcuts
content = _.reduce(this.shortcuts, _.partial(this._applyShortcut.bind(this), options.type), content); content = this.applyShortcuts(options.type, content);
return Q.nfcall(this.env.renderString.bind(this.env), content, context, options) return Q.nfcall(this.env.renderString.bind(this.env), content, context, options)
.fail(function(err) { .fail(function(err) {
if (_.isString(err)) err = new Error(err); if (_.isString(err)) err = new Error(err);
err.message = err.message.replace(/^Error: /, ""); err.message = err.message.replace(/^Error: /, '');
throw err; throw err;
}); });
@@ -398,7 +424,7 @@ TemplateEngine.prototype.renderPage = function(page) {
return that.book.statFile(page.path) return that.book.statFile(page.path)
.then(function(stat) { .then(function(stat) {
var context = { var context = {
// infos about the file // infos about the file
file: { file: {
path: page.path, path: page.path,
+35 -24
View File
@@ -1,10 +1,10 @@
var _ = require("lodash"); var _ = require('lodash');
var Q = require("q"); var Q = require('q');
var tmp = require("tmp"); var tmp = require('tmp');
var path = require("path"); var path = require('path');
var fs = require("graceful-fs"); var fs = require('graceful-fs');
var fsExtra = require("fs-extra"); var fsExtra = require('fs-extra');
var Ignore = require("fstream-ignore"); var Ignore = require('fstream-ignore');
var fsUtils = { var fsUtils = {
tmp: { tmp: {
@@ -30,10 +30,11 @@ var fsUtils = {
fs.exists(path, d.resolve); fs.exists(path, d.resolve);
return d.promise; return d.promise;
}, },
findFile: findFile,
existsSync: fs.existsSync.bind(fs), existsSync: fs.existsSync.bind(fs),
readFileSync: fs.readFileSync.bind(fs), readFileSync: fs.readFileSync.bind(fs),
clean: cleanFolder, clean: cleanFolder,
getUniqueFilename: getUniqueFilename, getUniqueFilename: getUniqueFilename
}; };
// Write a file // Write a file
@@ -57,14 +58,14 @@ function writeStream(filename, st) {
var wstream = fs.createWriteStream(filename); var wstream = fs.createWriteStream(filename);
wstream.on("finish", function () { wstream.on('finish', function () {
d.resolve(); d.resolve();
}); });
wstream.on("error", function (err) { wstream.on('error', function (err) {
d.reject(err); d.reject(err);
}); });
st.on("error", function(err) { st.on('error', function(err) {
d.reject(err); d.reject(err);
}); });
@@ -77,7 +78,7 @@ function writeStream(filename, st) {
function getUniqueFilename(base, filename) { function getUniqueFilename(base, filename) {
if (!filename) { if (!filename) {
filename = base; filename = base;
base = "/"; base = '/';
} }
filename = path.resolve(base, filename); filename = path.resolve(base, filename);
@@ -87,9 +88,8 @@ function getUniqueFilename(base, filename) {
var _filename = filename+ext; var _filename = filename+ext;
var i = 0; var i = 0;
while (1) { while (fs.existsSync(filename)) {
if (!fs.existsSync(filename)) break; _filename = filename+'_'+i+ext;
_filename = filename+"_"+i+ext;
i = i + 1; i = i + 1;
} }
@@ -115,20 +115,20 @@ function listFiles(root, options) {
}); });
// Add extra rules to ignore common folders // Add extra rules to ignore common folders
ig.addIgnoreRules(options.ignoreRules, "__custom_stuff"); ig.addIgnoreRules(options.ignoreRules, '__custom_stuff');
// Push each file to our list // Push each file to our list
ig.on("child", function (c) { ig.on('child', function (c) {
files.push( files.push(
c.path.substr(c.root.path.length + 1) + (c.props.Directory === true ? "/" : "") c.path.substr(c.root.path.length + 1) + (c.props.Directory === true ? '/' : '')
); );
}); });
ig.on("end", function() { ig.on('end', function() {
// Normalize paths on Windows // Normalize paths on Windows
if(process.platform === "win32") { if(process.platform === 'win32') {
return d.resolve(files.map(function(file) { return d.resolve(files.map(function(file) {
return file.replace(/\\/g, "/"); return file.replace(/\\/g, '/');
})); }));
} }
@@ -136,7 +136,7 @@ function listFiles(root, options) {
return d.resolve(files); return d.resolve(files);
}); });
ig.on("error", d.reject); ig.on('error', d.reject);
return d.promise; return d.promise;
} }
@@ -150,8 +150,8 @@ function cleanFolder(root) {
ignoreFiles: [], ignoreFiles: [],
ignoreRules: [ ignoreRules: [
// Skip Git and SVN stuff // Skip Git and SVN stuff
".git/", '.git/',
".svn/" '.svn/'
] ]
}) })
.then(function(files) { .then(function(files) {
@@ -179,4 +179,15 @@ function cleanFolder(root) {
}); });
} }
// Find a file in a folder (case incensitive)
// Return the real filename
function findFile(root, filename) {
return Q.nfcall(fs.readdir, root)
.then(function(files) {
return _.find(files, function(file) {
return (file.toLowerCase() == filename.toLowerCase());
});
});
}
module.exports = fsUtils; module.exports = fsUtils;
+19 -19
View File
@@ -1,14 +1,14 @@
var Q = require("q"); var Q = require('q');
var _ = require("lodash"); var _ = require('lodash');
var path = require("path"); var path = require('path');
var crc = require("crc"); var crc = require('crc');
var exec = Q.denodeify(require("child_process").exec); var exec = Q.denodeify(require('child_process').exec);
var URI = require("URIjs"); var URI = require('urijs');
var pathUtil = require("./path"); var pathUtil = require('./path');
var fs = require("./fs"); var fs = require('./fs');
var GIT_PREFIX = "git+"; var GIT_PREFIX = 'git+';
var GIT_TMP = null; var GIT_TMP = null;
@@ -30,20 +30,20 @@ function parseGitUrl(giturl) {
giturl = giturl.slice(GIT_PREFIX.length); giturl = giturl.slice(GIT_PREFIX.length);
uri = new URI(giturl); uri = new URI(giturl);
ref = uri.fragment() || "master"; ref = uri.fragment() || 'master';
uri.fragment(null); uri.fragment(null);
// Extract file inside the repo (after the .git) // Extract file inside the repo (after the .git)
fileParts =uri.path().split(".git"); fileParts =uri.path().split('.git');
filepath = fileParts.length > 1? fileParts.slice(1).join(".git") : ""; filepath = fileParts.length > 1? fileParts.slice(1).join('.git') : '';
if (filepath[0] == "/") filepath = filepath.slice(1); if (filepath[0] == '/') filepath = filepath.slice(1);
// Recreate pathname without the real filename // Recreate pathname without the real filename
uri.path(_.first(fileParts)+".git"); uri.path(_.first(fileParts)+'.git');
return { return {
host: uri.toString(), host: uri.toString(),
ref: ref || "master", ref: ref || 'master',
filepath: filepath filepath: filepath
}; };
} }
@@ -52,7 +52,7 @@ function parseGitUrl(giturl) {
function cloneGitRepo(host, ref) { function cloneGitRepo(host, ref) {
var isBranch = false; var isBranch = false;
ref = ref || "master"; ref = ref || 'master';
if (!validateSha(ref)) isBranch = true; if (!validateSha(ref)) isBranch = true;
return Q() return Q()
@@ -69,7 +69,7 @@ function cloneGitRepo(host, ref) {
// Return or clone the git repo // Return or clone the git repo
.then(function() { .then(function() {
// Unique ID for repo/ref combinaison // Unique ID for repo/ref combinaison
var repoId = crc.crc32(host+"#"+ref).toString(16); var repoId = crc.crc32(host+'#'+ref).toString(16);
// Absolute path to the folder // Absolute path to the folder
var repoPath = path.resolve(GIT_TMP, repoId); var repoPath = path.resolve(GIT_TMP, repoId);
@@ -79,9 +79,9 @@ function cloneGitRepo(host, ref) {
if (doExists) return; if (doExists) return;
// Clone repo // Clone repo
return exec("git clone "+host+" "+repoPath) return exec('git clone '+host+' '+repoPath)
.then(function() { .then(function() {
return exec("git checkout "+ref, { cwd: repoPath }); return exec('git checkout '+ref, { cwd: repoPath });
}); });
}) })
.thenResolve(repoPath); .thenResolve(repoPath);
+13 -13
View File
@@ -1,13 +1,13 @@
var _ = require("lodash"); var _ = require('lodash');
var path = require("path"); var path = require('path');
var fs = require("fs"); var fs = require('fs');
var i18n = require("i18n"); var i18n = require('i18n');
var I18N_PATH = path.resolve(__dirname, "../../theme/i18n/"); var I18N_PATH = path.resolve(__dirname, '../../theme/i18n/');
var DEFAULT_LANGUAGE = "en"; var DEFAULT_LANGUAGE = 'en';
var LOCALES = _.map(fs.readdirSync(I18N_PATH), function(lang) { var LOCALES = _.map(fs.readdirSync(I18N_PATH), function(lang) {
return path.basename(lang, ".json"); return path.basename(lang, '.json');
}); });
i18n.configure({ i18n.configure({
@@ -18,11 +18,11 @@ i18n.configure({
}); });
function compareLocales(lang, locale) { function compareLocales(lang, locale) {
var langMain = _.first(lang.split("-")); var langMain = _.first(lang.split('-'));
var langSecond = _.last(lang.split("-")); var langSecond = _.last(lang.split('-'));
var localeMain = _.first(locale.split("-")); var localeMain = _.first(locale.split('-'));
var localeSecond = _.last(locale.split("-")); var localeSecond = _.last(locale.split('-'));
if (locale == lang) return 100; if (locale == lang) return 100;
if (localeMain == langMain) return 50; if (localeMain == langMain) return 50;
@@ -42,8 +42,8 @@ var normalizeLanguage = _.memoize(function(lang) {
.filter(function(lang) { .filter(function(lang) {
return lang.score > 0; return lang.score > 0;
}) })
.sortBy("score") .sortBy('score')
.pluck("locale") .pluck('locale')
.last() .last()
.value(); .value();
return language || lang; return language || lang;
+77 -83
View File
@@ -1,31 +1,32 @@
var Q = require("q"); var Q = require('q');
var _ = require("lodash"); var _ = require('lodash');
var url = require("url"); var url = require('url');
var path = require("path"); var path = require('path');
var cheerio = require("cheerio"); var cheerio = require('cheerio');
var domSerializer = require("dom-serializer"); var domSerializer = require('dom-serializer');
var request = require("request"); var request = require('request');
var crc = require("crc"); var crc = require('crc');
var slug = require('github-slugid');
var links = require("./links"); var links = require('./links');
var imgUtils = require("./images"); var imgUtils = require('./images');
var fs = require("./fs"); var fs = require('./fs');
var batch = require("./batch"); var batch = require('./batch');
var parsableExtensions = require("gitbook-parsers").extensions; var parsableExtensions = require('gitbook-parsers').extensions;
// Map of images that have been converted
var imgConversionCache = {};
// Render a cheerio dom as html // Render a cheerio dom as html
var renderDom = function($, dom, options) { function renderDom($, dom, options) {
if (!dom && $._root && $._root.children) { if (!dom && $._root && $._root.children) {
dom = $._root.children; dom = $._root.children;
} }
options = options|| dom.options || $._options; options = options|| dom.options || $._options;
return domSerializer(dom, options); return domSerializer(dom, options);
}; }
// Map of images that have been converted
var imgConversionCache = {};
function replaceText($, el, search, replace, text_only ) { function replaceText($, el, search, replace, text_only ) {
return $(el).each(function(){ return $(el).each(function(){
@@ -59,7 +60,7 @@ function replaceText($, el, search, replace, text_only ) {
// robust way. // robust way.
$(node).before( new_val ); $(node).before( new_val );
// Don"t remove the node yet, or the loop will lose its place. // Don't remove the node yet, or the loop will lose its place.
remove.push( node ); remove.push( node );
} else { } else {
// The new value contains no HTML, so it can be set in this // The new value contains no HTML, so it can be set in this
@@ -79,7 +80,7 @@ function replaceText($, el, search, replace, text_only ) {
} }
function pregQuote( str ) { function pregQuote( str ) {
return (str+"").replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1"); return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, '\\$1');
} }
@@ -101,23 +102,30 @@ function normalizeHtml(src, options) {
// Find svg images to extract and process // Find svg images to extract and process
if (options.convertImages) { if (options.convertImages) {
$("svg").each(function() { $('svg').each(function() {
var content = renderDom($, $(this)); var content = renderDom($, $(this));
var svgId = _.uniqueId("svg"); var svgId = _.uniqueId('svg');
var dest = svgId+".svg"; var dest = svgId+'.svg';
// Generate filename // Generate filename
dest = "/"+fs.getUniqueFilename(outputRoot, dest); dest = '/'+fs.getUniqueFilename(outputRoot, dest);
svgContent[dest] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+content; svgContent[dest] = '<?xml version="1.0" encoding="UTF-8"?>'+content;
$(this).replaceWith($("<img>").attr("src", dest)); $(this).replaceWith($('<img>').attr('src', dest));
}); });
} }
// Generate ID for headings
$('h1,h2,h3,h4,h5,h6').each(function() {
if ($(this).attr('id')) return;
$(this).attr('id', slug($(this).text()));
});
// Find images to normalize // Find images to normalize
$("img").each(function() { $('img').each(function() {
var origin; var origin;
var src = $(this).attr("src"); var src = $(this).attr('src');
if (!src) return; if (!src) return;
var isExternal = links.isExternal(src); var isExternal = links.isExternal(src);
@@ -132,13 +140,13 @@ function normalizeHtml(src, options) {
// If image is external and ebook, then downlaod the images // If image is external and ebook, then downlaod the images
if (isExternal) { if (isExternal) {
origin = src; origin = src;
src = "/"+crc.crc32(origin).toString(16)+path.extname(origin); src = '/'+crc.crc32(origin).toString(16)+path.extname(url.parse(origin).pathname);
src = links.toAbsolute(src, options.base, options.output); src = links.toAbsolute(src, options.base, options.output);
isExternal = false; isExternal = false;
} }
var ext = path.extname(src); var ext = path.extname(src);
var srcAbs = links.join("/", options.base, src); var srcAbs = links.join('/', options.base, src);
// Test image extension // Test image extension
if (_.contains(imgUtils.INVALID, ext)) { if (_.contains(imgUtils.INVALID, ext)) {
@@ -147,29 +155,29 @@ function normalizeHtml(src, options) {
src = imgConversionCache[outputRoot][srcAbs]; src = imgConversionCache[outputRoot][srcAbs];
} else { } else {
// Not converted yet // Not converted yet
var dest = ""; var dest = '';
// Replace extension // Replace extension
dest = links.join(path.dirname(srcAbs), path.basename(srcAbs, ext)+".png"); dest = links.join(path.dirname(srcAbs), path.basename(srcAbs, ext)+'.png');
dest = dest[0] == "/"? dest.slice(1) : dest; dest = dest[0] == '/'? dest.slice(1) : dest;
// Get a name that doesn"t exists // Get a name that doesn't exists
dest = fs.getUniqueFilename(outputRoot, dest); dest = fs.getUniqueFilename(outputRoot, dest);
options.book.log.debug.ln("detect invalid image (will be converted to png):", srcAbs); options.book.log.debug.ln('detect invalid image (will be converted to png):', srcAbs);
// Add to cache // Add to cache
imgConversionCache[outputRoot][srcAbs] = "/"+dest; imgConversionCache[outputRoot][srcAbs] = '/'+dest;
// Push to convert // Push to convert
toConvert.push({ toConvert.push({
origin: origin, origin: origin,
content: svgContent[srcAbs], content: svgContent[srcAbs],
source: isExternal? srcAbs : path.join("./", srcAbs), source: isExternal? srcAbs : path.join('./', srcAbs),
dest: path.join("./", dest) dest: path.join('./', dest)
}); });
src = links.join("/", dest); src = links.join('/', dest);
} }
// Reset as relative to output // Reset as relative to output
@@ -180,17 +188,17 @@ function normalizeHtml(src, options) {
// Need to downlaod image // Need to downlaod image
toConvert.push({ toConvert.push({
origin: origin, origin: origin,
source: path.join("./", srcAbs) source: path.join('./', srcAbs)
}); });
} }
} }
$(this).attr("src", src); $(this).attr('src', src);
}); });
// Normalize links // Normalize links
$("a").each(function() { $('a').each(function() {
var href = $(this).attr("href"); var href = $(this).attr('href');
if (!href) return; if (!href) return;
if (links.isAnchor(href)) { if (links.isAnchor(href)) {
@@ -198,7 +206,7 @@ function normalizeHtml(src, options) {
} else if (links.isRelative(href)) { } else if (links.isRelative(href)) {
var parts = url.parse(href); var parts = url.parse(href);
var absolutePath = links.join(options.base, parts.pathname); var absolutePath = links.join(options.base, parts.pathname);
var anchor = parts.hash || ""; var anchor = parts.hash || '';
// If is in navigation relative: transform as content // If is in navigation relative: transform as content
@@ -209,33 +217,33 @@ function normalizeHtml(src, options) {
// If md/adoc/rst files is not in summary // If md/adoc/rst files is not in summary
// or for ebook, signal all files that are outside the summary // or for ebook, signal all files that are outside the summary
else if (_.contains(parsableExtensions, path.extname(absolutePath)) || else if (_.contains(parsableExtensions, path.extname(absolutePath)) ||
_.contains(["epub", "pdf", "mobi"], options.book.options.generator)) { _.contains(['epub', 'pdf', 'mobi'], options.book.options.generator)) {
options.book.log.warn.ln("page", options.input, "contains an hyperlink to resource outside spine \""+href+"\""); options.book.log.warn.ln('page', options.input, 'contains an hyperlink to resource outside spine \''+href+'\'');
} }
// Transform as absolute // Transform as absolute
href = links.toAbsolute("/"+absolutePath, options.base, options.output)+anchor; href = links.toAbsolute('/'+absolutePath, options.base, options.output)+anchor;
} else { } else {
// External links // External links
$(this).attr("target", "_blank"); $(this).attr('target', '_blank');
} }
// Transform extension // Transform extension
$(this).attr("href", href); $(this).attr('href', href);
}); });
// Highlight code blocks // Highlight code blocks
$("code").each(function() { $('code').each(function() {
// Normalize language // Normalize language
var lang = _.chain( var lang = _.chain(
($(this).attr("class") || "").split(" ") ($(this).attr('class') || '').split(' ')
) )
.map(function(cl) { .map(function(cl) {
// Markdown // Markdown
if (cl.search("lang-") === 0) return cl.slice("lang-".length); if (cl.search('lang-') === 0) return cl.slice('lang-'.length);
// Asciidoc // Asciidoc
if (cl.search("language-") === 0) return cl.slice("language-".length); if (cl.search('language-') === 0) return cl.slice('language-'.length);
return null; return null;
}) })
@@ -244,7 +252,7 @@ function normalizeHtml(src, options) {
.value(); .value();
var source = $(this).text(); var source = $(this).text();
var blk = options.book.template.applyBlock("code", { var blk = options.book.template.applyBlock('code', {
body: source, body: source,
kwargs: { kwargs: {
language: lang language: lang
@@ -261,12 +269,12 @@ function normalizeHtml(src, options) {
}); });
_.each(glossary, function(term) { _.each(glossary, function(term) {
var r = new RegExp( "\\b(" + pregQuote(term.name.toLowerCase()) + ")\\b" , "gi" ); var r = new RegExp( '\\b(' + pregQuote(term.name.toLowerCase()) + ')\\b' , 'gi' );
var includedInFiles = false; var includedInFiles = false;
$("*").each(function() { $('*').each(function() {
// Ignore codeblocks // Ignore codeblocks
if (_.contains(["code", "pre", "a"], this.name.toLowerCase())) return; if (_.contains(['code', 'pre', 'a'], this.name.toLowerCase())) return;
replaceText($, this, r, function(match) { replaceText($, this, r, function(match) {
// Add to files index in glossary // Add to files index in glossary
@@ -275,7 +283,7 @@ function normalizeHtml(src, options) {
term.files = term.files || []; term.files = term.files || [];
term.files.push(options.navigation[options.input]); term.files.push(options.navigation[options.input]);
} }
return "<a href=\""+links.toAbsolute("/GLOSSARY.html", options.base, options.output) + "#" + term.id+"\" class=\"glossary-term\" title=\""+_.escape(term.description)+"\">"+match+"</a>"; return '<a href=\''+links.toAbsolute('/GLOSSARY.html', options.base, options.output) + '#' + term.id+'\' class=\'glossary-term\' title=\''+_.escape(term.description)+'\'>'+match+'</a>';
}); });
}); });
}); });
@@ -291,7 +299,7 @@ function convertImages(images, options) {
if (!options.convertImages) return Q(); if (!options.convertImages) return Q();
var downloaded = []; var downloaded = [];
options.book.log.debug.ln("convert ", images.length, "images to png"); options.book.log.debug.ln('convert ', images.length, 'images to png');
return batch.execEach(images, { return batch.execEach(images, {
max: 100, max: 100,
@@ -303,13 +311,13 @@ function convertImages(images, options) {
// Write image if need to be download // Write image if need to be download
.then(function() { .then(function() {
if (!image.origin && !_.contains(downloaded, image.origin)) return; if (!image.origin && !_.contains(downloaded, image.origin)) return;
options.book.log.debug("download image", image.origin, "..."); options.book.log.debug('download image', image.origin, '...');
downloaded.push(image.origin); downloaded.push(image.origin);
return options.book.log.debug.promise(fs.writeStream(imgin, request(image.origin))) return options.book.log.debug.promise(fs.writeStream(imgin, request(image.origin)))
.fail(function(err) { .fail(function(err) {
if (!_.isError(err)) err = new Error(err); if (!_.isError(err)) err = new Error(err);
err.message = "Fail downloading "+image.origin+": "+err.message; err.message = 'Fail downloading '+image.origin+': '+err.message;
throw err; throw err;
}); });
}) })
@@ -324,13 +332,13 @@ function convertImages(images, options) {
.then(function() { .then(function() {
if (!image.dest) return; if (!image.dest) return;
var imgout = path.resolve(options.book.options.output, image.dest); var imgout = path.resolve(options.book.options.output, image.dest);
options.book.log.debug("convert image", image.source, "to", image.dest, "..."); options.book.log.debug('convert image', image.source, 'to', image.dest, '...');
return options.book.log.debug.promise(imgUtils.convertSVG(imgin, imgout)); return options.book.log.debug.promise(imgUtils.convertSVG(imgin, imgout));
}); });
} }
}) })
.then(function() { .then(function() {
options.book.log.debug.ok(images.length+" images converted with success"); options.book.log.debug.ok(images.length+' images converted with success');
}); });
} }
@@ -344,16 +352,16 @@ function normalizePage(sections, options) {
convertImages: false, convertImages: false,
// Current file path // Current file path
input: ".", input: '.',
// Navigation to use to transform path // Navigation to use to transform path
navigation: {}, navigation: {},
// Directory parent of the file currently in rendering process // Directory parent of the file currently in rendering process
base: "./", base: './',
// Directory parent from the html output // Directory parent from the html output
output: "./", output: './',
// Glossary terms // Glossary terms
glossary: [] glossary: []
@@ -363,7 +371,7 @@ function normalizePage(sections, options) {
var toConvert = []; var toConvert = [];
sections = _.map(sections, function(section) { sections = _.map(sections, function(section) {
if (section.type != "normal") return section; if (section.type != 'normal') return section;
var out = normalizeHtml(section.content, options); var out = normalizeHtml(section.content, options);
@@ -374,27 +382,13 @@ function normalizePage(sections, options) {
return Q() return Q()
.then(function() { .then(function() {
toConvert = _.uniq(toConvert, "source"); toConvert = _.uniq(toConvert, 'source');
return convertImages(toConvert, options); return convertImages(toConvert, options);
}) })
.thenResolve(sections); .thenResolve(sections);
} }
// Extract text from sections
function extractText(sections) {
return _.reduce(sections, function(prev, section) {
if (section.type != "normal") return prev;
var $ = cheerio.load(section.content);
$("*").each(function() {
prev = prev+" "+$(this).text();
});
return prev;
}, "");
}
module.exports = { module.exports = {
normalize: normalizePage, normalize: normalizePage
extractText: extractText
}; };
+1 -1
View File
@@ -1,4 +1,4 @@
var _ = require("lodash"); var _ = require('lodash');
// Returns from a navigation and a current file, a snapshot of current detailed state // Returns from a navigation and a current file, a snapshot of current detailed state
function calculProgress(navigation, current) { function calculProgress(navigation, current) {
+19
View File
@@ -0,0 +1,19 @@
var semver = require('semver');
var pkg = require('../package.json');
var VERSION = pkg.version;
var VERSION_STABLE = VERSION.replace(/\-(\S+)/g, '');
// Test if current current gitbook version satisfies a condition
// We can't directly use samver.satisfies since it will break all plugins when gitbook version is a prerelease (beta, alpha)
function satisfies(condition) {
// Test with real version
if (semver.satisfies(VERSION, condition)) return true;
// Test with future stable release
return semver.satisfies(VERSION_STABLE, condition);
}
module.exports = {
satisfies: satisfies
};
+28 -13
View File
@@ -1,20 +1,22 @@
{ {
"name": "gitbook", "name": "gitbook",
"version": "2.4.2", "version": "2.6.2",
"homepage": "https://www.gitbook.com", "homepage": "https://www.gitbook.com",
"description": "Library and cmd utility to generate GitBooks", "description": "Library and cmd utility to generate GitBooks",
"main": "lib/index.js", "main": "lib/index.js",
"dependencies": { "dependencies": {
"q": "1.0.1", "q": "1.0.1",
"lunr": "0.5.12",
"lodash": "3.10.1", "lodash": "3.10.1",
"graceful-fs": "3.0.5", "graceful-fs": "3.0.5",
"resolve": "0.6.3", "resolve": "0.6.3",
"fs-extra": "0.16.5", "fs-extra": "0.16.5",
"fstream-ignore": "1.0.2", "fstream-ignore": "1.0.2",
"gitbook-parsers": "0.8.2", "gitbook-parsers": "0.8.7",
"gitbook-plugin-highlight": "1.0.3", "gitbook-plugin-highlight": "1.0.3",
"nunjucks": "mozilla/nunjucks#dc89bf91611a2101731c2c06afcf5c32160b4dc9", "gitbook-plugin-sharing": "1.0.1",
"gitbook-plugin-search": "1.1.0",
"gitbook-plugin-fontsettings": "1.0.2",
"nunjucks": "2.2.0",
"nunjucks-autoescape": "1.0.0", "nunjucks-autoescape": "1.0.0",
"nunjucks-filter": "1.0.0", "nunjucks-filter": "1.0.0",
"i18n": "0.5.0", "i18n": "0.5.0",
@@ -24,26 +26,39 @@
"gitbook-plugin-livereload": "0.0.1", "gitbook-plugin-livereload": "0.0.1",
"chokidar": "~1.0.5", "chokidar": "~1.0.5",
"send": "0.2.0", "send": "0.2.0",
"tiny-lr": "0.1.5", "tiny-lr": "0.2.1",
"tmp": "0.0.24", "tmp": "0.0.24",
"crc": "3.2.1", "crc": "3.2.1",
"bash-color": "0.0.3", "bash-color": "0.0.3",
"URIjs": "1.14.1", "urijs": "1.17.0",
"request": "2.51.0", "request": "2.51.0",
"npm": "2.4.1", "npm": "2.4.1",
"dom-serializer": "0.1.0", "dom-serializer": "0.1.0",
"spawn-cmd": "0.0.2", "spawn-cmd": "0.0.2",
"escape-string-regexp": "1.0.3" "escape-string-regexp": "1.0.3",
"juice": "1.5.0",
"jsonschema": "1.0.2",
"json-schema-defaults": "0.1.1",
"merge-defaults": "0.2.1",
"github-slugid": "1.0.0"
}, },
"devDependencies": { "devDependencies": {
"eslint": "1.5.0",
"mocha": "2.3.2", "mocha": "2.3.2",
"should": "7.1.0", "should": "7.1.0",
"grunt": "~0.4.2", "should-promised": "0.3.1",
"grunt-cli": "0.1.11", "gulp": "^3.8.11",
"grunt-contrib-copy": "0.5.0", "gulp-rename": "^1.2.2",
"grunt-contrib-less": "~0.5.0", "gulp-uglify": "1.1.0",
"grunt-contrib-requirejs": "0.4.1", "gulp-less": "3.0.2",
"grunt-bower-install-simple": "0.9.2" "gulp-minify-css": "1.0.0",
"gulp-util": "3.0.6",
"browserify": "11.0.1",
"merge-stream": "0.1.7",
"vinyl-source-stream": "1.1.0",
"jquery": "2.1.4",
"mousetrap": "1.5.3",
"font-awesome": "4.1.0"
}, },
"scripts": { "scripts": {
"test": "node_modules/.bin/mocha --reporter spec --timeout 15000" "test": "node_modules/.bin/mocha --reporter spec --timeout 15000"
+18 -16
View File
@@ -1,27 +1,29 @@
var _ = require("lodash"); var _ = require('lodash');
var fs = require("fs"); var fs = require('fs');
var path = require("path"); var path = require('path');
var should = require("should"); var should = require('should');
var cheerio = require("cheerio"); var cheerio = require('cheerio');
should.Assertion.add("file", function(file, description) { require('should-promised');
this.params = { actual: this.obj.toString(), operator: "have file " + file, message: description };
this.obj.should.have.property("options").which.is.an.Object(); should.Assertion.add('file', function(file, description) {
this.obj.options.should.have.property("output").which.is.a.String(); this.params = { actual: this.obj.toString(), operator: 'have file ' + file, message: description };
this.obj.should.have.property('options').which.is.an.Object();
this.obj.options.should.have.property('output').which.is.a.String();
this.assert(fs.existsSync(path.resolve(this.obj.options.output, file))); this.assert(fs.existsSync(path.resolve(this.obj.options.output, file)));
}); });
should.Assertion.add("jsonfile", function(file, description) { should.Assertion.add('jsonfile', function(file, description) {
this.params = { actual: this.obj.toString(), operator: "have valid jsonfile " + file, message: description }; this.params = { actual: this.obj.toString(), operator: 'have valid jsonfile ' + file, message: description };
this.obj.should.have.property("options").which.is.an.Object(); this.obj.should.have.property('options').which.is.an.Object();
this.obj.options.should.have.property("output").which.is.a.String(); this.obj.options.should.have.property('output').which.is.a.String();
this.assert(JSON.parse(fs.readFileSync(path.resolve(this.obj.options.output, file), { encoding: "utf-8" }))); this.assert(JSON.parse(fs.readFileSync(path.resolve(this.obj.options.output, file), { encoding: 'utf-8' })));
}); });
should.Assertion.add("html", function(rules, description) { should.Assertion.add('html', function(rules, description) {
this.params = { actual: "HTML string", operator: "valid html", message: description }; this.params = { actual: 'HTML string', operator: 'valid html', message: description };
var $ = cheerio.load(this.obj); var $ = cheerio.load(this.obj);
_.each(rules, function(validations, query) { _.each(rules, function(validations, query) {
+3
View File
@@ -1,5 +1,8 @@
# Readme # Readme
<p id="test-plugin-block-shortcuts-1">$$test_block1$$</p>
<p id="test-plugin-block-shortcuts-2">{% include "./block.md" %}</p>
### Relative ### Relative
<p id="t1">{% include "./hello.md" %}</p> <p id="t1">{% include "./hello.md" %}</p>
+1
View File
@@ -0,0 +1 @@
$$test_block2$$
+3
View File
@@ -0,0 +1,3 @@
# Hello World
## Hello {#hello-custom}
View File
+1
View File
@@ -0,0 +1 @@
== Readme for the bookk
+1
View File
@@ -0,0 +1 @@
# Readme for GitHub
+1
View File
@@ -0,0 +1 @@
# Summary
+5
View File
@@ -0,0 +1,5 @@
{
"structure": {
"readme": "README.adoc"
}
}
+5
View File
@@ -0,0 +1,5 @@
# Glossary
### Hello
Hello world
+22 -22
View File
@@ -1,63 +1,63 @@
var path = require("path"); var path = require('path');
var fs = require("fs"); var fs = require('fs');
var Plugin = require("../lib/plugin"); var Plugin = require('../lib/plugin');
var PLUGINS_ROOT = path.resolve(__dirname, "plugins"); var PLUGINS_ROOT = path.resolve(__dirname, 'plugins');
describe("Code Highlighting", function () { describe('Code Highlighting', function () {
var book, PAGE; var book, PAGE;
before(function() { before(function() {
return books.generate("highlight", "website", { return books.generate('highlight', 'website', {
prepare: function(_book) { prepare: function(_book) {
book = _book; book = _book;
var plugin = new Plugin(book, "replace_highlight"); var plugin = new Plugin(book, 'replace_highlight');
plugin.load("./replace_highlight", PLUGINS_ROOT); plugin.load('./replace_highlight', PLUGINS_ROOT);
book.plugins.load(plugin); book.plugins.load(plugin);
} }
}) })
.then(function() { .then(function() {
PAGE = fs.readFileSync( PAGE = fs.readFileSync(
path.join(book.options.output, "index.html"), path.join(book.options.output, 'index.html'),
{ encoding: "utf-8" } { encoding: 'utf-8' }
); );
}); });
}); });
it("should correctly replace highlighting", function() { it('should correctly replace highlighting', function() {
PAGE.should.be.html({ PAGE.should.be.html({
"code": { 'code': {
index: 0, index: 0,
text: "code_test 1\n_code" text: 'code_test 1\n_code'
} }
}); });
}); });
it("should correctly replace highlighting with language", function() { it('should correctly replace highlighting with language', function() {
PAGE.should.be.html({ PAGE.should.be.html({
"code": { 'code': {
index: 1, index: 1,
text: "lang_test 2\n_lang" text: 'lang_test 2\n_lang'
} }
}); });
}); });
it("should correctly replace highlighting for inline code", function() { it('should correctly replace highlighting for inline code', function() {
PAGE.should.be.html({ PAGE.should.be.html({
"code": { 'code': {
index: 2, index: 2,
text: "code_test 3_code" text: 'code_test 3_code'
} }
}); });
}); });
it("should correctly replace highlighting for inline code with html tags", function() { it('should correctly replace highlighting for inline code with html tags', function() {
PAGE.should.be.html({ PAGE.should.be.html({
"code": { 'code': {
index: 3, index: 3,
text: "code_<test>_code" text: 'code_<test>_code'
} }
}); });
}); });
+17 -17
View File
@@ -1,37 +1,37 @@
describe("Configuration", function () { describe('Configuration', function () {
it("should extract default title from README", function() { it('should extract default title from README', function() {
return books.parse("basic") return books.parse('basic')
.then(function(book) { .then(function(book) {
book.options.title.should.be.equal("Readme"); book.options.title.should.be.equal('Readme');
}); });
}); });
it("should extract default description from README", function() { it('should extract default description from README', function() {
return books.parse("basic") return books.parse('basic')
.then(function(book) { .then(function(book) {
book.options.description.should.be.equal("Default description for the book."); book.options.description.should.be.equal('Default description for the book.');
}); });
}); });
it("should correctly load from json (book.json)", function() { it('should correctly load from json (book.json)', function() {
return books.parse("config-json") return books.parse('config-json')
.then(function(book) { .then(function(book) {
book.options.title.should.be.equal("json-config"); book.options.title.should.be.equal('json-config');
}); });
}); });
it("should correctly load from JavaScript (book.js)", function() { it('should correctly load from JavaScript (book.js)', function() {
return books.parse("config-js") return books.parse('config-js')
.then(function(book) { .then(function(book) {
book.options.title.should.be.equal("js-config"); book.options.title.should.be.equal('js-config');
}); });
}); });
it("should provide configuration on book.config.get", function() { it('should provide configuration on book.config.get', function() {
return books.parse("basic") return books.parse('basic')
.then(function(book) { .then(function(book) {
book.config.get("description").should.be.equal("Default description for the book."); book.config.get('description').should.be.equal('Default description for the book.');
book.getConfig("description").should.be.equal("Default description for the book."); book.getConfig('description').should.be.equal('Default description for the book.');
}); });
}); });
}); });
+21 -21
View File
@@ -1,66 +1,66 @@
var fs = require("fs"); var fs = require('fs');
var path = require("path"); var path = require('path');
describe("ConRefs", function () { describe('ConRefs', function () {
var book, readme; var book, readme;
before(function() { before(function() {
return books.generate("conrefs", "website") return books.generate('conrefs', 'website')
.then(function(_book) { .then(function(_book) {
book = _book; book = _book;
readme = fs.readFileSync( readme = fs.readFileSync(
path.join(book.options.output, "index.html"), path.join(book.options.output, 'index.html'),
{ encoding: "utf-8" } { encoding: 'utf-8' }
); );
}); });
}); });
it("should handle local references", function() { it('should handle local references', function() {
readme.should.be.html({ readme.should.be.html({
".page-inner p#t1": { '.page-inner p#t1': {
count: 1, count: 1,
text: "Hello World", text: 'Hello World',
trim: true trim: true
} }
}); });
}); });
it("should handle local references with absolute paths", function() { it('should handle local references with absolute paths', function() {
readme.should.be.html({ readme.should.be.html({
".page-inner p#t2": { '.page-inner p#t2': {
count: 1, count: 1,
text: "Hello World", text: 'Hello World',
trim: true trim: true
} }
}); });
}); });
it("should correctly include file from git reference", function() { it('should correctly include file from git reference', function() {
readme.should.be.html({ readme.should.be.html({
".page-inner p#t3": { '.page-inner p#t3': {
count: 1, count: 1,
text: "Hello from git", text: 'Hello from git',
trim: true trim: true
} }
}); });
}); });
it("should correctly handle deep include in git reference", function() { it('should correctly handle deep include in git reference', function() {
readme.should.be.html({ readme.should.be.html({
".page-inner p#t4": { '.page-inner p#t4': {
count: 1, count: 1,
text: "First Hello. Hello from git", text: 'First Hello. Hello from git',
trim: true trim: true
} }
}); });
}); });
it("should correctly handle absolute include in git reference", function() { it('should correctly handle absolute include in git reference', function() {
readme.should.be.html({ readme.should.be.html({
".page-inner p#t5": { '.page-inner p#t5': {
count: 1, count: 1,
text: "First Hello. Hello from git", text: 'First Hello. Hello from git',
trim: true trim: true
} }
}); });
+25 -21
View File
@@ -1,66 +1,70 @@
var fs = require("fs"); var fs = require('fs');
var path = require("path"); var path = require('path');
describe("eBook generator", function () { describe('eBook generator', function () {
describe("Basic Book", function() { describe('Basic Book', function() {
var book; var book;
before(function() { before(function() {
return books.generate("basic", "ebook") return books.generate('basic', 'ebook')
.then(function(_book) { .then(function(_book) {
book = _book; book = _book;
}); });
}); });
it("should correctly output a SUMMARY.html", function() { it('should correctly output a SUMMARY.html', function() {
book.should.have.file("SUMMARY.html"); book.should.have.file('SUMMARY.html');
}); });
it("should correctly copy assets", function() { it('should correctly copy assets', function() {
book.should.have.file("gitbook"); book.should.have.file('gitbook');
book.should.have.file("gitbook/style.css"); book.should.have.file('gitbook/ebook.css');
});
it('should not copy website assets', function() {
book.should.not.have.file('gitbook/style.css');
}); });
}); });
describe("Custom styles", function() { describe('Custom styles', function() {
var book; var book;
before(function() { before(function() {
return books.generate("style-print", "ebook") return books.generate('style-print', 'ebook')
.then(function(_book) { .then(function(_book) {
book = _book; book = _book;
}); });
}); });
it("should remove default print.css", function() { it('should remove default print.css', function() {
var PAGE = fs.readFileSync( var PAGE = fs.readFileSync(
path.join(book.options.output, "index.html"), path.join(book.options.output, 'index.html'),
{ encoding: "utf-8" } { encoding: 'utf-8' }
); );
// There are 2 styles (one from plugin-highlight and the new style) // There are 2 styles (one from plugin-highlight and the new style)
PAGE.should.be.html({ PAGE.should.be.html({
"link": { 'link': {
count: 2 count: 2
} }
}); });
PAGE.should.be.html({ PAGE.should.be.html({
"link[href=\"./styles/print.css\"]": { 'link[href=\'./styles/print.css\']': {
count: 1 count: 1
} }
}); });
PAGE.should.be.html({ PAGE.should.be.html({
"link[href=\"gitbook/plugins/gitbook-plugin-highlight/ebook.css\"]": { 'link[href="gitbook/plugins/gitbook-plugin-highlight/ebook.css"]': {
count: 1 count: 1
} }
}); });
}); });
it("should correctly print.css", function() { it('should correctly copy print.css', function() {
book.should.have.file("styles"); book.should.have.file('styles');
book.should.have.file("styles/print.css"); book.should.have.file('styles/print.css');
}); });
}); });
}); });
+11
View File
@@ -0,0 +1,11 @@
describe('Formatting', function () {
it('should provide formatting with book.formatString', function() {
return books.parse('basic')
.then(function(book) {
return book.formatString('markdown', 'this is a **test**');
})
.then(function(content) {
content.should.equal('<p>this is a <strong>test</strong></p>\n');
});
});
});
+37
View File
@@ -0,0 +1,37 @@
var path = require('path');
var fs = require('fs');
describe('Headings', function () {
var book, PAGE;
before(function() {
return books.generate('headings', 'website')
.then(function(_book) {
book = _book;
PAGE = fs.readFileSync(
path.join(book.options.output, 'index.html'),
{ encoding: 'utf-8' }
);
});
});
describe('IDs', function() {
it('should correctly generate an ID', function() {
PAGE.should.be.html({
'h1#hello-world': {
count: 1
}
});
});
it('should correctly accept custom ID', function() {
PAGE.should.be.html({
'h2#hello-custom': {
count: 1
}
});
});
});
});
+23 -17
View File
@@ -1,13 +1,13 @@
var os = require("os"); var os = require('os');
var path = require("path"); var path = require('path');
var Q = require("q"); var Q = require('q');
var _ = require("lodash"); var _ = require('lodash');
var fsUtil = require("../lib/utils/fs"); var fsUtil = require('../lib/utils/fs');
var Book = require("../").Book; var Book = require('../').Book;
var LOG_LEVELS = require("../").LOG_LEVELS; var LOG_LEVELS = require('../').LOG_LEVELS;
require("./assertions"); require('./assertions');
var BOOKS = {}; var BOOKS = {};
@@ -20,7 +20,7 @@ function generateBook(bookId, test, opts) {
prepare: function() {} prepare: function() {}
}); });
return parseBook(bookId, test) return parseBook(bookId, test, opts)
.then(function(book) { .then(function(book) {
return Q(opts.prepare(book)) return Q(opts.prepare(book))
@@ -32,21 +32,27 @@ function generateBook(bookId, test, opts) {
} }
// Generate and return a book // Generate and return a book
function parseBook(bookId, test) { function parseBook(bookId, test, opts) {
test = test || "website"; opts = _.defaults(opts || {}, {
BOOKS[bookId] = BOOKS[bookId] || {}; testId: ''
if (BOOKS[bookId][test]) return Q(BOOKS[bookId][test]); });
BOOKS[bookId][test] = new Book(path.resolve(__dirname, "books", bookId), { test = test || 'website';
var testId = [test, opts.testId].join('-');
BOOKS[bookId] = BOOKS[bookId] || {};
if (BOOKS[bookId][testId]) return Q(BOOKS[bookId][testId]);
BOOKS[bookId][testId] = new Book(path.resolve(__dirname, 'books', bookId), {
logLevel: LOG_LEVELS.DISABLED, logLevel: LOG_LEVELS.DISABLED,
config: { config: {
output: path.resolve(TMPDIR, bookId+"-"+test) output: path.resolve(TMPDIR, bookId+'-'+testId)
} }
}); });
return BOOKS[bookId][test].parse() return BOOKS[bookId][testId].parse()
.then(function() { .then(function() {
return BOOKS[bookId][test]; return BOOKS[bookId][testId];
}); });
} }
+13 -13
View File
@@ -1,24 +1,24 @@
var fs = require("fs"); var fs = require('fs');
var path = require("path"); var path = require('path');
var should = require("should"); var should = require('should');
var Book = require("../").Book; var Book = require('../').Book;
var LOG_LEVELS = require("../").LOG_LEVELS; var LOG_LEVELS = require('../').LOG_LEVELS;
describe("Init Books", function () { describe('Init Books', function () {
var initRoot; var initRoot;
before(function() { before(function() {
initRoot = path.resolve(__dirname, "books/init"); initRoot = path.resolve(__dirname, 'books/init');
return Book.init(initRoot, { return Book.init(initRoot, {
logLevel: LOG_LEVELS.DISABLED, logLevel: LOG_LEVELS.DISABLED
}); });
}); });
it("should create all chapters", function() { it('should create all chapters', function() {
should(fs.existsSync(path.resolve(initRoot, "hello.md"))).be.ok(); should(fs.existsSync(path.resolve(initRoot, 'hello.md'))).be.ok();
should(fs.existsSync(path.resolve(initRoot, "hello2.md"))).be.ok(); should(fs.existsSync(path.resolve(initRoot, 'hello2.md'))).be.ok();
should(fs.existsSync(path.resolve(initRoot, "hello3/hello4.md"))).be.ok(); should(fs.existsSync(path.resolve(initRoot, 'hello3/hello4.md'))).be.ok();
should(fs.existsSync(path.resolve(initRoot, "hello3/hello5/hello6.md"))).be.ok(); should(fs.existsSync(path.resolve(initRoot, 'hello3/hello5/hello6.md'))).be.ok();
}); });
}); });
+37 -37
View File
@@ -1,91 +1,91 @@
var fs = require("fs"); var fs = require('fs');
var path = require("path"); var path = require('path');
describe("JSON generator", function () { describe('JSON generator', function () {
describe("Basic Book", function() { describe('Basic Book', function() {
var book; var book;
before(function() { before(function() {
return books.generate("basic", "json") return books.generate('basic', 'json')
.then(function(_book) { .then(function(_book) {
book = _book; book = _book;
}); });
}); });
it("should correctly output a README.json", function() { it('should correctly output a README.json', function() {
book.should.have.file("README.json"); book.should.have.file('README.json');
}); });
it("should output a valid json", function() { it('should output a valid json', function() {
book.should.have.jsonfile("README.json"); book.should.have.jsonfile('README.json');
}); });
describe("Page Format", function() { describe('Page Format', function() {
var page; var page;
before(function() { before(function() {
page = JSON.parse( page = JSON.parse(
fs.readFileSync( fs.readFileSync(
path.join(book.options.output, "README.json"), path.join(book.options.output, 'README.json'),
{ encoding: "utf-8" } { encoding: 'utf-8' }
) )
); );
}); });
it("should contains valid section", function() { it('should contains valid section', function() {
page.should.have.property("sections").with.lengthOf(1); page.should.have.property('sections').with.lengthOf(1);
page.sections[0].should.have.property("content").which.is.a.String(); page.sections[0].should.have.property('content').which.is.a.String();
page.sections[0].should.have.property("type", "normal"); page.sections[0].should.have.property('type', 'normal');
}); });
it("should contains valid progress", function() { it('should contains valid progress', function() {
page.should.have.property("progress"); page.should.have.property('progress');
page.progress.should.have.property("chapters").with.lengthOf(1); page.progress.should.have.property('chapters').with.lengthOf(1);
page.progress.should.have.property("current"); page.progress.should.have.property('current');
}); });
it("should contains no languages", function() { it('should contains no languages', function() {
page.should.have.property("langs").with.lengthOf(0); page.should.have.property('langs').with.lengthOf(0);
}); });
}); });
}); });
describe("Multilingual Book", function() { describe('Multilingual Book', function() {
var book; var book;
before(function() { before(function() {
return books.generate("languages", "json") return books.generate('languages', 'json')
.then(function(_book) { .then(function(_book) {
book = _book; book = _book;
}); });
}); });
it("should correctly output READMEs", function() { it('should correctly output READMEs', function() {
book.should.have.file("README.json"); book.should.have.file('README.json');
book.should.have.file("en/README.json"); book.should.have.file('en/README.json');
book.should.have.file("fr/README.json"); book.should.have.file('fr/README.json');
}); });
it("should output valid json", function() { it('should output valid json', function() {
book.should.have.jsonfile("README.json"); book.should.have.jsonfile('README.json');
book.should.have.jsonfile("en/README.json"); book.should.have.jsonfile('en/README.json');
book.should.have.jsonfile("fr/README.json"); book.should.have.jsonfile('fr/README.json');
}); });
describe("Page Format", function() { describe('Page Format', function() {
var page; var page;
before(function() { before(function() {
page = JSON.parse( page = JSON.parse(
fs.readFileSync( fs.readFileSync(
path.join(book.options.output, "README.json"), path.join(book.options.output, 'README.json'),
{ encoding: "utf-8" } { encoding: 'utf-8' }
) )
); );
}); });
it("should contains no languages", function() { it('should contains no languages', function() {
page.should.have.property("langs").with.lengthOf(2); page.should.have.property('langs').with.lengthOf(2);
}); });
}); });
}); });
+166 -100
View File
@@ -1,47 +1,48 @@
var _ = require("lodash"); var _ = require('lodash');
var should = require("should"); var fs = require('fs');
var path = require("path"); var should = require('should');
var path = require('path');
var Plugin = require("../lib/plugin"); var Plugin = require('../lib/plugin');
var parsers = require("gitbook-parsers"); var parsers = require('gitbook-parsers');
var PLUGINS_ROOT = path.resolve(__dirname, "plugins"); var PLUGINS_ROOT = path.resolve(__dirname, 'plugins');
describe("Plugins", function () { describe('Plugins', function () {
var book; var book;
before(function() { before(function() {
return books.parse("basic") return books.parse('basic')
.then(function(_book) { .then(function(_book) {
book = _book; book = _book;
}); });
}); });
describe("Invalid", function() { describe('Invalid', function() {
var plugin; var plugin;
before(function() { before(function() {
plugin = new Plugin(book, "invalid"); plugin = new Plugin(book, 'invalid');
plugin.load("./invalid", PLUGINS_ROOT); plugin.load('./invalid', PLUGINS_ROOT);
}); });
it("should be detected", function() { it('should be detected', function() {
should(plugin.isValid()).be.exactly(false); should(plugin.isValid()).be.exactly(false);
}); });
}); });
describe("Empty", function() { describe('Empty', function() {
var plugin; var plugin;
before(function() { before(function() {
plugin = new Plugin(book, "empty"); plugin = new Plugin(book, 'empty');
plugin.load("./empty", PLUGINS_ROOT); plugin.load('./empty', PLUGINS_ROOT);
}); });
it("should valid a plugin", function() { it('should valid a plugin', function() {
should(plugin.isValid()).be.exactly(true); should(plugin.isValid()).be.exactly(true);
}); });
it("should return an empty list of resources", function() { it('should return an empty list of resources', function() {
return plugin.getResources() return plugin.getResources()
.then(function(resources) { .then(function(resources) {
_.each(Plugin.RESOURCES, function(resName) { _.each(Plugin.RESOURCES, function(resName) {
@@ -51,97 +52,128 @@ describe("Plugins", function () {
}); });
}); });
describe("Resources", function() { describe('Configuration', function() {
var plugin; var plugin;
before(function() { before(function() {
plugin = new Plugin(book, "resources"); plugin = new Plugin(book, 'testconfig');
plugin.load("./resources", PLUGINS_ROOT); plugin.load('./config', PLUGINS_ROOT);
});
it('should throw error for invalid configuration', function() {
return plugin.validateConfig({})
.should.be.rejectedWith('Configuration Error: pluginsConfig.testconfig.testRequired is required');
});
it('should throw error for invalid types', function() {
return plugin.validateConfig({
testRequired: 'hello'
})
.should.be.rejectedWith('Configuration Error: pluginsConfig.testconfig.testRequired is not of a type(s) number');
});
it('should extend with default values', function() {
return plugin.validateConfig({
testRequired: 12
})
.should.be.fulfilledWith({
hello: 'world',
testRequired: 12
});
});
});
describe('Resources', function() {
var plugin;
before(function() {
plugin = new Plugin(book, 'resources');
plugin.load('./resources', PLUGINS_ROOT);
return book.plugins.load(plugin); return book.plugins.load(plugin);
}); });
it("should valid a plugin", function() { it('should valid a plugin', function() {
should(plugin.isValid()).be.exactly(true); should(plugin.isValid()).be.exactly(true);
}); });
describe("Website", function() { describe('Website', function() {
it("should return a valid list of resources", function() { it('should return a valid list of resources', function() {
return plugin.getResources("website") return plugin.getResources('website')
.then(function(resources) { .then(function(resources) {
resources.js.should.have.lengthOf(1); resources.js.should.have.lengthOf(1);
}); });
}); });
it("should extend books plugins", function() { it('should extend books plugins', function() {
var resources = book.plugins.resources("website"); var resources = book.plugins.resources('website');
resources.js.should.have.lengthOf(1); resources.js.should.have.lengthOf(5);
}); });
}); });
describe("eBook", function() { describe('eBook', function() {
it("should return a valid list of resources", function() { it('should return a valid list of resources', function() {
return plugin.getResources("ebook") return plugin.getResources('ebook')
.then(function(resources) { .then(function(resources) {
resources.css.should.have.lengthOf(1); resources.css.should.have.lengthOf(1);
}); });
}); });
it("should extend books plugins", function() { it('should extend books plugins', function() {
var resources = book.plugins.resources("ebook"); var resources = book.plugins.resources('ebook');
// There is resources from highlight plugin and this plugin // There is resources from highlight plugin and this plugin
resources.css.should.have.lengthOf(2); resources.css.should.have.lengthOf(2);
should.exist(_.find(resources.css, { should.exist(_.find(resources.css, {
path: "./resources/test" path: 'gitbook-plugin-resources/test'
})); }));
}); });
}); });
}); });
describe("Filters", function() { describe('Filters', function() {
var plugin; var plugin;
before(function() { before(function() {
plugin = new Plugin(book, "filters"); plugin = new Plugin(book, 'filters');
plugin.load("./filters", PLUGINS_ROOT); plugin.load('./filters', PLUGINS_ROOT);
return book.plugins.load(plugin); return book.plugins.load(plugin);
}); });
it("should valid a plugin", function() { it('should valid a plugin', function() {
should(plugin.isValid()).be.exactly(true); should(plugin.isValid()).be.exactly(true);
}); });
it("should return a map of filters", function() { it('should return a map of filters', function() {
var filters = plugin.getFilters(); var filters = plugin.getFilters();
_.size(filters).should.equal(2); _.size(filters).should.equal(2);
filters.should.have.property("hello"); filters.should.have.property('hello');
filters.should.have.property("helloCtx"); filters.should.have.property('helloCtx');
}); });
it("should correctly extend template filters", function() { it('should correctly extend template filters', function() {
return book.template.renderString("{{ \"World\"|hello }}") return book.template.renderString('{{ \'World\'|hello }}')
.then(function(content) { .then(function(content) {
content.should.equal("Hello World"); content.should.equal('Hello World');
}); });
}); });
it("should correctly set book as context", function() { it('should correctly set book as context', function() {
return book.template.renderString("{{ \"root\"|helloCtx }}") return book.template.renderString('{{ \'root\'|helloCtx }}')
.then(function(content) { .then(function(content) {
content.should.equal("root:"+book.root); content.should.equal('root:'+book.root);
}); });
}); });
}); });
describe("Blocks", function() { describe('Blocks', function() {
var plugin; var plugin;
before(function() { before(function() {
plugin = new Plugin(book, "blocks"); plugin = new Plugin(book, 'blocks');
plugin.load("./blocks", PLUGINS_ROOT); plugin.load('./blocks', PLUGINS_ROOT);
return book.plugins.load(plugin); return book.plugins.load(plugin);
}); });
@@ -151,89 +183,123 @@ describe("Plugins", function () {
.then(book.template.postProcess); .then(book.template.postProcess);
}; };
it("should valid a plugin", function() { it('should valid a plugin', function() {
should(plugin.isValid()).be.exactly(true); should(plugin.isValid()).be.exactly(true);
}); });
it("should correctly extend template blocks", function() { it('should correctly extend template blocks', function() {
return testTpl("{% test %}hello{% endtest %}") return testTpl('{% test %}hello{% endtest %}')
.then(function(content) { .then(function(content) {
content.should.equal("testhellotest"); content.should.equal('testhellotest');
}); });
}); });
it("should correctly accept shortcuts", function() { describe('Shortcuts', function() {
return testTpl("$$hello$$", {}, { it('should correctly accept shortcuts', function() {
type: "markdown" return testTpl('$$hello$$', {}, {
type: 'markdown'
}) })
.then(function(content) { .then(function(content) {
content.should.equal("testhellotest"); content.should.equal('testhellotest');
});
});
it('should correctly apply shortcuts to included file', function() {
return books.generate('conrefs', 'website', {
testId: 'include-plugins',
prepare: function(bookConref) {
plugin = new Plugin(bookConref, 'blocks');
plugin.load('./blocks', PLUGINS_ROOT);
return bookConref.plugins.load(plugin);
}
})
.then(function(bookConref) {
var readme = fs.readFileSync(
path.join(bookConref.options.output, 'index.html'),
{ encoding: 'utf-8' }
);
readme.should.be.html({
'.page-inner p#test-plugin-block-shortcuts-1': {
count: 1,
text: 'testtest_block1test',
trim: true
},
'.page-inner p#test-plugin-block-shortcuts-2': {
count: 1,
text: 'testtest_block2test',
trim: true
}
});
});
});
});
it('should correctly extend template blocks with defined end', function() {
return testTpl('{% test2 %}hello{% endtest2end %}')
.then(function(content) {
content.should.equal('test2hellotest2');
}); });
}); });
it("should correctly extend template blocks with defined end", function() { it('should correctly extend template blocks with sub-blocks', function() {
return testTpl("{% test2 %}hello{% endtest2end %}") return testTpl('{% test3join separator=";" %}hello{% also %}world{% endtest3join %}')
.then(function(content) { .then(function(content) {
content.should.equal("test2hellotest2"); content.should.equal('hello;world');
}); });
}); });
it("should correctly extend template blocks with sub-blocks", function() { it('should correctly extend template blocks with different sub-blocks', function() {
return testTpl("{% test3join separator=\";\" %}hello{% also %}world{% endtest3join %}") return testTpl('{% test4join separator=";" %}hello{% also %}the{% finally %}world{% endtest4join %}')
.then(function(content) { .then(function(content) {
content.should.equal("hello;world"); content.should.equal('hello;the;world');
}); });
}); });
it("should correctly extend template blocks with different sub-blocks", function() { it('should correctly extend template blocks with arguments (1)', function() {
return testTpl("{% test4join separator=\";\" %}hello{% also %}the{% finally %}world{% endtest4join %}") return testTpl('{% test5args "a" %}{% endtest5args %}')
.then(function(content) { .then(function(content) {
content.should.equal("hello;the;world"); content.should.equal('test5atest5');
}); });
}); });
it("should correctly extend template blocks with arguments (1)", function() { it('should correctly extend template blocks with arguments (2)', function() {
return testTpl("{% test5args \"a\" %}{% endtest5args %}") return testTpl('{% test5args "a", "b" %}{% endtest5args %}')
.then(function(content) { .then(function(content) {
content.should.equal("test5atest5"); content.should.equal('test5a,btest5');
}); });
}); });
it("should correctly extend template blocks with arguments (2)", function() { it('should correctly extend template blocks with arguments (3)', function() {
return testTpl("{% test5args 'a', 'b' %}{% endtest5args %}") return testTpl('{% test5args "a", "b", "c" %}{% endtest5args %}')
.then(function(content) { .then(function(content) {
content.should.equal("test5a,btest5"); content.should.equal('test5a,b,ctest5');
}); });
}); });
it("should correctly extend template blocks with arguments (3)", function() { it('should correctly extend template blocks with args and kwargs', function() {
return testTpl("{% test5args 'a', 'b', 'c' %}{% endtest5args %}") return testTpl('{% test5kwargs "a", "b", "c", d="test", e="test2" %}{% endtest5kwargs %}')
.then(function(content) { .then(function(content) {
content.should.equal("test5a,b,ctest5"); content.should.equal('test5a,b,c,d:test,e:test2,__keywords:truetest5');
}); });
}); });
it("should correctly extend template blocks with args and kwargs", function() { it('should correctly extend template blocks with access to context', function() {
return testTpl("{% test5kwargs 'a', 'b', 'c', d='test', e='test2' %}{% endtest5kwargs %}") return testTpl('{% set name = "john" %}{% test6context %}{% endtest6context %}', {})
.then(function(content) { .then(function(content) {
content.should.equal("test5a,b,c,d:test,e:test2,__keywords:truetest5"); content.should.equal('test6johntest6');
}); });
});
it("should correctly extend template blocks with access to context", function() {
return testTpl("{% set name = 'john' %}{% test6context %}{% endtest6context %}", {})
.then(function(content) {
content.should.equal("test6johntest6");
});
}); });
}); });
describe("Blocks without parsing", function() { describe('Blocks without parsing', function() {
var plugin; var plugin;
before(function() { before(function() {
plugin = new Plugin(book, "blocks"); plugin = new Plugin(book, 'blocks');
plugin.load("./blocks", PLUGINS_ROOT); plugin.load('./blocks', PLUGINS_ROOT);
return book.plugins.load(plugin); return book.plugins.load(plugin);
}); });
@@ -242,21 +308,21 @@ describe("Plugins", function () {
var filetype = parsers.get(markup); var filetype = parsers.get(markup);
return book.template.renderString(str, args, options) return book.template.renderString(str, args, options)
.then(filetype.page).get("sections").get(0).get("content") .then(filetype.page).get('sections').get(0).get('content')
.then(book.template.postProcess); .then(book.template.postProcess);
}; };
it("should correctly process unparsable for markdown", function() { it('should correctly process unparsable for markdown', function() {
return testTpl(".md", "{% test %}**hello**{% endtest %}") return testTpl('.md', '{% test %}**hello**{% endtest %}')
.then(function(content) { .then(function(content) {
content.should.equal("<p>test**hello**test</p>\n"); content.should.equal('<p>test**hello**test</p>\n');
}); });
}); });
it("should correctly process unparsable for asciidoc", function() { it('should correctly process unparsable for asciidoc', function() {
return testTpl(".adoc", "{% test %}**hello**{% endtest %}") return testTpl('.adoc', '{% test %}**hello**{% endtest %}')
.then(function(content) { .then(function(content) {
content.should.equal("<div class=\"paragraph\">\n<p>test**hello**test</p>\n</div>"); content.should.equal('<div class="paragraph">\n<p>test**hello**test</p>\n</div>');
}); });
}); });
}); });
+1
View File
@@ -0,0 +1 @@
module.exports = {};
+21
View File
@@ -0,0 +1,21 @@
{
"name": "gitbook-plugin-testconfig",
"description": "Test plugin configuration",
"main": "index.js",
"version": "0.0.1",
"engines": {
"gitbook": "*"
},
"gitbook": {
"properties": {
"hello": {
"type": "string",
"default": "world"
},
"testRequired": {
"type": "number",
"required": true
}
}
}
}
+20
View File
@@ -0,0 +1,20 @@
describe('Structure', function () {
var book;
before(function() {
return books.parse('structure')
.then(function(_book) {
book = _book;
});
});
it('should prioritize structure defined in book.json', function() {
book.readmeFile.should.equal('README.adoc');
});
it('should be case incensitive', function() {
book.glossaryFile.should.equal('glossary.md');
book.glossary.should.have.lengthOf(1);
});
});
+13 -9
View File
@@ -1,22 +1,26 @@
describe("Website generator", function () { describe('Website generator', function () {
describe("Basic Book", function() { describe('Basic Book', function() {
var book; var book;
before(function() { before(function() {
return books.generate("basic", "website") return books.generate('basic', 'website')
.then(function(_book) { .then(function(_book) {
book = _book; book = _book;
}); });
}); });
it("should correctly output an index.html", function() { it('should correctly output an index.html', function() {
book.should.have.file("index.html"); book.should.have.file('index.html');
}); });
it("should correctly copy assets", function() { it('should correctly copy assets', function() {
book.should.have.file("gitbook"); book.should.have.file('gitbook');
book.should.have.file("gitbook/app.js"); book.should.have.file('gitbook/app.js');
book.should.have.file("gitbook/style.css"); book.should.have.file('gitbook/style.css');
});
it('should not copy ebook assets', function() {
book.should.not.have.file('gitbook/ebook.css');
}); });
}); });
}); });
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
.section.glossary>h1,.section.toc>h1{text-align:center}.page .section b,.page .section dl dt,.page .section strong,.page .section table th{font-weight:700}.link-inherit,.link-inherit:focus,.link-inherit:hover{color:inherit}.hidden{display:none}.section.toc>ol{margin:0;padding:0}.section.toc li{list-style:none}.section.toc li .inner{display:block;border-bottom:1px dotted #eee;margin-bottom:4px}.section.toc li .inner a,.section.toc li .inner span{padding-right:5px;border-bottom:1px solid #fff;margin-bottom:-1px}.section.toc li .inner .page{float:right;padding-left:5px}.section.toc li ol{margin:0;padding:0 0 0 2em}.dir-rtl .section.toc li .inner .page{float:left}.dir-rtl .section.toc li ol{margin:0;padding:0 2em 0 0}.section.glossary .glossary-entry{margin-bottom:40px}.section.glossary .glossary-entry h2 a,.section.glossary .glossary-entry h2 a:hover{color:inherit;text-decoration:none}.section.glossary .glossary-entry .glossary-index{list-style:none;margin:0;padding:0}.section.glossary .glossary-entry .glossary-index li{display:inline;margin:0 8px;white-space:nowrap}body{font-family:sans-serif;color:#000;text-size-adjust:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}.page .section{display:block;word-wrap:break-word;overflow:hidden;color:#000;line-height:1.6}.page .section *{-moz-box-sizing:border-box;box-sizing:border-box}.page .section>:first-child{margin-top:0!important}.page .section>:last-child{margin-bottom:0!important}.page .section blockquote,.page .section code,.page .section figure,.page .section img,.page .section pre,.page .section table,.page .section tr{page-break-inside:avoid}.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section p{orphans:3;widows:3}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5{page-break-after:avoid}.page .section em{font-style:italic}.page .section blockquote,.page .section dl,.page .section ol,.page .section p,.page .section table,.page .section ul{margin-top:0;margin-bottom:.8em}.page .section a{color:#4183c4;text-decoration:none;background:0 0}.page .section a:active,.page .section a:focus,.page .section a:hover{outline:0;text-decoration:underline}.page .section img{border:0;max-width:100%}.page .section hr{height:4px;padding:0;margin:1.6em 0;overflow:hidden;background-color:#e7e7e7;border:none}.page .section hr:after,.page .section hr:before{display:table;content:" "}.page .section hr:after{clear:both}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section h6{margin-top:1.2em;margin-bottom:.8em;font-weight:700}.page .section h1{font-size:2em}.page .section h2{font-size:1.75em}.page .section h3{font-size:1.5em}.page .section h4{font-size:1.25em}.page .section h5{font-size:1em}.page .section h6{font-size:1em;color:#777}.page .section code,.page .section pre{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;direction:ltr;border:none}.page .section pre{overflow:auto;word-wrap:normal;margin:0 0 1.2em;padding:.85em 1em;background:#f7f7f7}.page .section pre>code{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;font-size:.85em;white-space:pre;background:0 0}.page .section pre>code:after,.page .section pre>code:before{content:normal}.page .section code{padding:.2em;margin:0;font-size:.85em;background-color:#f7f7f7}.page .section code:after,.page .section code:before{letter-spacing:-.2em;content:"\00a0"}.page .section table{display:table;width:100%;border-collapse:collapse;border-spacing:0;overflow:auto}.page .section table td,.page .section table th{padding:6px 13px;border:1px solid #ddd}.page .section table tr{background-color:#fff;border-top:1px solid #ccc}.page .section table tr:nth-child(2n){background-color:#f8f8f8}.page .section ol,.page .section ul{padding:0 0 0 2em;margin:0 0 .8em}.page .section ol ol,.page .section ol ul,.page .section ul ol,.page .section ul ul{margin-top:0;margin-bottom:0}.page .section ol ol{list-style-type:lower-roman}.page .section blockquote{margin:0 0 .8em;padding:0 15px;color:#858585;border-left:4px solid #e5e5e5}.page .section blockquote:first-child{margin-top:0}.page .section blockquote:last-child{margin-bottom:0}.page .section dl{padding:0}.page .section dl dt{padding:0;margin-top:.8em;font-style:italic}.page .section dl dd{padding:0 .8em;margin-bottom:.8em}.page .section dd{margin-left:0}.page .book-chapter{display:none}
+1
View File
@@ -0,0 +1 @@
.section.glossary>h1,.section.toc>h1{text-align:center}.page .section b,.page .section dl dt,.page .section strong,.page .section table th{font-weight:700}.link-inherit,.link-inherit:focus,.link-inherit:hover{color:inherit}.hidden{display:none}.section.toc>ol{margin:0;padding:0}.section.toc li{list-style:none}.section.toc li .inner{display:block;border-bottom:1px dotted #eee;margin-bottom:4px}.section.toc li .inner a,.section.toc li .inner span{padding-right:5px;border-bottom:1px solid #fff;margin-bottom:-1px}.section.toc li .inner .page{float:right;padding-left:5px}.section.toc li ol{margin:0;padding:0 0 0 2em}.dir-rtl .section.toc li .inner .page{float:left}.dir-rtl .section.toc li ol{margin:0;padding:0 2em 0 0}.section.glossary .glossary-entry{margin-bottom:40px}.section.glossary .glossary-entry h2 a,.section.glossary .glossary-entry h2 a:hover{color:inherit;text-decoration:none}.section.glossary .glossary-entry .glossary-index{list-style:none;margin:0;padding:0}.section.glossary .glossary-entry .glossary-index li{display:inline;margin:0 8px;white-space:nowrap}body{font-family:serif;color:#000;text-size-adjust:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}.page .section{display:block;word-wrap:break-word;overflow:hidden;color:#000;line-height:1.6}.page .section *{-moz-box-sizing:border-box;box-sizing:border-box}.page .section>:first-child{margin-top:0!important}.page .section>:last-child{margin-bottom:0!important}.page .section blockquote,.page .section code,.page .section figure,.page .section img,.page .section pre,.page .section table,.page .section tr{page-break-inside:avoid}.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section p{orphans:3;widows:3}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5{page-break-after:avoid}.page .section em{font-style:italic}.page .section blockquote,.page .section dl,.page .section ol,.page .section p,.page .section table,.page .section ul{margin-top:0;margin-bottom:.8em}.page .section a{color:#4183c4;text-decoration:none;background:0 0}.page .section a:active,.page .section a:focus,.page .section a:hover{outline:0;text-decoration:underline}.page .section img{border:0;max-width:100%}.page .section hr{height:4px;padding:0;margin:1.6em 0;overflow:hidden;background-color:#e7e7e7;border:none}.page .section hr:after,.page .section hr:before{display:table;content:" "}.page .section hr:after{clear:both}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section h6{margin-top:1.2em;margin-bottom:.8em;font-weight:700}.page .section h1{font-size:2em}.page .section h2{font-size:1.75em}.page .section h3{font-size:1.5em}.page .section h4{font-size:1.25em}.page .section h5{font-size:1em}.page .section h6{font-size:1em;color:#777}.page .section code,.page .section pre{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;direction:ltr;border:none}.page .section pre{overflow:auto;word-wrap:normal;margin:0 0 1.2em;padding:.85em 1em;background:#f7f7f7}.page .section pre>code{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;font-size:.85em;white-space:pre;background:0 0}.page .section pre>code:after,.page .section pre>code:before{content:normal}.page .section code{padding:.2em;margin:0;font-size:.85em;background-color:#f7f7f7}.page .section code:after,.page .section code:before{letter-spacing:-.2em;content:"\00a0"}.page .section table{display:table;width:100%;border-collapse:collapse;border-spacing:0;overflow:auto}.page .section table td,.page .section table th{padding:6px 13px;border:1px solid #ddd}.page .section table tr{background-color:#fff;border-top:1px solid #ccc}.page .section table tr:nth-child(2n){background-color:#f8f8f8}.page .section ol,.page .section ul{padding:0 0 0 2em;margin:0 0 .8em}.page .section ol ol,.page .section ol ul,.page .section ul ol,.page .section ul ul{margin-top:0;margin-bottom:0}.page .section ol ol{list-style-type:lower-roman}.page .section blockquote{margin:0 0 .8em;padding:0 15px;color:#858585;border-left:4px solid #e5e5e5}.page .section blockquote:first-child{margin-top:0}.page .section blockquote:last-child{margin-bottom:0}.page .section dl{padding:0}.page .section dl dt{padding:0;margin-top:.8em;font-style:italic}.page .section dl dd{padding:0 .8em;margin-bottom:.8em}.page .section dd{margin-left:0}.page .book-chapter{display:none}
+1
View File
@@ -0,0 +1 @@
.section.glossary>h1,.section.toc>h1{text-align:center}.page .section b,.page .section dl dt,.page .section strong,.page .section table th{font-weight:700}.link-inherit,.link-inherit:focus,.link-inherit:hover{color:inherit}.hidden{display:none}.section.toc>ol{margin:0;padding:0}.section.toc li{list-style:none}.section.toc li .inner{display:block;border-bottom:1px dotted #eee;margin-bottom:4px}.section.toc li .inner a,.section.toc li .inner span{padding-right:5px;border-bottom:1px solid #fff;margin-bottom:-1px}.section.toc li .inner .page{float:right;padding-left:5px}.section.toc li ol{margin:0;padding:0 0 0 2em}.dir-rtl .section.toc li .inner .page{float:left}.dir-rtl .section.toc li ol{margin:0;padding:0 2em 0 0}.section.glossary .glossary-entry{margin-bottom:40px}.section.glossary .glossary-entry h2 a,.section.glossary .glossary-entry h2 a:hover{color:inherit;text-decoration:none}.section.glossary .glossary-entry .glossary-index{list-style:none;margin:0;padding:0}.section.glossary .glossary-entry .glossary-index li{display:inline;margin:0 8px;white-space:nowrap}body{font-family:serif;color:#000;text-size-adjust:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}.page .section{display:block;word-wrap:break-word;overflow:hidden;color:#000;line-height:1.6}.page .section *{-moz-box-sizing:border-box;box-sizing:border-box}.page .section>:first-child{margin-top:0!important}.page .section>:last-child{margin-bottom:0!important}.page .section blockquote,.page .section code,.page .section figure,.page .section img,.page .section pre,.page .section table,.page .section tr{page-break-inside:avoid}.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section p{orphans:3;widows:3}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5{page-break-after:avoid}.page .section em{font-style:italic}.page .section blockquote,.page .section dl,.page .section ol,.page .section p,.page .section table,.page .section ul{margin-top:0;margin-bottom:.8em}.page .section a{color:#000;text-decoration:none;background:0 0}.page .section a:active,.page .section a:focus,.page .section a:hover{outline:0;text-decoration:underline}.page .section img{border:0;max-width:100%}.page .section hr{height:4px;padding:0;margin:1.6em 0;overflow:hidden;background-color:#e7e7e7;border:none}.page .section hr:after,.page .section hr:before{display:table;content:" "}.page .section hr:after{clear:both}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section h6{margin-top:1.2em;margin-bottom:.8em;font-weight:700}.page .section h1{font-size:2em}.page .section h2{font-size:1.75em}.page .section h3{font-size:1.5em}.page .section h4{font-size:1.25em}.page .section h5{font-size:1em}.page .section h6{font-size:1em;color:#777}.page .section code,.page .section pre{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;direction:ltr;border:none}.page .section pre{overflow:auto;word-wrap:normal;margin:0 0 1.2em;padding:.85em 1em;background:#f7f7f7}.page .section pre>code{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;font-size:.85em;white-space:pre;background:0 0}.page .section pre>code:after,.page .section pre>code:before{content:normal}.page .section code{padding:.2em;margin:0;font-size:.85em;background-color:#f7f7f7}.page .section code:after,.page .section code:before{letter-spacing:-.2em;content:"\00a0"}.page .section table{display:table;width:100%;border-collapse:collapse;border-spacing:0;overflow:auto}.page .book-chapter,.page-toc li .inner span.page{display:none}.page .section table td,.page .section table th{padding:6px 13px;border:1px solid #ddd}.page .section table tr{background-color:#fff;border-top:1px solid #ccc}.page .section table tr:nth-child(2n){background-color:#f8f8f8}.page .section ol,.page .section ul{padding:0 0 0 2em;margin:0 0 .8em}.page .section ol ol,.page .section ol ul,.page .section ul ol,.page .section ul ul{margin-top:0;margin-bottom:0}.page .section ol ol{list-style-type:lower-roman}.page .section blockquote{margin:0 0 .8em;padding:0 15px;color:#858585;border-left:4px solid #e5e5e5}.page .section blockquote:first-child{margin-top:0}.page .section blockquote:last-child{margin-bottom:0}.page .section dl{padding:0}.page .section dl dt{padding:0;margin-top:.8em;font-style:italic}.page .section dl dd{padding:0 .8em;margin-bottom:.8em}.page .section dd{margin-left:0}a{color:#000;text-decoration:underline!important}.page-toc li{list-style-type:lower-alpha}.page-toc li .inner{border:none}
+1
View File
@@ -0,0 +1 @@
.section.glossary>h1,.section.toc>h1{text-align:center}.page .section b,.page .section dl dt,.page .section strong,.page .section table th{font-weight:700}.link-inherit,.link-inherit:focus,.link-inherit:hover{color:inherit}.hidden{display:none}.section.toc>ol{margin:0;padding:0}.section.toc li{list-style:none}.section.toc li .inner{display:block;border-bottom:1px dotted #eee;margin-bottom:4px}.section.toc li .inner a,.section.toc li .inner span{padding-right:5px;border-bottom:1px solid #fff;margin-bottom:-1px}.section.toc li .inner .page{float:right;padding-left:5px}.section.toc li ol{margin:0;padding:0 0 0 2em}.dir-rtl .section.toc li .inner .page{float:left}.dir-rtl .section.toc li ol{margin:0;padding:0 2em 0 0}.section.glossary .glossary-entry{margin-bottom:40px}.section.glossary .glossary-entry h2 a,.section.glossary .glossary-entry h2 a:hover{color:inherit;text-decoration:none}.section.glossary .glossary-entry .glossary-index{list-style:none;margin:0;padding:0}.section.glossary .glossary-entry .glossary-index li{display:inline;margin:0 8px;white-space:nowrap}body{font-family:sans-serif;color:#000;text-size-adjust:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}.page .section{display:block;word-wrap:break-word;overflow:hidden;color:#000;line-height:1.6}.page .section *{-moz-box-sizing:border-box;box-sizing:border-box}.page .section>:first-child{margin-top:0!important}.page .section>:last-child{margin-bottom:0!important}.page .section blockquote,.page .section code,.page .section figure,.page .section img,.page .section pre,.page .section table,.page .section tr{page-break-inside:avoid}.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section p{orphans:3;widows:3}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5{page-break-after:avoid}.page .section em{font-style:italic}.page .section blockquote,.page .section dl,.page .section ol,.page .section p,.page .section table,.page .section ul{margin-top:0;margin-bottom:.8em}.page .section a{color:#4183c4;text-decoration:none;background:0 0}.page .section a:active,.page .section a:focus,.page .section a:hover{outline:0;text-decoration:underline}.page .section img{border:0;max-width:100%}.page .section hr{height:4px;padding:0;margin:1.6em 0;overflow:hidden;background-color:#e7e7e7;border:none}.page .section hr:after,.page .section hr:before{display:table;content:" "}.page .section hr:after{clear:both}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section h6{margin-top:1.2em;margin-bottom:.8em;font-weight:700}.page .section h1{font-size:2em}.page .section h2{font-size:1.75em}.page .section h3{font-size:1.5em}.page .section h4{font-size:1.25em}.page .section h5{font-size:1em}.page .section h6{font-size:1em;color:#777}.page .section code,.page .section pre{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;direction:ltr;border:none}.page .section pre{overflow:auto;word-wrap:normal;margin:0 0 1.2em;padding:.85em 1em;background:#f7f7f7}.page .section pre>code{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;font-size:.85em;white-space:pre;background:0 0}.page .section pre>code:after,.page .section pre>code:before{content:normal}.page .section code{padding:.2em;margin:0;font-size:.85em;background-color:#f7f7f7}.page .section code:after,.page .section code:before{letter-spacing:-.2em;content:"\00a0"}.page .section table{display:table;width:100%;border-collapse:collapse;border-spacing:0;overflow:auto}.page .section table td,.page .section table th{padding:6px 13px;border:1px solid #ddd}.page .section table tr{background-color:#fff;border-top:1px solid #ccc}.page .section table tr:nth-child(2n){background-color:#f8f8f8}.page .section ol,.page .section ul{padding:0 0 0 2em;margin:0 0 .8em}.page .section ol ol,.page .section ol ul,.page .section ul ol,.page .section ul ul{margin-top:0;margin-bottom:0}.page .section ol ol{list-style-type:lower-roman}.page .section blockquote{margin:0 0 .8em;padding:0 15px;color:#858585;border-left:4px solid #e5e5e5}.page .section blockquote:first-child{margin-top:0}.page .section blockquote:last-child{margin-bottom:0}.page .section dl{padding:0}.page .section dl dt{padding:0;margin-top:.8em;font-style:italic}.pdf-footer,.pdf-header{margin-top:20px;color:#aaa}.page .section dl dd{padding:0 .8em;margin-bottom:.8em}.page .section dd{margin-left:0}.page .book-chapter{display:none}.pdf-footer{padding-top:10px;border-top:1px solid #eee}.pdf-footer .footer-pages-count{float:right}.pdf-header{padding-bottom:10px;border-bottom:1px solid #eee}
-1
View File
@@ -1 +0,0 @@
.link-inherit{color:inherit}.link-inherit:hover,.link-inherit:focus{color:inherit}.hidden{display:none}.page.page-toc .glossary{margin-bottom:40px}.page.page-toc .glossary h2 a,.page.page-toc .glossary h2 a:hover{color:inherit;text-decoration:none}.page.page-toc .glossary .glossary-index{list-style:none;margin:0;padding:0}.page.page-toc .glossary .glossary-index li{display:inline;margin:0 8px;white-space:nowrap}.page .book-chapter{display:none}body .page{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;line-height:1.4;color:#000;overflow:hidden;line-height:1.6;word-wrap:break-word;display:block}body .page>*:first-child{margin-top:0!important}body .page>*:last-child{margin-bottom:0!important}body .page a{background:transparent}body .page a:active,body .page a:hover{outline:0}body .page strong{font-weight:bold}body .page h1{font-size:2em;margin:.67em 0}body .page img{border:0}body .page hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}body .page pre{overflow:auto}body .page code,body .page pre{font-family:monospace,monospace;font-size:1em;direction:ltr}body .page table{border-collapse:collapse;border-spacing:0}body .page td,body .page th{padding:0}body .page *{-moz-box-sizing:border-box;box-sizing:border-box}body .page a{color:#4183c4;text-decoration:none}body .page a:hover,body .page a:focus,body .page a:active{text-decoration:underline}body .page hr{height:0;margin:15px 0;overflow:hidden;background:transparent;border:0;border-bottom:1px solid #ddd}body .page hr:before,body .page hr:after{display:table;content:" "}body .page hr:after{clear:both}body .page h1,body .page h2,body .page h3,body .page h4,body .page h5,body .page h6{margin-top:15px;margin-bottom:15px;line-height:1.1}body .page h1{font-size:30px}body .page h2{font-size:21px}body .page h3{font-size:16px}body .page h4{font-size:14px}body .page h5{font-size:12px}body .page h6{font-size:11px}body .page blockquote{margin:0}body .page ul,body .page ol{padding:0;margin-top:0;margin-bottom:0}body .page ol ol{list-style-type:lower-roman}body .page dd{margin-left:0}body .page code,body .page pre{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;font-size:12px}body .page pre{margin-top:0;margin-bottom:0}body .page .markdown-body>*:first-child{margin-top:0!important}body .page .markdown-body>*:last-child{margin-bottom:0!important}body .page .anchor{position:absolute;top:0;bottom:0;left:0;display:block;padding-right:6px;padding-left:30px;margin-left:-30px}body .page .anchor:focus{outline:0}body .page h1,body .page h2,body .page h3,body .page h4,body .page h5,body .page h6{position:relative;margin-top:1em;margin-bottom:16px;font-weight:bold;line-height:1.4}body .page h1{padding-bottom:.3em;font-size:2.25em;line-height:1.2;border-bottom:1px solid #eee}body .page h2{padding-bottom:.3em;font-size:1.75em;line-height:1.225;border-bottom:1px solid #eee}body .page h3{font-size:1.5em;line-height:1.43}body .page h4{font-size:1.25em}body .page h5{font-size:1em}body .page h6{font-size:1em;color:#777}body .page p,body .page blockquote,body .page ul,body .page ol,body .page dl,body .page table,body .page pre{margin-top:0;margin-bottom:16px}body .page hr{height:4px;padding:0;margin:16px 0;background-color:#e7e7e7;border:0 none}body .page ul,body .page ol{padding-left:2em}body .page ol ol,body .page ol ul,body .page ul ul{margin-top:0;margin-bottom:0}body .page dl{padding:0}body .page dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:bold}body .page dl dd{padding:0 16px;margin-bottom:16px}body .page blockquote{padding:0 15px;color:#777;border-left:4px solid #ddd}body .page blockquote>:first-child{margin-top:0}body .page blockquote>:last-child{margin-bottom:0}body .page table{display:block;width:100%;overflow:auto}body .page table th{font-weight:bold}body .page table th,body .page table td{padding:6px 13px;border:1px solid #ddd}body .page table tr{background-color:#fff;border-top:1px solid #ccc}body .page table tr:nth-child(2n){background-color:#f8f8f8}body .page img{max-width:100%;-moz-box-sizing:border-box;box-sizing:border-box;page-break-inside:avoid}body .page code{padding:0;padding-top:.2em;padding-bottom:.2em;margin:0;font-size:85%;background-color:#f7f7f7;border-radius:3px}body .page code:before,body .page code:after{letter-spacing:-0.2em;content:"\00a0"}body .page pre>code{padding:0;margin:0;font-size:100%;white-space:pre;background:transparent;border:0}body .page .highlight pre,body .page pre{padding:16px;overflow:auto;font-size:85%;line-height:1.45;background-color:#f7f7f7;border:0;border-radius:3px}body .page pre{word-wrap:normal}body .page pre code{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}body .page pre code:before,body .page pre code:after{content:normal}body .page .highlight{background:#fff}
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 248 KiB

After

Width:  |  Height:  |  Size: 248 KiB

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

+9
View File
File diff suppressed because one or more lines are too long
+20
View File
@@ -0,0 +1,20 @@
{
"LANGS_CHOOSE": "Zvolte jazyk",
"GLOSSARY": "Slovníček",
"GLOSSARY_INDEX": "Rejstřík",
"GLOSSARY_OPEN": "Slovníček",
"GITBOOK_LINK": "Publikováno pomocí GitBook",
"SUMMARY": "Obsah",
"SUMMARY_INTRODUCTION": "Úvod",
"SUMMARY_TOGGLE": "Obsah",
"SEARCH_TOGGLE": "Hledání",
"SEARCH_PLACEHOLDER": "Vyhledat",
"FONTSETTINGS_TOGGLE": "Nastavení písma",
"SHARE_TOGGLE": "Sdílet",
"SHARE_ON": "Sdílet na {{platform}}",
"FONTSETTINGS_WHITE": "Bílá",
"FONTSETTINGS_SEPIA": "Sépie",
"FONTSETTINGS_NIGHT": "Noc",
"FONTSETTINGS_SANS": "Bezpatkové",
"FONTSETTINGS_SERIF": "Patkové"
}
+20
View File
@@ -0,0 +1,20 @@
{
"LANGS_CHOOSE": "Välj språk",
"GLOSSARY": "Gloslista",
"GLOSSARY_INDEX": "Index",
"GLOSSARY_OPEN": "Gloslista",
"GITBOOK_LINK": "Publicera med GitBook",
"SUMMARY": "Innehållsförteckning",
"SUMMARY_INTRODUCTION": "Inledning",
"SUMMARY_TOGGLE": "Innehållsförteckning",
"SEARCH_TOGGLE": "Sök",
"SEARCH_PLACEHOLDER": "Skriv sökord",
"FONTSETTINGS_TOGGLE": "Textinställningar",
"SHARE_TOGGLE": "Dela",
"SHARE_ON": "Dela på {{platform}}",
"FONTSETTINGS_WHITE": "Ljus",
"FONTSETTINGS_SEPIA": "Sepia",
"FONTSETTINGS_NIGHT": "Mörk",
"FONTSETTINGS_SANS": "Sans",
"FONTSETTINGS_SERIF": "Serif"
}
+20
View File
@@ -0,0 +1,20 @@
{
"LANGS_CHOOSE": "Виберіть мову",
"GLOSSARY": "Глосарій",
"GLOSSARY_INDEX": "Глосарій",
"GLOSSARY_OPEN": "Глосарій",
"GITBOOK_LINK": "Опубліковано за допомогою GitBook",
"SUMMARY": "Зміст",
"SUMMARY_INTRODUCTION": "Вступ",
"SUMMARY_TOGGLE": "Зміст",
"SEARCH_TOGGLE": "Пошук",
"SEARCH_PLACEHOLDER": "Введіть для пошуку",
"FONTSETTINGS_TOGGLE": "Шрифт",
"SHARE_TOGGLE": "Поділиться",
"SHARE_ON": "Поділитися в {{platform}}",
"FONTSETTINGS_WHITE": "Світлий",
"FONTSETTINGS_SEPIA": "Сепія",
"FONTSETTINGS_NIGHT": "Темний",
"FONTSETTINGS_SANS": "Sans",
"FONTSETTINGS_SERIF": "Serif"
}
-7
View File
@@ -1,7 +0,0 @@
define([
"jQuery"
], function($) {
var events = $({});
return events;
});
-103
View File
@@ -1,103 +0,0 @@
define([
"jQuery",
"utils/storage"
], function($, storage) {
var fontState;
var THEMES = {
"white": 0,
"sepia": 1,
"night": 2
};
var FAMILY = {
"serif": 0,
"sans": 1
};
var enlargeFontSize = function(e){
if (fontState.size < 4){
fontState.size++;
fontState.save();
}
};
var reduceFontSize = function(e){
if (fontState.size > 0){
fontState.size--;
fontState.save();
}
};
var changeFontFamily = function(){
var index = $(this).data("font");
fontState.family = index;
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 update = function() {
var $book = $(".book");
$(".font-settings .font-family-list li").removeClass("active");
$(".font-settings .font-family-list li:nth-child("+(fontState.family+1)+")").addClass("active");
$book[0].className = $book[0].className.replace(/\bfont-\S+/g, '');
$book.addClass("font-size-"+fontState.size);
$book.addClass("font-family-"+fontState.family);
if(fontState.theme !== 0) {
$book[0].className = $book[0].className.replace(/\bcolor-theme-\S+/g, '');
$book.addClass("color-theme-"+fontState.theme);
}
};
var init = function(config) {
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: config.size || 2,
family: FAMILY[config.family || "sans"],
theme: THEMES[config.theme || "white"]
});
fontState.save = function(){
storage.set("fontState",fontState);
update();
};
update();
//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 .button", changeFontFamily);
$(document).on('click', "#font-settings-wrapper .color-theme-list .button", changeColorTheme);
};
return {
init: init,
update: update
}
});
-38
View File
@@ -1,38 +0,0 @@
define([
"jQuery",
"Mousetrap",
"core/navigation",
"core/sidebar",
"core/search"
], function($, Mousetrap, navigation, sidebar, search){
// Bind keyboard shortcuts
var init = function() {
// Next
Mousetrap.bind(['right'], function(e) {
navigation.goNext();
return false;
});
// Prev
Mousetrap.bind(['left'], function(e) {
navigation.goPrev();
return false;
});
// Toggle Summary
Mousetrap.bind(['s'], function(e) {
sidebar.toggle();
return false;
});
// Toggle Search
Mousetrap.bind(['f'], function(e) {
search.toggle();
return false;
});
};
return {
init: init
};
});
-16
View File
@@ -1,16 +0,0 @@
define([
"jQuery"
], function($) {
var showLoading = function(p) {
$(".book").addClass("is-loading");
p.always(function() {
$(".book").removeClass("is-loading");
});
return p;
};
return {
show: showLoading
};
});
-174
View File
@@ -1,174 +0,0 @@
define([
"jQuery",
"utils/url",
"core/events",
"core/state",
"core/progress",
"core/loading",
"core/search"
], function($, URL, events, state, progress, loading, search) {
var prev, next;
var usePushState = (typeof history.pushState !== "undefined");
var handleNavigation = function(relativeUrl, push) {
var url = URL.join(window.location.pathname, relativeUrl);
console.log("navigate to ", url, "baseurl="+relativeUrl, "current="+window.location.pathname);
if (!usePushState) {
// Refresh the page to the new URL if pushState not supported
location.href = relativeUrl;
return
}
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
// !! Warning !!: we only update necessary portions to avoid strange behavior (page flickering etc ...)
////
// Update title
document.title = $pageHead.find("title").text();
// Reference to $("head");
var $head = $("head");
// Update next & prev <link> tags
// Remove old
$head.find("link[rel=prev]").remove();
$head.find("link[rel=next]").remove();
// Add new next * prev <link> tags
$head.append($pageHead.find("link[rel=prev]"));
$head.append($pageHead.find("link[rel=next]"));
// Merge body
var bodyClass = $(".book").attr("class");
var scrollPosition = $('.book-summary .summary').scrollTop();
$pageBody.toggleClass("with-summary", $(".book").hasClass("with-summary"))
$(".book").replaceWith($pageBody);
$(".book").attr("class", bodyClass);
$('.book-summary .summary').scrollTop(scrollPosition);
// Update state
state.update($("html"));
// recover search keyword
search.recover();
preparePage();
})
.fail(function (e) {
location.href = relativeUrl;
}));
};
var updateNavigationPosition = function() {
var bodyInnerWidth, pageWrapperWidth;
bodyInnerWidth = parseInt($('.body-inner').css('width'), 10);
pageWrapperWidth = parseInt($('.page-wrapper').css('width'), 10);
$('.navigation-next').css('margin-right', (bodyInnerWidth - pageWrapperWidth) + 'px');
};
var preparePage = function() {
var $bookBody = $(".book-body");
var $bookInner = $bookBody.find(".body-inner");
var $pageWrapper = $bookInner.find(".page-wrapper");
// Show progress
progress.show();
// Update navigation position
updateNavigationPosition();
// Focus on content
$pageWrapper.focus();
// Reset scroll
$bookInner.scrollTop(0);
$bookBody.scrollTop(0);
// Notify
events.trigger("page.change");
};
var isLeftClickEvent = function (e) {
return e.button === 0;
};
var isModifiedEvent = function (e) {
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
};
var handlePagination = function (e) {
if (isModifiedEvent(e) || !isLeftClickEvent(e)) {
return;
}
e.stopPropagation();
e.preventDefault();
var url = $(this).attr('href');
if (url) handleNavigation(url, true);
};
var goNext = function() {
var url = $(".navigation-next").attr("href");
if (url) handleNavigation(url, true);
};
var goPrev = function() {
var url = $(".navigation-prev").attr("href");
if (url) handleNavigation(url, true);
};
var init = function() {
// Prevent cache so that using the back button works
// See: http://stackoverflow.com/a/15805399/983070
$.ajaxSetup({
cache: false
});
// Recreate first page when the page loads.
history.replaceState({ path: window.location.href }, '');
// Back Button Hijacking :(
window.onpopstate = function (event) {
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
preparePage();
};
return {
init: init,
goNext: goNext,
goPrev: goPrev
};
});
-73
View File
@@ -1,73 +0,0 @@
define([
"lodash",
"jQuery",
"utils/storage",
"core/state"
], function(_, $, storage, state) {
// Get current level
var getCurrentLevel = function() {
return state.level;
};
// Return all levels
var getLevels = function () {
var levels = $(".book-summary li[data-level]");
return _.map(levels, function(level) {
return $(level).data("level").toString();
});
};
// Return a map chapter -> number (timestamp)
var getProgress = function () {
// Current level
var progress = storage.get("progress", {});
// Levels
var levels = getLevels();
_.each(levels, function(level) {
progress[level] = progress[level] || 0;
});
return progress;
};
// Change value of progress for a level
var markProgress = function (level, state) {
var progress = getProgress();
if (state == null) {
state = true;
}
progress[level] = state
? Date.now()
: 0;
storage.set("progress", progress);
};
// Show progress
var showProgress = function () {
var progress = getProgress();
var $summary = $(".book-summary");
_.each(progress, function (value, level) {
$summary.find("li[data-level='"+level+"']").toggleClass("done", value > 0);
});
// Mark current progress if we have not already
if (!progress[getCurrentLevel()]) {
markProgress(getCurrentLevel(), true);
}
};
return {
'current': getCurrentLevel,
'levels': getLevels,
'get': getProgress,
'mark': markProgress,
'show': showProgress
};
});
-114
View File
@@ -1,114 +0,0 @@
define([
"jQuery",
"lodash",
"lunr",
"utils/storage",
"core/state",
"core/sidebar"
], function($, _, lunr, storage, state, sidebar) {
var index = null;
// Use a specific idnex
var useIndex = function(data) {
index = lunr.Index.load(data);
};
// Load complete index
var loadIndex = function() {
$.getJSON(state.basePath+"/search_index.json")
.then(useIndex);
};
// Search for a term
var search = function(q) {
if (!index) return;
var results = _.chain(index.search(q))
.map(function(result) {
var parts = result.ref.split("#")
return {
path: parts[0],
hash: parts[1]
}
})
.value();
return results;
};
// Toggle search bar
var toggleSearch = function(_state) {
if (state != null && isSearchOpen() == _state) return;
var $searchInput = $(".book-search input");
state.$book.toggleClass("with-search", _state);
// If search bar is open: focus input
if (isSearchOpen()) {
sidebar.toggle(true);
$searchInput.focus();
} else {
$searchInput.blur();
$searchInput.val("");
sidebar.filter(null);
}
};
// Return true if search bar is open
var isSearchOpen = function() {
return state.$book.hasClass("with-search");
};
var init = function() {
loadIndex();
// Toggle search
$(document).on("click", ".book-header .toggle-search", function(e) {
e.preventDefault();
toggleSearch();
});
// Type in search bar
$(document).on("keyup", ".book-search input", function(e) {
var key = (e.keyCode ? e.keyCode : e.which);
var q = $(this).val();
if (key == 27) {
e.preventDefault();
toggleSearch(false);
return;
}
if (q.length == 0) {
sidebar.filter(null);
storage.remove("keyword");
} else {
var results = search(q);
sidebar.filter(
_.pluck(results, "path")
);
storage.set("keyword", q);
}
})
};
// filter sidebar menu with current search keyword
var recoverSearch = function() {
var keyword = storage.get("keyword", "");
if(keyword.length > 0) {
if(!isSearchOpen()){
toggleSearch();
}
sidebar.filter(_.pluck(search(keyword), "path"));
}
$(".book-search input").val(keyword);
};
return {
init: init,
search: search,
toggle: toggleSearch,
recover:recoverSearch
};
});
-56
View File
@@ -1,56 +0,0 @@
define([
"jQuery",
"lodash",
"utils/storage",
"utils/platform",
"core/state"
], function($, _, storage, platform, state) {
// Toggle sidebar with or withour animation
var toggleSidebar = function(_state, animation) {
if (state != null && isOpen() == _state) return;
if (animation == null) animation = true;
state.$book.toggleClass("without-animation", !animation);
state.$book.toggleClass("with-summary", _state);
storage.set("sidebar", isOpen());
};
// Return true if sidebar is open
var isOpen = function() {
return state.$book.hasClass("with-summary");
};
// Prepare sidebar: state and toggle button
var init = function() {
// Toggle summary
$(document).on("click", ".book-header .toggle-summary", function(e) {
e.preventDefault();
toggleSidebar();
});
// Init last state if not mobile
if (!platform.isMobile) {
toggleSidebar(storage.get("sidebar", true), false);
}
};
// Filter summary with a list of path
var filterSummary = function(paths) {
var $summary = $(".book-summary");
$summary.find("li").each(function() {
var path = $(this).data("path");
var st = paths == null || _.contains(paths, path);
$(this).toggle(st);
if (st) $(this).parents("li").show();
});
};
return {
init: init,
toggle: toggleSidebar,
filter: filterSummary
}
});
-18
View File
@@ -1,18 +0,0 @@
define([
"jQuery"
], function() {
var state = {};
state.update = function(dom) {
var $book = $(dom.find(".book"));
state.$book = $book;
state.level = $book.data("level");
state.basePath = $book.data("basepath");
state.revision = $book.data("revision");
};
state.update($);
return state;
});
+25
View File
@@ -0,0 +1,25 @@
var $ = require('jquery');
function toggleDropdown(e) {
var $dropdown = $(e.currentTarget).parent().find('.dropdown-menu');
$dropdown.toggleClass('open');
e.stopPropagation();
e.preventDefault();
}
function closeDropdown(e) {
$('.dropdown-menu').removeClass('open');
}
// Bind all dropdown
function init() {
$(document).on('click', '.toggle-dropdown', toggleDropdown);
$(document).on('click', '.dropdown-menu', function(e){ e.stopPropagation(); });
$(document).on('click', closeDropdown);
}
module.exports = {
init: init
};
+4
View File
@@ -0,0 +1,4 @@
var $ = require('jquery');
module.exports = $({});
-49
View File
@@ -1,49 +0,0 @@
define([
"jQuery",
"utils/storage",
"utils/sharing",
"utils/dropdown",
"core/events",
"core/font-settings",
"core/state",
"core/keyboard",
"core/navigation",
"core/progress",
"core/sidebar",
"core/search"
], function($, storage, sharing, dropdown, events, fontSettings, state, keyboard, navigation, progress, sidebar, search){
var start = function(config) {
var $book;
$book = state.$book;
// Init sidebar
sidebar.init();
// Load search
search.init();
// Init keyboard
keyboard.init();
// Bind sharing button
sharing.init();
// Bind dropdown
dropdown.init();
// Init navigation
navigation.init();
//Init font settings
fontSettings.init(config.fontSettings || {});
events.trigger("start", config);
}
return {
start: start,
events: events,
state: state
};
});
+84
View File
@@ -0,0 +1,84 @@
var $ = require('jquery');
var _ = require('lodash');
var storage = require('./storage');
var dropdown = require('./dropdown');
var events = require('./events');
var state = require('./state');
var keyboard = require('./keyboard');
var navigation = require('./navigation');
var sidebar = require('./sidebar');
var toolbar = require('./toolbar');
function start(config) {
// Init sidebar
sidebar.init();
// Init keyboard
keyboard.init();
// Bind dropdown
dropdown.init();
// Init navigation
navigation.init();
// Add action to toggle sidebar
toolbar.createButton({
index: 0,
icon: 'fa fa-align-justify',
onClick: function(e) {
e.preventDefault();
sidebar.toggle();
}
});
events.trigger('start', config);
navigation.notify();
}
// Export APIs for plugins
var gitbook = {
start: start,
events: events,
state: state,
// UI sections
toolbar: toolbar,
sidebar: sidebar,
// Read/Write the localstorage
storage: storage,
// Create keyboard shortcuts
keyboard: keyboard
};
// Modules mapping for plugins
var MODULES = {
'gitbook': gitbook,
'jquery': $,
'lodash': _
};
window.gitbook = gitbook;
window.$ = $;
window.jQuery = $;
window.require = function(mods, fn) {
mods = _.map(mods, function(mod) {
mod = mod.toLowerCase();
if (!MODULES[mod]) {
throw new Error('GitBook module '+mod+' doesn\'t exist');
}
return MODULES[mod];
});
fn.apply(null, mods);
};
module.exports = {};
+36
View File
@@ -0,0 +1,36 @@
var Mousetrap = require('mousetrap');
var navigation = require('./navigation');
var sidebar = require('./sidebar');
// Bind a keyboard shortcuts
function bindShortcut(keys, fn) {
Mousetrap.bind(keys, function(e) {
fn();
return false;
});
}
// Bind keyboard shortcuts
function init() {
// Next
bindShortcut(['right'], function(e) {
navigation.goNext();
});
// Prev
bindShortcut(['left'], function(e) {
navigation.goPrev();
});
// Toggle Summary
bindShortcut(['s'], function(e) {
sidebar.toggle();
});
}
module.exports = {
init: init,
bind: bindShortcut
};
+14
View File
@@ -0,0 +1,14 @@
var state = require('./state');
function showLoading(p) {
state.$book.addClass('is-loading');
p.always(function() {
state.$book.removeClass('is-loading');
});
return p;
}
module.exports = {
show: showLoading
};
+165
View File
@@ -0,0 +1,165 @@
var $ = require('jquery');
var url = require('url');
var events = require('./events');
var state = require('./state');
var loading = require('./loading');
var usePushState = (typeof history.pushState !== 'undefined');
function handleNavigation(relativeUrl, push) {
var uri = url.resolve(window.location.pathname, relativeUrl);
if (!usePushState) {
// Refresh the page to the new URL if pushState not supported
location.href = relativeUrl;
return;
}
return loading.show($.get(uri)
.done(function (html) {
// Push url to history
if (push) history.pushState({ path: uri }, null, uri);
// 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
// !! Warning !!: we only update necessary portions to avoid strange behavior (page flickering etc ...)
// Update title
document.title = $pageHead.find('title').text();
// Reference to $('head');
var $head = $('head');
// Update next & prev <link> tags
// Remove old
$head.find('link[rel=prev]').remove();
$head.find('link[rel=next]').remove();
// Add new next * prev <link> tags
$head.append($pageHead.find('link[rel=prev]'));
$head.append($pageHead.find('link[rel=next]'));
// Merge body
var bodyClass = $('.book').attr('class');
var scrollPosition = $('.book-summary .summary').scrollTop();
$pageBody.toggleClass('with-summary', $('.book').hasClass('with-summary'));
$('.book').replaceWith($pageBody);
$('.book').attr('class', bodyClass);
$('.book-summary .summary').scrollTop(scrollPosition);
// Update state
state.update($('html'));
preparePage();
})
.fail(function (e) {
location.href = relativeUrl;
}));
}
function updateNavigationPosition() {
var bodyInnerWidth, pageWrapperWidth;
bodyInnerWidth = parseInt($('.body-inner').css('width'), 10);
pageWrapperWidth = parseInt($('.page-wrapper').css('width'), 10);
$('.navigation-next').css('margin-right', (bodyInnerWidth - pageWrapperWidth) + 'px');
}
function notifyPageChange() {
events.trigger('page.change');
}
function preparePage(notify) {
var $bookBody = $('.book-body');
var $bookInner = $bookBody.find('.body-inner');
var $pageWrapper = $bookInner.find('.page-wrapper');
// Update navigation position
updateNavigationPosition();
// Focus on content
$pageWrapper.focus();
// Reset scroll
$bookInner.scrollTop(0);
$bookBody.scrollTop(0);
// Notify
if (notify !== false) notifyPageChange();
}
function isLeftClickEvent(e) {
return e.button === 0;
}
function isModifiedEvent(e) {
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
}
function handlePagination(e) {
if (isModifiedEvent(e) || !isLeftClickEvent(e)) {
return;
}
e.stopPropagation();
e.preventDefault();
var url = $(this).attr('href');
if (url) handleNavigation(url, true);
}
function goNext() {
var url = $('.navigation-next').attr('href');
if (url) handleNavigation(url, true);
}
function goPrev() {
var url = $('.navigation-prev').attr('href');
if (url) handleNavigation(url, true);
}
function init() {
// Prevent cache so that using the back button works
// See: http://stackoverflow.com/a/15805399/983070
$.ajaxSetup({
cache: false
});
// Recreate first page when the page loads.
history.replaceState({ path: window.location.href }, '');
// Back Button Hijacking :(
window.onpopstate = function (event) {
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
preparePage(false);
}
module.exports = {
init: init,
goNext: goNext,
goPrev: goPrev,
notify: notifyPageChange
};
+5
View File
@@ -0,0 +1,5 @@
module.exports = {
isMobile: function() {
return (document.width <= 600);
}
};
+56
View File
@@ -0,0 +1,56 @@
var $ = require('jquery');
var _ = require('lodash');
var storage = require('./storage');
var platform = require('./platform');
var state = require('./state');
// Toggle sidebar with or withour animation
function toggleSidebar(_state, animation) {
if (state != null && isOpen() == _state) return;
if (animation == null) animation = true;
state.$book.toggleClass('without-animation', !animation);
state.$book.toggleClass('with-summary', _state);
storage.set('sidebar', isOpen());
}
// Return true if sidebar is open
function isOpen() {
return state.$book.hasClass('with-summary');
}
// Prepare sidebar: state and toggle button
function init() {
// Init last state if not mobile
if (!platform.isMobile()) {
toggleSidebar(storage.get('sidebar', true), false);
}
// Close sidebar after clicking a link on mobile
$(document).on('click', '.book-summary li.chapter a', function(e) {
if (platform.isMobile()) toggleSidebar(false, false);
});
}
// Filter summary with a list of path
function filterSummary(paths) {
var $summary = $('.book-summary');
$summary.find('li').each(function() {
var path = $(this).data('path');
var st = paths == null || _.contains(paths, path);
$(this).toggle(st);
if (st) $(this).parents('li').show();
});
}
module.exports = {
init: init,
isOpen: isOpen,
toggle: toggleSidebar,
filter: filterSummary
};
+38
View File
@@ -0,0 +1,38 @@
var $ = require('jquery');
var url = require('url');
var path = require('path');
var state = {};
state.update = function(dom) {
var $book = $(dom.find('.book'));
state.$book = $book;
state.level = $book.data('level');
state.basePath = $book.data('basepath');
// If book is multilingual, language of this book
state.innerLanguage = $book.data('innerlanguage');
// Date of build
state.revision = $book.data('revision');
// Original path of the file
state.filepath = $book.data('filepath');
// Title of the chapter
state.chapterTitle = $book.data('chapter-title');
// Absolute url to the root of the book (inner book)
state.root = url.resolve(
location.protocol+'//'+location.host,
path.dirname(path.resolve(location.pathname.replace(/\/$/, '/index.html'), state.basePath))
).replace(/\/?$/, '/');
// Absolute root to the language (for multilingual book)
state.bookRoot = state.innerLanguage? url.resolve(state.root, '..') : state.root;
};
state.update($);
module.exports = state;
+37
View File
@@ -0,0 +1,37 @@
var baseKey = '';
/*
* Simple module for storing data in the browser's local storage
*/
module.exports = {
setBaseKey: function(key) {
baseKey = key;
},
// Write something in localstorage
set: function(key, value) {
key = baseKey+':'+key;
try {
localStorage[key] = JSON.stringify(value);
} catch(e) {}
},
// Read a value from localstorage
get: function(key, def) {
key = baseKey+':'+key;
if (localStorage[key] === undefined) return def;
try {
var v = JSON.parse(localStorage[key]);
return v == null ? def : v;;
} catch(err) {
return localStorage[key] || def;
}
},
// Remove a key from localstorage
remove: function(key) {
key = baseKey+':'+key;
localStorage.removeItem(key);
}
};
+181
View File
@@ -0,0 +1,181 @@
var $ = require('jquery');
var _ = require('lodash');
var events = require('./events');
// List of created buttons
var buttons = [];
// Insert a jquery element at a specific position
function insertAt(parent, selector, index, element) {
var lastIndex = parent.children(selector).size();
if (index < 0) {
index = Math.max(0, lastIndex + 1 + index);
}
parent.append(element);
if (index < lastIndex) {
parent.children(selector).eq(index).before(parent.children(selector).last());
}
}
// Default click handler
function defaultOnClick(e) {
e.preventDefault();
}
// Create a dropdown menu
function createDropdownMenu(dropdown) {
var $menu = $('<div>', {
'class': 'dropdown-menu',
'html': '<div class="dropdown-caret"><span class="caret-outer"></span><span class="caret-inner"></span></div>'
});
if (_.isString(dropdown)) {
$menu.append(dropdown);
} else {
var groups = _.map(dropdown, function(group) {
if (_.isArray(group)) return group;
else return [group];
});
// Create buttons groups
_.each(groups, function(group) {
var $group = $('<div>', {
'class': 'buttons'
});
var sizeClass = 'size-'+group.length;
// Append buttons
_.each(group, function(btn) {
btn = _.defaults(btn || {}, {
text: '',
className: '',
onClick: defaultOnClick
});
var $btn = $('<button>', {
'class': 'button '+sizeClass+' '+btn.className,
'text': btn.text
});
$btn.click(btn.onClick);
$group.append($btn);
});
$menu.append($group);
});
}
return $menu;
}
// Create a new button in the toolbar
function createButton(opts) {
opts = _.defaults(opts || {}, {
// Aria label for the button
label: '',
// Icon to show
icon: '',
// Inner text
text: '',
// Right or left position
position: 'left',
// Other class name to add to the button
className: '',
// Triggered when user click on the button
onClick: defaultOnClick,
// Button is a dropdown
dropdown: null,
// Position in the toolbar
index: null
});
buttons.push(opts);
updateButton(opts);
}
// Update a button
function updateButton(opts) {
var $result;
var $toolbar = $('.book-header');
var $title = $toolbar.find('h1');
// Build class name
var positionClass = 'pull-'+opts.position;
// Create button
var $btn = $('<a>', {
'class': 'btn',
'text': opts.text? ' ' + opts.text : '',
'aria-label': opts.label,
'href': '#'
});
// Bind click
$btn.click(opts.onClick);
// Prepend icon
if (opts.icon) {
$('<i>', {
'class': opts.icon
}).prependTo($btn);
}
// Prepare dropdown
if (opts.dropdown) {
var $container = $('<div>', {
'class': 'dropdown '+positionClass+' '+opts.className
});
// Add button to container
$btn.addClass('toggle-dropdown');
$container.append($btn);
// Create inner menu
var $menu = createDropdownMenu(opts.dropdown);
// Menu position
$menu.addClass('dropdown-'+(opts.position == 'right'? 'left' : 'right'));
$container.append($menu);
$result = $container;
} else {
$btn.addClass(positionClass);
$btn.addClass(opts.className);
$result = $btn;
}
$result.addClass('js-toolbar-action');
if (_.isNumber(opts.index) && opts.index >= 0) {
insertAt($toolbar, '.btn, .dropdown, h1', opts.index, $result);
} else {
$result.insertBefore($title);
}
}
// Update all buttons
function updateAllButtons() {
$('.js-toolbar-action').remove();
_.each(buttons, updateButton);
}
// When page changed, reset buttons
events.bind('page.change', function() {
updateAllButtons();
});
module.exports = {
createButton: createButton
};
-27
View File
@@ -1,27 +0,0 @@
define([
"jQuery"
], function($) {
var toggleDropdown = function(e) {
var $dropdown = $(e.currentTarget).parent().find(".dropdown-menu");
$dropdown.toggleClass("open");
e.stopPropagation();
e.preventDefault();
};
var closeDropdown = function(e) {
$(".dropdown-menu").removeClass("open");
};
// Bind all dropdown
var init = function() {
$(document).on('click', ".toggle-dropdown", toggleDropdown);
$(document).on('click', ".dropdown-menu", function(e){ e.stopPropagation(); });
$(document).on("click", closeDropdown);
};
return {
init: init
};
});

Some files were not shown because too many files have changed in this diff Show More