This commit is contained in:
Jinzhu 2016-03-28 10:40:33 +08:00
parent e0cc1afd91
commit 01f571cb77
41 changed files with 902 additions and 256 deletions

View File

@ -16,7 +16,7 @@
<link rel="stylesheet" href="gitbook/gitbook-plugin-highlight/website.css"> <link rel="stylesheet" href="gitbook/gitbook-plugin-fontsettings/website.css">
@ -24,7 +24,7 @@
<link rel="stylesheet" href="gitbook/gitbook-plugin-fontsettings/website.css"> <link rel="stylesheet" href="gitbook/gitbook-plugin-highlight/website.css">
@ -70,7 +70,7 @@
data-chapter-title="Advanced Usage" data-chapter-title="Advanced Usage"
data-filepath="advanced.md" data-filepath="advanced.md"
data-basepath="." data-basepath="."
data-revision="Wed Mar 23 2016 07:30:36 GMT+0800 (CST)" data-revision="Mon Mar 28 2016 10:39:40 GMT+0800 (CST)"
data-innerlanguage=""> data-innerlanguage="">
@ -811,15 +811,11 @@ db.SetLogger(log.New(os.Stdout, <span class="hljs-string">&quot;\r\n&quot;</span
<script src="gitbook/app.js"></script> <script src="gitbook/app.js"></script>
<script src="gitbook/gitbook-plugin-github/plugin.js"></script> <script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script>
<script src="gitbook/gitbook-plugin-edit-link/plugin.js"></script> <script src="gitbook/gitbook-plugin-sharing/buttons.js"></script>
<script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
@ -831,11 +827,15 @@ db.SetLogger(log.New(os.Stdout, <span class="hljs-string">&quot;\r\n&quot;</span
<script src="gitbook/gitbook-plugin-sharing/buttons.js"></script> <script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
<script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script> <script src="gitbook/gitbook-plugin-edit-link/plugin.js"></script>
<script src="gitbook/gitbook-plugin-github/plugin.js"></script>
<script> <script>

View File

@ -16,7 +16,7 @@
<link rel="stylesheet" href="gitbook/gitbook-plugin-highlight/website.css"> <link rel="stylesheet" href="gitbook/gitbook-plugin-fontsettings/website.css">
@ -24,7 +24,7 @@
<link rel="stylesheet" href="gitbook/gitbook-plugin-fontsettings/website.css"> <link rel="stylesheet" href="gitbook/gitbook-plugin-highlight/website.css">
@ -70,7 +70,7 @@
data-chapter-title="Associations" data-chapter-title="Associations"
data-filepath="associations.md" data-filepath="associations.md"
data-basepath="." data-basepath="."
data-revision="Wed Mar 23 2016 07:30:36 GMT+0800 (CST)" data-revision="Mon Mar 28 2016 10:39:40 GMT+0800 (CST)"
data-innerlanguage=""> data-innerlanguage="">
@ -876,15 +876,11 @@ db.Model(&amp;user).Association(<span class="hljs-string">&quot;Languages&quot;<
<script src="gitbook/app.js"></script> <script src="gitbook/app.js"></script>
<script src="gitbook/gitbook-plugin-github/plugin.js"></script> <script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script>
<script src="gitbook/gitbook-plugin-edit-link/plugin.js"></script> <script src="gitbook/gitbook-plugin-sharing/buttons.js"></script>
<script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
@ -896,11 +892,15 @@ db.Model(&amp;user).Association(<span class="hljs-string">&quot;Languages&quot;<
<script src="gitbook/gitbook-plugin-sharing/buttons.js"></script> <script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
<script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script> <script src="gitbook/gitbook-plugin-edit-link/plugin.js"></script>
<script src="gitbook/gitbook-plugin-github/plugin.js"></script>
<script> <script>

View File

@ -16,7 +16,7 @@
<link rel="stylesheet" href="gitbook/gitbook-plugin-highlight/website.css"> <link rel="stylesheet" href="gitbook/gitbook-plugin-fontsettings/website.css">
@ -24,7 +24,7 @@
<link rel="stylesheet" href="gitbook/gitbook-plugin-fontsettings/website.css"> <link rel="stylesheet" href="gitbook/gitbook-plugin-highlight/website.css">
@ -70,7 +70,7 @@
data-chapter-title="Callbacks" data-chapter-title="Callbacks"
data-filepath="callbacks.md" data-filepath="callbacks.md"
data-basepath="." data-basepath="."
data-revision="Wed Mar 23 2016 07:30:36 GMT+0800 (CST)" data-revision="Mon Mar 28 2016 10:39:40 GMT+0800 (CST)"
data-innerlanguage=""> data-innerlanguage="">
@ -733,15 +733,11 @@ If you want to use those changes in your callbacks, you need to run your SQL in
<script src="gitbook/app.js"></script> <script src="gitbook/app.js"></script>
<script src="gitbook/gitbook-plugin-github/plugin.js"></script> <script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script>
<script src="gitbook/gitbook-plugin-edit-link/plugin.js"></script> <script src="gitbook/gitbook-plugin-sharing/buttons.js"></script>
<script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
@ -753,11 +749,15 @@ If you want to use those changes in your callbacks, you need to run your SQL in
<script src="gitbook/gitbook-plugin-sharing/buttons.js"></script> <script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
<script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script> <script src="gitbook/gitbook-plugin-edit-link/plugin.js"></script>
<script src="gitbook/gitbook-plugin-github/plugin.js"></script>
<script> <script>

View File

@ -16,7 +16,7 @@
<link rel="stylesheet" href="gitbook/gitbook-plugin-highlight/website.css"> <link rel="stylesheet" href="gitbook/gitbook-plugin-fontsettings/website.css">
@ -24,7 +24,7 @@
<link rel="stylesheet" href="gitbook/gitbook-plugin-fontsettings/website.css"> <link rel="stylesheet" href="gitbook/gitbook-plugin-highlight/website.css">
@ -68,7 +68,7 @@
data-chapter-title="Change Log" data-chapter-title="Change Log"
data-filepath="changelog.md" data-filepath="changelog.md"
data-basepath="." data-basepath="."
data-revision="Wed Mar 23 2016 07:30:36 GMT+0800 (CST)" data-revision="Mon Mar 28 2016 10:39:40 GMT+0800 (CST)"
data-innerlanguage=""> data-innerlanguage="">
@ -695,15 +695,11 @@
<script src="gitbook/app.js"></script> <script src="gitbook/app.js"></script>
<script src="gitbook/gitbook-plugin-github/plugin.js"></script> <script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script>
<script src="gitbook/gitbook-plugin-edit-link/plugin.js"></script> <script src="gitbook/gitbook-plugin-sharing/buttons.js"></script>
<script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
@ -715,11 +711,15 @@
<script src="gitbook/gitbook-plugin-sharing/buttons.js"></script> <script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
<script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script> <script src="gitbook/gitbook-plugin-edit-link/plugin.js"></script>
<script src="gitbook/gitbook-plugin-github/plugin.js"></script>
<script> <script>

View File

@ -16,7 +16,7 @@
<link rel="stylesheet" href="gitbook/gitbook-plugin-highlight/website.css"> <link rel="stylesheet" href="gitbook/gitbook-plugin-fontsettings/website.css">
@ -24,7 +24,7 @@
<link rel="stylesheet" href="gitbook/gitbook-plugin-fontsettings/website.css"> <link rel="stylesheet" href="gitbook/gitbook-plugin-highlight/website.css">
@ -70,7 +70,7 @@
data-chapter-title="CRUD: Reading and Writing Data" data-chapter-title="CRUD: Reading and Writing Data"
data-filepath="curd.md" data-filepath="curd.md"
data-basepath="." data-basepath="."
data-revision="Wed Mar 23 2016 07:30:36 GMT+0800 (CST)" data-revision="Mon Mar 28 2016 10:39:40 GMT+0800 (CST)"
data-innerlanguage=""> data-innerlanguage="">
@ -1303,15 +1303,11 @@ db.Unscoped().Delete(&amp;order)
<script src="gitbook/app.js"></script> <script src="gitbook/app.js"></script>
<script src="gitbook/gitbook-plugin-github/plugin.js"></script> <script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script>
<script src="gitbook/gitbook-plugin-edit-link/plugin.js"></script> <script src="gitbook/gitbook-plugin-sharing/buttons.js"></script>
<script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
@ -1323,11 +1319,15 @@ db.Unscoped().Delete(&amp;order)
<script src="gitbook/gitbook-plugin-sharing/buttons.js"></script> <script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
<script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script> <script src="gitbook/gitbook-plugin-edit-link/plugin.js"></script>
<script src="gitbook/gitbook-plugin-github/plugin.js"></script>
<script> <script>

View File

@ -16,7 +16,7 @@
<link rel="stylesheet" href="gitbook/gitbook-plugin-highlight/website.css"> <link rel="stylesheet" href="gitbook/gitbook-plugin-fontsettings/website.css">
@ -24,7 +24,7 @@
<link rel="stylesheet" href="gitbook/gitbook-plugin-fontsettings/website.css"> <link rel="stylesheet" href="gitbook/gitbook-plugin-highlight/website.css">
@ -70,7 +70,7 @@
data-chapter-title="Database" data-chapter-title="Database"
data-filepath="database.md" data-filepath="database.md"
data-basepath="." data-basepath="."
data-revision="Wed Mar 23 2016 07:30:36 GMT+0800 (CST)" data-revision="Mon Mar 28 2016 10:39:40 GMT+0800 (CST)"
data-innerlanguage=""> data-innerlanguage="">
@ -788,15 +788,11 @@ db.Model(&amp;User{}).RemoveIndex(<span class="hljs-string">&quot;idx_user_name&
<script src="gitbook/app.js"></script> <script src="gitbook/app.js"></script>
<script src="gitbook/gitbook-plugin-github/plugin.js"></script> <script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script>
<script src="gitbook/gitbook-plugin-edit-link/plugin.js"></script> <script src="gitbook/gitbook-plugin-sharing/buttons.js"></script>
<script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
@ -808,11 +804,15 @@ db.Model(&amp;User{}).RemoveIndex(<span class="hljs-string">&quot;idx_user_name&
<script src="gitbook/gitbook-plugin-sharing/buttons.js"></script> <script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
<script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script> <script src="gitbook/gitbook-plugin-edit-link/plugin.js"></script>
<script src="gitbook/gitbook-plugin-github/plugin.js"></script>
<script> <script>

View File

@ -16,7 +16,7 @@
<link rel="stylesheet" href="gitbook/gitbook-plugin-highlight/website.css"> <link rel="stylesheet" href="gitbook/gitbook-plugin-fontsettings/website.css">
@ -24,7 +24,7 @@
<link rel="stylesheet" href="gitbook/gitbook-plugin-fontsettings/website.css"> <link rel="stylesheet" href="gitbook/gitbook-plugin-highlight/website.css">
@ -70,7 +70,7 @@
data-chapter-title="Development" data-chapter-title="Development"
data-filepath="development.md" data-filepath="development.md"
data-basepath="." data-basepath="."
data-revision="Wed Mar 23 2016 07:30:36 GMT+0800 (CST)" data-revision="Mon Mar 28 2016 10:39:40 GMT+0800 (CST)"
data-innerlanguage=""> data-innerlanguage="">
@ -736,15 +736,11 @@ db.Callback().RowQuery().Register(<span class="hljs-string">&quot;publish:update
<script src="gitbook/app.js"></script> <script src="gitbook/app.js"></script>
<script src="gitbook/gitbook-plugin-github/plugin.js"></script> <script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script>
<script src="gitbook/gitbook-plugin-edit-link/plugin.js"></script> <script src="gitbook/gitbook-plugin-sharing/buttons.js"></script>
<script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
@ -756,11 +752,15 @@ db.Callback().RowQuery().Register(<span class="hljs-string">&quot;publish:update
<script src="gitbook/gitbook-plugin-sharing/buttons.js"></script> <script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
<script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script> <script src="gitbook/gitbook-plugin-edit-link/plugin.js"></script>
<script src="gitbook/gitbook-plugin-github/plugin.js"></script>
<script> <script>

View File

@ -87,7 +87,7 @@ type User struct {
type User struct {} // default table name is `users` type User struct {} // default table name is `users`
// set User's table name to be `profiles // set User's table name to be `profiles
type (User) TableName() string { func (User) TableName() string {
return "profiles" return "profiles"
} }

View File

@ -12,7 +12,7 @@ You can publish and host books easily online using [gitbook.com](https://www.git
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. 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 [toolchain.gitbook.com](http://toolchain.gitbook.com/).
![Image](https://raw.github.com/GitbookIO/gitbook/master/preview.png) ![Image](https://raw.github.com/GitbookIO/gitbook/master/preview.png)
@ -32,14 +32,14 @@ We're always happy to help out with your books or any other questions you might
## Features ## Features
* [Output as a website or ebook (pdf, epub, mobi)](http://help.gitbook.com/format/output.html) * Write using [Markdown](http://toolchain.gitbook.com/syntax/markdown.html) or [AsciiDoc](http://toolchain.gitbook.com/syntax/asciidoc.html)
* [Multi-Languages](http://help.gitbook.com/format/languages.html) * Output as a website or [ebook (pdf, epub, mobi)](http://toolchain.gitbook.com/ebook.html)
* [Glossary](http://help.gitbook.com/format/glossary.html) * [Multi-Languages](http://toolchain.gitbook.com/languages.html)
* [Cover](http://help.gitbook.com/format/cover.html) * [Lexicon / Glossary](http://toolchain.gitbook.com/lexicon.html)
* [AsciiDoc Support](http://help.gitbook.com/format/asciidoc.html) * [Cover](http://toolchain.gitbook.com/ebook.html)
* [Variables and Templating](http://help.gitbook.com/format/templating.html) * [Variables and Templating](http://toolchain.gitbook.com/templating/)
* [Content References](http://help.gitbook.com/format/conrefs.html) * [Content References](http://toolchain.gitbook.com/templating/conrefs.html)
* [Plugins](http://help.gitbook.com/format/plugins.html) * [Plugins](http://toolchain.gitbook.com/plugins/)
* [Beautiful default theme](https://github.com/GitbookIO/theme-default) * [Beautiful default theme](https://github.com/GitbookIO/theme-default)
## Publish your book ## Publish your book

View File

@ -3,7 +3,7 @@ var pkg = require('./package.json');
module.exports = { module.exports = {
// Documentation for GitBook is stored under "docs" // Documentation for GitBook is stored under "docs"
root: './docs', root: './docs',
title: 'GitBook Documentation', title: 'GitBook Toolchain Documentation',
// Enforce use of GitBook v3 // Enforce use of GitBook v3
gitbook: pkg.version, gitbook: pkg.version,

View File

@ -1,4 +1,4 @@
# GitBook Format Documentation # GitBook Toolchain Documentation
This document aims to be a comprehensive guide to GitBook. It contains the full documentation for version **{{ book.version }}**. Help for GitBook.com specific questions can be found at [help.gitbook.com](https://help.gitbook.com). This document aims to be a comprehensive guide to GitBook. It contains the full documentation for version **{{ book.version }}**. Help for GitBook.com specific questions can be found at [help.gitbook.com](https://help.gitbook.com).

29
gitbook/docs/ebook.md Normal file
View File

@ -0,0 +1,29 @@
# Generating eBooks and PDFs
GitBook can generates a website, but can also output content as ebook (ePub, Mobi, PDF).
### Installing ebook-convert
`ebook-convert` is required to generate ebooks (epub, mobi, pdf).
##### OS X
Download the [Calibre application](https://calibre-ebook.com/download). After moving the `calibre.app` to your Applications folder create a symbolic link to the ebook-convert tool:
```
$ sudo ln -s ~/Applications/calibre.app/Contents/MacOS/ebook-convert /usr/bin
```
You can replace `/usr/bin` with any directory that is in your $PATH.
### Cover
Covers are used for all the ebook formats. It's an important part of an ebook brandline.
A good cover should respect the following guidelines:
* Size of 1800x2360 (pixels)
* No border
* Clearly visible book title
* Any important text should be visible in the small version

View File

@ -1,5 +1,9 @@
# GitBook FAQ # GitBook FAQ
This page gathers common questions and answers concerning the GitBook format and toolchain.
Questions about GitBook.com and the Editor are gather into the [help.gitbook.com's FAQ](http://help.gitbook.com/faq.html).
#### How can I host/publish my book? #### How can I host/publish my book?
Books can easily be published and hosted on [GitBook.com](https://www.gitbook.com). But GitBook output can be hosted on any static file hosting solution. Books can easily be published and hosted on [GitBook.com](https://www.gitbook.com). But GitBook output can be hosted on any static file hosting solution.
@ -10,6 +14,20 @@ Any text editor should work! But we advise using the [GitBook Editor](https://ww
--- ---
#### Does GitBook supports RTL/bi-directional text ?
The GitBook format supports right to left, and bi-directional writing. To enable it, you either need to specify a language (ex: `ar`), or force GitBook to use RTL in your `book.json`:
``` json
{
"language": "ar",
"direction": "rtl"
}
```
With version 3.0 of GitBook, it's automatically detected according to the content.
_Note that, while the output book will indeed respect RTL, the Editor doesn't support RTL writing yet_.
#### Should I use an `.html` or `.md` extensions in my links? #### Should I use an `.html` or `.md` extensions in my links?
You should always use paths and the `.md` extensions when linking to your files, GitBook will automatically replace these paths by the appropriate link when the pointing file is referenced in the Table of Contents. You should always use paths and the `.md` extensions when linking to your files, GitBook will automatically replace these paths by the appropriate link when the pointing file is referenced in the Table of Contents.

View File

@ -23,7 +23,27 @@ Adding a nested list to a parent chapter will create subchapters.
Each chapter has a dedicated page (`part#/README.md`) and is split into subchapters. Each chapter has a dedicated page (`part#/README.md`) and is split into subchapters.
##### Example with subchapters split into parts ##### Anchors
Chaptersi n the Table of Contents can be pointing to specific part of a file using anchor.
```markdown
# Summary
### Part I
* [Part I](part1/README.md)
* [Writing is nice](part1/README.md#writing)
* [GitBook is nice](part1/README.md#gitbook)
* [Part II](part2/README.md)
* [We love feedback](part2/README.md#feedback)
* [Better tools for authors](part2/README.md#tools)
```
##### Parts
The Table of Contents can be divided into parts separated by headings or horizontal lines:
```markdown ```markdown
# Summary # Summary
@ -37,9 +57,13 @@ Each chapter has a dedicated page (`part#/README.md`) and is split into subchapt
* [We love feedback](part2/feedback_please.md) * [We love feedback](part2/feedback_please.md)
* [Better tools for authors](part2/better_tools.md) * [Better tools for authors](part2/better_tools.md)
----
* [Last part without title](part3/title.md)
``` ```
Here, parts are just groups of chapters and do not have dedicated pages, but will show in the navigation for example. Parts are just groups of chapters and do not have dedicated pages, but according to the theme, it will show in the navigation.
### Pages ### Pages

View File

@ -12,6 +12,14 @@ var value = book.config.get('title', 'Default Value');
// Resolve a filename to an absolute path // Resolve a filename to an absolute path
var filepath = book.resolve('README.md'); var filepath = book.resolve('README.md');
// Render an inline markup string
book.renderInline('markdown', 'This is **Markdown**')
.then(function(str) { ... })
// Render a markup string (block mode)
book.renderBlock('markdown', '* This is **Markdown**')
.then(function(str) { ... })
``` ```
#### Output instance #### Output instance

View File

@ -0,0 +1,65 @@
# AsciiDoc
Since version `2.0.0`, GitBook can also accept AsciiDoc as an input format.
Please refer to the [AsciiDoc Syntax Quick Reference](http://asciidoctor.org/docs/asciidoc-syntax-quick-reference/) for more informations about the format.
Just like for markdown, GitBook is using some special files to extract structures: `README.adoc`, `SUMMARY.adoc`, `LANGS.adoc` and `GLOSSARY.adoc`.
### README.adoc
This is the main entry of your book: the introduction. This file is **required**.
### SUMMARY.adoc
This file defines the list of chapters and subchapters. Just like in Markdown, the `SUMMARY.adoc`'s format is simply a list of links, the name of the link is used as the chapter's name, and the target is a path to that chapter's file.
Subchapters are defined simply by adding a nested list to a parent chapter.
```asciidoc
= Summary
. link:chapter-1/README.adoc[Chapter 1]
.. link:chapter-1/ARTICLE1.adoc[Article 1]
.. link:chapter-1/ARTICLE2.adoc[Article 2]
... link:chapter-1/ARTICLE-1-2-1.adoc[Article 1.2.1]
. link:chapter-2/README.adoc[Chapter 2]
. link:chapter-3/README.adoc[Chapter 3]
. link:chapter-4/README.adoc[Chapter 4]
.. Unfinished article
. Unfinished Chapter
```
### LANGS.adoc
For [Multi-Languages](./languages.md) books, this file is used to define the different supported languages and translations.
This file is following the same syntax as the `SUMMARY.adoc`:
```asciidoc
= Languages
. link:en/[English]
. link:fr/[French]
```
### GLOSSARY.adoc
This file is used to define terms. [See the glossary section](./lexicon.md).
```asciidoc
= Glossary
== Magic
Sufficiently advanced technology, beyond the understanding of the
observer producing a sense of wonder.
== PHP
A popular web programming language, used by many large websites such
as Facebook. Rasmus Lerdorf originally created PHP in 1994 to power
his personal homepage (PHP originally stood for "Personal Home Page"
but now stands for "PHP: Hypertext Preprocessor"). ```

View File

@ -0,0 +1,223 @@
# Markdown
Most of the examples from this documentation are in Markdown. Markdown is default parser for GitBook, but one can also opt for the [AsciiDoc syntax](asciidoc.md).
Heres an overview of Markdown syntax that you can use with GitBook (same as GitHub with some additions).
### Headings
To create a heading, add one to six `#` symbols before your heading text. The number of # you use will determine the size of the heading.
```markdown
# This is an <h1> tag
## This is an <h2> tag
###### This is an <h6> tag
```
GitBook supports a nice way for explicitly setting the header ID. If you follow the header text with an opening curly bracket (separated from the text with a least one space), a hash, the ID and a closing curly bracket, the ID is set on the header. If you use the trailing hash feature of atx style headers, the header ID has to go after the trailing hashes. For example:
```markdown
Hello {#id}
-----
# Hello {#id}
# Hello # {#id}
```
### Paragraphs and Line Breaks {#paragraphs}
A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines. (A blank line is any line that looks like a blank line — a line containing nothing but spaces or tabs is considered blank.) Normal paragraphs should not be indented with spaces or tabs.
```
Here's a line for us to start with.
This line is separated from the one above by two newlines, so it will be a *separate paragraph*.
```
### Emphasis {#emphasis}
```markdown
*This text will be italic*
_This will also be italic_
**This text will be bold**
__This will also be bold__
~~This text will be crossed out.~~
_You **can** combine them_
```
### Lists {#lists}
Markdown supports ordered (numbered) and unordered (bulleted) lists.
##### Unordered
Unordered lists use asterisks, pluses, and hyphens — interchangably — as list markers:
```markdown
* Item 1
* Item 2
* Item 2a
* Item 2b
```
##### Ordered
Ordered lists use numbers followed by periods:
```markdown
1. Item 1
2. Item 2
3. Item 3
* Item 3a
* Item 3b
```
### Links {#links}
Markdown supports two style of links: inline and reference.
A simple link can be created by surrounding the text with square brackets and the link URL with parentheses:
```markdown
This is [an example](http://example.com/ "Title") inline link with a title.
[This link](http://example.net/) has no title attribute.
```
Links can point to relative paths, anchors or absolute urls.
### References
There is another way to create links which does not interrupt the text flow. The URL and title are defined using a reference name and this reference name is then used in square brackets instead of the link URL:
```markdown
This is [an example][id] reference-style link.
```
Then, anywhere in the document, you define your link label like this, on a line by itself:
```markdown
[id]: http://example.com/ "Optional Title Here"
```
### Images {#images}
Images can be created in a similar way than links: just use an exclamation mark before the square brackets. The link text will become the alternative text of the image and the link URL specifies the image source:
```markdown
An image: ![gras](img/image.jpg)
```
### Blockquotes {#blockquotes}
A blockquote is started using the `>` marker followed by an optional space; all following lines that are also started with the blockquote marker belong to the blockquote. You can use any block-level elements inside a blockquote:
```markdown
As Kanye West said:
> We're living the future so
> the present is our past.
```
### Tables {#tables}
You can create tables by assembling a list of words and dividing them with hyphens `-` (for the first row), and then separating each column with a pipe `|`:
```markdown
| First Header | Second Header |
| ------------- | ------------- |
| Content Cell | Content Cell |
| Content Cell | Content Cell |
```
The pipes on either end of the table are optional. Cells can vary in width and do not need to be perfectly aligned within columns. There must be at least three hyphens in each column of the header row.
### Code {#code}
Markdown supports two different code block styles. One uses lines indented with either four spaces or one tab whereas the other uses lines with tilde characters as delimiters therefore the content does not need to be indented:
```markdown
This is a sample code block.
Continued here.
```
##### Fenced code blocks
You can create fenced code blocks by placing triple backticks ` ``` ` before and after the code block. We recommend placing a blank line before and after code blocks to make the raw formatting easier to read.
```
function test() {
console.log("notice the blank line before this function?");
}
```
##### Syntax highlighting
You can add an optional language identifier to enable syntax highlighting in your fenced code block.
For example, to syntax highlight Ruby code:
```ruby
require 'redcarpet'
markdown = Redcarpet.new("Hello World!")
puts markdown.to_html
```
##### Inline code
Text phrases can be marked up as code by surrounding them with backticks:
Use `gitbook` to convert the `text` in markdown
syntax to HTML.
### Footnotes
GitBook supports a simple syntax for such footnotes. Footnotes are relative to each pages.
```markdown
Text prior to footnote reference.[^2]
[^2]: Comment to include in footnote.
```
### HTML
GitBook supports use of raw HTML in your text, Markdown syntax in HTML is not processed:
```
<div>
Markdown here will not be **parsed**
</div>
```
### Horizontal Rule
Horizontal Rules can be inserted using three or more asterisks, dashes or underscores, optionally separated by spaces or tabs, on an otherwise blank line:
```markdown
Three or more...
---
Hyphens
***
Asterisks
```
### Ignoring Markdown formatting
You can tell GitBook to ignore (or escape) Markdown formatting by using `\` before the Markdown character.
```
Let's rename \*our-new-project\* to \*our-old-project\*.
```

View File

@ -87,3 +87,13 @@ Current version is {{ softwareVersion }}.
##### include and block ##### include and block
Inclusion and inheritance is detailled in the [Content References](conrefs.md) section. Inclusion and inheritance is detailled in the [Content References](conrefs.md) section.
### Escaping
If you want GitBook to ignore any of the special templating tags, you can use raw and anything inside of it will be output as plain text.
``` twig
{% raw %}
this will {{ not be processed }}
{% endraw %}
```

View File

@ -0,0 +1,18 @@
# Builtin Templating Helpers
GitBook provides a serie of builtin filters and blocks to help you write templates.
### Filters
`value|default(default, [boolean])`If value is strictly undefined, return default, otherwise value. If boolean is true, any JavaScript falsy value will return default (false, "", etc)
`arr|sort(reverse, caseSens, attr)`
Sort arr with JavaScript's arr.sort function. If reverse is true, result will be reversed. Sort is case-insensitive by default, but setting caseSens to true makes it case-sensitive. If attr is passed, will compare attr from each item.
### Blocks
`{% markdown %}Markdown string{% endmarkdown %}`
Render inline markdown
`{% asciidoc %}AsciiDoc string{% endasciidoc %}`
Render inline asciidoc

View File

@ -12,6 +12,7 @@ The following is a reference of the available data during book's parsing and the
| `file` | File associated with the current page specific information | | `file` | File associated with the current page specific information |
| `summary` | Information about the table of contents | | `summary` | Information about the table of contents |
| `languages` | List of languages for multi-lingual books | | `languages` | List of languages for multi-lingual books |
| `output` | Information about the output generator |
| `config` | Dump of the `book.json` | | `config` | Dump of the `book.json` |
### Book Variables ### Book Variables
@ -62,3 +63,11 @@ The whole table of contents (`SUMMARY.md`) can be accessed:
| `languages.list` | List of languages for this book | | `languages.list` | List of languages for this book |
Languages are defined by `{ id: 'en', title: 'English' }`. Languages are defined by `{ id: 'en', title: 'English' }`.
### Output Variables
| Variable | Description |
| -------- | ----------- |
| `output.name` | Name of the output generator, possible values are `website`, `json`, `ebook` |
| `output.format` | When `output.name == "ebook"`, `format` defines the ebook format that will be generated, possible values are `pdf`, `epub` or `mobi` |

View File

@ -2,13 +2,11 @@
Since version 3.0.0, GitBook can be easily themed. Books are using by default the [theme-default](https://github.com/GitbookIO/theme-default). Since version 3.0.0, GitBook can be easily themed. Books are using by default the [theme-default](https://github.com/GitbookIO/theme-default).
The theme to use is specified in the [book's configuration](../config.md) using key `theme`.
> **Caution**: Custom theming can block some plugins from working correctly. > **Caution**: Custom theming can block some plugins from working correctly.
### Structure of a theme ### Structure of a theme
A theme is a folder containing templates and assets. All the templates are optionnal, since theme are always extending the default theme. A theme is a plugin containing templates and assets. All the templates are optionnal, since theme are always extending the default theme.
| Folder | Description | | Folder | Description |
| -------- | ----------- | | -------- | ----------- |

View File

@ -320,7 +320,7 @@ Book.prototype.findParsableFile = function(filename) {
if (!realFilepath) return null; if (!realFilepath) return null;
return { return {
parser: parsers.get(ext), parser: parsers.getByExt(ext),
path: realFilepath path: realFilepath
}; };
}); });
@ -359,6 +359,23 @@ Book.prototype.isInLanguageBook = function(filename) {
}); });
}; };
// ----- Parser Methods
// Render a markup string in inline mode
Book.prototype.renderInline = function(type, src) {
var parser = parsers.get(type);
return parser.inline(src)
.get('content');
};
// Render a markup string in block mode
Book.prototype.renderBlock = function(type, src) {
var parser = parsers.get(type);
return parser.page(src)
.get('content');
};
// ----- DEPRECATED METHODS // ----- DEPRECATED METHODS
Book.prototype.contentLink = error.deprecateMethod(function(s) { Book.prototype.contentLink = error.deprecateMethod(function(s) {

View File

@ -5,8 +5,10 @@ var path = require('path');
var Promise = require('../utils/promise'); var Promise = require('../utils/promise');
var pathUtil = require('../utils/path'); var pathUtil = require('../utils/path');
var location = require('../utils/location'); var location = require('../utils/location');
var error = require('../utils/error');
var PluginsManager = require('../plugins'); var PluginsManager = require('../plugins');
var TemplateEngine = require('../template'); var TemplateEngine = require('../template');
var gitbook = require('../gitbook');
/* /*
Output is like a stream interface for a parsed book Output is like a stream interface for a parsed book
@ -37,6 +39,9 @@ function Output(book, opts, parent) {
this.ignore = Ignore(); this.ignore = Ignore();
} }
// Default name for generator
Output.prototype.name = 'base';
// Default extension for output // Default extension for output
Output.prototype.defaultExtension = '.html'; Output.prototype.defaultExtension = '.html';
@ -221,16 +226,31 @@ Output.prototype.onLanguageBook = function(book) {
// ---- Utilities ---- // ---- Utilities ----
// Return conetxt for the output itself
Output.prototype.getSelfContext = function() {
return {
name: this.name
};
};
// Return a default context for templates // Return a default context for templates
Output.prototype.getContext = function() { Output.prototype.getContext = function() {
return _.extend( var ctx = _.extend(
{}, {
output: this.getSelfContext()
},
this.book.getContext(), this.book.getContext(),
this.book.langs.getContext(), this.book.langs.getContext(),
this.book.summary.getContext(), this.book.summary.getContext(),
this.book.glossary.getContext(), this.book.glossary.getContext(),
this.book.config.getContext() this.book.config.getContext(),
gitbook.getContext()
); );
// Deprecated fields
error.deprecateField(ctx.gitbook, 'generator', this.name, '"gitbook.generator" property is deprecated, use "output.name" instead');
return ctx;
}; };
// Resolve a file path in the context of a specific page // Resolve a file path in the context of a specific page

View File

@ -21,6 +21,15 @@ var EbookOutput = assetsInliner(_EbookOutput);
EbookOutput.prototype.name = 'ebook'; EbookOutput.prototype.name = 'ebook';
// Return context for templating
// Incldue type of ebbook generated
EbookOutput.prototype.getSelfContext = function() {
var ctx = EbookOutput.super_.prototype.getSelfContext.apply(this);
ctx.format = this.opts.format;
return ctx;
};
// Finish generation, create ebook using ebook-convert // Finish generation, create ebook using ebook-convert
EbookOutput.prototype.finish = function() { EbookOutput.prototype.finish = function() {
var that = this; var that = this;

View File

@ -16,7 +16,7 @@ JSONOutput.prototype.onPage = function(page) {
// Write as json // Write as json
.then(function() { .then(function() {
var json = page.getContext(); var json = page.getOutputContext(that);
// Delete some private properties // Delete some private properties
delete json.config; delete json.config;

View File

@ -8,13 +8,10 @@ var Promise = require('../utils/promise');
var location = require('../utils/location'); var location = require('../utils/location');
var fs = require('../utils/fs'); var fs = require('../utils/fs');
var defaultFilters = require('../template/filters'); var defaultFilters = require('../template/filters');
var FSLoader = require('../template/fs-loader');
var conrefsLoader = require('./conrefs'); var conrefsLoader = require('./conrefs');
var Output = require('./base'); var Output = require('./base');
// Tranform a theme ID into a plugin
function themeID(plugin) {
return 'theme-' + plugin;
}
// Directory for a theme with the templates // Directory for a theme with the templates
function templatesPath(dir) { function templatesPath(dir) {
@ -57,32 +54,11 @@ WebsiteOutput.prototype.prepare = function() {
}) })
.then(function() { .then(function() {
var themeName = that.book.config.get('theme');
that.theme = that.plugins.get(themeID(themeName));
that.themeDefault = that.plugins.get(themeID('default'));
if (!that.theme) {
throw new Error('Theme "' + themeName + '" is not installed, add "' + themeID(themeName) + '" to your "book.json"');
}
if (that.themeDefault.root != that.theme.root) {
that.log.info.ln('build using theme "' + themeName + '"');
}
// This list is ordered to give priority to templates in the book // This list is ordered to give priority to templates in the book
var searchPaths = _.chain([ var searchPaths = _.pluck(that.plugins.list(), 'root');
// The book itself can contains a "_layouts" folder // The book itself can contains a "_layouts" folder
that.book.root, searchPaths.unshift(that.book.root);
// Installed plugin (it can be identical to themeDefault.root)
that.theme.root,
// Is default theme still installed
that.themeDefault? that.themeDefault.root : null
])
.compact()
.uniq()
.value();
// Load i18n // Load i18n
_.each(searchPaths.concat().reverse(), function(searchPath) { _.each(searchPaths.concat().reverse(), function(searchPath) {
@ -92,7 +68,7 @@ WebsiteOutput.prototype.prepare = function() {
that.i18n.load(i18nRoot); that.i18n.load(i18nRoot);
}); });
that.env = new nunjucks.Environment(new nunjucks.FileSystemLoader(_.map(searchPaths, templatesPath))); that.env = new nunjucks.Environment(new FSLoader(_.map(searchPaths, templatesPath)));
// Add GitBook default filters // Add GitBook default filters
_.each(defaultFilters, function(fn, filter) { _.each(defaultFilters, function(fn, filter) {
@ -142,21 +118,11 @@ WebsiteOutput.prototype.prepare = function() {
.then(function() { .then(function() {
if (that.book.isLanguageBook()) return; if (that.book.isLanguageBook()) return;
return Promise.serie([
// Assets from the book are already copied // Assets from the book are already copied
// The order is reversed from the template's one // Copy assets from plugins (start with default plugins)
return Promise.serie(that.plugins.list().reverse(), function(plugin) {
// Is default theme still installed
that.themeDefault && that.themeDefault.root != that.theme.root?
that.themeDefault.root : null,
// Installed plugin (it can be identical to themeDefault.root)
that.theme.root
], function(folder) {
if (!folder) return;
// Copy assets only if exists (don't fail otherwise) // Copy assets only if exists (don't fail otherwise)
var assetFolder = path.join(folder, '_assets', that.name); var assetFolder = path.join(plugin.root, '_assets', that.name);
if (!fs.existsSync(assetFolder)) return; if (!fs.existsSync(assetFolder)) return;
that.log.debug.ln('copy assets from theme', assetFolder); that.log.debug.ln('copy assets from theme', assetFolder);
@ -164,7 +130,7 @@ WebsiteOutput.prototype.prepare = function() {
assetFolder, assetFolder,
that.resolve('gitbook'), that.resolve('gitbook'),
{ {
deleteFirst: false, // Delete "to" before deleteFirst: false,
overwrite: true, overwrite: true,
confirm: true confirm: true
} }
@ -190,7 +156,7 @@ WebsiteOutput.prototype.onPage = function(page) {
// Render the page template with the same context as the json output // Render the page template with the same context as the json output
.then(function() { .then(function() {
return that.render('page', page.getContext()); return that.render('page', page.getOutputContext(that));
}) })
// Write the HTML file // Write the HTML file
@ -243,13 +209,10 @@ WebsiteOutput.prototype.outputMultilingualIndex = function() {
// Templates are stored in `_layouts` folders // Templates are stored in `_layouts` folders
WebsiteOutput.prototype.render = function(tpl, context) { WebsiteOutput.prototype.render = function(tpl, context) {
var filename = this.templateName(tpl); var filename = this.templateName(tpl);
context = _.extend(context, { context = _.extend(context, {
template: { template: {
// Same template but in the default theme self: filename
default: this.themeDefault? path.resolve(templatesPath(this.themeDefault.root), filename) : null,
// Same template but in the theme
theme: path.resolve(templatesPath(this.theme.root), filename)
}, },
plugins: { plugins: {

View File

@ -41,7 +41,7 @@ function Page(book, filename) {
// Can we parse it? // Can we parse it?
extension = path.extname(this.path); extension = path.extname(this.path);
this.parser = parsers.get(extension); this.parser = parsers.getByExt(extension);
if (!this.parser) throw error.ParsingError(new Error('Can\'t parse file "'+this.path+'"')); if (!this.parser) throw error.ParsingError(new Error('Can\'t parse file "'+this.path+'"'));
this.type = this.parser.name; this.type = this.parser.name;
@ -116,8 +116,7 @@ Page.prototype.getContext = function() {
if (dir == 'neutral') dir = null; if (dir == 'neutral') dir = null;
} }
return _.extend( return {
{
file: { file: {
path: this.path, path: this.path,
mtime: this.mtime, mtime: this.mtime,
@ -128,18 +127,16 @@ Page.prototype.getContext = function() {
next: next? next.getContext() : null, next: next? next.getContext() : null,
previous: prev? prev.getContext() : null, previous: prev? prev.getContext() : null,
level: article? article.level : null, level: article? article.level : null,
depth: article? article.depth : 0, depth: article? article.depth() : 0,
content: this.content, content: this.content,
dir: dir dir: dir
}) })
}, };
gitbook.getContext(), };
this.book.getContext(),
this.book.langs.getContext(), // Return complete context for templating (page + book + summary + ...)
this.book.summary.getContext(), Page.prototype.getOutputContext = function(output) {
this.book.glossary.getContext(), return _.extend({}, this.getContext(), output.getContext());
this.book.config.getContext()
);
}; };
// Parse the page and return its content // Parse the page and return its content
@ -184,7 +181,7 @@ Page.prototype.toHTML = function(output) {
// Render template // Render template
.then(function() { .then(function() {
return output.template.render(that.content, that.getContext(), { return output.template.render(that.content, that.getOutputContext(output), {
path: that.path path: that.path
}) })
.then(that.update); .then(that.update);

View File

@ -37,11 +37,20 @@ function createParser(parser, base) {
nparser.page = Promise.wrapfn(parser.page); nparser.page = Promise.wrapfn(parser.page);
nparser.page.prepare = Promise.wrapfn(parser.page.prepare || _.identity); nparser.page.prepare = Promise.wrapfn(parser.page.prepare || _.identity);
nparser.inline = Promise.wrapfn(parser.inline);
return nparser; return nparser;
} }
// Return a specific parser
function getParser(name) {
return _.find(PARSERS, {
name: name
});
}
// Return a specific parser according to an extension // Return a specific parser according to an extension
function getParser(ext) { function getParserByExt(ext) {
return _.find(PARSERS, function(input) { return _.find(PARSERS, function(input) {
return input.name == ext || _.contains(input.extensions, ext); return input.name == ext || _.contains(input.extensions, ext);
}); });
@ -56,5 +65,6 @@ module.exports = {
all: PARSERS, all: PARSERS,
extensions: _.flatten(_.pluck(PARSERS, 'extensions')), extensions: _.flatten(_.pluck(PARSERS, 'extensions')),
get: getParser, get: getParser,
getByExt: getParserByExt,
getForFile: getParserForFile getForFile: getParserForFile
}; };

View File

@ -21,6 +21,11 @@ function PluginsManager(book) {
_.bindAll(this); _.bindAll(this);
} }
// Returns the list of plugins
PluginsManager.prototype.list = function() {
return this.plugins;
};
// Return count of plugins loaded // Return count of plugins loaded
PluginsManager.prototype.count = function() { PluginsManager.prototype.count = function() {
return _.size(this.plugins); return _.size(this.plugins);
@ -33,24 +38,21 @@ PluginsManager.prototype.get = function(name) {
}); });
}; };
// Load a plugin, or a list of plugins // Load a plugin (could be a BookPlugin or {name,path})
PluginsManager.prototype.load = function(name) { PluginsManager.prototype.load = function(plugin) {
var that = this; var that = this;
if (_.isArray(name)) { if (_.isArray(plugin)) {
return Promise.serie(name, function(_name) { return Promise.serie(plugin, that.load);
return that.load(_name);
});
} }
return Promise() return Promise()
// Initiate and load the plugin // Initiate and load the plugin
.then(function() { .then(function() {
var plugin; if (!(plugin instanceof BookPlugin)) {
plugin = new BookPlugin(that.book, plugin.name, plugin.path);
if (!_.isString(name)) plugin = name; }
else plugin = new BookPlugin(that.book, name);
if (that.get(plugin.id)) { if (that.get(plugin.id)) {
throw new Error('Plugin "'+plugin.id+'" is already loaded'); throw new Error('Plugin "'+plugin.id+'" is already loaded');
@ -68,10 +70,41 @@ PluginsManager.prototype.load = function(name) {
// Load all plugins from the book's configuration // Load all plugins from the book's configuration
PluginsManager.prototype.loadAll = function() { PluginsManager.prototype.loadAll = function() {
var plugins = _.pluck(this.book.config.get('plugins'), 'name'); var that = this;
var pluginNames = _.pluck(this.book.config.get('plugins'), 'name');
this.log.info.ln('loading', plugins.length, 'plugins'); return registry.list(this.book)
return this.load(plugins); .then(function(plugins) {
// Filter out plugins not listed of first level
// (aka pre-installed plugins)
plugins = _.filter(plugins, function(plugin) {
return (
plugin.depth > 1 ||
_.contains(pluginNames, plugin.name)
);
});
// Sort plugins to match list in book.json
plugins.sort(function(a, b){
return pluginNames.indexOf(a.name) < pluginNames.indexOf(b.name) ? -1 : 1;
});
// Log state
that.log.info.ln(_.size(plugins) + ' are installed');
if (_.size(pluginNames) != _.size(plugins)) that.log.info.ln(_.size(pluginNames) + ' explicitly listed');
// Verify that all plugins are present
var notInstalled = _.filter(pluginNames, function(name) {
return !_.find(plugins, { name: name });
});
if (_.size(notInstalled) > 0) {
throw new Error('Couldn\'t locate plugins "' + notInstalled.join(', ') + '", Run \'gitbook install\' to install plugins from registry.');
}
// Load plugins
return that.load(plugins);
});
}; };
// Setup a plugin // Setup a plugin

View File

@ -24,13 +24,14 @@ function isModuleNotFound(err) {
return err.message.indexOf('Cannot find module') >= 0; return err.message.indexOf('Cannot find module') >= 0;
} }
function BookPlugin(book, pluginId) { function BookPlugin(book, pluginId, pluginFolder) {
this.book = book; this.book = book;
this.log = this.book.log.prefix(pluginId); this.log = this.book.log.prefix(pluginId);
this.id = pluginId; this.id = pluginId;
this.npmId = registry.npmId(pluginId); this.npmId = registry.npmId(pluginId);
this.root; this.root = pluginFolder;
this.packageInfos = undefined; this.packageInfos = undefined;
this.content = undefined; this.content = undefined;
@ -51,8 +52,7 @@ BookPlugin.prototype.bind = function(fn) {
return fn.bind(compatibility.pluginCtx(this)); return fn.bind(compatibility.pluginCtx(this));
}; };
// Load this plugin // Load this plugin from its root folder
// An optional folder to search in can be passed
BookPlugin.prototype.load = function(folder) { BookPlugin.prototype.load = function(folder) {
var that = this; var that = this;
@ -60,18 +60,12 @@ BookPlugin.prototype.load = function(folder) {
return Promise.reject(new Error('Plugin "' + this.id + '" is already loaded')); return Promise.reject(new Error('Plugin "' + this.id + '" is already loaded'));
} }
// Fodlers to search plugins in
var searchPaths = _.compact([
folder,
this.book.resolve('node_modules'),
__dirname
]);
// Try loading plugins from different location // Try loading plugins from different location
var p = Promise.some(searchPaths, function(baseDir) { var p = Promise()
.then(function() {
// Locate plugin and load pacjage.json // Locate plugin and load pacjage.json
try { try {
var res = resolve.sync(that.npmId + '/package.json', { basedir: baseDir }); var res = resolve.sync('./package.json', { basedir: that.root });
that.root = path.dirname(res); that.root = path.dirname(res);
that.packageInfos = require(res); that.packageInfos = require(res);
@ -81,12 +75,12 @@ BookPlugin.prototype.load = function(folder) {
that.packageInfos = undefined; that.packageInfos = undefined;
that.content = undefined; that.content = undefined;
return false; return;
} }
// Load plugin JS content // Load plugin JS content
try { try {
that.content = require(resolve.sync(that.npmId, { basedir: baseDir })); that.content = require(that.root);
} catch(err) { } catch(err) {
// It's no big deal if the plugin doesn't have an "index.js" // It's no big deal if the plugin doesn't have an "index.js"
// (For example: themes) // (For example: themes)
@ -98,8 +92,6 @@ BookPlugin.prototype.load = function(folder) {
}); });
} }
} }
return true;
}) })
.then(that.validate) .then(that.validate)
@ -122,18 +114,15 @@ BookPlugin.prototype.load = function(folder) {
// This method throws erros if plugin is invalid // This method throws erros if plugin is invalid
BookPlugin.prototype.validate = function() { BookPlugin.prototype.validate = function() {
var isValid = ( var isValid = (
this.isLoaded() &&
this.packageInfos && this.packageInfos &&
this.packageInfos.name && this.packageInfos.name &&
this.packageInfos.engines && this.packageInfos.engines &&
this.packageInfos.engines.gitbook this.packageInfos.engines.gitbook
); );
if (!this.isLoaded()) {
throw new Error('Couldn\'t locate plugin "' + this.id + '", Run \'gitbook install\' to install plugins from registry.');
}
if (!isValid) { if (!isValid) {
throw new Error('Invalid plugin "' + this.id + '"'); throw new Error('Error loading plugin "' + this.id + '" at "' + this.root + '"');
} }
if (!gitbook.satisfies(this.packageInfos.engines.gitbook)) { if (!gitbook.satisfies(this.packageInfos.engines.gitbook)) {

View File

@ -1,7 +1,9 @@
var npm = require('npm'); var npm = require('npm');
var npmi = require('npmi'); var npmi = require('npmi');
var path = require('path');
var semver = require('semver'); var semver = require('semver');
var _ = require('lodash'); var _ = require('lodash');
var readInstalled = require('read-installed');
var Promise = require('../utils/promise'); var Promise = require('../utils/promise');
var gitbook = require('../gitbook'); var gitbook = require('../gitbook');
@ -21,7 +23,7 @@ function pluginId(name) {
// Validate an NPM plugin ID // Validate an NPM plugin ID
function validateId(name) { function validateId(name) {
return name.indexOf(PLUGIN_PREFIX) === 0; return name && name.indexOf(PLUGIN_PREFIX) === 0;
} }
// Initialize NPM for operations // Initialize NPM for operations
@ -104,6 +106,52 @@ function installPlugin(book, plugin, version) {
}); });
} }
// List all packages installed inside a folder
// Returns an ordered list of plugins
function listInstalled(folder) {
var options = {
dev: false,
log: function() {},
depth: 4
};
var results = [];
function onPackage(pkg, isRoot) {
if (!validateId(pkg.name)){
if (!isRoot) return;
} else {
results.push({
name: pluginId(pkg.name),
version: pkg.version,
path: pkg.realPath,
depth: pkg.depth
});
}
_.each(pkg.dependencies, function(dep) {
onPackage(dep);
});
}
return Promise.nfcall(readInstalled, folder, options)
.then(function(data) {
onPackage(data, true);
return _.uniq(results, 'name');
});
}
// List installed plugins for a book (defaults and installed)
function listPlugins(book) {
return Promise.all([
listInstalled(path.resolve(__dirname, '../..')),
listInstalled(book.root)
])
.spread(function(defaultPlugins, plugins) {
var results = plugins.concat(defaultPlugins);
return _.uniq(results, 'name');
});
}
module.exports = { module.exports = {
npmId: npmId, npmId: npmId,
pluginId: pluginId, pluginId: pluginId,
@ -111,5 +159,7 @@ module.exports = {
resolve: resolveVersion, resolve: resolveVersion,
link: linkPlugin, link: linkPlugin,
install: installPlugin install: installPlugin,
list: listPlugins,
listInstalled: listInstalled
}; };

View File

@ -12,5 +12,25 @@ module.exports = {
html: false, html: false,
body: blk.body body: blk.body
}; };
},
// Render some markdown to HTML
markdown: function(blk) {
return this.book.renderInline('markdown', blk.body)
.then(function(out) {
return { body: out };
});
},
asciidoc: function(blk) {
return this.book.renderInline('asciidoc', blk.body)
.then(function(out) {
return { body: out };
});
},
markup: function(blk) {
return this.book.renderInline(this.ctx.file.type, blk.body)
.then(function(out) {
return { body: out };
});
} }
}; };

View File

@ -0,0 +1,80 @@
var _ = require('lodash');
var fs = require('fs');
var path = require('path');
var nunjucks = require('nunjucks');
/*
Nunjucks loader similar to FileSystemLoader, but avoid infinite looping
*/
function isRelative(filename) {
return (filename.indexOf('./') === 0 || filename.indexOf('../') === 0);
}
var Loader = nunjucks.Loader.extend({
init: function(searchPaths) {
this.searchPaths = _.map(searchPaths, path.normalize);
},
getSource: function(fullpath) {
if (!fullpath) return null;
fullpath = this.resolve(null, fullpath);
if(!fullpath) {
return null;
}
return {
src: fs.readFileSync(fullpath, 'utf-8'),
path: fullpath,
noCache: true
};
},
// We handle absolute paths ourselves in ".resolve"
isRelative: function() {
return true;
},
resolve: function(from, to) {
var searchPaths = this.searchPaths;
// Relative template like "./test.html"
if (isRelative(to) && from) {
return path.resolve(path.dirname(from), to);
}
// Determine in which search folder we currently are
var originalSearchPath = _.chain(this.searchPaths)
.sortBy(function(s) {
return -s.length;
})
.find(function(basePath) {
return (from && from.indexOf(basePath) === 0);
})
.value();
var originalFilename = originalSearchPath? path.relative(originalSearchPath, from) : null;
// If we are including same file from a different search path
// Slice the search paths to avoid including from previous ones
if (originalFilename == to) {
var currentIndex = searchPaths.indexOf(originalSearchPath);
searchPaths = searchPaths.slice(currentIndex + 1);
}
// Absolute template to resolve in root folder
var resultFolder = _.find(searchPaths, function(basePath) {
var p = path.resolve(basePath, to);
return (
p.indexOf(basePath) === 0
&& fs.existsSync(p)
);
});
if (!resultFolder) return null;
return path.resolve(resultFolder, to);
}
});
module.exports = Loader;

View File

@ -68,19 +68,23 @@ function TemplateEngine(output) {
// Bind a function to a context // Bind a function to a context
// Filters and blocks are binded to this context // Filters and blocks are binded to this context
TemplateEngine.prototype.bindContext = function(func) { TemplateEngine.prototype.bindContext = function(func) {
var that = this;
return function() {
var ctx = { var ctx = {
ctx: this.ctx, ctx: this.ctx,
book: this.book, book: that.book,
output: this.output output: that.output
}; };
error.deprecateField(ctx, 'generator', this.output.name, '"generator" property is deprecated, use "output.generator" instead'); error.deprecateField(ctx, 'generator', that.output.name, '"generator" property is deprecated, use "output.generator" instead');
return _.bind(func, ctx); return func.apply(ctx, arguments);
};
}; };
// Interpolate a string content to replace shortcuts according to the filetype // Interpolate a string content to replace shortcuts according to the filetype
TemplateEngine.prototype.interpolate = function(filepath, source) { TemplateEngine.prototype.interpolate = function(filepath, source) {
var parser = parsers.get(path.extname(filepath)); var parser = parsers.getByExt(path.extname(filepath));
var type = parser? parser.name : null; var type = parser? parser.name : null;
return this.applyShortcuts(type, source); return this.applyShortcuts(type, source);
@ -339,7 +343,7 @@ TemplateEngine.prototype.processBlock = function(blk) {
} }
// Return it as a position marker // Return it as a position marker
return '@%@'+blk.id+'@%@'; return '{{-%'+blk.id+'%-}}';
}; };
// Render a string (without post processing) // Render a string (without post processing)
@ -387,7 +391,7 @@ TemplateEngine.prototype.applyShortcut = function(content, shortcut) {
TemplateEngine.prototype.replaceBlocks = function(content) { TemplateEngine.prototype.replaceBlocks = function(content) {
var that = this; var that = this;
return content.replace(/\@\%\@([\s\S]+?)\@\%\@/g, function(match, key) { return content.replace(/\{\{\-\%([\s\S]+?)\%\-\}\}/g, function(match, key) {
var blk = that.blockBodies[key]; var blk = that.blockBodies[key];
if (!blk) return match; if (!blk) return match;

View File

@ -19,8 +19,8 @@
"escape-string-regexp": "1.0.5", "escape-string-regexp": "1.0.5",
"eslint": "^2.2.0", "eslint": "^2.2.0",
"front-matter": "2.0.6", "front-matter": "2.0.6",
"gitbook-asciidoc": "1.0.2", "gitbook-asciidoc": "1.1.0",
"gitbook-markdown": "1.0.3", "gitbook-markdown": "1.2.0",
"gitbook-plugin-fontsettings": "1.0.2", "gitbook-plugin-fontsettings": "1.0.2",
"gitbook-plugin-highlight": "2.0.0", "gitbook-plugin-highlight": "2.0.0",
"gitbook-plugin-livereload": "0.0.1", "gitbook-plugin-livereload": "0.0.1",
@ -40,9 +40,10 @@
"moment": "2.11.2", "moment": "2.11.2",
"npm": "3.7.5", "npm": "3.7.5",
"npmi": "1.0.1", "npmi": "1.0.1",
"nunjucks": "2.3.0", "nunjucks": "2.4.1",
"nunjucks-autoescape": "1.0.1", "nunjucks-autoescape": "1.0.1",
"q": "1.4.1", "q": "1.4.1",
"read-installed": "^4.0.3",
"request": "2.69.0", "request": "2.69.0",
"resolve": "0.6.3", "resolve": "0.6.3",
"rmdir": "1.2.0", "rmdir": "1.2.0",

View File

@ -1,3 +1,5 @@
var fs = require('fs');
var mock = require('./mock'); var mock = require('./mock');
var WebsiteOutput = require('../lib/output/website'); var WebsiteOutput = require('../lib/output/website');
@ -95,5 +97,28 @@ describe('Website Output', function() {
}); });
}); });
describe('Theming', function() {
var output;
before(function() {
return mock.outputDefaultBook(WebsiteOutput, {
'_layouts/website/page.html': '{% extends "website/page.html" %}{% block body %}{{ super() }}<div id="theming-added"></div>{% endblock %}'
})
.then(function(_output) {
output = _output;
});
});
it('should extend default theme', function() {
var readme = fs.readFileSync(output.resolve('index.html'), 'utf-8');
readme.should.be.html({
'#theming-added': {
count: 1
}
});
});
});
}); });

View File

@ -36,8 +36,12 @@ describe('Page', function() {
'variables/page/dir/ltr.md': 'This is english: {{ page.dir }}', 'variables/page/dir/ltr.md': 'This is english: {{ page.dir }}',
'variables/page/dir/rtl.md': 'بسيطة {{ page.dir }}', 'variables/page/dir/rtl.md': 'بسيطة {{ page.dir }}',
'variables/config/title.md': '{{ config.title}}', 'variables/config/title.md': '{{ config.title}}',
'variables/gitbook/generator.md': '{{ gitbook.generator }}',
'GLOSSARY.md': '# Glossary\n\n\n## abracadabra\n\nthis is the description' 'GLOSSARY.md': '# Glossary\n\n\n## abracadabra\n\nthis is the description',
'blocks/markdown.md': 'Hello <span>{% markdown %}**World**{% endmarkdown %}</span>',
'blocks/asciidoc.md': 'Hello <span>{% asciidoc %}^super^script phrase{% endasciidoc %}</span>'
}, [ }, [
{ {
title: 'Test page.next', title: 'Test page.next',
@ -358,6 +362,12 @@ describe('Page', function() {
.should.be.fulfilledWith('<p>Hello World</p>\n'); .should.be.fulfilledWith('<p>Hello World</p>\n');
}); });
it('should set gitbook.generator', function() {
var page = book.addPage('variables/gitbook/generator.md');
return page.toHTML(output)
.should.be.fulfilledWith('<p>base</p>\n');
});
describe('page.dir', function() { describe('page.dir', function() {
it('should detect ltr', function() { it('should detect ltr', function() {
var page = book.addPage('variables/page/dir/ltr.md'); var page = book.addPage('variables/page/dir/ltr.md');
@ -406,4 +416,16 @@ describe('Page', function() {
}); });
}); });
}); });
describe('Default Blocks', function() {
it('should render block "markdown"', function() {
return book.addPage('blocks/markdown.md').toHTML(output)
.should.finally.equal('<p>Hello <span><strong>World</strong></span></p>\n');
});
it('should render block "asciidoc"', function() {
return book.addPage('blocks/asciidoc.md').toHTML(output)
.should.finally.equal('<p>Hello <span><sup>super</sup>script phrase</span></p>\n');
});
});
}); });

View File

@ -9,6 +9,10 @@ var BookPlugin = require('../lib/plugins/plugin');
var PLUGINS_ROOT = path.resolve(__dirname, 'node_modules'); var PLUGINS_ROOT = path.resolve(__dirname, 'node_modules');
function TestPlugin(book, name) {
return new BookPlugin(book, name, path.resolve(PLUGINS_ROOT, 'gitbook-plugin-'+name));
}
describe('Plugins', function() { describe('Plugins', function() {
var book; var book;
@ -90,7 +94,7 @@ describe('Plugins', function() {
describe('Configuration', function() { describe('Configuration', function() {
it('should fail loading a plugin with an invalid configuration', function() { it('should fail loading a plugin with an invalid configuration', function() {
var plugin = new BookPlugin(book, 'test-config'); var plugin = TestPlugin(book, 'test-config');
return plugin.load(PLUGINS_ROOT) return plugin.load(PLUGINS_ROOT)
.should.be.rejectedWith('Error with book\'s configuration: pluginsConfig.test-config.myProperty is required'); .should.be.rejectedWith('Error with book\'s configuration: pluginsConfig.test-config.myProperty is required');
}); });
@ -108,7 +112,7 @@ describe('Plugins', function() {
.then(function(book2) { .then(function(book2) {
return book2.prepareConfig() return book2.prepareConfig()
.then(function() { .then(function() {
var plugin = new BookPlugin(book2, 'test-config'); var plugin = TestPlugin(book2, 'test-config');
return plugin.load(PLUGINS_ROOT); return plugin.load(PLUGINS_ROOT);
}) })
.then(function() { .then(function() {
@ -122,7 +126,7 @@ describe('Plugins', function() {
var plugin; var plugin;
before(function() { before(function() {
plugin = new BookPlugin(book, 'test-resources'); plugin = TestPlugin(book, 'test-resources');
return plugin.load(PLUGINS_ROOT); return plugin.load(PLUGINS_ROOT);
}); });
@ -146,7 +150,7 @@ describe('Plugins', function() {
var plugin, filters; var plugin, filters;
before(function() { before(function() {
plugin = new BookPlugin(book, 'test-filters'); plugin = TestPlugin(book, 'test-filters');
return plugin.load(PLUGINS_ROOT) return plugin.load(PLUGINS_ROOT)
.then(function() { .then(function() {
@ -171,7 +175,7 @@ describe('Plugins', function() {
var plugin, blocks; var plugin, blocks;
before(function() { before(function() {
plugin = new BookPlugin(book, 'test-blocks'); plugin = TestPlugin(book, 'test-blocks');
return plugin.load(PLUGINS_ROOT) return plugin.load(PLUGINS_ROOT)
.then(function() { .then(function() {
@ -196,7 +200,7 @@ describe('Plugins', function() {
var plugin; var plugin;
before(function() { before(function() {
plugin = new BookPlugin(book, 'test-hooks'); plugin = TestPlugin(book, 'test-hooks');
return plugin.load(PLUGINS_ROOT); return plugin.load(PLUGINS_ROOT);
}); });

View File

@ -16,7 +16,7 @@
<link rel="stylesheet" href="gitbook/gitbook-plugin-highlight/website.css"> <link rel="stylesheet" href="gitbook/gitbook-plugin-fontsettings/website.css">
@ -24,7 +24,7 @@
<link rel="stylesheet" href="gitbook/gitbook-plugin-fontsettings/website.css"> <link rel="stylesheet" href="gitbook/gitbook-plugin-highlight/website.css">
@ -68,7 +68,7 @@
data-chapter-title="Getting Started with GORM" data-chapter-title="Getting Started with GORM"
data-filepath="README.md" data-filepath="README.md"
data-basepath="." data-basepath="."
data-revision="Wed Mar 23 2016 07:30:36 GMT+0800 (CST)" data-revision="Mon Mar 28 2016 10:39:40 GMT+0800 (CST)"
data-innerlanguage=""> data-innerlanguage="">
@ -720,15 +720,11 @@
<script src="gitbook/app.js"></script> <script src="gitbook/app.js"></script>
<script src="gitbook/gitbook-plugin-github/plugin.js"></script> <script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script>
<script src="gitbook/gitbook-plugin-edit-link/plugin.js"></script> <script src="gitbook/gitbook-plugin-sharing/buttons.js"></script>
<script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
@ -740,11 +736,15 @@
<script src="gitbook/gitbook-plugin-sharing/buttons.js"></script> <script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
<script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script> <script src="gitbook/gitbook-plugin-edit-link/plugin.js"></script>
<script src="gitbook/gitbook-plugin-github/plugin.js"></script>
<script> <script>

View File

@ -16,7 +16,7 @@
<link rel="stylesheet" href="gitbook/gitbook-plugin-highlight/website.css"> <link rel="stylesheet" href="gitbook/gitbook-plugin-fontsettings/website.css">
@ -24,7 +24,7 @@
<link rel="stylesheet" href="gitbook/gitbook-plugin-fontsettings/website.css"> <link rel="stylesheet" href="gitbook/gitbook-plugin-highlight/website.css">
@ -70,7 +70,7 @@
data-chapter-title="Models" data-chapter-title="Models"
data-filepath="models.md" data-filepath="models.md"
data-basepath="." data-basepath="."
data-revision="Wed Mar 23 2016 07:30:36 GMT+0800 (CST)" data-revision="Mon Mar 28 2016 10:39:40 GMT+0800 (CST)"
data-innerlanguage=""> data-innerlanguage="">
@ -716,7 +716,7 @@
<pre><code class="lang-go"><span class="hljs-keyword">type</span> User <span class="hljs-keyword">struct</span> {} <span class="hljs-comment">// default table name is `users`</span> <pre><code class="lang-go"><span class="hljs-keyword">type</span> User <span class="hljs-keyword">struct</span> {} <span class="hljs-comment">// default table name is `users`</span>
<span class="hljs-comment">// set User&apos;s table name to be `profiles</span> <span class="hljs-comment">// set User&apos;s table name to be `profiles</span>
<span class="hljs-keyword">type</span> (User) TableName() <span class="hljs-keyword">string</span> { <span class="hljs-keyword">func</span> (User) TableName() <span class="hljs-keyword">string</span> {
<span class="hljs-keyword">return</span> <span class="hljs-string">&quot;profiles&quot;</span> <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;profiles&quot;</span>
} }
@ -801,15 +801,11 @@ db.Model(&amp;user).Update(<span class="hljs-string">&quot;name&quot;</span>, <s
<script src="gitbook/app.js"></script> <script src="gitbook/app.js"></script>
<script src="gitbook/gitbook-plugin-github/plugin.js"></script> <script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script>
<script src="gitbook/gitbook-plugin-edit-link/plugin.js"></script> <script src="gitbook/gitbook-plugin-sharing/buttons.js"></script>
<script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
@ -821,11 +817,15 @@ db.Model(&amp;user).Update(<span class="hljs-string">&quot;name&quot;</span>, <s
<script src="gitbook/gitbook-plugin-sharing/buttons.js"></script> <script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
<script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script> <script src="gitbook/gitbook-plugin-edit-link/plugin.js"></script>
<script src="gitbook/gitbook-plugin-github/plugin.js"></script>
<script> <script>

File diff suppressed because one or more lines are too long