Remove not used files for github pages

This commit is contained in:
Jinzhu 2017-01-02 22:13:56 +08:00
parent aa47843d25
commit a48d6f4b8e
139 changed files with 18 additions and 12122 deletions

View File

@ -836,7 +836,7 @@ db.SetLogger(log.New(os.Stdout, <span class="hljs-string">&quot;\r\n&quot;</span
<script>
var gitbook = gitbook || [];
gitbook.push(function() {
gitbook.page.hasChanged({"page":{"title":"Advanced Usage","level":"1.6","depth":1,"next":{"title":"Error Handling","level":"1.6.1","depth":2,"anchor":"#error-handling","path":"advanced.md","ref":"advanced.md#error-handling","articles":[]},"previous":{"title":"Callbacks","level":"1.5","depth":1,"path":"callbacks.md","ref":"callbacks.md","articles":[]},"dir":"ltr"},"config":{"plugins":["github","edit-link"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"fontsettings":{"theme":"night","family":"sans","size":2},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"label":"Edit This Page","base":"https://github.com/jinzhu/gorm/edit/gh-pages/documents/"},"highlight":{},"search":{},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"GORM Guide","gitbook":"*"},"file":{"path":"advanced.md","mtime":"2017-01-02T12:58:30.000Z","type":"markdown"},"gitbook":{"version":"3.2.2","time":"2017-01-02T13:57:16.915Z"},"basePath":".","book":{"language":""}});
gitbook.page.hasChanged({"page":{"title":"Advanced Usage","level":"1.6","depth":1,"next":{"title":"Error Handling","level":"1.6.1","depth":2,"anchor":"#error-handling","path":"advanced.md","ref":"advanced.md#error-handling","articles":[]},"previous":{"title":"Callbacks","level":"1.5","depth":1,"path":"callbacks.md","ref":"callbacks.md","articles":[]},"dir":"ltr"},"config":{"plugins":["github","edit-link"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"fontsettings":{"theme":"night","family":"sans","size":2},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"label":"Edit This Page","base":"https://github.com/jinzhu/gorm/edit/gh-pages/documents/"},"highlight":{},"search":{},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"GORM Guide","gitbook":"*"},"file":{"path":"advanced.md","mtime":"2017-01-02T12:58:30.000Z","type":"markdown"},"gitbook":{"version":"3.2.2","time":"2017-01-02T14:12:37.025Z"},"basePath":".","book":{"language":""}});
});
</script>
</div>

View File

@ -915,7 +915,7 @@ db.Model(&amp;user).Association(<span class="hljs-string">&quot;Languages&quot;<
<script>
var gitbook = gitbook || [];
gitbook.push(function() {
gitbook.page.hasChanged({"page":{"title":"Associations","level":"1.3.3","depth":2,"next":{"title":"Belongs To","level":"1.3.3.1","depth":3,"anchor":"#belongs-to","path":"associations.md","ref":"associations.md#belongs-to","articles":[]},"previous":{"title":"Conventions & Overriding","level":"1.3.2","depth":2,"anchor":"#conventions","path":"models.md","ref":"models.md#conventions","articles":[]},"dir":"ltr"},"config":{"plugins":["github","edit-link"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"fontsettings":{"theme":"night","family":"sans","size":2},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"label":"Edit This Page","base":"https://github.com/jinzhu/gorm/edit/gh-pages/documents/"},"highlight":{},"search":{},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"GORM Guide","gitbook":"*"},"file":{"path":"associations.md","mtime":"2017-01-02T13:43:54.000Z","type":"markdown"},"gitbook":{"version":"3.2.2","time":"2017-01-02T13:57:16.915Z"},"basePath":".","book":{"language":""}});
gitbook.page.hasChanged({"page":{"title":"Associations","level":"1.3.3","depth":2,"next":{"title":"Belongs To","level":"1.3.3.1","depth":3,"anchor":"#belongs-to","path":"associations.md","ref":"associations.md#belongs-to","articles":[]},"previous":{"title":"Conventions & Overriding","level":"1.3.2","depth":2,"anchor":"#conventions","path":"models.md","ref":"models.md#conventions","articles":[]},"dir":"ltr"},"config":{"plugins":["github","edit-link"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"fontsettings":{"theme":"night","family":"sans","size":2},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"label":"Edit This Page","base":"https://github.com/jinzhu/gorm/edit/gh-pages/documents/"},"highlight":{},"search":{},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"GORM Guide","gitbook":"*"},"file":{"path":"associations.md","mtime":"2017-01-02T13:43:54.000Z","type":"markdown"},"gitbook":{"version":"3.2.2","time":"2017-01-02T14:12:37.025Z"},"basePath":".","book":{"language":""}});
});
</script>
</div>

View File

@ -773,7 +773,7 @@ If you want to use those changes in your callbacks, you need to run your SQL in
<script>
var gitbook = gitbook || [];
gitbook.push(function() {
gitbook.page.hasChanged({"page":{"title":"Callbacks","level":"1.5","depth":1,"next":{"title":"Advanced Usage","level":"1.6","depth":1,"path":"advanced.md","ref":"advanced.md","articles":[{"title":"Error Handling","level":"1.6.1","depth":2,"anchor":"#error-handling","path":"advanced.md","ref":"advanced.md#error-handling","articles":[]},{"title":"Transactions","level":"1.6.2","depth":2,"anchor":"#transactions","path":"advanced.md","ref":"advanced.md#transactions","articles":[]},{"title":"Raw SQL & SQL Builder","level":"1.6.3","depth":2,"anchor":"#sql-builder","path":"advanced.md","ref":"advanced.md#sql-builder","articles":[]},{"title":"Generic database interface sql.DB","level":"1.6.4","depth":2,"anchor":"#generic-database-interface-sqldb","path":"advanced.md","ref":"advanced.md#generic-database-interface-sqldb","articles":[]},{"title":"Composite Primary Key","level":"1.6.5","depth":2,"anchor":"#compose-primary-key","path":"advanced.md","ref":"advanced.md#compose-primary-key","articles":[]},{"title":"Overriding Logger","level":"1.6.6","depth":2,"anchor":"#logger","path":"advanced.md","ref":"advanced.md#logger","articles":[]}]},"previous":{"title":"Associations","level":"1.4.6","depth":2,"anchor":"#associations","path":"crud.md","ref":"crud.md#associations","articles":[]},"dir":"ltr"},"config":{"plugins":["github","edit-link"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"fontsettings":{"theme":"night","family":"sans","size":2},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"label":"Edit This Page","base":"https://github.com/jinzhu/gorm/edit/gh-pages/documents/"},"highlight":{},"search":{},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"GORM Guide","gitbook":"*"},"file":{"path":"callbacks.md","mtime":"2017-01-02T12:58:30.000Z","type":"markdown"},"gitbook":{"version":"3.2.2","time":"2017-01-02T13:57:16.915Z"},"basePath":".","book":{"language":""}});
gitbook.page.hasChanged({"page":{"title":"Callbacks","level":"1.5","depth":1,"next":{"title":"Advanced Usage","level":"1.6","depth":1,"path":"advanced.md","ref":"advanced.md","articles":[{"title":"Error Handling","level":"1.6.1","depth":2,"anchor":"#error-handling","path":"advanced.md","ref":"advanced.md#error-handling","articles":[]},{"title":"Transactions","level":"1.6.2","depth":2,"anchor":"#transactions","path":"advanced.md","ref":"advanced.md#transactions","articles":[]},{"title":"Raw SQL & SQL Builder","level":"1.6.3","depth":2,"anchor":"#sql-builder","path":"advanced.md","ref":"advanced.md#sql-builder","articles":[]},{"title":"Generic database interface sql.DB","level":"1.6.4","depth":2,"anchor":"#generic-database-interface-sqldb","path":"advanced.md","ref":"advanced.md#generic-database-interface-sqldb","articles":[]},{"title":"Composite Primary Key","level":"1.6.5","depth":2,"anchor":"#compose-primary-key","path":"advanced.md","ref":"advanced.md#compose-primary-key","articles":[]},{"title":"Overriding Logger","level":"1.6.6","depth":2,"anchor":"#logger","path":"advanced.md","ref":"advanced.md#logger","articles":[]}]},"previous":{"title":"Associations","level":"1.4.6","depth":2,"anchor":"#associations","path":"crud.md","ref":"crud.md#associations","articles":[]},"dir":"ltr"},"config":{"plugins":["github","edit-link"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"fontsettings":{"theme":"night","family":"sans","size":2},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"label":"Edit This Page","base":"https://github.com/jinzhu/gorm/edit/gh-pages/documents/"},"highlight":{},"search":{},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"GORM Guide","gitbook":"*"},"file":{"path":"callbacks.md","mtime":"2017-01-02T12:58:30.000Z","type":"markdown"},"gitbook":{"version":"3.2.2","time":"2017-01-02T14:12:37.025Z"},"basePath":".","book":{"language":""}});
});
</script>
</div>

View File

@ -743,7 +743,7 @@
<script>
var gitbook = gitbook || [];
gitbook.push(function() {
gitbook.page.hasChanged({"page":{"title":"Change Log","level":"1.8","depth":1,"previous":{"title":"Write Plugins","level":"1.7.2","depth":2,"anchor":"#write-plugins","path":"development.md","ref":"development.md#write-plugins","articles":[]},"dir":"ltr"},"config":{"plugins":["github","edit-link"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"fontsettings":{"theme":"night","family":"sans","size":2},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"label":"Edit This Page","base":"https://github.com/jinzhu/gorm/edit/gh-pages/documents/"},"highlight":{},"search":{},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"GORM Guide","gitbook":"*"},"file":{"path":"changelog.md","mtime":"2017-01-02T12:58:30.000Z","type":"markdown"},"gitbook":{"version":"3.2.2","time":"2017-01-02T13:57:16.915Z"},"basePath":".","book":{"language":""}});
gitbook.page.hasChanged({"page":{"title":"Change Log","level":"1.8","depth":1,"previous":{"title":"Write Plugins","level":"1.7.2","depth":2,"anchor":"#write-plugins","path":"development.md","ref":"development.md#write-plugins","articles":[]},"dir":"ltr"},"config":{"plugins":["github","edit-link"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"fontsettings":{"theme":"night","family":"sans","size":2},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"label":"Edit This Page","base":"https://github.com/jinzhu/gorm/edit/gh-pages/documents/"},"highlight":{},"search":{},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"GORM Guide","gitbook":"*"},"file":{"path":"changelog.md","mtime":"2017-01-02T12:58:30.000Z","type":"markdown"},"gitbook":{"version":"3.2.2","time":"2017-01-02T14:12:37.025Z"},"basePath":".","book":{"language":""}});
});
</script>
</div>

View File

@ -1213,8 +1213,14 @@ db.Unscoped().Delete(&amp;order)
Name: <span class="hljs-string">&quot;jinzhu&quot;</span>,
BillingAddress: Address{Address1: <span class="hljs-string">&quot;Billing Address - Address 1&quot;</span>},
ShippingAddress: Address{Address1: <span class="hljs-string">&quot;Shipping Address - Address 1&quot;</span>},
Emails: []Email{{Email: <span class="hljs-string">&quot;jinzhu@example.com&quot;</span>}, {Email: <span class="hljs-string">&quot;jinzhu-2@example@example.com&quot;</span>}},
Languages: []Language{{Name: <span class="hljs-string">&quot;ZH&quot;</span>}, {Name: <span class="hljs-string">&quot;EN&quot;</span>}},
Emails: []Email{
{Email: <span class="hljs-string">&quot;jinzhu@example.com&quot;</span>},
{Email: <span class="hljs-string">&quot;jinzhu-2@example@example.com&quot;</span>},
},
Languages: []Language{
{Name: <span class="hljs-string">&quot;ZH&quot;</span>},
{Name: <span class="hljs-string">&quot;EN&quot;</span>},
},
}
db.Create(&amp;user)
@ -1296,7 +1302,7 @@ db.Set(<span class="hljs-string">&quot;gorm:save_associations&quot;</span>, <spa
<script>
var gitbook = gitbook || [];
gitbook.push(function() {
gitbook.page.hasChanged({"page":{"title":"CRUD: Reading and Writing Data","level":"1.4","depth":1,"next":{"title":"Create","level":"1.4.1","depth":2,"anchor":"#create","path":"crud.md","ref":"crud.md#create","articles":[]},"previous":{"title":"Association Mode","level":"1.3.3.6","depth":3,"anchor":"#association-mode","path":"associations.md","ref":"associations.md#association-mode","articles":[]},"dir":"ltr"},"config":{"plugins":["github","edit-link"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"fontsettings":{"theme":"night","family":"sans","size":2},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"label":"Edit This Page","base":"https://github.com/jinzhu/gorm/edit/gh-pages/documents/"},"highlight":{},"search":{},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"GORM Guide","gitbook":"*"},"file":{"path":"crud.md","mtime":"2017-01-02T12:58:30.000Z","type":"markdown"},"gitbook":{"version":"3.2.2","time":"2017-01-02T13:57:16.915Z"},"basePath":".","book":{"language":""}});
gitbook.page.hasChanged({"page":{"title":"CRUD: Reading and Writing Data","level":"1.4","depth":1,"next":{"title":"Create","level":"1.4.1","depth":2,"anchor":"#create","path":"crud.md","ref":"crud.md#create","articles":[]},"previous":{"title":"Association Mode","level":"1.3.3.6","depth":3,"anchor":"#association-mode","path":"associations.md","ref":"associations.md#association-mode","articles":[]},"dir":"ltr"},"config":{"plugins":["github","edit-link"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"fontsettings":{"theme":"night","family":"sans","size":2},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"label":"Edit This Page","base":"https://github.com/jinzhu/gorm/edit/gh-pages/documents/"},"highlight":{},"search":{},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"GORM Guide","gitbook":"*"},"file":{"path":"crud.md","mtime":"2017-01-02T14:09:40.000Z","type":"markdown"},"gitbook":{"version":"3.2.2","time":"2017-01-02T14:12:37.025Z"},"basePath":".","book":{"language":""}});
});
</script>
</div>

View File

@ -818,7 +818,7 @@ db.Model(&amp;User{}).RemoveIndex(<span class="hljs-string">&quot;idx_user_name&
<script>
var gitbook = gitbook || [];
gitbook.push(function() {
gitbook.page.hasChanged({"page":{"title":"Database","level":"1.2","depth":1,"next":{"title":"Database Connection","level":"1.2.1","depth":2,"anchor":"#connecting-to-a-database","path":"database.md","ref":"database.md#connecting-to-a-database","articles":[]},"previous":{"title":"Getting Started with GORM","level":"1.1","depth":1,"path":"README.md","ref":"README.md","articles":[]},"dir":"ltr"},"config":{"plugins":["github","edit-link"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"fontsettings":{"theme":"night","family":"sans","size":2},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"label":"Edit This Page","base":"https://github.com/jinzhu/gorm/edit/gh-pages/documents/"},"highlight":{},"search":{},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"GORM Guide","gitbook":"*"},"file":{"path":"database.md","mtime":"2017-01-02T12:58:30.000Z","type":"markdown"},"gitbook":{"version":"3.2.2","time":"2017-01-02T13:57:16.915Z"},"basePath":".","book":{"language":""}});
gitbook.page.hasChanged({"page":{"title":"Database","level":"1.2","depth":1,"next":{"title":"Database Connection","level":"1.2.1","depth":2,"anchor":"#connecting-to-a-database","path":"database.md","ref":"database.md#connecting-to-a-database","articles":[]},"previous":{"title":"Getting Started with GORM","level":"1.1","depth":1,"path":"README.md","ref":"README.md","articles":[]},"dir":"ltr"},"config":{"plugins":["github","edit-link"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"fontsettings":{"theme":"night","family":"sans","size":2},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"label":"Edit This Page","base":"https://github.com/jinzhu/gorm/edit/gh-pages/documents/"},"highlight":{},"search":{},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"GORM Guide","gitbook":"*"},"file":{"path":"database.md","mtime":"2017-01-02T12:58:30.000Z","type":"markdown"},"gitbook":{"version":"3.2.2","time":"2017-01-02T14:12:37.025Z"},"basePath":".","book":{"language":""}});
});
</script>
</div>

View File

@ -772,7 +772,7 @@ db.Callback().RowQuery().Register(<span class="hljs-string">&quot;publish:update
<script>
var gitbook = gitbook || [];
gitbook.push(function() {
gitbook.page.hasChanged({"page":{"title":"Development","level":"1.7","depth":1,"next":{"title":"Architecture","level":"1.7.1","depth":2,"anchor":"#architecture","path":"development.md","ref":"development.md#architecture","articles":[]},"previous":{"title":"Overriding Logger","level":"1.6.6","depth":2,"anchor":"#logger","path":"advanced.md","ref":"advanced.md#logger","articles":[]},"dir":"ltr"},"config":{"plugins":["github","edit-link"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"fontsettings":{"theme":"night","family":"sans","size":2},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"label":"Edit This Page","base":"https://github.com/jinzhu/gorm/edit/gh-pages/documents/"},"highlight":{},"search":{},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"GORM Guide","gitbook":"*"},"file":{"path":"development.md","mtime":"2017-01-02T12:58:30.000Z","type":"markdown"},"gitbook":{"version":"3.2.2","time":"2017-01-02T13:57:16.915Z"},"basePath":".","book":{"language":""}});
gitbook.page.hasChanged({"page":{"title":"Development","level":"1.7","depth":1,"next":{"title":"Architecture","level":"1.7.1","depth":2,"anchor":"#architecture","path":"development.md","ref":"development.md#architecture","articles":[]},"previous":{"title":"Overriding Logger","level":"1.6.6","depth":2,"anchor":"#logger","path":"advanced.md","ref":"advanced.md#logger","articles":[]},"dir":"ltr"},"config":{"plugins":["github","edit-link"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"fontsettings":{"theme":"night","family":"sans","size":2},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"label":"Edit This Page","base":"https://github.com/jinzhu/gorm/edit/gh-pages/documents/"},"highlight":{},"search":{},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"GORM Guide","gitbook":"*"},"file":{"path":"development.md","mtime":"2017-01-02T12:58:30.000Z","type":"markdown"},"gitbook":{"version":"3.2.2","time":"2017-01-02T14:12:37.025Z"},"basePath":".","book":{"language":""}});
});
</script>
</div>

View File

@ -1,10 +0,0 @@
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

View File

@ -1,2 +0,0 @@
docs/**/*
test/node_modules/**/*

View File

@ -1,19 +0,0 @@
{
"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"
}

View File

@ -1 +0,0 @@
./docs

View File

@ -1,10 +0,0 @@
sudo: false
language: node_js
node_js:
- "stable"
- "4.1"
- "0.12"
before_install:
- npm install svgexport -g
after_success:
- npm run lint

View File

@ -1,67 +0,0 @@
Authors
=======
Also see https://github.com/GitbookIO/gitbook/graphs/contributors.
Names below are ordered by first contribution.
Author
------
- Samy Pessé <samypesse@gmail.com> (@SamyPesse)
- Aaron O'Mullan <aaron.omullan@gmail.com> (@AaronO)
Contributors
------------
- Nijiko Yonskai (@Nijikokun)
- Herman Starikov (@Hermanya)
Translators
------------
- German
- Winnie (@winniehell)
- Italian
- Giulio Bonanome (@gbonanome)
- Russian
- Andrey Akinshin (@AndreyAkinshin)
- Norwegian
- Knut Melvær (@kmelve)
- Persian (Farsi)
- Mohammad Hossein Mojtahedi (@mhm5000)
- Polish
- Łukasz Szeremeta (@lszeremeta)
- Portuguese
- Ryan O'Mullan (@RyanOM)
- Spanish
- Ryan O'Mullan (@RyanOM)
- Simplifiled Chinese
- Hu Hao (@howiehu)
- Traditional Chinese
- Hu Hao (@howiehu)
- French
- Samy Pessé (@SamyPesse)
- Romanian
- Iancu Aurel (@awrelll)
- Finnish
- Tommi Savikko (@savikko)
- Japanese
- Iancu Aurel (@awrelll)
- Korean
- Iancu Aurel (@awrelll)
- SangYeob Yu (@deminoth)
- Vietnamese
- Hong Nguyen (@nghong)
- Hebrew
- @a-moses
- Ukrainian
- @Karnaukhov
- Czech
- @mjanda
- Swedish
- Jacob Burenstam (@buren)
- Turkish
- Turan Konan (@turankonan)
- Catalan
- Esaú García Sánchez-Torija (@egasato)

View File

@ -1,336 +0,0 @@
# Release notes
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## 3.0.0 (pre)
- Summary can contain external links and anchors (Fix [#776](https://github.com/GitbookIO/gitbook/issues/776))
- Summary can contain differents entitled sections
- Glossary is generated as a normal page
- Headings are no longer annotated with glossary terms
- Themes are now published as a plugin, with ability to extend it from the book source
- `links.sidebar` configuration is no longer supported, use summary sections instead
- `pdf.headerTemplate` and `pdf.footerTemplate` have been replaced by a template in theme/book: `_layout/ebook/pdf_header.html` and `_layout/ebook/pdf_footer.html`
- Markdown parser is now using CommonMark
- Root folder for the book can be specified in a `.gitbook` file
- Multi-lingual books share assets folder
- YAML front matter is parsed and can extend page's properties
- Fix `uk` translation
## 2.6.7
- Fix bug with filenames including spaces
- Add Turkish and Catalan translations
## 2.6.6
- Fix custom CSS that are generated by plugins for PDF output
## 2.6.5
- Fix support for plugins generating custom stylesheets (`styles-less` and `styles-sass`)
- Fix glossary terms being replaced in script (ex: math)
## 2.6.4
- Fix regression introduced by `2.6.3` of single HTML tags in markdown
## 2.6.3
- Fix parsing bug with inline HTML in Markdown
## 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
- Default plugins should not be installed by `gitbook install`
- Limit search index size to avoid crash during generation
- Fix code highlighting for html without language specified
- Fix warning message for gitbook version when building a multilingual book
## 2.4.1
- Fix disabling of default plugins, ex: `-highlight`
## 2.4.0
- Fix page being updated when user wants to open a link in a new tab
- Plugins can now replaced default code highlighter
- Add semantic information for screen readers (web version)
- Content references accept absolute paths, resolved to book folder
- Improve overall reliability
## 2.3.3
- Fix bug in SUMMARY parsing preventing multiple entries without filenames
## 2.3.2
- Fix blocks (like maths) in Asciidoc
- Fix error when checking gitbook version
## 2.3.1
- Fix black font color for ebooks (mobi, pdf and epub)
- Fix ISO code for korean language
- Fix korean translation
- Fix syntax highlighting for asciidoc
- Fix inline html escaping in markdown
- Add warning for file outside SUMMARY
- Force SUMMARY entries to be unique by filename
## 2.3.0
- Fix nunjucks issue with multiple `{% raw %}` blocks
- Fix crash when git conref failed
- Fix crash when failed to download remote image (better error message)
- Fix flicking effect when changing page (big UX improvement)
- Add Hebrew translation (`he`)
- Add utility method `book.config.get` for plugins
- Hooks `page:before` and `page` are no longer deprecated
- Remove webfonts to make website lighter
- Make glossary's order case insensitive
## 2.2.0
- Fix direction in code blocks (always LTR)
- Add options `chapterMark` and `pageBreaksBefore` for PDF
- Update code highlighting library
- `book.json` accessible as `config` in templating syntax
- Add Vietnamese translation (`vi`)
## 2.1.0
- Fix error in calcul of `levels` in table of contents, error introduced a few versions ago
- Add optional `styles/print.css` to replace `print.css` used in ebook
## 2.0.4
- Fix `{% raw %}`, got confused with "fake" variable declarations
- Fix title of language chooser
- Fix the X-UA-Compatible meta tag
- Move style sheets to the <head> section
## 2.0.3
- Fix `gitbook init` for SUMMARY with empty entries
- Fix escaping of code blocks in markdown
## 2.0.2
- Fix relative links in windows
- Improve watcher in serve command (switch to chokidar)
- Add Romanian translation (`ro`)
- Add Finish translation (`fi`)
- Add Japanese translation (`jp`)
- Add Korean translation (`kr`)
## 2.0.1
- Improve error logging (display file, line and column)
- Add back support for `options.originalInput`
- Don't process math in markdown parser (delegated to `mathjax` plugin)
- Fix some cases of code blocks escaping
- Fix i18n for introduction title in json format
- Fix reload when book configuration is updated
- Fix backslashes in url when building on windows
## 2.0.0
- Fix page title of introduction
- Ignore codeblocks when replacing glossary terms
- Improve Unit Tests
- Fix scrolling position in website when preparing page
## 2.0.0-beta.5
- Fix progress order in json format
## 2.0.0-beta.4
- Fix default generator for use programmatically
- Add option "author" for html meta tags
- Fix links normalization (content and hash)
## 2.0.0-beta.3
- Fix odd cases with code blocks escaping
## 2.0.0-beta.2
- Fix definition of entry point title using SUMMARY.md
## 2.0.0-beta.1
- Fix windows incompatibility
- Add support for rtl (enabled by default for `ar` and `fa`)
- Escape code blocks in markdown parser
- Add Persian/Farsi translation (`fa`)
- Add Arabic translation (`ar`)
- Add Bengali translation (`bn`)
- Provide generator name in template context
## 2.0.0-alpha.9
- Fix links in sidebar
- Fix normalization of html link (README to index)
- Fix html snippets escaping
## 2.0.0-alpha.8
- Improve locale detection for i18n
- Fix chapter name for Glossary in pdf
- Don't escape html in glossary items
- Fix generation of multilingual book as ebook
- Add "post" block attribute to post-process
## 2.0.0-alpha.7
- Fix display of glossary in ebook formats
- Add default footer and header to pdf
- Fix generation of json format compatible with 1.x.x
- Add Simplifiled Chinese and Traditional Chinese translations
## 2.0.0-alpha.6
- Add es and pt translations
- Fix replacement of glossary terms
## 2.0.0-alpha.5
- Fix copy of files/covers
- Add back `finish:before` hook
## 2.0.0-alpha.4
- Fix copy of cover for multilingal books
## 2.0.0-alpha.3
- Norwegian translation
- Load plugins from book in priority
## 2.0.0-alpha.3
- Fix init command
- Update parsers to fix spaces in summary (`gitbook-parsers@0.3.1`)
## 2.0.0-alpha.1
- Externalize parsing into `gitbook-parsers` module
- Supports AsciiDoc and reStructuredText
- Hooks for page (`page:*`) are now deprecated, plugins should extend filters and blocks instead
- Hooks `summary` and `glossary` (after and before) have been removed
- Exercises and Quizzes are no longer parsed in the markdown parser
- Support for more markdown extensions: `.markdown`, `.mdown`
- Templates are rendered with nunjucks instead of swig, syntax is almost compatible, there is some changes with contexts and filters. `{{ super() }}` should be use instead of `{% parent %}`
- Clean output folder on build without removing `.git` and `.svn`
- MathJAX is no longer a default plugin
- SVG images are converted to PNG during generation of ebooks
- i18n in website and ebook (ru, it, de, fr)
- New templating syntax
- Content references (both internal and external)
- Glossary terms are handled during generation (also in ebook format)
## 1.5.0
- Fix `serve` command, broken by `1.4.2`
- Add nicer `dark` theme :)
## 1.4.2
- Force `process.exit` after builds, to prevent (possibly) lingering plugins
## 1.4.1
- Fix command 'install' without arguments
## 1.4.0
- Add command `gitbook install` to install plugins from book.json
- `package.json` is no longer necessary
## 1.3.4
- Add glossary to ebooks
- Fix autocover with new hook "finish:before"
- Add X-UA-Compatible meta tag for IE
## 1.3.3
- Fix parsing of lexed content using the client library
## 1.3.2
- ePub files are now passing validation from epubcheck
- Fix replacement of multiple glossary terms in a single sentence
- Fix on windows deep relative links
- Fix search indexer
## 1.3.1
- Fix error with links in markdown
## 1.3.0
- Bundle gitbook parsing library as a client side library in `gitbook.js` and `gitbook.min.js`
## 1.2.0
- Improvements on ebook generation
- Fix incorrect follow of links in ebook generation
- Move Table of Contents at the beginning of the ebook
- Update to last highlight.js (includes Swift)
- Includes of templates and variables (from book.json)
## 1.1.1
- Rewrite quiz logic to be more robust
- Improve integration of glossary
- Improve generation of ebook by using a multiple HTML pages input source
- Fix incorrect page breaks after h1 and h2 divs
- New options to set header and footer in PDF generation
## 1.1.0
- Plugins can now extend the ebook generation (pdf, epub, mobi)
- Update `kramed` to version 0.4.3
## 1.0.3
- Update `mathjax` plugin and MathJAx to version 2.4
- Update `highlight.js` to 8.2.0
## 1.0.2
- Update `mathjax` plugin, fixes issues with inline math rendering (no longer wanted)
## 1.0.1
- New inline math convention (kramdown's), using `$$` rather than `$` as delimiters
- Fix instapaper sharing
- The `exercises` & `quizzes` plugins are now by default
## 1.0.0
- New design
- Support for glossary
- Support for sharing to instapaper
- Support for footnotes
## 0.7.1
- Update `fs-extra` to `0.10.0` (fixes potential race conditions)
## 0.7.0
- Add page break in ebook (pdf, epub, mobi) between chapters/articles
- Start using kramed instead of marked
- Fix display of inline math
- Switch to graceful-fs to fix EMFILE errors
- Add sharing to weibo.com
## 0.6.2
- Support generating a plugin's book info dynamically
- Improve navigation on dark theme
- Improve path normalization when parsing SUMMARY.md
## 0.6.0
- Generate header id the same as github
- Custom links can be added at top of sidebar
- Summary can now be transformed by plugins
- Support importing code snippets

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2014 FriendCode Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,51 +0,0 @@
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)
[![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).
You can publish and host books easily online using [gitbook.com](https://www.gitbook.com). A desktop editor is [also available](https://www.gitbook.com/editor).
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 [toolchain.gitbook.com](http://toolchain.gitbook.com/).
![Image](https://raw.github.com/GitbookIO/gitbook/master/preview.png)
## Getting started
GitBook can be used either on your computer for building local books or on GitBook.com for hosting them. To get started, check out [the installation instructions in the documentation](docs/setup.md).
## Usage examples
GitBook can be used to create book, public documentation, enterprise manual, thesis, research papers, etc.
You can find a [list of real-world examples](docs/examples.md) in the documentation.
## Help and Support
We're always happy to help out with your books or any other questions you might have. You can ask a question on the following contact form at [gitbook.com/contact](https://www.gitbook.com/contact) or signal an issue on [GitHub](https://github.com/GitbookIO/gitbook).
## Features
* Write using [Markdown](http://toolchain.gitbook.com/syntax/markdown.html) or [AsciiDoc](http://toolchain.gitbook.com/syntax/asciidoc.html)
* Output as a website or [ebook (pdf, epub, mobi)](http://toolchain.gitbook.com/ebook.html)
* [Multi-Languages](http://toolchain.gitbook.com/languages.html)
* [Lexicon / Glossary](http://toolchain.gitbook.com/lexicon.html)
* [Cover](http://toolchain.gitbook.com/ebook.html)
* [Variables and Templating](http://toolchain.gitbook.com/templating/)
* [Content References](http://toolchain.gitbook.com/templating/conrefs.html)
* [Plugins](http://toolchain.gitbook.com/plugins/)
* [Beautiful default theme](https://github.com/GitbookIO/theme-default)
## Publish your book
The platform [GitBook.com](https://www.gitbook.com/) is like an "Heroku for books": you can create a book on it (public, or private) and update it using **git push**.
## Licensing
GitBook is licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for the full license text.

File diff suppressed because one or more lines are too long

View File

@ -1,29 +0,0 @@
# Fix line endings in Windows. (runs before repo cloning)
init:
- git config --global core.autocrlf input
# Test against these versions of Node.js.
environment:
matrix:
- nodejs_version: "0.12"
- nodejs_version: "4.1"
# Install scripts. (runs after repo cloning)
install:
# Get the latest stable version of Node.js or io.js
- ps: Install-Product node $env:nodejs_version
# install svgexport
- npm install svgexport -g
# install modules
- npm install
# Post-install test scripts.
test_script:
# Output useful info for debugging.
- node --version
- npm --version
# run tests
- npm test
# Don't actually build.
build: off

View File

@ -1,8 +0,0 @@
#! /usr/bin/env node
/* eslint-disable no-console */
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('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"'));

View File

@ -1,24 +0,0 @@
var pkg = require('./package.json');
module.exports = {
// Documentation for GitBook is stored under "docs"
root: './docs',
title: 'GitBook Toolchain Documentation',
// Enforce use of GitBook v3
gitbook: pkg.version,
// Use the "official" theme
plugins: ['theme-official', 'sitemap'],
theme: 'official',
variables: {
version: pkg.version
},
pluginsConfig: {
sitemap: {
hostname: 'https://docs.gitbook.com'
}
}
};

View File

@ -1,26 +0,0 @@
# 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).
### What is GitBook?
GitBook is a command line tool (and Node.js library) for building beautiful books using GitHub/Git and Markdown (or AsciiDoc). . This documentation has been generated using GitBook.
GitBook can output your content as a website ([customizable](themes/README.md) and [extensibles](plugins/README.md)) or as an ebook (PDF, ePub or Mobi).
[GitBook.com](https://www.gitbook.com) is the online platform to create and host books built using the GitBook format. It offers hosting, collaboration features and an [easy-to-use editor](https://www.gitbook.com/editor).
### Help and Support
We're always happy to help out with your books or any other questions you might have. You can ask a question on the following contact form at [gitbook.com/contact](https://www.gitbook.com/contact) or signal an issue on [GitHub](https://github.com/GitbookIO/gitbook).
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.
### FAQ
Some questions are frequently asked. If you have a problem you should [check this out](faq.md) first.
### Contribute to this documentation
You can contribute to improve this documentation on [GitHub](https://github.com/GitbookIO/gitbook) by signaling issues or proposing changes.

View File

@ -1,23 +0,0 @@
{% extends template.theme %}
{% block header_nav %}
<a href="https://github.com/GitbookIO/gitbook/blob/master/docs/{{ file.path }}" target="_blank" class="btn btn-link pull-right hidden-xs">
<i class="octicon octicon-mark-github"></i> Edit on GitHub
</a>
<a href="https://github.com/GitbookIO/gitbook/blob/master/CHANGES.md" target="_blank" class="btn btn-link pull-right hidden-xs">
{{ book.version }}
</a>
{% endblock %}
{% block page %}
{{ super() }}
<hr>
<div class="btn-group btn-group-justified">
{% if page.previous and page.previous.path %}
<a class="btn" href="{{ page.previous.path|resolveFile }}"><b>Previous:</b> {{ page.previous.title }}</a>
{% endif %}
{% if page.next and page.next.path %}
<a class="btn" href="{{ page.next.path|resolveFile }}"><b>Next:</b> {{ page.next.title }}</a>
{% endif %}
</div>
{% endblock %}

View File

@ -1,65 +0,0 @@
# 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

@ -1,11 +0,0 @@
var pkg = require('../package.json');
module.exports = {
title: 'GitBook Documentation',
plugins: ['theme-official'],
theme: 'official',
variables: {
version: pkg.version
}
};

View File

@ -1,48 +0,0 @@
# Configuration
GitBook allows you to customize your book using a flexible configuration. These options are specified in a `book.json` file. For authors unfamiliar with the JSON syntax, you can validate the syntax using tools such as [JSONlint](http://jsonlint.com).
### General Settings
| Variable | Description |
| -------- | ----------- |
| `root` | Path to the root folder containing all the book's files, except `book.json`|
| `title` | Title of your book, default value is extracted from the README. On GitBook.com this field is pre-filled. |
| `description` | Description of your book, default value is extracted from the README. On GitBook.com this field is pre-filled. |
| `author` | Name of the author. On GitBook.com this field is pre-filled. |
| `isbn` | ISBN of the book |
| `language` | [ISO code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) of the book's language, default value is `en` |
| `direction` | Text's direction. Can be `rtl` or `ltr`, the default value depends on the value of `language` |
| `gitbook` | Version of GitBook that should be used. Uses the [SemVer](http://semver.org) specification and accepts conditions like `">= 3.0.0"` |
### Plugins
Plugins and their configurations are specified in the `book.json`. See [the plugins section](plugins/README.md) for more details.
| Variable | Description |
| -------- | ----------- |
| `plugins` | List of plugins to load |
| `pluginsConfig` |Configuration for plugins |
### Theme
Since version 3.0.0, GitBook can use themes. See [the theming section](themes/README.md) for more details.
| Variable | Description |
| -------- | ----------- |
| `theme` | The theme to use for the book |
### PDF Options
PDF Output can be customized using a set of options in the `book.json`:
| Variable | Description |
| -------- | ----------- |
| `pdf.pageNumbers` | Add page numbers to the bottom of every page (default is `true`) |
| `pdf.fontSize` | Base font size (default is `12`) |
| `pdf.fontFamily` | Base font family (default is `Arial`) |
| `pdf.paperSize` | Paper size, options are `'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'legal', 'letter'` (default is `a4`) |
| `pdf.margin.top` | Top margin (default is `56`) |
| `pdf.margin.bottom` | Bottom margin (default is `56`) |
| `pdf.margin.right` | Right margin (default is `62`) |
| `pdf.margin.left` | Left margin (default is `62`) |

View File

@ -1,55 +0,0 @@
# Content References
Content referencing (conref) is a convenient mechanism for reuse of content from other files or books.
### Importing local files
Importing an other file's content is really easy using the `include` tag:
```
{% include "./test.md" %}
```
### Importing file from another book
GitBook can also resolve the include path by using git:
```
{% include "git+https://github.com/GitbookIO/documentation.git/README.md#0.0.1" %}
```
The format of git url is:
```
git+https://user@hostname/project/blah.git/file#commit-ish
```
The real git url part should finish with `.git`, the filename to import is extracted after the `.git` till the fragment of the url.
The `commit-ish` can be any tag, sha, or branch which can be supplied as an argument to `git checkout`. The default is `master`.
### Inheritance
Template inheritance is a way to make it easy to reuse templates. When writing a template, you can define "blocks" that child templates can override. The inheritance chain can be as long as you like.
`block` defines a section on the template and identifies it with a name. Base templates can specify blocks and child templates can override them with new content.
```
{% extends "./mypage.md" %}
{% block pageContent %}
# This is my page content
{% endblock %}
```
In the file `mypage.md`, you should specify the blocks that can be extent:
```
{% block pageContent %}
This is the default content
{% endblock %}
# License
{% import "./LICENSE" %}
```

View File

@ -1,21 +0,0 @@
# Cover
Covers are used for all the ebook formats, and to make books more elegant on GitBook.com.
To provide a cover, place a **cover.jpg** file at the root directory of your book. Adding a **cover_small.jpg** will specify a smaller version of the cover. The cover should be a **JPEG** file.
### Best Sizes
| | Big | Small |
|:-------------------:|:-----------:|:-----------------:|
| **File** | `cover.jpg` | `cover_small.jpg` |
| **Size(in pixels)** | 1800x2360 | 200x262 |
### Guidelines
A good cover respects the following guidelines:
* No border
* Clearly visible book title
* Any important text should be visible in the small version

View File

@ -1,29 +0,0 @@
# 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,27 +0,0 @@
---
description: Real world examples of content published using GitBook.
---
# Examples
More than 50,000 books have been published on [GitBook.com](https://www.gitbook.com/explore).
### Books
- [Front-end Handbook](https://www.gitbook.com/book/frontendmasters/front-end-handbook/details) by [Cody Lindley](http://codylindley.com)
- [How to make an Operating System](https://www.gitbook.com/book/samypesse/how-to-create-an-operating-system/details) by [@SamyPesse](https://github.com/SamyPesse)
- [Building Web Apps with Go](https://www.gitbook.com/book/codegangsta/building-web-apps-with-go/details) by [@codegangsta](https://github.com/codegangsta)
- [Django Girls Tutorial](http://tutorial.djangogirls.org/en/index.html) by [Django Girls](https://djangogirls.org)
- [Linux Inside](https://www.gitbook.com/book/0xax/linux-insides/details) by [0xAX](https://twitter.com/0xAX)
- [Learn Javascript](https://www.gitbook.com/book/gitbookio/javascript/details) by [GitBook](https://twitter.com/GitbookIO)
### Research Papers
- [TowCenter Collection](https://www.gitbook.com/@towcenter) by [Columbia Journalism School](http://www.journalism.columbia.edu/)
- [Block Relaxation Algorithms in Statistics](https://www.gitbook.com/@jandeleeuw) by Jan de Leeuw
### Documentation
- [DuckDuckHack Documentation](http://docs.duckduckhack.com) by [DuckDuckGo](https://duckduckgo.com/about)
- This documentation

View File

@ -1,55 +0,0 @@
# 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?
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.
#### What can I use to edit my content?
Any text editor should work! But we advise using the [GitBook Editor](https://www.gitbook.com/editor). [GitBook.com](https://www.gitbook.com) also provides a web version of this editor.
---
#### 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?
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.
#### Can I create a GitBook in a sub-directory of my repository?
Yes, GitBooks can be created in [sub-directories](structure.md#subdirectory). GitBook.com and the CLI also looks by default in a serie of [folders](structure.md).
#### Does GitBook supports RTL languages?
Yes, GitBook automatically detect the direction in your pages (`rtl` or `ltr`) and adjust the layout accordingly. The direction can also be specified globally in the [book.json](config.md).
---
#### Does GitBook support Math equations?
GitBook supports math equations and TeX thanks to plugins. There are currently 2 official plugins to display math: [mathjax](https://plugins.gitbook.com/plugin/mathjax) and [katex](https://plugins.gitbook.com/plugin/katex).
#### Can I customize/theme the output?
Yes, both the website and ebook outputs can be customized using [themes](themes/README.md).
#### Can I add interactive content (videos, etc)?
GitBook is very [extensible](plugins/README.md). You can use [existing plugins](https://plugins.gitbook.com) or create your own!

View File

@ -1,17 +0,0 @@
# Multi-Languages
GitBook supports building books written in multiple languages. Each language should be a sub-directory following the normal GitBook format, and a file named `LANGS.md` should be present at the root of the repository with the following format:
```markdown
# Languages
* [English](en/)
* [French](fr/)
* [Español](es/)
```
### Configuration for each language
When a language book (ex: `en`) has a `book.json`, its configuration will extend the main configuration.
The only exception is plugins, plugins are specified globally, and language specific plugins cannot be specified.

View File

@ -1,14 +0,0 @@
# Glossary
Allows you to specify terms and their respective definitions to be displayed as annotations. Based on those terms, GitBook will automatically build an index and highlight those terms in pages.
The `GLOSSARY.md` format is very simple :
```markdown
# Term
Definition for this term
# Another term
With it's definition, this can contain bold text
and all other kinds of inline markup ...
```

View File

@ -1,104 +0,0 @@
# Pages and Summary
### Summary
GitBook uses a `SUMMARY.md` file to define the structure of chapters and subchapters of the book. The `SUMMARY.md` file is used to generate the book's table of contents.
The format of `SUMMARY.md` is just a list of links. The link's title is used as the chapter's title, and the link's target is a path to that chapter's file.
Adding a nested list to a parent chapter will create subchapters.
##### Simple example
```markdown
# Summary
* [Part I](part1/README.md)
* [Writing is nice](part1/writing.md)
* [GitBook is nice](part1/gitbook.md)
* [Part II](part2/README.md)
* [We love feedback](part2/feedback_please.md)
* [Better tools for authors](part2/better_tools.md)
```
Each chapter has a dedicated page (`part#/README.md`) and is split into subchapters.
##### 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
# Summary
### Part I
* [Writing is nice](part1/writing.md)
* [GitBook is nice](part1/gitbook.md)
### Part II
* [We love feedback](part2/feedback_please.md)
* [Better tools for authors](part2/better_tools.md)
----
* [Last part without title](part3/title.md)
```
Parts are just groups of chapters and do not have dedicated pages, but according to the theme, it will show in the navigation.
### Pages
#### Markdown syntax
Most of the files for GitBook use the Markdown syntax by default. GitBook infers your pages's structure from it. The syntax used is similar to the [GitHub Flavored Markdown syntax](https://guides.github.com/features/mastering-markdown/). One can also opt for the [AsciiDoc syntax](asciidoc.md).
##### Example of a chapter file
``` markdown
# Title of the chapter
This is a great introduction.
## Section 1
Markdown will dictates _most_ of your **book's structure**
## Section 2
...
```
#### Front Matter
Pages can contain an optional front matter. It can be used to define the page's description. The front matter must be the first thing in the file and must take the form of valid YAML set between triple-dashed lines. Here is a basic example:
```yaml
---
description: This is a short description of my page
---
# The content of my page
...
```
The front matter can define variables of your own, they will be added to the [page variable](templating/variables.md) so you can use them in your templating.

View File

@ -1,26 +0,0 @@
# Plugins
Plugins are the best way to extend GitBook functionalities (ebook and website). There exist plugins to do a lot of things: bring math formulas display support, track visits using Google Analytic, etc.
### How to find plugins?
Plugins can be easily searched on [plugins.gitbook.com](https://plugins.gitbook.com).
### How to install a plugin?
Once you find a plugin that you want to install, you need to add it to your `book.json`:
```
{
"plugins": ["myPlugin", "anotherPlugin"]
}
```
You can also specify a specific version using: `"myPlugin@0.3.1"`. By default GitBook will resolve the latest version of the plugin compatbile with the current GitBook version.
Plugins are automatically installed on [GitBook.com](https://www.gitbook.com). Locally, run `gitbook install` to install and prepare all plugins for your books.
### Configuring plugins
PLugins specific configurations are stored in `pluginsConfig`. You have to refer to the documentation of the plugin itself for details about the available options.

View File

@ -1,28 +0,0 @@
# Plugins
Plugins are the best way to extend GitBook functionalities (ebook and website). There exist plugins to do a lot of things: bring math formulas display support, track visits using Google Analytic, etc.
### How to find plugins?
Plugins can be easily searched on [plugins.gitbook.com](https://plugins.gitbook.com).
### How to install a plugin?
Once you find a plugin that you want to install, you need to add it to your `book.json`:
```
{
"plugins": ["myPlugin", "anotherPlugin"]
}
```
You can also specify a specific version using: `"myPlugin@0.3.1"`. By default GitBook will resolve the latest version of the plugin compatbile with the current GitBook version.
### GitBook.com
Plugins are automatically installed on [GitBook.com](https://www.gitbook.com). Locally, run `gitbook install` to install and prepare all plugins for your books.
### Configuring plugins
Plugins specific configurations are stored in `pluginsConfig`. You have to refer to the documentation of the plugin itself for details about the available options.

View File

@ -1,97 +0,0 @@
# Context and APIs
GitBooks provides different APIs and contexts to plugins. These APIs can vary according to the GitBook version being used, your plugin should specify the `engines.gitbook` field in `package.json` accordingly.
#### Book instance
The `Book` class is the central point of GitBook, it centralize all access read methods. This class is defined in [book.js](https://github.com/GitbookIO/gitbook/blob/master/lib/book.js).
```js
// Read configuration from book.json
var value = book.config.get('title', 'Default Value');
// Resolve a filename to an absolute path
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
The `Output` class represent the output/write process.
```js
// Return root folder for the output
var root = output.root();
// Resolve a file in the output folder
var filepath = output.resolve('myimage.png');
// Convert a filename to an URL (returns a path to an html file)
var fileurl = output.toURL('mychapter/README.md');
// Write a file in the output folder
output.write('hello.txt', 'Hello World')
.then(function() { ... });
// Copy a file to the output folder
output.copyFile('./myfile.jpg', 'cover.jpg')
.then(function() { ... });
// Verify that a file exists
output.hasFile('hello.txt')
.then(function(exists) { ... });
```
#### Page instance
A page instance represent the current parsed page.
```js
// Title of the page (from SUMMARY)
page.title
// Content of the page (Markdown/Asciidoc/HTML according to the stage)
page.content
// Relative path in the book
page.path
// Absolute path to the file
page.rawPath
// Type of parser used for this file
page.type ('markdown' or 'asciidoc')
```
#### Context for Blocks and Filters
Blocks and filters have access to the same context, this context is bind to the template engine execution:
```js
{
// Current templating syntax
"ctx": {
// For example, after a {% set message = "hello" %}
"message": "hello"
},
// Book instance
"book" <Book>,
// Output instance
"output": <Output>
}
```
For example a filter or block function can access the current book using: `this.book`.
#### Context for Hooks
Hooks only have access to the `<Book>` instance using `this.book`.

View File

@ -1,62 +0,0 @@
# Extend Blocks
Extending templating blocks is the best way to provide extra functionalities to authors.
The most common usage is to process the content within some tags at runtime. It's like [filters](./filters.md), but on steroids because you aren't confined to a single expression.
### Defining a new block
Blocks are defined by the plugin, blocks is a map of name associated with a block descriptor. The block descriptor needs to contain at least a `process` method.
```js
module.exports = {
blocks: {
tag1: {
process: function(block) {
return "Hello "+block.body+", How are you?";
}
}
}
};
```
The `process` should return the html content that will replace the tag. Refer to [Context and APIs](./api.md) to learn more about `this` and GitBook API.
### Handling block arguments
Arguments can be passed to blocks:
```
{% tag1 "argument 1", "argument 2", name="Test" %}
This is the body of the block.
{% endtag1 %}
```
And arguments are easily accessible in the `process` method:
```js
module.exports = {
blocks: {
tag1: {
process: function(block) {
// block.args equals ["argument 1", "argument 2"]
// block.kwargs equals { "name": "Test" }
}
}
}
};
```
### Handling sub-blocks
A defined block can be parsed into different sub-blocks, for example let's consider the source:
```
{% myTag %}
Main body
{% subblock1 %}
Body of sub-block 1
{% subblock 2 %}
Body of sub-block 1
{% endmyTag %}
```

View File

@ -1,74 +0,0 @@
# Create and publish a plugin
A GitBook plugin is a node package published on NPM that follow a defined convention.
## Structure
#### package.json
The `package.json` is a manifest format for describing **Node.js modules**. GitBook plugins are built on top of Node modules. It declares dependencies, version, ownership, and other information required to run a plugin in GitBook. This document describes the schema in detail.
A plugin manifest `package.json` can also contain details about the required configuration. The configuration schema is defined in the `gitbook` field of the `package.json` (This field follow the [JSON-Schema](http://json-schema.org) guidelines):
```js
{
"name": "gitbook-plugin-mytest",
"version": "0.0.1",
"description": "This is my first GitBook plugin",
"engines": {
"gitbook": ">1.x.x"
},
"gitbook": {
"properties": {
"myConfigKey": {
"type": "string",
"default": "it's the default value",
"description": "It defines my awesome config!"
}
}
}
}
```
You can learn more about `package.json` from the [NPM documentation](https://docs.npmjs.com/files/package.json).
The **package name** must begin with `gitbook-plugin-` and the **package engines** should contains `gitbook`.
#### index.js
The `index.js` is main entry point of your plugin runtime:
```js
module.exports = {
// Map of hooks
hooks: {},
// Map of new blocks
blocks: {},
// Map of new filters
filters: {}
};
```
## Publish your plugin
GitBook plugins can be published on [NPM](https://www.npmjs.com).
To publish a new plugin, you need to create an account on [npmjs.com](https://www.npmjs.com) then publish it from the command line:
```
$ npm publish
```
## Private plugins
Private plugins can be hosted on GitHub and included using `git` urls:
```
{
"plugins": [
"myplugin@git+https://github.com/MyCompany/mygitbookplugin.git#1.0.0"
]
}
```

View File

@ -1,57 +0,0 @@
# Extend Filters
Filters are essentially functions that can be applied to variables. They are called with a pipe operator (`|`) and can take arguments.
```
{{ foo | title }}
{{ foo | join(",") }}
{{ foo | replace("foo", "bar") | capitalize }}
```
### Defining a new filter
Plugins can extend filters by defining custom functions in their entry point under the `filters` scope.
A filter function takes as first argument the content to filter, and should return the new content.
Refer to [Context and APIs](./api.md) to learn more about `this` and GitBook API.
```js
module.exports = {
filters: {
hello: function(name) {
return 'Hello '+name;
}
}
};
```
The filter `hello` can then be used in the book:
```
{{ "Aaron"|hello }}, how are you?
```
### Handling block arguments
Arguments can be passed to filters:
```
Hello {{ "Samy"|fullName("Pesse", man=true}} }}
```
Arguments are passed to the function, named-arguments are passed as a last argument (object).
```js
module.exports = {
filters: {
fullName: function(firstName, lastName, kwargs) {
var name = firstName + ' ' + lastName;
if (kwargs.man) name = "Mr" + name;
else name = "Mrs" + name;
return name;
}
}
};
```

View File

@ -1,90 +0,0 @@
# Hooks
Hooks is a method of augmenting or altering the behavior of the process, with custom callbacks.
### List of hooks
### Relative to the global pipeline
| Name | Description | Arguments |
| ---- | ----------- | --------- |
| `init` | Called after parsing the book, before generating output and pages. | None |
| `finish:before` | Called after generating the pages, before copying assets, cover, ... | None |
| `finish` | Called after everything else. | None |
### Relative to the page pipeline
> It is recommended using [templating](./templating.md) to extend page parsing.
| Name | Description | Arguments |
| ---- | ----------- | --------- |
| `page:before` | Called before running the templating engine on the page | Page Object |
| `page` | Called before outputting and indexing the page. | Page Object |
##### Page Object
```js
{
// Parser named
"type": "markdown",
// File Path relative to book root
"path": "page.md",
// Absolute file path
"rawpath": "/usr/...",
// Title of the page in the SUMMARY
"title": "",
// Content of the page
// Markdown/Asciidoc in "page:before"
// HTML in "page"
"content": "# Hello"
}
```
##### Example to add a title
In the `page:before` hook, `page.content` is the markdown/asciidoc content.
```js
{
"page:before": function(page) {
page.content = "# Title\n" +page.content;
return page;
}
}
```
##### Example to replace some html
In the `page` hook, `page.content` is the HTML generated from the markdown/asciidoc conversion.
```js
{
"page": function(page) {
page.content = page.content.replace("<b>", "<strong>")
.replace("</b>", "</strong>");
return page;
}
}
```
### Asynchronous Operations
Hooks callbacks can be asynchronous and return promises.
Example:
```js
{
"init": function() {
return writeSomeFile()
.then(function() {
return writeAnotherFile();
});
}
}
```

View File

@ -1,22 +0,0 @@
# Testing your plugin
### Testing your plugin locally
Testing your plugin on your book before plushing it is possible using [npm link](https://docs.npmjs.com/cli/link).
In the plugin's folder, run:
```
$ npm link
```
The nin your book's folder:
```
$ npm link gitbook-plugin-<plugin's name>
```
### Unit testing on Travis
[gitbook-tester](https://github.com/todvora/gitbook-tester) makes it easy to write **Node.js/Mocha** unit tests for your plugins. Using [Travis.org](https://travis.org), tests can be run on each commits/tags.

View File

@ -1,69 +0,0 @@
# Setup and Installation of GitBook
Getting GitBook installed and ready-to-go should only take a few minutes.
### GitBook.com
[GitBook.com](https://www.gitbook.com) is an easy to use solution to write, publish and host books. It is the easiest solution for publishing your content and collaborating on it.
It integrates well with the [GitBook Editor](https://www.gitbook.com/editor).
### Local Installation
##### Requirements
Installing GitBook is easy and straightforward. Your system just needs to meet these two requirements:
* NodeJS (v4.0.0 and above are adviced)
* Windows, Linux, Unix, or Mac OS X
##### Install with NPM
The best way to install GitBook is via **NPM**. At the terminal prompt, simply run the following command to install GitBook:
```
$ npm install gitbook-cli -g
```
`gitbook-cli` is an utility to install and use multiple versions of GitBook on the same system. It will automatically install the required version of GitBook to build a book.
##### Create a book
GitBook can setup a boilerplate book:
```
$ gitbook init
```
If you wish to create the book into a new directory, you can do so by running `gitbook init ./directory`
Preview and serve your book using:
```
$ gitbook serve
```
Or build the static website using:
```
$ gitbook build
```
##### Install pre-releases
`gitbook-cli` makes it easy to download and install other versions of GitBook to test with your book:
```
$ gitbook fetch beta
```
Use `gitbook ls-remote` to list remote versions available for install.
##### Debugging
You can use the options `--log=debug` and `--debug` to get better error messages (with stack trace). For example:
```
$ gitbook build ./ --log=debug --debug
```

View File

@ -1,66 +0,0 @@
# Directory Structure
GitBook uses a simple directory structure. All Markdown/Asciidoc files listed in the [SUMMARY](pages.md) will be transformed as HTML. Multi-Lingual books have a slightly [different structure](languages.md).
A basic GitBook usually looks something like this:
```
.
├── book.json
├── README.md
├── SUMMARY.md
├── chapter-1/
| ├── README.md
| └── something.md
└── chapter-2/
├── README.md
└── something.md
```
An overview of what each of these does:
| File | Description |
| -------- | ----------- |
| `book.json` | Stores [configuration](config.md) data (__optional__) |
| `README.md` | Preface / Introduction for your book (**required**) |
| `SUMMARY.md` | Table of Contents (See [Pages](pages.md)) (__optional__) |
| `GLOSSARY.md` | Lexicon / List of terms to annotate (See [Glossary](lexicon.md)) (__optional__) |
### Static files and Images
A static file is a file that is not listed in the `SUMMARY.md`. All static files, unless [ignored](#ignore), are copied to the output.
### Ignoring files & folders {#ignore}
GitBook will read the `.gitignore`, `.bookignore` and `.ignore` files to get a list of files and folders to skip.
The format inside those files, follows the same convention as `.gitignore`:
```
# This is a comment
# Ignore the file test.md
test.md
# Ignore everything in the directory "bin"
bin/*
```
### Project integration with subdirectory {#subdirectory}
For software projects, you can use a subdirectory (like `docs/`) to store the book for the project's documentation. You can configure the [`root` option](config.md) to indicate the folder where GitBook can find the book's files:
```
.
├── book.json
└── docs/
├── README.md
└── SUMMARY.md
```
With `book.json` containing:
```
{
"root": "./docs"
}
```

View File

@ -1,65 +0,0 @@
# 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

@ -1,223 +0,0 @@
# 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

@ -1,90 +0,0 @@
# Templating
GitBook uses the Nunjucks templating language to process pages and theme's templates.
The Nunjucks syntax is very similar to **Jinja2** or **Liquid**.
### Variables
A variable looks up a value from the template context. If you wanted to simply display a variable, you would do:
```twig
{{ username }}
```
This looks up username from the context and displays it. Variable names can have dots in them which lookup properties, just like javascript. You can also use the square bracket syntax.
```twig
{{ foo.bar }}
{{ foo["bar"] }}
```
If a value is undefined, nothing is displayed. The following all output nothing if foo is undefined: `{{ foo }}`, `{{ foo.bar }}`, `{{ foo.bar.baz }}`.
GitBook provides a set of [context variables](variables.md).
### Filters
Filters are essentially functions that can be applied to variables. They are called with a pipe operator (`|`) and can take arguments.
```twig
{{ foo | title }}
{{ foo | join(",") }}
{{ foo | replace("foo", "bar") | capitalize }}
```
The third example shows how you can chain filters. It would display "Bar", by first replacing "foo" with "bar" and then capitalizing it.
### Tags
##### if
`if` tests a condition and lets you selectively display content. It behaves exactly as javascript's if behaves.
```twig
{% if variable %}
It is true
{% endif %}
```
If variable is defined and evaluates to true, "It is true" will be displayed. Otherwise, nothing will be.
You can specify alternate conditions with elif and else:
```twig
{% if hungry %}
I am hungry
{% elif tired %}
I am tired
{% else %}
I am good!
{% endif %}
```
##### for
`for` iterates over arrays and dictionaries.
```twig
# Chapters about GitBook
{% for article in glossary.terms['gitbook'].articles %}
* [{{ article.title }}]({{ article.path }})
{% endfor %}
```
##### set
`set` lets you create/modify a variable.
```twig
{% set softwareVersion = "1.0.0" %}
Current version is {{ softwareVersion }}.
[Download it](website.com/download/{{ softwareVersion }})
```
##### include and block
Inclusion and inheritance is detailled in the [ConRefs](conrefs.md) section.

View File

@ -1,99 +0,0 @@
# Templating
GitBook uses the [Nunjucks templating language](https://mozilla.github.io/nunjucks/) to process pages and theme's templates.
The Nunjucks syntax is very similar to **Jinja2** or **Liquid**. Its syntax uses surrounding braces `{ }` to mark content that needs to be processed.
### Variables
A variable looks up a value from the template context. If you wanted to simply display a variable, you would use the `{{ variable }}` syntax. For example :
```twig
My name is {{ name }}, nice to meet you
```
This looks up username from the context and displays it. Variable names can have dots in them which lookup properties, just like JavaScript. You can also use the square bracket syntax.
```twig
{{ foo.bar }}
{{ foo["bar"] }}
```
If a value is undefined, nothing is displayed. The following all output nothing if foo is undefined: `{{ foo }}`, `{{ foo.bar }}`, `{{ foo.bar.baz }}`.
GitBook provides a set of [predefined variables](variables.md) from the context.
### Filters
Filters are essentially functions that can be applied to variables. They are called with a pipe operator (`|`) and can take arguments.
```twig
{{ foo | title }}
{{ foo | join(",") }}
{{ foo | replace("foo", "bar") | capitalize }}
```
The third example shows how you can chain filters. It would display "Bar", by first replacing "foo" with "bar" and then capitalizing it.
### Tags
##### if
`if` tests a condition and lets you selectively display content. It behaves exactly as JavaScript's `if` behaves.
```twig
{% if variable %}
It is true
{% endif %}
```
If variable is defined and evaluates to true, "It is true" will be displayed. Otherwise, nothing will be.
You can specify alternate conditions with `elif` and `else`:
```twig
{% if hungry %}
I am hungry
{% elif tired %}
I am tired
{% else %}
I am good!
{% endif %}
```
##### for
`for` iterates over arrays and dictionaries.
```twig
# Chapters about GitBook
{% for article in glossary.terms['gitbook'].articles %}
* [{{ article.title }}]({{ article.path }})
{% endfor %}
```
##### set
`set` lets you create/modify a variable.
```twig
{% set softwareVersion = "1.0.0" %}
Current version is {{ softwareVersion }}.
[Download it](website.com/download/{{ softwareVersion }})
```
##### include and block
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

@ -1,18 +0,0 @@
# 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

@ -1,55 +0,0 @@
# Content References
Content referencing (conref) is a convenient mechanism to reuse content from other files or books.
### Importing local files
Importing an other file's content is easy using the `include` tag:
```
{% include "./test.md" %}
```
### Importing file from another book
GitBook can also resolve the include path by using git:
```
{% include "git+https://github.com/GitbookIO/documentation.git/README.md#0.0.1" %}
```
The format of git url is:
```
git+https://user@hostname/owner/project.git/file#commit-ish
```
The real git url part should finish with `.git`, the filename to import is extracted after the `.git` till the fragment of the url.
The `commit-ish` can be any tag, sha, or branch which can be supplied as an argument to `git checkout`. The default is `master`.
### Inheritance
Template inheritance is a way to make it easy to reuse templates. When writing a template, you can define "blocks" that child templates can override. The inheritance chain can be as long as you like.
`block` defines a section on the template and identifies it with a name. Base templates can specify blocks and child templates can override them with new content.
```
{% extends "./mypage.md" %}
{% block pageContent %}
# This is my page content
{% endblock %}
```
In the file `mypage.md`, you should specify the blocks that can be extended:
```
{% block pageContent %}
This is the default content
{% endblock %}
# License
{% import "./LICENSE" %}
```

View File

@ -1,73 +0,0 @@
# Variables
The following is a reference of the available data during book's parsing and theme generation.
### Global Variables
| Variable | Description |
| -------- | ----------- |
| `book` | Book-wide information + configuration settings from `book.json`. See below for details. |
| `gitbook` | GitBook specific information |
| `page` | Current page specific information |
| `file` | File associated with the current page specific information |
| `summary` | Information about the table of contents |
| `languages` | List of languages for multi-lingual books |
| `output` | Information about the output generator |
| `config` | Dump of the `book.json` |
### Book Variables
| Variable | Description |
| -------- | ----------- |
| `book.[CONFIGURATION_DATA]` | All the `variables` set via the `book.json` are available through the book variable. |
| `book.language` | Current language for a multilingual book |
### GitBook Variables
| Variable | Description |
| -------- | ----------- |
| `gitbook.time` | The current time (when you run the `gitbook` command) . |
| `gitbook.version` | Version of GitBook used to generate the book |
### File Variables
| Variable | Description |
| -------- | ----------- |
| `file.path` | The path to the raw page |
| `file.mtime` | Modified Time. Last time the file was modified |
| `file.type` | The name of the parser used to compile this file (ex: `markdown`, `asciidoc`, etc) |
#### Page Variables
| Variable | Description |
| -------- | ----------- |
| `page.title` | Title of the page |
| `page.previous` | Previous page in the Table of Contents (can be `null`) |
| `page.next` | Next page in the Table of Contents (can be `null`) |
| `page.dir` | Text direction, based on configuration or detected from content (`rtl` or `ltr`) |
#### Table of Contents Variables
| Variable | Description |
| -------- | ----------- |
| `summary.parts` | List of sections in the Table of Contents |
The whole table of contents (`SUMMARY.md`) can be accessed:
`summary.parts[0].articles[0].title` will return the title of the first article.
#### Multi-lingual book Variable
| Variable | Description |
| -------- | ----------- |
| `languages.list` | List of languages for this book |
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

@ -1,26 +0,0 @@
# Theming
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.
### 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.
| Folder | Description |
| -------- | ----------- |
| `_layouts` | Main folder containing all the templates |
| `_layouts/website/page.html` | Template for a normal page |
| `_layouts/ebook/page.html` | Template for a normal page during ebook generation (PDF< ePub, Mobi) |
### Extend/Customize theme in a book
Authors can extend the templates of a theme directly from the book source (without creating an external theme). Templates will be resolved in the `_layouts` folder of the book first, then in
### Publish a theme
Themes are published as plugins ([see related docs](plugins.md)) with a `theme-` prefix. For example the theme `awesome` will be loaded from `theme-awesome` plugin, and then from `gitbook-plugin-theme-awesome` NPM package.

View File

@ -1,24 +0,0 @@
# Theming
Since version 3.0.0, GitBook can be easily themed. Books are using by default the [theme-default](https://github.com/GitbookIO/theme-default).
> **Caution**: Custom theming can block some plugins from working correctly.
### Structure of a 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 |
| -------- | ----------- |
| `_layouts` | Main folder containing all the templates |
| `_layouts/website/page.html` | Template for a normal page |
| `_layouts/ebook/page.html` | Template for a normal page during ebook generation (PDF< ePub, Mobi) |
### Extend/Customize theme in a book
Authors can extend the templates of a theme directly from the book source (without creating an external theme). Templates will be resolved in the `_layouts` folder of the book first, then in
### Publish a theme
Themes are published as plugins ([see related docs](../plugins/README.md)) with a `theme-` prefix. For example the theme `awesome` will be loaded from the `theme-awesome` plugin, and then from the `gitbook-plugin-theme-awesome` NPM package.

View File

@ -1,64 +0,0 @@
# Variables
The following is a reference of the available data during book's parsing and theme generation.
### Global Variables
| Variable | Description |
| -------- | ----------- |
| `book` | Bookwide information + configuration settings from `book.json`. See below for details. |
| `gitbook` | GitBook specific information |
| `page` | Current page specific information |
| `file` | File associated with the current page specific information |
| `summary` | Information about the table of contents |
| `languages` | List of languages for multi-lingual books |
| `config` | Dump of the `book.json` |
### Book Variables
| Variable | Description |
| -------- | ----------- |
| `book.[CONFIGURATION_DATA]` | All the `variables` set via the `book.json` are available through the book variable. |
| `book.language` | Current language for a multilingual book |
### GitBook Variables
| Variable | Description |
| -------- | ----------- |
| `gitbook.time` | The current time (when you run the `gitbook` command). |
| `gitbook.version` | Version of GitBook used to generate the book |
### File Variables
| Variable | Description |
| -------- | ----------- |
| `file.path` | The path to the raw page |
| `file.mtime` | Modified Time, Time when file data last modified |
| `file.type` | The name of the parser used to compile this file (ex: `markdown`, `asciidoc`, etc) |
#### Page Variables
| Variable | Description |
| -------- | ----------- |
| `page.title` | Title of the page |
| `page.previous` | Previous page in the Table of Contents (can be `null`) |
| `page.next` | Next page in the Table of Contents (can be `null`) |
| `page.dir` | Text direction, based on configuration or detected from content (`rtl` or `ltr`) |
#### Table of Contents Variables
| Variable | Description |
| -------- | ----------- |
| `summary.parts` | List of sections in the Table of Contents |
Thw whole table of contents (`SUMMARY.md`) can be accessed:
`summary.parts[0].articles[0].title` will return the title of the first article.
#### Multi-lingual book Variable
| Variable | Description |
| -------- | ----------- |
| `languages.list` | List of languages for this book |
Languages are defined by `{ id: 'en', title: 'English' }`.

View File

@ -1,12 +0,0 @@
require(["gitbook"], function(gitbook) {
gitbook.events.bind("page.change", function() {
// For Anker tag link
if (location.hash) {
// deley for other event
setTimeout(function(){
// wait for loading
document.location = location.hash;
}, 300);
}
});
});

View File

@ -1,151 +0,0 @@
require(["gitbook", "lodash", "jQuery"], function(gitbook, _, $) {
var fontState;
var THEMES = {
"white": 0,
"sepia": 1,
"night": 2
};
var FAMILY = {
"serif": 0,
"sans": 1
};
// Save current font settings
function saveFontSettings() {
gitbook.storage.set("fontState", fontState);
update();
}
// Increase font size
function enlargeFontSize(e) {
e.preventDefault();
if (fontState.size >= 4) return;
fontState.size++;
saveFontSettings();
};
// Decrease font size
function reduceFontSize(e) {
e.preventDefault();
if (fontState.size <= 0) return;
fontState.size--;
saveFontSettings();
};
// Change font family
function changeFontFamily(index, e) {
e.preventDefault();
fontState.family = index;
saveFontSettings();
};
// Change type of color
function changeColorTheme(index, e) {
e.preventDefault();
var $book = $(".book");
if (fontState.theme !== 0)
$book.removeClass("color-theme-"+fontState.theme);
fontState.theme = index;
if (fontState.theme !== 0)
$book.addClass("color-theme-"+fontState.theme);
saveFontSettings();
};
function update() {
var $book = gitbook.state.$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);
}
};
function init(config) {
var $bookBody, $book;
//Find DOM elements.
$book = gitbook.state.$book;
$bookBody = $book.find(".book-body");
// Instantiate font state object
fontState = gitbook.storage.get("fontState", {
size: config.size || 2,
family: FAMILY[config.family || "sans"],
theme: THEMES[config.theme || "white"]
});
update();
};
gitbook.events.bind("start", function(e, config) {
var opts = config.fontsettings;
// Create buttons in toolbar
gitbook.toolbar.createButton({
icon: 'fa fa-font',
label: 'Font Settings',
className: 'font-settings',
dropdown: [
[
{
text: 'A',
className: 'font-reduce',
onClick: reduceFontSize
},
{
text: 'A',
className: 'font-enlarge',
onClick: enlargeFontSize
}
],
[
{
text: 'Serif',
onClick: _.partial(changeFontFamily, 0)
},
{
text: 'Sans',
onClick: _.partial(changeFontFamily, 1)
}
],
[
{
text: 'White',
onClick: _.partial(changeColorTheme, 0)
},
{
text: 'Sepia',
onClick: _.partial(changeColorTheme, 1)
},
{
text: 'Night',
onClick: _.partial(changeColorTheme, 2)
}
]
]
});
// Init current settings
init(opts);
});
});

View File

@ -1,11 +0,0 @@
(function() {
var newEl = document.createElement('script'),
firstScriptTag = document.getElementsByTagName('script')[0];
if (firstScriptTag) {
newEl.async = 1;
newEl.src = '//' + window.location.hostname + ':35729/livereload.js';
firstScriptTag.parentNode.insertBefore(newEl, firstScriptTag);
}
})();

View File

@ -1,231 +0,0 @@
/**
* prism.js Coy theme for JavaScript, CoffeeScript, CSS and HTML
* Based on https://github.com/tshedor/workshop-wp-theme (Example: http://workshop.kansan.com/category/sessions/basics or http://workshop.timshedor.com/category/sessions/basics);
* @author Tim Shedor
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
/* Code blocks */
pre[class*="language-"] {
position: relative;
margin: .5em 0;
-webkit-box-shadow: -1px 0px 0px 0px #358ccb, 0px 0px 0px 1px #dfdfdf;
-moz-box-shadow: -1px 0px 0px 0px #358ccb, 0px 0px 0px 1px #dfdfdf;
box-shadow: -1px 0px 0px 0px #358ccb, 0px 0px 0px 1px #dfdfdf;
border-left: 10px solid #358ccb;
background-color: #fdfdfd;
background-image: -webkit-linear-gradient(transparent 50%, rgba(69, 142, 209, 0.04) 50%);
background-image: -moz-linear-gradient(transparent 50%, rgba(69, 142, 209, 0.04) 50%);
background-image: -ms-linear-gradient(transparent 50%, rgba(69, 142, 209, 0.04) 50%);
background-image: -o-linear-gradient(transparent 50%, rgba(69, 142, 209, 0.04) 50%);
background-image: linear-gradient(transparent 50%, rgba(69, 142, 209, 0.04) 50%);
background-size: 3em 3em;
background-origin: content-box;
overflow: visible;
max-height: 30em;
}
code[class*="language"] {
max-height: inherit;
height: 100%;
padding: 0 1em;
display: block;
overflow: auto;
}
/* Margin bottom to accomodate shadow */
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background-color: #fdfdfd;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
margin-bottom: 1em;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
position: relative;
padding: .2em;
-webkit-border-radius: 0.3em;
-moz-border-radius: 0.3em;
-ms-border-radius: 0.3em;
-o-border-radius: 0.3em;
border-radius: 0.3em;
color: #c92c2c;
border: 1px solid rgba(0, 0, 0, 0.1);
}
pre[class*="language-"]:before,
pre[class*="language-"]:after {
content: '';
z-index: -2;
display: block;
position: absolute;
bottom: 0.75em;
left: 0.18em;
width: 40%;
height: 20%;
-webkit-box-shadow: 0px 13px 8px #979797;
-moz-box-shadow: 0px 13px 8px #979797;
box-shadow: 0px 13px 8px #979797;
-webkit-transform: rotate(-2deg);
-moz-transform: rotate(-2deg);
-ms-transform: rotate(-2deg);
-o-transform: rotate(-2deg);
transform: rotate(-2deg);
}
:not(pre) > code[class*="language-"]:after,
pre[class*="language-"]:after {
right: 0.75em;
left: auto;
-webkit-transform: rotate(2deg);
-moz-transform: rotate(2deg);
-ms-transform: rotate(2deg);
-o-transform: rotate(2deg);
transform: rotate(2deg);
}
.token.comment,
.token.block-comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #7D8B99;
}
.token.punctuation {
color: #5F6364;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.function-name,
.token.constant,
.token.symbol,
.token.deleted {
color: #c92c2c;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.function,
.token.builtin,
.token.inserted {
color: #2f9c0a;
}
.token.operator,
.token.entity,
.token.url,
.token.variable {
color: #a67f59;
background: rgba(255, 255, 255, 0.5);
}
.token.atrule,
.token.attr-value,
.token.keyword,
.token.class-name {
color: #1990b8;
}
.token.regex,
.token.important {
color: #e90;
}
.language-css .token.string,
.style .token.string {
color: #a67f59;
background: rgba(255, 255, 255, 0.5);
}
.token.important {
font-weight: normal;
}
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
.namespace {
opacity: .7;
}
@media screen and (max-width: 767px) {
pre[class*="language-"]:before,
pre[class*="language-"]:after {
bottom: 14px;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
}
/* Plugin styles */
.token.tab:not(:empty):before,
.token.cr:before,
.token.lf:before {
color: #e0d7d1;
}
/* Plugin styles: Line Numbers */
pre[class*="language-"].line-numbers {
padding-left: 0;
}
pre[class*="language-"].line-numbers code {
padding-left: 3.8em;
}
pre[class*="language-"].line-numbers .line-numbers-rows {
left: 0;
}
/* Plugin styles: Line Highlight */
pre[class*="language-"][data-line] {
padding-top: 0;
padding-bottom: 0;
padding-left: 0;
}
pre[data-line] code {
position: relative;
padding-left: 4em;
}
pre .line-highlight {
margin-top: 0;
}

View File

@ -1,126 +0,0 @@
/**
* prism.js Dark theme for JavaScript, CSS and HTML
* Based on the slides of the talk /Reg(exp){2}lained/
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: white;
text-shadow: 0 -.1em .2em black;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
pre[class*="language-"],
:not(pre) > code[class*="language-"] {
background: hsl(30, 20%, 25%);
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
border: .3em solid hsl(30, 20%, 40%);
border-radius: .5em;
box-shadow: 1px 1px .5em black inset;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .15em .2em .05em;
border-radius: .3em;
border: .13em solid hsl(30, 20%, 40%);
box-shadow: 1px 1px .3em -.1em black inset;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: hsl(30, 20%, 50%);
}
.token.punctuation {
opacity: .7;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol {
color: hsl(350, 40%, 70%);
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: hsl(75, 70%, 60%);
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string,
.token.variable {
color: hsl(40, 90%, 60%);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: hsl(350, 40%, 70%);
}
.token.regex,
.token.important {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
.token.deleted {
color: red;
}

View File

@ -1,115 +0,0 @@
/**
* prism.js Funky theme
* Based on Polyfilling the gaps talk slides http://lea.verou.me/polyfilling-the-gaps/
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
font-family: Consolas, Monaco, 'Andale Mono', monospace;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
/* Code blocks */
pre[class*="language-"] {
padding: .4em .8em;
margin: .5em 0;
overflow: auto;
background: url('data:image/svg+xml;charset=utf-8,<svg%20version%3D"1.1"%20xmlns%3D"http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg"%20width%3D"100"%20height%3D"100"%20fill%3D"rgba(0%2C0%2C0%2C.2)">%0D%0A<polygon%20points%3D"0%2C50%2050%2C0%200%2C0"%20%2F>%0D%0A<polygon%20points%3D"0%2C100%2050%2C100%20100%2C50%20100%2C0"%20%2F>%0D%0A<%2Fsvg>');
background-size: 1em 1em;
}
code[class*="language-"] {
background: black;
color: white;
box-shadow: -.3em 0 0 .3em black, .3em 0 0 .3em black;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .2em;
border-radius: .3em;
box-shadow: none;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #aaa;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol {
color: #0cf;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin {
color: yellow;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.toke.variable,
.token.inserted {
color: yellowgreen;
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: deeppink;
}
.token.regex,
.token.important {
color: orange;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
.token.deleted {
color: red;
}

View File

@ -1,119 +0,0 @@
/**
* okaidia theme for JavaScript, CSS and HTML
* Loosely based on Monokai textmate theme by http://www.monokai.nl/
* @author ocodia
*/
code[class*="language-"],
pre[class*="language-"] {
color: #f8f8f2;
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
font-family: Consolas, Monaco, 'Andale Mono', monospace;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
border-radius: 0.3em;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #272822;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #f8f8f2;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.constant,
.token.symbol,
.token.deleted {
color: #f92672;
}
.token.boolean,
.token.number {
color: #ae81ff;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #a6e22e;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string,
.token.variable {
color: #f8f8f2;
}
.token.atrule,
.token.attr-value,
.token.function {
color: #e6db74;
}
.token.keyword {
color: #66d9ef;
}
.token.regex,
.token.important {
color: #fd971f;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}

View File

@ -1,119 +0,0 @@
/**
* prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML
* Based on https://github.com/chriskempson/tomorrow-theme
* @author Rose Pritchard
*/
code[class*="language-"],
pre[class*="language-"] {
color: #ccc;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #2d2d2d;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
}
.token.comment,
.token.block-comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #999;
}
.token.punctuation {
color: #ccc;
}
.token.tag,
.token.attr-name,
.token.namespace,
.token.deleted {
color: #e2777a;
}
.token.function-name {
color: #6196cc;
}
.token.boolean,
.token.number,
.token.function {
color: #f08d49;
}
.token.property,
.token.class-name,
.token.constant,
.token.symbol {
color: #f8c555;
}
.token.selector,
.token.important,
.token.atrule,
.token.keyword,
.token.builtin {
color: #cc99cd;
}
.token.string,
.token.char,
.token.attr-value,
.token.regex,
.token.variable {
color: #7ec699;
}
.token.operator,
.token.entity,
.token.url {
color: #67cdcc;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
.token.inserted {
color: green;
}

View File

@ -1,199 +0,0 @@
/**
* prism.js Twilight theme
* Based (more or less) on the Twilight theme originally of Textmate fame.
* @author Remy Bach
*/
code[class*="language-"],
pre[class*="language-"] {
color: white;
direction: ltr;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
text-align: left;
text-shadow: 0 -.1em .2em black;
white-space: pre;
word-spacing: normal;
word-break: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"],
:not(pre) > code[class*="language-"] {
background: hsl(0, 0%, 8%); /* #141414 */
}
/* Code blocks */
pre[class*="language-"] {
border-radius: .5em;
border: .3em solid hsl(0, 0%, 33%); /* #282A2B */
box-shadow: 1px 1px .5em black inset;
margin: .5em 0;
overflow: auto;
padding: 1em;
}
pre[class*="language-"]::selection {
/* Safari */
background: hsl(200, 4%, 16%); /* #282A2B */
}
pre[class*="language-"]::selection {
/* Firefox */
background: hsl(200, 4%, 16%); /* #282A2B */
}
/* Text Selection colour */
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: hsla(0, 0%, 93%, 0.15); /* #EDEDED */
}
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
code[class*="language-"]::selection, code[class*="language-"] ::selection {
text-shadow: none;
background: hsla(0, 0%, 93%, 0.15); /* #EDEDED */
}
/* Inline code */
:not(pre) > code[class*="language-"] {
border-radius: .3em;
border: .13em solid hsl(0, 0%, 33%); /* #545454 */
box-shadow: 1px 1px .3em -.1em black inset;
padding: .15em .2em .05em;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: hsl(0, 0%, 47%); /* #777777 */
}
.token.punctuation {
opacity: .7;
}
.namespace {
opacity: .7;
}
.token.tag,
.token.boolean,
.token.number,
.token.deleted {
color: hsl(14, 58%, 55%); /* #CF6A4C */
}
.token.keyword,
.token.property,
.token.selector,
.token.constant,
.token.symbol,
.token.builtin {
color: hsl(53, 89%, 79%); /* #F9EE98 */
}
.token.attr-name,
.token.attr-value,
.token.string,
.token.char,
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string,
.token.variable,
.token.inserted {
color: hsl(76, 21%, 52%); /* #8F9D6A */
}
.token.atrule {
color: hsl(218, 22%, 55%); /* #7587A6 */
}
.token.regex,
.token.important {
color: hsl(42, 75%, 65%); /* #E9C062 */
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
pre[data-line] {
padding: 1em 0 1em 3em;
position: relative;
}
/* Markup */
.language-markup .token.tag,
.language-markup .token.attr-name,
.language-markup .token.punctuation {
color: hsl(33, 33%, 52%); /* #AC885B */
}
/* Make the tokens sit above the line highlight so the colours don't look faded. */
.token {
position: relative;
z-index: 1;
}
.line-highlight {
background: -moz-linear-gradient(left, hsla(0, 0%, 33%, .1) 70%, hsla(0, 0%, 33%, 0)); /* #545454 */
background: -o-linear-gradient(left, hsla(0, 0%, 33%, .1) 70%, hsla(0, 0%, 33%, 0)); /* #545454 */
background: -webkit-linear-gradient(left, hsla(0, 0%, 33%, .1) 70%, hsla(0, 0%, 33%, 0)); /* #545454 */
background: hsla(0, 0%, 33%, 0.25); /* #545454 */
background: linear-gradient(left, hsla(0, 0%, 33%, .1) 70%, hsla(0, 0%, 33%, 0)); /* #545454 */
border-bottom: 1px dashed hsl(0, 0%, 33%); /* #545454 */
border-top: 1px dashed hsl(0, 0%, 33%); /* #545454 */
left: 0;
line-height: inherit;
margin-top: 0.75em; /* Same as .prisms padding-top */
padding: inherit 0;
pointer-events: none;
position: absolute;
right: 0;
white-space: pre;
z-index: 0;
}
.line-highlight:before,
.line-highlight[data-end]:after {
background-color: hsl(215, 15%, 59%); /* #8794A6 */
border-radius: 999px;
box-shadow: 0 1px white;
color: hsl(24, 20%, 95%); /* #F5F2F0 */
content: attr(data-start);
font: bold 65%/1.5 sans-serif;
left: .6em;
min-width: 1em;
padding: 0 .5em;
position: absolute;
text-align: center;
text-shadow: none;
top: .4em;
vertical-align: .3em;
}
.line-highlight[data-end]:after {
bottom: .4em;
content: attr(data-end);
top: auto;
}

View File

@ -1,135 +0,0 @@
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
code[class*="language-"]::selection, code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #a67f59;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}

View File

@ -1,69 +0,0 @@
var _ = require('lodash');
function BackboneFile(book) {
if (!(this instanceof BackboneFile)) return new BackboneFile(book);
this.book = book;
this.log = this.book.log;
// Filename in the book
this.path = '';
this.parser;
_.bindAll(this);
}
// Type of the backbone file
BackboneFile.prototype.type = '';
// Parse a backbone file
BackboneFile.prototype.parse = function() {
// To be implemented by each child
};
// Handle case where file doesn't exists
BackboneFile.prototype.parseNotFound = function() {
};
// Return true if backbone file exists
BackboneFile.prototype.exists = function() {
return Boolean(this.path);
};
// Locate a backbone file, could be .md, .asciidoc, etc
BackboneFile.prototype.locate = function() {
var that = this;
var filename = this.book.config.getStructure(this.type, true);
this.log.debug.ln('locating', this.type, ':', filename);
return this.book.findParsableFile(filename)
.then(function(result) {
if (!result) return;
that.path = result.path;
that.parser = result.parser;
});
};
// Read and parse the file
BackboneFile.prototype.load = function() {
var that = this;
this.log.debug.ln('loading', this.type, ':', that.path);
return this.locate()
.then(function() {
if (!that.path) return that.parseNotFound();
that.log.debug.ln(that.type, 'located at', that.path);
return that.book.readFile(that.path)
// Parse it
.then(function(content) {
return that.parse(content);
});
});
};
module.exports = BackboneFile;

View File

@ -1,99 +0,0 @@
var _ = require('lodash');
var util = require('util');
var BackboneFile = require('./file');
// Normalize a glossary entry name into a unique id
function nameToId(name) {
return name.toLowerCase()
.replace(/[\/\\\?\%\*\:\;\|\"\'\\<\\>\#\$\(\)\!\.\@]/g, '')
.replace(/ /g, '_')
.trim();
}
/*
A glossary entry is represented by a name and a short description
An unique id for the entry is generated using its name
*/
function GlossaryEntry(name, description) {
if (!(this instanceof GlossaryEntry)) return new GlossaryEntry(name, description);
this.name = name;
this.description = description;
Object.defineProperty(this, 'id', {
get: _.bind(this.getId, this)
});
}
// Normalizes a glossary entry's name to create an ID
GlossaryEntry.prototype.getId = function() {
return nameToId(this.name);
};
/*
A glossary is a list of entries stored in a GLOSSARY.md file
*/
function Glossary() {
BackboneFile.apply(this, arguments);
this.entries = [];
}
util.inherits(Glossary, BackboneFile);
Glossary.prototype.type = 'glossary';
// Get templating context
Glossary.prototype.getContext = function() {
if (!this.path) return {};
return {
glossary: {
path: this.path
}
};
};
// Parse the readme content
Glossary.prototype.parse = function(content) {
var that = this;
return this.parser.glossary(content)
.then(function(entries) {
that.entries = _.map(entries, function(entry) {
return new GlossaryEntry(entry.name, entry.description);
});
});
};
// Return an entry by its id
Glossary.prototype.get = function(id) {
return _.find(this.entries, {
id: id
});
};
// Find an entry by its name
Glossary.prototype.find = function(name) {
return this.get(nameToId(name));
};
// Return false if glossary has entries (and exists)
Glossary.prototype.isEmpty = function(id) {
return _.size(this.entries) === 0;
};
// Convert the glossary to a list of annotations
Glossary.prototype.annotations = function() {
return _.map(this.entries, function(entry) {
return {
id: entry.id,
name: entry.name,
description: entry.description,
href: '/' + this.path + '#' + entry.id
};
}, this);
};
module.exports = Glossary;

View File

@ -1,8 +0,0 @@
module.exports = {
Readme: require('./readme'),
Summary: require('./summary'),
Glossary: require('./glossary'),
Langs: require('./langs')
};

View File

@ -1,81 +0,0 @@
var _ = require('lodash');
var path = require('path');
var util = require('util');
var BackboneFile = require('./file');
function Language(title, folder) {
var that = this;
this.title = title;
this.folder = folder;
Object.defineProperty(this, 'id', {
get: function() {
return path.basename(that.folder);
}
});
}
/*
A Langs is a list of languages stored in a LANGS.md file
*/
function Langs() {
BackboneFile.apply(this, arguments);
this.languages = [];
}
util.inherits(Langs, BackboneFile);
Langs.prototype.type = 'langs';
// Parse the readme content
Langs.prototype.parse = function(content) {
var that = this;
return this.parser.langs(content)
.then(function(langs) {
that.languages = _.map(langs, function(entry) {
return new Language(entry.title, entry.path);
});
});
};
// Return the list of languages
Langs.prototype.list = function() {
return this.languages;
};
// Return default/main language for the book
Langs.prototype.getDefault = function() {
return _.first(this.languages);
};
// Return true if a language is the default one
// "lang" cam be a string (id) or a Language entry
Langs.prototype.isDefault = function(lang) {
lang = lang.id || lang;
return (this.cound() > 0 && this.getDefault().id == lang);
};
// Return the count of languages
Langs.prototype.count = function() {
return _.size(this.languages);
};
// Return templating context for the languages list
Langs.prototype.getContext = function() {
if (this.count() == 0) return {};
return {
languages: {
list: _.map(this.languages, function(lang) {
return {
id: lang.id,
title: lang.title
};
})
}
};
};
module.exports = Langs;

View File

@ -1,26 +0,0 @@
var util = require('util');
var BackboneFile = require('./file');
function Readme() {
BackboneFile.apply(this, arguments);
this.title;
this.description;
}
util.inherits(Readme, BackboneFile);
Readme.prototype.type = 'readme';
// Parse the readme content
Readme.prototype.parse = function(content) {
var that = this;
return this.parser.readme(content)
.then(function(out) {
that.title = out.title;
that.description = out.description;
});
};
module.exports = Readme;

View File

@ -1,348 +0,0 @@
var _ = require('lodash');
var util = require('util');
var location = require('../utils/location');
var error = require('../utils/error');
var BackboneFile = require('./file');
/*
An article represent an entry in the Summary.
It's defined by a title, a reference, and children articles,
the reference (ref) can be a filename + anchor or an external file (optional)
*/
function TOCArticle(def, parent) {
// Title
this.title = def.title;
// Parent TOCPart or TOCArticle
this.parent = parent;
// As string indicating the overall position
// ex: '1.0.0'
this.level;
this._next;
this._prev;
// When README has been automatically added
this.isAutoIntro = def.isAutoIntro;
this.isIntroduction = def.isIntroduction;
this.validate();
// Path can be a relative path or an url, or nothing
this.ref = def.path;
if (this.ref && !this.isExternal()) {
var parts = this.ref.split('#');
this.path = (parts.length > 1? parts.slice(0, -1).join('#') : this.ref);
this.anchor = (parts.length > 1? '#' + _.last(parts) : null);
// Normalize path to remove ('./', etc)
this.path = location.normalize(this.path);
}
this.articles = _.map(def.articles || [], function(article) {
if (article instanceof TOCArticle) return article;
return new TOCArticle(article, this);
}, this);
}
// Validate the article
TOCArticle.prototype.validate = function() {
if (!this.title) {
throw error.ParsingError(new Error('SUMMARY entries should have an non-empty title'));
}
};
// Iterate over all articles in this articles
TOCArticle.prototype.walk = function(iter, base) {
base = base || this.level;
_.each(this.articles, function(article, i) {
var level = levelId(base, i);
if (iter(article, level) === false) {
return false;
}
article.walk(iter, level);
});
};
// Return templating context for an article
TOCArticle.prototype.getContext = function() {
return {
level: this.level,
title: this.title,
depth: this.depth(),
path: this.isExternal()? undefined : this.path,
anchor: this.isExternal()? undefined : this.anchor,
url: this.isExternal()? this.ref : undefined
};
};
// Return true if is pointing to a file
TOCArticle.prototype.hasLocation = function() {
return Boolean(this.path);
};
// Return true if is pointing to an external location
TOCArticle.prototype.isExternal = function() {
return location.isExternal(this.ref);
};
// Return true if this article is the introduction
TOCArticle.prototype.isIntro = function() {
return Boolean(this.isIntroduction);
};
// Return true if has children
TOCArticle.prototype.hasChildren = function() {
return this.articles.length > 0;
};
// Return true if has an article as parent
TOCArticle.prototype.hasParent = function() {
return !(this.parent instanceof TOCPart);
};
// Return depth of this article
TOCArticle.prototype.depth = function() {
return this.level.split('.').length;
};
// Return next article in the TOC
TOCArticle.prototype.next = function() {
return this._next;
};
// Return previous article in the TOC
TOCArticle.prototype.prev = function() {
return this._prev;
};
// Map over all articles
TOCArticle.prototype.map = function(iter) {
return _.map(this.articles, iter);
};
/*
A part of a ToC is a composed of a tree of articles and an optiona title
*/
function TOCPart(part, parent) {
if (!(this instanceof TOCPart)) return new TOCPart(part, parent);
TOCArticle.apply(this, arguments);
}
util.inherits(TOCPart, TOCArticle);
// Validate the part
TOCPart.prototype.validate = function() { };
// Return a sibling (next or prev) of this part
TOCPart.prototype.sibling = function(direction) {
var parts = this.parent.parts;
var pos = _.findIndex(parts, this);
if (parts[pos + direction]) {
return parts[pos + direction];
}
return null;
};
// Iterate over all entries of the part
TOCPart.prototype.walk = function(iter, base) {
var articles = this.articles;
if (articles.length == 0) return;
// Has introduction?
if (articles[0].isIntro()) {
if (iter(articles[0], '0') === false) {
return;
}
articles = articles.slice(1);
}
_.each(articles, function(article, i) {
var level = levelId(base, i);
if (iter(article, level) === false) {
return false;
}
article.walk(iter, level);
});
};
// Return templating context for a part
TOCPart.prototype.getContext = function(onArticle) {
onArticle = onArticle || function(article) {
return article.getContext();
};
return {
title: this.title,
articles: this.map(onArticle)
};
};
/*
A summary is composed of a list of parts, each composed wit a tree of articles.
*/
function Summary() {
BackboneFile.apply(this, arguments);
this.parts = [];
this._length = 0;
}
util.inherits(Summary, BackboneFile);
Summary.prototype.type = 'summary';
// Prepare summary when non existant
Summary.prototype.parseNotFound = function() {
this.update([]);
};
// Parse the summary content
Summary.prototype.parse = function(content) {
var that = this;
return this.parser.summary(content)
.then(function(summary) {
that.update(summary.parts);
});
};
// Return templating context for the summary
Summary.prototype.getContext = function() {
function onArticle(article) {
var result = article.getContext();
if (article.hasChildren()) {
result.articles = article.map(onArticle);
}
return result;
}
return {
summary: {
parts: _.map(this.parts, function(part) {
return part.getContext(onArticle);
})
}
};
};
// Iterate over all entries of the summary
// iter is called with an TOCArticle
Summary.prototype.walk = function(iter) {
var hasMultipleParts = this.parts.length > 1;
_.each(this.parts, function(part, i) {
part.walk(iter, hasMultipleParts? levelId('', i) : null);
});
};
// Find a specific article using a filter
Summary.prototype.find = function(filter) {
var result;
this.walk(function(article) {
if (filter(article)) {
result = article;
return false;
}
});
return result;
};
// Flatten the list of articles
Summary.prototype.flatten = function() {
var result = [];
this.walk(function(article) {
result.push(article);
});
return result;
};
// Return the first TOCArticle for a specific page (or path)
Summary.prototype.getArticle = function(page) {
if (!_.isString(page)) page = page.path;
return this.find(function(article) {
return article.path == page;
});
};
// Return the first TOCArticle for a specific level
Summary.prototype.getArticleByLevel = function(lvl) {
return this.find(function(article) {
return article.level == lvl;
});
};
// Return the count of articles in the summary
Summary.prototype.count = function() {
return this._length;
};
// Prepare the summary
Summary.prototype.update = function(parts) {
var that = this;
that.parts = _.map(parts, function(part) {
return new TOCPart(part, that);
});
// Create first part if none
if (that.parts.length == 0) {
that.parts.push(new TOCPart({}, that));
}
// Add README as first entry
var firstArticle = that.parts[0].articles[0];
if (!firstArticle || firstArticle.path != that.book.readme.path) {
that.parts[0].articles.unshift(new TOCArticle({
title: 'Introduction',
path: that.book.readme.path,
isAutoIntro: true
}, that.parts[0]));
}
that.parts[0].articles[0].isIntroduction = true;
// Update the count and indexing of "level"
var prev = undefined;
that._length = 0;
that.walk(function(article, level) {
// Index level
article.level = level;
// Chain articles
article._prev = prev;
if (prev) prev._next = article;
prev = article;
that._length += 1;
});
};
// Return a level string from a base level and an index
function levelId(base, i) {
i = i + 1;
return (base? [base || '', i] : [i]).join('.');
}
module.exports = Summary;

View File

@ -1,405 +0,0 @@
var _ = require('lodash');
var path = require('path');
var Ignore = require('ignore');
var Config = require('./config');
var Readme = require('./backbone/readme');
var Glossary = require('./backbone/glossary');
var Summary = require('./backbone/summary');
var Langs = require('./backbone/langs');
var Page = require('./page');
var pathUtil = require('./utils/path');
var error = require('./utils/error');
var Promise = require('./utils/promise');
var Logger = require('./utils/logger');
var parsers = require('./parsers');
var initBook = require('./init');
/*
The Book class is an interface for parsing books content.
It does not require to run on Node.js, isnce it only depends on the fs implementation
*/
function Book(opts) {
if (!(this instanceof Book)) return new Book(opts);
this.opts = _.defaults(opts || {}, {
fs: null,
// Root path for the book
root: '',
// Extend book configuration
config: {},
// Log function
log: function(msg) {
process.stdout.write(msg);
},
// Log level
logLevel: 'info'
});
if (!opts.fs) throw error.ParsingError(new Error('Book requires a fs instance'));
// Root path for the book
this.root = opts.root;
// If multi-lingual, book can have a parent
this.parent = opts.parent;
if (this.parent) {
this.language = path.relative(this.parent.root, this.root);
}
// A book is linked to an fs, to access its content
this.fs = opts.fs;
// Rules to ignore some files
this.ignore = Ignore();
this.ignore.addPattern([
// Skip Git stuff
'.git/',
// Skip OS X meta data
'.DS_Store',
// Skip stuff installed by plugins
'node_modules',
// Skip book outputs
'_book',
'*.pdf',
'*.epub',
'*.mobi'
]);
// Create a logger for the book
this.log = new Logger(opts.log, opts.logLevel);
// Create an interface to access the configuration
this.config = new Config(this, opts.config);
// Interfaces for the book structure
this.readme = new Readme(this);
this.summary = new Summary(this);
this.glossary = new Glossary(this);
// Multilinguals book
this.langs = new Langs(this);
this.books = [];
// List of page in the book
this.pages = {};
// Deprecation for templates
Object.defineProperty(this, 'options', {
get: function () {
this.log.warn.ln('"options" property is deprecated, use config.get(key) instead');
var cfg = this.config.dump();
error.deprecateField(cfg, 'book', (this.output? this.output.name : null), '"options.generator" property is deprecated, use "output.name" instead');
// options.generator
cfg.generator = this.output? this.output.name : null;
// options.output
cfg.output = this.output? this.output.root() : null;
return cfg;
}
});
_.bindAll(this);
// Loop for template filters/blocks
error.deprecateField(this, 'book', this, '"book" property is deprecated, use "this" directly instead');
}
// Return templating context for the book
Book.prototype.getContext = function() {
var variables = this.config.get('variables', {});
return {
book: _.extend({
language: this.language
}, variables)
};
};
// Parse and prepare the configuration, fail if invalid
Book.prototype.prepareConfig = function() {
var that = this;
return this.config.load()
.then(function() {
var rootFolder = that.config.get('root');
if (!rootFolder) return;
that.originalRoot = that.root;
that.root = path.resolve(that.root, rootFolder);
});
};
// Resolve a path in the book source
// Enforce that the output path is in the scope
Book.prototype.resolve = function() {
var filename = path.resolve.apply(path, [this.root].concat(_.toArray(arguments)));
if (!this.isFileInScope(filename)) {
throw error.FileOutOfScopeError({
filename: filename,
root: this.root
});
}
return filename;
};
// Return false if a file is outside the book' scope
Book.prototype.isFileInScope = function(filename) {
filename = path.resolve(this.root, filename);
// Is the file in the scope of the parent?
if (this.parent && this.parent.isFileInScope(filename)) return true;
// Is file in the root folder?
return pathUtil.isInRoot(this.root, filename);
};
// Parse .gitignore, etc to extract rules
Book.prototype.parseIgnoreRules = function() {
var that = this;
return Promise.serie([
'.ignore',
'.gitignore',
'.bookignore'
], function(filename) {
return that.readFile(filename)
.then(function(content) {
that.ignore.addPattern(content.toString().split(/\r?\n/));
}, function() {
return Promise();
});
});
};
// Parse the whole book
Book.prototype.parse = function() {
var that = this;
return Promise()
.then(this.prepareConfig)
.then(this.parseIgnoreRules)
// Parse languages
.then(function() {
return that.langs.load();
})
.then(function() {
if (that.isMultilingual()) {
if (that.isLanguageBook()) {
throw error.ParsingError(new Error('A multilingual book as a language book is forbidden'));
}
that.log.info.ln('Parsing multilingual book, with', that.langs.count(), 'languages');
// Create a new book for each language and parse it
return Promise.serie(that.langs.list(), function(lang) {
that.log.debug.ln('Preparing book for language', lang.id);
var langBook = new Book(_.extend({}, that.opts, {
parent: that,
config: that.config.dump(),
root: that.resolve(lang.id)
}));
that.books.push(langBook);
return langBook.parse();
});
}
return Promise()
// Parse the readme
.then(that.readme.load)
.then(function() {
if (!that.readme.exists()) {
throw new error.FileNotFoundError({ filename: 'README' });
}
// Default configuration to infos extracted from readme
if (!that.config.get('title')) that.config.set('title', that.readme.title);
if (!that.config.get('description')) that.config.set('description', that.readme.description);
})
// Parse the summary
.then(that.summary.load)
.then(function() {
if (!that.summary.exists()) {
that.log.warn.ln('no summary file in this book');
}
// Index summary's articles
that.summary.walk(function(article) {
if (!article.hasLocation() || article.isExternal()) return;
that.addPage(article.path);
});
})
// Parse the glossary
.then(that.glossary.load)
// Add the glossary as a page
.then(function() {
if (!that.glossary.exists()) return;
that.addPage(that.glossary.path);
});
});
};
// Mark a filename as being parsable
Book.prototype.addPage = function(filename) {
if (this.hasPage(filename)) return this.getPage(filename);
filename = pathUtil.normalize(filename);
this.pages[filename] = new Page(this, filename);
return this.pages[filename];
};
// Return a page by its filename (or undefined)
Book.prototype.getPage = function(filename) {
filename = pathUtil.normalize(filename);
return this.pages[filename];
};
// Return true, if has a specific page
Book.prototype.hasPage = function(filename) {
return Boolean(this.getPage(filename));
};
// Test if a file is ignored, return true if it is
Book.prototype.isFileIgnored = function(filename) {
return this.ignore.filter([filename]).length == 0;
};
// Read a file in the book, throw error if ignored
Book.prototype.readFile = function(filename) {
if (this.isFileIgnored(filename)) return Promise.reject(new error.FileNotFoundError({ filename: filename }));
return this.fs.readAsString(this.resolve(filename));
};
// Get stat infos about a file
Book.prototype.statFile = function(filename) {
if (this.isFileIgnored(filename)) return Promise.reject(new error.FileNotFoundError({ filename: filename }));
return this.fs.stat(this.resolve(filename));
};
// Find a parsable file using a filename
Book.prototype.findParsableFile = function(filename) {
var that = this;
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) {
// Stop if already find a parser
if (output) return output;
var filepath = basename+ext;
return that.fs.findFile(that.root, filepath)
.then(function(realFilepath) {
if (!realFilepath) return null;
return {
parser: parsers.getByExt(ext),
path: realFilepath
};
});
});
}, Promise(null));
};
// Return true if book is associated to a language
Book.prototype.isLanguageBook = function() {
return Boolean(this.parent);
};
Book.prototype.isSubBook = Book.prototype.isLanguageBook;
// Return true if the book is main instance of a multilingual book
Book.prototype.isMultilingual = function() {
return this.langs.count() > 0;
};
// Return true if file is in the scope of this book
Book.prototype.isInBook = function(filename) {
return pathUtil.isInRoot(
this.root,
filename
);
};
// Return true if file is in the scope of a child book
Book.prototype.isInLanguageBook = function(filename) {
var that = this;
return _.some(this.langs.list(), function(lang) {
return pathUtil.isInRoot(
that.resolve(lang.id),
that.resolve(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
Book.prototype.contentLink = error.deprecateMethod(function(s) {
return this.output.toURL(s);
}, '.contentLink() is deprecated, use ".output.toURL()" instead');
Book.prototype.contentPath = error.deprecateMethod(function(s) {
return this.output.toURL(s);
}, '.contentPath() is deprecated, use ".output.toURL()" instead');
Book.prototype.isSubBook = error.deprecateMethod(function() {
return this.isLanguageBook();
}, '.isSubBook() is deprecated, use ".isLanguageBook()" instead');
// Initialize a book
Book.init = function(fs, root, opts) {
var book = new Book(_.extend(opts || {}, {
root: root,
fs: fs
}));
return initBook(book);
};
module.exports = Book;

View File

@ -1,140 +0,0 @@
var _ = require('lodash');
var path = require('path');
var Book = require('../book');
var NodeFS = require('../fs/node');
var Logger = require('../utils/logger');
var Promise = require('../utils/promise');
var fs = require('../utils/fs');
var JSONOutput = require('../output/json');
var WebsiteOutput = require('../output/website');
var EBookOutput = require('../output/ebook');
var nodeFS = new NodeFS();
var LOG_OPTION = {
name: 'log',
description: 'Minimum log level to display',
values: _.chain(Logger.LEVELS)
.keys()
.map(function(s) {
return s.toLowerCase();
})
.value(),
defaults: 'info'
};
var FORMAT_OPTION = {
name: 'format',
description: 'Format to build to',
values: ['website', 'json', 'ebook'],
defaults: 'website'
};
var FORMATS = {
json: JSONOutput,
website: WebsiteOutput,
ebook: EBookOutput
};
// Commands which is processing a book
// the root of the book is the first argument (or current directory)
function bookCmd(fn) {
return function(args, kwargs) {
var input = path.resolve(args[0] || process.cwd());
var book = new Book({
fs: nodeFS,
root: input,
logLevel: kwargs.log
});
return fn(book, args.slice(1), kwargs);
};
}
// Commands which is working on a Output instance
function outputCmd(fn) {
return bookCmd(function(book, args, kwargs) {
var Out = FORMATS[kwargs.format];
var outputFolder = undefined;
// Set output folder
if (args[0]) {
outputFolder = path.resolve(process.cwd(), args[0]);
}
return fn(new Out(book, {
root: outputFolder
}), args);
});
}
// Command to generate an ebook
function ebookCmd(format) {
return {
name: format + ' [book] [output] [file]',
description: 'generates ebook '+format,
options: [
LOG_OPTION
],
exec: bookCmd(function(book, args, kwargs) {
return fs.tmpDir()
.then(function(dir) {
var ext = '.'+format;
var outputFile = path.resolve(process.cwd(), args[0] || ('book' + ext));
var output = new EBookOutput(book, {
root: dir,
format: format
});
return output.book.parse()
.then(function() {
return output.generate();
})
// Copy the ebook files
.then(function() {
if (output.book.isMultilingual()) {
return Promise.serie(output.book.langs.list(), function(lang) {
var _outputFile = path.join(
path.dirname(outputFile),
path.basename(outputFile, ext) + '_' + lang.id + ext
);
return fs.copy(
path.resolve(dir, lang.id, 'index' + ext),
_outputFile
);
})
.thenResolve(output.book.langs.count());
} else {
return fs.copy(
path.resolve(dir, 'index' + ext),
outputFile
).thenResolve(1);
}
})
.then(function(n) {
output.book.log.info.ok(n+' file(s) generated');
output.book.log.info('cleaning up... ');
return output.book.log.info.promise(fs.rmDir(dir));
});
});
})
};
}
module.exports = {
nodeFS: nodeFS,
bookCmd: bookCmd,
outputCmd: outputCmd,
ebookCmd: ebookCmd,
options: {
log: LOG_OPTION,
format: FORMAT_OPTION
},
FORMATS: FORMATS
};

View File

@ -1,199 +0,0 @@
/* eslint-disable no-console */
var _ = require('lodash');
var path = require('path');
var tinylr = require('tiny-lr');
var Promise = require('../utils/promise');
var PluginsManager = require('../plugins');
var Book = require('../book');
var helper = require('./helper');
var Server = require('./server');
var watch = require('./watch');
module.exports = {
commands: [
{
name: 'init [book]',
description: 'setup and create files for chapters',
options: [
helper.options.log
],
exec: function(args) {
var input = path.resolve(args[0] || process.cwd());
return Book.init(helper.nodeFS, input);
}
},
{
name: 'parse [book]',
description: 'parse and returns debug information for a book',
options: [
helper.options.log
],
exec: helper.bookCmd(function(book) {
return book.parse()
.then(function() {
book.log.info.ln('Book located in:', book.root);
book.log.info.ln('');
if (book.config.exists()) book.log.info.ln('Configuration:', book.config.path);
if (book.isMultilingual()) {
book.log.info.ln('Multilingual book detected:', book.langs.path);
} else {
book.log.info.ln('Readme:', book.readme.path);
book.log.info.ln('Summary:', book.summary.path);
if (book.glossary.exists()) book.log.info.ln('Glossary:', book.glossary.path);
book.log.info.ln('Pages:');
_.each(book.pages, function(page) {
book.log.info.ln('\t-', page.path);
});
}
});
})
},
{
name: 'install [book]',
description: 'install all plugins dependencies',
options: [
helper.options.log
],
exec: helper.bookCmd(function(book, args) {
var plugins = new PluginsManager(book);
return book.config.load()
.then(function() {
return plugins.install();
});
})
},
{
name: 'build [book] [output]',
description: 'build a book',
options: [
helper.options.log,
helper.options.format
],
exec: helper.outputCmd(function(output, args, kwargs) {
return output.book.parse()
.then(function() {
return output.generate();
});
})
},
helper.ebookCmd('pdf'),
helper.ebookCmd('epub'),
helper.ebookCmd('mobi'),
{
name: 'serve [book]',
description: 'Build then serve a book from a directory',
options: [
{
name: 'port',
description: 'Port for server to listen on',
defaults: 4000
},
{
name: 'lrport',
description: 'Port for livereload server to listen on',
defaults: 35729
},
{
name: 'watch',
description: 'Enable/disable file watcher',
defaults: true
},
helper.options.format,
helper.options.log
],
exec: function(args, kwargs) {
var input = path.resolve(args[0] || process.cwd());
var server = new Server();
// Init livereload server
var lrServer = tinylr({});
var port = kwargs.port;
var lrPath;
var generate = function() {
// Stop server if running
if (server.isRunning()) console.log('Stopping server');
return server.stop()
// Generate the book
.then(function() {
var book = new Book({
fs: helper.nodeFS,
root: input,
logLevel: kwargs.log
});
return book.parse()
.then(function() {
// Add livereload plugin
book.config.set('plugins',
book.config.get('plugins')
.concat([
{ name: 'livereload' }
])
);
var Out = helper.FORMATS[kwargs.format];
var output = new Out(book);
return output.generate()
.thenResolve(output);
});
})
// Start server and watch changes
.then(function(output) {
console.log();
console.log('Starting server ...');
return server.start(output.root(), port)
.then(function() {
console.log('Serving book on http://localhost:'+port);
if (lrPath) {
// trigger livereload
lrServer.changed({
body: {
files: [lrPath]
}
});
}
if (!kwargs.watch) return;
return watch(output.book.root)
.then(function(filepath) {
// set livereload path
lrPath = filepath;
console.log('Restart after change in file', filepath);
console.log('');
return generate();
});
});
});
};
return Promise.nfcall(lrServer.listen.bind(lrServer), kwargs.lrport)
.then(function() {
console.log('Live reload server started on port:', kwargs.lrport);
console.log('Press CTRL+C to quit ...');
console.log('');
return generate();
});
}
}
]
};

View File

@ -1,96 +0,0 @@
var events = require('events');
var http = require('http');
var send = require('send');
var util = require('util');
var url = require('url');
var Promise = require('../utils/promise');
var Server = function() {
this.running = null;
this.dir = null;
this.port = 0;
this.sockets = [];
};
util.inherits(Server, events.EventEmitter);
// Return true if the server is running
Server.prototype.isRunning = function() {
return !!this.running;
};
// Stop the server
Server.prototype.stop = function() {
var that = this;
if (!this.isRunning()) return Promise();
var d = Promise.defer();
this.running.close(function(err) {
that.running = null;
that.emit('state', false);
if (err) d.reject(err);
else d.resolve();
});
for (var i = 0; i < this.sockets.length; i++) {
this.sockets[i].destroy();
}
return d.promise;
};
Server.prototype.start = function(dir, port) {
var that = this, pre = Promise();
port = port || 8004;
if (that.isRunning()) pre = this.stop();
return pre
.then(function() {
var d = Promise.defer();
that.running = http.createServer(function(req, res){
// Render error
function error(err) {
res.statusCode = err.status || 500;
res.end(err.message);
}
// Redirect to directory's index.html
function redirect() {
res.statusCode = 301;
res.setHeader('Location', req.url + '/');
res.end('Redirecting to ' + req.url + '/');
}
// Send file
send(req, url.parse(req.url).pathname, {
root: dir
})
.on('error', error)
.on('directory', redirect)
.pipe(res);
});
that.running.on('connection', function (socket) {
that.sockets.push(socket);
socket.setTimeout(4000);
socket.on('close', function () {
that.sockets.splice(that.sockets.indexOf(socket), 1);
});
});
that.running.listen(port, function(err) {
if (err) return d.reject(err);
that.port = port;
that.dir = dir;
that.emit('state', true);
d.resolve();
});
return d.promise;
});
};
module.exports = Server;

View File

@ -1,42 +0,0 @@
var _ = require('lodash');
var path = require('path');
var chokidar = require('chokidar');
var Promise = require('../utils/promise');
var parsers = require('../parsers');
// Watch a folder and resolve promise once a file is modified
function watch(dir) {
var d = Promise.defer();
dir = path.resolve(dir);
var toWatch = [
'book.json', 'book.js'
];
// Watch all parsable files
_.each(parsers.extensions, function(ext) {
toWatch.push('**/*'+ext);
});
var watcher = chokidar.watch(toWatch, {
cwd: dir,
ignored: '_book/**',
ignoreInitial: true
});
watcher.once('all', function(e, filepath) {
watcher.close();
d.resolve(filepath);
});
watcher.once('error', function(err) {
watcher.close();
d.reject(err);
});
return d.promise;
}
module.exports = watch;

View File

@ -1,132 +0,0 @@
var _ = require('lodash');
var semver = require('semver');
var gitbook = require('../gitbook');
var Promise = require('../utils/promise');
var validator = require('./validator');
var plugins = require('./plugins');
// Config files to tested (sorted)
var CONFIG_FILES = [
'book.js',
'book.json'
];
/*
Config is an interface for the book's configuration stored in "book.json" (or "book.js")
*/
function Config(book, baseConfig) {
this.book = book;
this.fs = book.fs;
this.log = book.log;
this.path = '';
this.baseConfig = baseConfig || {};
this.replace({});
}
// Load configuration of the book
// and verify that the configuration is satisfying
Config.prototype.load = function() {
var that = this;
var isLanguageBook = this.book.isLanguageBook();
// Try all potential configuration file
return Promise.some(CONFIG_FILES, function(filename) {
that.log.debug.ln('try loading configuration from', filename);
return that.fs.loadAsObject(that.book.resolve(filename))
.then(function(_config) {
that.log.debug.ln('configuration loaded from', filename);
that.path = filename;
return that.replace(_config);
})
.fail(function(err) {
if (err.code != 'MODULE_NOT_FOUND') throw(err);
else return Promise(false);
});
})
.then(function() {
if (!isLanguageBook) {
if (!gitbook.satisfies(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(gitbook.version, 'patch'), that.options.gitbook)) {
that.log.warn.ln('gitbook version specified in your book.json might be too strict for future patches, \'>='+(_.first(gitbook.version.split('.'))+'.x.x')+'\' is more adequate');
}
that.options.plugins = plugins.toList(that.options.plugins);
} else {
// Multilingual book should inherits the plugins list from parent
that.options.plugins = that.book.parent.config.get('plugins');
}
that.options.gitbook = gitbook.version;
});
};
// Replace the whole configuration
Config.prototype.replace = function(options) {
var that = this;
// Extend base config
options = _.defaults(_.cloneDeep(options), this.baseConfig);
// Validate the config
this.options = validator.validate(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;
}
});
};
// Return true if book has a configuration file
Config.prototype.exists = function() {
return Boolean(this.path);
};
// Return path to a structure file
// Strip the extension by default
Config.prototype.getStructure = function(name, dontStripExt) {
var filename = this.options.structure[name];
if (dontStripExt) return filename;
filename = filename.split('.').slice(0, -1).join('.');
return filename;
};
// Return a configuration using a key and a default value
Config.prototype.get = function(key, def) {
return _.get(this.options, key, def);
};
// Update a configuration
Config.prototype.set = function(key, value) {
return _.set(this.options, key, value);
};
// Return a dump of the configuration
Config.prototype.dump = function() {
return _.cloneDeep(this.options);
};
// Return templating context
Config.prototype.getContext = function() {
return {
config: this.book.config.dump()
};
};
module.exports = Config;

View File

@ -1,67 +0,0 @@
var _ = require('lodash');
// Default plugins added to each books
var DEFAULT_PLUGINS = ['highlight', 'search', 'sharing', 'fontsettings', 'theme-default'];
// Return true if a plugin is a default plugin
function isDefaultPlugin(name, version) {
return _.contains(DEFAULT_PLUGINS, name);
}
// Normalize a list of plugins to use
function normalizePluginsList(plugins) {
// Normalize list to an array
plugins = _.isString(plugins) ? plugins.split(',') : (plugins || []);
// Remove empty parts
plugins = _.compact(plugins);
// Divide as {name, version} to handle format like 'myplugin@1.0.0'
plugins = _.map(plugins, function(plugin) {
if (plugin.name) return plugin;
var parts = plugin.split('@');
var name = parts[0];
var version = parts[1];
return {
'name': name,
'version': version // optional
};
});
// List plugins to remove
var toremove = _.chain(plugins)
.filter(function(plugin) {
return plugin.name.length > 0 && plugin.name[0] == '-';
})
.map(function(plugin) {
return plugin.name.slice(1);
})
.value();
// Merge with defaults
_.each(DEFAULT_PLUGINS, function(plugin) {
if (_.find(plugins, { name: plugin })) {
return;
}
plugins.push({
'name': plugin
});
});
// Remove plugin that start with '-'
plugins = _.filter(plugins, function(plugin) {
return !_.contains(toremove, plugin.name) && !(plugin.name.length > 0 && plugin.name[0] == '-');
});
// Remove duplicates
plugins = _.uniq(plugins, 'name');
return plugins;
}
module.exports = {
isDefaultPlugin: isDefaultPlugin,
toList: normalizePluginsList
};

View File

@ -1,188 +0,0 @@
module.exports = {
'$schema': 'http://json-schema.org/schema#',
'id': 'https://gitbook.com/schemas/book.json',
'title': 'GitBook Configuration',
'type': 'object',
'properties': {
'root': {
'type': 'string',
'title': 'Path fro the root folder containing the book\'s content'
},
'title': {
'type': 'string',
'title': 'Title of the book, default is extracted from README'
},
'isbn': {
'type': 'string',
'title': 'ISBN for published book'
},
'author': {
'type': 'string',
'title': 'Name of the author'
},
'gitbook': {
'type': 'string',
'default': '*',
'title': 'GitBook version to match'
},
'direction': {
'type': 'string',
'enum': ['ltr', 'rtl'],
'title': 'Direction of texts, default is detected in the pages'
},
'theme': {
'type': 'string',
'default': 'default',
'title': 'Name of the theme plugin to use'
},
'variables': {
'type': 'object',
'title': 'Templating context variables'
},
'plugins': {
'oneOf': [
{ '$ref': '#/definitions/pluginsArray' },
{ '$ref': '#/definitions/pluginsString' }
],
'default': []
},
'pluginsConfig': {
'type': 'object',
'title': 'Configuration for plugins'
},
'structure': {
'type': 'object',
'properties': {
'langs': {
'default': 'LANGS.md',
'type': 'string',
'title': 'File to use as languages index',
'pattern': '^[0-9a-zA-Z ... ]+$'
},
'readme': {
'default': 'README.md',
'type': 'string',
'title': 'File to use as preface',
'pattern': '^[0-9a-zA-Z ... ]+$'
},
'glossary': {
'default': 'GLOSSARY.md',
'type': 'string',
'title': 'File to use as glossary index',
'pattern': '^[0-9a-zA-Z ... ]+$'
},
'summary': {
'default': 'SUMMARY.md',
'type': 'string',
'title': 'File to use as table of contents',
'pattern': '^[0-9a-zA-Z ... ]+$'
}
},
'additionalProperties': false
},
'pdf': {
'type': 'object',
'title': 'PDF specific configurations',
'properties': {
'pageNumbers': {
'type': 'boolean',
'default': true,
'title': 'Add page numbers to the bottom of every page'
},
'fontSize': {
'type': 'integer',
'minimum': 8,
'maximum': 30,
'default': 12,
'title': 'Font size for the PDF output'
},
'fontFamily': {
'type': 'string',
'default': 'Arial',
'title': 'Font family for the PDF output'
},
'paperSize': {
'type': 'string',
'enum': ['a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'legal', 'letter'],
'default': 'a4',
'title': 'Paper size for the PDF'
},
'chapterMark': {
'type': 'string',
'enum': ['pagebreak', 'rule', 'both', 'none'],
'default': 'pagebreak',
'title': 'How to mark detected chapters'
},
'pageBreaksBefore': {
'type': 'string',
'default': '/',
'title': 'An XPath expression. Page breaks are inserted before the specified elements. To disable use the expression: "/"'
},
'margin': {
'type': 'object',
'properties': {
'right': {
'type': 'integer',
'title': 'Right Margin',
'minimum': 0,
'maximum': 100,
'default': 62
},
'left': {
'type': 'integer',
'title': 'Left Margin',
'minimum': 0,
'maximum': 100,
'default': 62
},
'top': {
'type': 'integer',
'title': 'Top Margin',
'minimum': 0,
'maximum': 100,
'default': 56
},
'bottom': {
'type': 'integer',
'title': 'Bottom Margin',
'minimum': 0,
'maximum': 100,
'default': 56
}
}
}
}
}
},
'required': [],
'definitions': {
'pluginsArray': {
'type': 'array',
'items': {
'oneOf': [
{ '$ref': '#/definitions/pluginObject' },
{ '$ref': '#/definitions/pluginString' }
]
}
},
'pluginsString': {
'type': 'string'
},
'pluginString': {
'type': 'string'
},
'pluginObject': {
'type': 'object',
'properties': {
'name': {
'type': 'string'
},
'version': {
'type': 'string'
}
},
'additionalProperties': false,
'required': ['name']
}
}
};

View File

@ -1,28 +0,0 @@
var jsonschema = require('jsonschema');
var jsonSchemaDefaults = require('json-schema-defaults');
var mergeDefaults = require('merge-defaults');
var schema = require('./schema');
var error = require('../utils/error');
// Validate a book.json content
// And return a mix with the default value
function validate(bookJson) {
var v = new jsonschema.Validator();
var result = v.validate(bookJson, schema, {
propertyName: 'config'
});
// Throw error
if (result.errors.length > 0) {
throw new error.ConfigurationError(new Error(result.errors[0].stack));
}
// Insert default values
var defaults = jsonSchemaDefaults(schema);
return mergeDefaults(bookJson, defaults);
}
module.exports = {
validate: validate
};

View File

@ -1,106 +0,0 @@
var _ = require('lodash');
var path = require('path');
var Promise = require('../utils/promise');
/*
A filesystem is an interface to read files
GitBook can works with a virtual filesystem, for example in the browser.
*/
// .readdir return files/folder as a list of string, folder ending with '/'
function pathIsFolder(filename) {
return _.last(filename) == '/' || _.last(filename) == '\\';
}
function FS() {
}
// Check if a file exists, run a Promise(true) if that's the case, Promise(false) otherwise
FS.prototype.exists = function(filename) {
// To implement for each fs
};
// Read a file and returns a promise with the content as a buffer
FS.prototype.read = function(filename) {
// To implement for each fs
};
// Read stat infos about a file
FS.prototype.stat = function(filename) {
// To implement for each fs
};
// List files/directories in a directory
FS.prototype.readdir = function(folder) {
// To implement for each fs
};
// These methods don't require to be redefined, by default it uses .exists, .read, .write, .list
// For optmization, it can be redefined:
// List files in a directory
FS.prototype.listFiles = function(folder) {
return this.readdir(folder)
.then(function(files) {
return _.reject(files, pathIsFolder);
});
};
// List all files in the fs
FS.prototype.listAllFiles = function(folder) {
var that = this;
return this.readdir(folder)
.then(function(files) {
return _.reduce(files, function(prev, file) {
return prev.then(function(output) {
var isDirectory = pathIsFolder(file);
if (!isDirectory) {
output.push(file);
return output;
} else {
return that.listAllFiles(path.join(folder, file))
.then(function(files) {
return output.concat(_.map(files, function(_file) {
return path.join(file, _file);
}));
});
}
});
}, Promise([]));
});
};
// Read a file as a string (utf-8)
FS.prototype.readAsString = function(filename) {
return this.read(filename)
.then(function(buf) {
return buf.toString('utf-8');
});
};
// Find a file in a folder (case incensitive)
// Return the real filename
FS.prototype.findFile = function findFile(root, filename) {
return this.listFiles(root)
.then(function(files) {
return _.find(files, function(file) {
return (file.toLowerCase() == filename.toLowerCase());
});
});
};
// Load a JSON file
// By default, fs only supports JSON
FS.prototype.loadAsObject = function(filename) {
return this.readAsString(filename)
.then(function(str) {
return JSON.parse(str);
});
};
module.exports = FS;

View File

@ -1,66 +0,0 @@
var _ = require('lodash');
var util = require('util');
var path = require('path');
var fs = require('../utils/fs');
var Promise = require('../utils/promise');
var BaseFS = require('./');
function NodeFS() {
BaseFS.call(this);
}
util.inherits(NodeFS, BaseFS);
// Check if a file exists, run a Promise(true) if that's the case, Promise(false) otherwise
NodeFS.prototype.exists = function(filename) {
return fs.exists(filename);
};
// Read a file and returns a promise with the content as a buffer
NodeFS.prototype.read = function(filename) {
return fs.readFile(filename);
};
// Read stat infos about a file
NodeFS.prototype.stat = function(filename) {
return fs.stat(filename);
};
// List files in a directory
NodeFS.prototype.readdir = function(folder) {
return fs.readdir(folder)
.then(function(files) {
return _.chain(files)
.map(function(file) {
if (file == '.' || file == '..') return;
var stat = fs.statSync(path.join(folder, file));
if (stat.isDirectory()) file = file + path.sep;
return file;
})
.compact()
.value();
});
};
// Load a JSON/JS file
NodeFS.prototype.loadAsObject = function(filename) {
return Promise()
.then(function() {
var jsFile;
try {
jsFile = require.resolve(filename);
// Invalidate node.js cache for livreloading
delete require.cache[jsFile];
return require(jsFile);
}
catch(err) {
return Promise.reject(err);
}
});
};
module.exports = NodeFS;

View File

@ -1,33 +0,0 @@
var semver = require('semver');
var pkg = require('../package.json');
var VERSION = pkg.version;
var VERSION_STABLE = VERSION.replace(/\-(\S+)/g, '');
var START_TIME = new Date();
// Verify that this gitbook version satisfies a requirement
// 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);
}
// Return templating/json context for gitbook itself
function getContext() {
return {
gitbook: {
version: pkg.version,
time: START_TIME
}
};
}
module.exports = {
version: pkg.version,
satisfies: satisfies,
getContext: getContext
};

View File

@ -1,7 +0,0 @@
var Book = require('./book');
var cli = require('./cli');
module.exports = {
Book: Book,
commands: cli.commands
};

View File

@ -1,67 +0,0 @@
var path = require('path');
var fs = require('./utils/fs');
var Promise = require('./utils/promise');
// Initialize folder structure for a book
// Read SUMMARY to created the right chapter
function initBook(book) {
var extensionToUse = '.md';
book.log.info.ln('init book at', book.root);
return fs.mkdirp(book.root)
.then(function() {
return book.config.load();
})
.then(function() {
book.log.info.ln('detect structure from SUMMARY (if it exists)');
return book.summary.load();
})
.then(function() {
var summary = book.summary.path || 'SUMMARY.md';
var articles = book.summary.flatten();
// Use extension of summary
extensionToUse = path.extname(summary);
// Readme doesn't have a path
if (!articles[0].path) {
articles[0].path = 'README' + extensionToUse;
}
// Summary doesn't exists? create one
if (!book.summary.path) {
articles.push({
title: 'Summary',
path: 'SUMMARY'+extensionToUse
});
}
// Create files that don't exist
return Promise.serie(articles, function(article) {
if (!article.path) return;
var absolutePath = book.resolve(article.path);
return fs.exists(absolutePath)
.then(function(exists) {
if(exists) {
book.log.info.ln('found', article.path);
return;
} else {
book.log.info.ln('create', article.path);
}
return fs.mkdirp(path.dirname(absolutePath))
.then(function() {
return fs.writeFile(absolutePath, '# '+article.title+'\n\n');
});
});
});
})
.then(function() {
book.log.info.ln('initialization is finished');
});
}
module.exports = initBook;

View File

@ -1,140 +0,0 @@
var util = require('util');
var path = require('path');
var crc = require('crc');
var FolderOutput = require('./folder')();
var Promise = require('../utils/promise');
var fs = require('../utils/fs');
var imagesUtil = require('../utils/images');
var location = require('../utils/location');
var DEFAULT_ASSETS_FOLDER = 'assets';
/*
Mixin to inline all the assets in a book:
- Outline <svg> tags
- Download remote images
- Convert .svg images as png
*/
module.exports = function assetsInliner(Base) {
Base = Base || FolderOutput;
function AssetsInliner() {
Base.apply(this, arguments);
// Map of svg already converted
this.svgs = {};
this.inlineSvgs = {};
// Map of images already downloaded
this.downloaded = {};
}
util.inherits(AssetsInliner, Base);
// Output a SVG buffer as a file
AssetsInliner.prototype.onOutputSVG = function(page, svg) {
this.log.debug.ln('output svg from', page.path);
// Convert svg buffer to a png file
return this.convertSVGBuffer(svg)
// Return relative path from the page
.then(function(filename) {
return page.relative('/' + filename);
});
};
// Output an image as a file
AssetsInliner.prototype.onOutputImage = function(page, src) {
var that = this;
return Promise()
// Download file if external
.then(function() {
if (!location.isExternal(src)) return;
return that.downloadAsset(src)
.then(function(_asset) {
src = '/' + _asset;
});
})
.then(function() {
// Resolve src to a relative filepath to the book's root
src = page.resolveLocal(src);
// Already a PNG/JPG/.. ?
if (path.extname(src).toLowerCase() != '.svg') {
return src;
}
// Convert SVG to PNG
return that.convertSVGFile(that.resolve(src));
})
// Return relative path from the page
.then(function(filename) {
return page.relative(filename);
});
};
// Download an asset if not already download; returns the output file
AssetsInliner.prototype.downloadAsset = function(src) {
if (this.downloaded[src]) return Promise(this.downloaded[src]);
var that = this;
var ext = path.extname(src);
var hash = crc.crc32(src).toString(16);
// Create new file
return this.createNewFile(DEFAULT_ASSETS_FOLDER, hash + ext)
.then(function(filename) {
that.downloaded[src] = filename;
that.log.debug.ln('downloading asset', src);
return fs.download(src, that.resolve(filename))
.thenResolve(filename);
});
};
// Convert a .svg into an .png
// Return the output filename for the .png
AssetsInliner.prototype.convertSVGFile = function(src) {
if (this.svgs[src]) return Promise(this.svgs[src]);
var that = this;
var hash = crc.crc32(src).toString(16);
// Create new file
return this.createNewFile(DEFAULT_ASSETS_FOLDER, hash + '.png')
.then(function(filename) {
that.svgs[src] = filename;
return imagesUtil.convertSVGToPNG(src, that.resolve(filename))
.thenResolve(filename);
});
};
// Convert an inline svg into an .png
// Return the output filename for the .png
AssetsInliner.prototype.convertSVGBuffer = function(buf) {
var that = this;
var hash = crc.crc32(buf).toString(16);
// Already converted?
if (this.inlineSvgs[hash]) return Promise(this.inlineSvgs[hash]);
return this.createNewFile(DEFAULT_ASSETS_FOLDER, hash + '.png')
.then(function(filename) {
that.inlineSvgs[hash] = filename;
return imagesUtil.convertSVGBufferToPNG(buf, that.resolve(filename))
.thenResolve(filename);
});
};
return AssetsInliner;
};

View File

@ -1,296 +0,0 @@
var _ = require('lodash');
var Ignore = require('ignore');
var path = require('path');
var Promise = require('../utils/promise');
var pathUtil = require('../utils/path');
var location = require('../utils/location');
var error = require('../utils/error');
var PluginsManager = require('../plugins');
var TemplateEngine = require('../template');
var gitbook = require('../gitbook');
/*
Output is like a stream interface for a parsed book
to output "something".
The process is mostly on the behavior of "onPage" and "onAsset"
*/
function Output(book, opts, parent) {
_.bindAll(this);
this.parent = parent;
this.opts = _.defaults({}, opts || {}, {
directoryIndex: true
});
this.book = book;
book.output = this;
this.log = this.book.log;
// Create plugins manager
this.plugins = new PluginsManager(this.book);
// Create template engine
this.template = new TemplateEngine(this);
// Files to ignore in output
this.ignore = Ignore();
}
// Default name for generator
Output.prototype.name = 'base';
// Default extension for output
Output.prototype.defaultExtension = '.html';
// Start the generation, for a parsed book
Output.prototype.generate = function() {
var that = this;
var isMultilingual = this.book.isMultilingual();
return Promise()
// Load all plugins
.then(function() {
return that.plugins.loadAll()
.then(function() {
that.template.addFilters(that.plugins.getFilters());
that.template.addBlocks(that.plugins.getBlocks());
});
})
// Transform the configuration
.then(function() {
return that.plugins.hook('config', that.book.config.dump())
.then(function(cfg) {
that.book.config.replace(cfg);
});
})
// Initialize the generation
.then(function() {
return that.plugins.hook('init');
})
.then(function() {
that.log.info.ln('preparing the generation');
return that.prepare();
})
// Process all files
.then(function() {
that.log.debug.ln('listing files');
return that.book.fs.listAllFiles(that.book.root);
})
// We want to process assets first, then pages
// Since pages can have logic based on existance of assets
.then(function(files) {
// Split into pages/assets
var byTypes = _.chain(files)
.filter(that.ignore.createFilter())
// Ignore file present in a language book
.filter(function(filename) {
return !(isMultilingual && that.book.isInLanguageBook(filename));
})
.groupBy(function(filename) {
return (that.book.hasPage(filename)? 'page' : 'asset');
})
.value();
return Promise.serie(byTypes.asset, function(filename) {
that.log.debug.ln('copy asset', filename);
return that.onAsset(filename);
})
.then(function() {
return Promise.serie(byTypes.page, function(filename) {
that.log.debug.ln('process page', filename);
return that.onPage(that.book.getPage(filename));
});
});
})
// Generate sub-books
.then(function() {
if (!that.book.isMultilingual()) return;
return Promise.serie(that.book.books, function(subbook) {
that.log.info.ln('');
that.log.info.ln('start generation of language "' + path.relative(that.book.root, subbook.root) + '"');
var out = that.onLanguageBook(subbook);
return out.generate();
});
})
// Finish the generation
.then(function() {
return that.plugins.hook('finish:before');
})
.then(function() {
that.log.debug.ln('finishing the generation');
return that.finish();
})
.then(function() {
return that.plugins.hook('finish');
})
.then(function() {
if (!that.book.isLanguageBook()) that.log.info.ln('');
that.log.info.ok('generation finished with success!');
});
};
// Prepare the generation
Output.prototype.prepare = function() {
this.ignore.addPattern(_.compact([
'.gitignore',
'.ignore',
'.bookignore',
'node_modules',
'_layouts',
// The configuration file should not be copied in the output
this.book.config.path,
// Structure file to ignore
this.book.summary.path,
this.book.langs.path
]));
};
// Write a page (parsable file), ex: markdown, etc
Output.prototype.onPage = function(page) {
return page.toHTML(this);
};
// Copy an asset file (non-parsable), ex: images, etc
Output.prototype.onAsset = function(filename) {
};
// Finish the generation
Output.prototype.finish = function() {
};
// Resolve an HTML link
Output.prototype.onRelativeLink = function(currentPage, href) {
var to = currentPage.followPage(href);
// Replace by an .html link
if (to) {
href = to.path;
// Recalcul as relative link
href = currentPage.relative(href);
// Replace .md by .html
href = this.toURL(href);
}
return href;
};
// Output a SVG buffer as a file
Output.prototype.onOutputSVG = function(page, svg) {
return null;
};
// Output an image as a file
// Normalize the relative link
Output.prototype.onOutputImage = function(page, imgFile) {
imgFile = page.resolveLocal(imgFile);
return page.relative(imgFile);
};
// Read a template by its source URL
Output.prototype.onGetTemplate = function(sourceUrl) {
throw new Error('template not found '+sourceUrl);
};
// Generate a source URL for a template
Output.prototype.onResolveTemplate = function(from, to) {
return path.resolve(path.dirname(from), to);
};
// Prepare output for a language book
Output.prototype.onLanguageBook = function(book) {
return new this.constructor(book, this.opts, this);
};
// ---- Utilities ----
// Return conetxt for the output itself
Output.prototype.getSelfContext = function() {
return {
name: this.name
};
};
// Return a default context for templates
Output.prototype.getContext = function() {
var ctx = _.extend(
{
output: this.getSelfContext()
},
this.book.getContext(),
this.book.langs.getContext(),
this.book.summary.getContext(),
this.book.glossary.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
// Result is an "absolute path relative to the output folder"
Output.prototype.resolveForPage = function(page, href) {
if (_.isString(page)) page = this.book.getPage(page);
href = page.relative(href);
return this.onRelativeLink(page, href);
};
// Filename for output
// READMEs are replaced by index.html
// /test/README.md -> /test/index.html
Output.prototype.outputPath = function(filename, ext) {
ext = ext || this.defaultExtension;
var output = filename;
if (
path.basename(filename, path.extname(filename)) == 'README' ||
output == this.book.readme.path
) {
output = path.join(path.dirname(output), 'index'+ext);
} else {
output = pathUtil.setExtension(output, ext);
}
return output;
};
// Filename for output
// /test/index.html -> /test/
Output.prototype.toURL = function(filename, ext) {
var href = this.outputPath(filename, ext);
if (path.basename(href) == 'index.html' && this.opts.directoryIndex) {
href = path.dirname(href) + '/';
}
return location.normalize(href);
};
module.exports = Output;

View File

@ -1,67 +0,0 @@
var path = require('path');
var util = require('util');
var folderOutput = require('./folder');
var Git = require('../utils/git');
var fs = require('../utils/fs');
var pathUtil = require('../utils/path');
var location = require('../utils/location');
/*
Mixin for output to resolve git conrefs
*/
module.exports = function conrefsLoader(Base) {
Base = folderOutput(Base);
function ConrefsLoader() {
Base.apply(this, arguments);
this.git = new Git();
}
util.inherits(ConrefsLoader, Base);
// Read a template by its source URL
ConrefsLoader.prototype.onGetTemplate = function(sourceURL) {
var that = this;
return this.git.resolve(sourceURL)
.then(function(filepath) {
// Is local file
if (!filepath) {
filepath = that.book.resolve(sourceURL);
} else {
that.book.log.debug.ln('resolve from git', sourceURL, 'to', filepath);
}
// Read file from absolute path
return fs.readFile(filepath)
.then(function(source) {
return {
src: source.toString('utf8'),
path: filepath
};
});
});
};
// Generate a source URL for a template
ConrefsLoader.prototype.onResolveTemplate = function(from, to) {
// If origin is in the book, we enforce result file to be in the book
if (this.book.isInBook(from)) {
var href = location.toAbsolute(to, path.dirname(from), '');
return this.book.resolve(href);
}
// If origin is in a git repository, we resolve file in the git repository
var gitRoot = this.git.resolveRoot(from);
if (gitRoot) {
return pathUtil.resolveInRoot(gitRoot, to);
}
// If origin is not in the book (include from a git content ref)
return path.resolve(path.dirname(from), to);
};
return ConrefsLoader;
};

View File

@ -1,199 +0,0 @@
var _ = require('lodash');
var util = require('util');
var juice = require('juice');
var command = require('../utils/command');
var fs = require('../utils/fs');
var Promise = require('../utils/promise');
var error = require('../utils/error');
var WebsiteOutput = require('./website');
var assetsInliner = require('./assets-inliner');
function _EbookOutput() {
WebsiteOutput.apply(this, arguments);
// ebook-convert does not support link like "./"
this.opts.directoryIndex = false;
}
util.inherits(_EbookOutput, WebsiteOutput);
var EbookOutput = assetsInliner(_EbookOutput);
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
EbookOutput.prototype.finish = function() {
var that = this;
if (that.book.isMultilingual()) {
return EbookOutput.super_.prototype.finish.apply(that);
}
return Promise()
.then(function() {
return EbookOutput.super_.prototype.finish.apply(that);
})
// Generate SUMMARY.html
.then(function() {
return that.render('summary', that.getContext())
.then(function(html) {
return that.writeFile(
'SUMMARY.html',
html
);
});
})
// Start ebook-convert
.then(function() {
return that.ebookConvertOption();
})
.then(function(options) {
if (!that.opts.format) return;
var cmd = [
'ebook-convert',
that.resolve('SUMMARY.html'),
that.resolve('index.'+that.opts.format),
command.optionsToShellArgs(options)
].join(' ');
return command.exec(cmd)
.progress(function(data) {
that.book.log.debug(data);
})
.fail(function(err) {
if (err.code == 127) {
throw error.RequireInstallError({
cmd: 'ebook-convert',
install: 'Install it from Calibre: https://calibre-ebook.com'
});
}
throw error.EbookError(err);
});
});
};
// Generate header/footer for PDF
EbookOutput.prototype.getPDFTemplate = function(tpl) {
var that = this;
var context = _.extend(
{
// Nunjucks context mapping to ebook-convert templating
page: {
num: '_PAGENUM_',
title: '_TITLE_',
section: '_SECTION_'
}
},
this.getContext()
);
return this.render('pdf_'+tpl, context)
// Inline css, include css relative to the output folder
.then(function(output) {
return Promise.nfcall(juice.juiceResources, output, {
webResources: {
relativeTo: that.root()
}
});
});
};
// Locate the cover file to use
// Use configuration or search a "cover.jpg" file
// For multi-lingual book, it can use the one from the main book
EbookOutput.prototype.locateCover = function() {
var cover = this.book.config.get('cover', 'cover.jpg');
// Resolve to absolute
cover = this.resolve(cover);
// Cover doesn't exist and multilingual?
if (!fs.existsSync(cover)) {
if (this.parent) return this.parent.locateCover();
else return undefined;
}
return cover;
};
// Generate options for ebook-convert
EbookOutput.prototype.ebookConvertOption = function() {
var that = this;
var options = {
'--cover': this.locateCover(),
'--title': that.book.config.get('title'),
'--comments': that.book.config.get('description'),
'--isbn': that.book.config.get('isbn'),
'--authors': that.book.config.get('author'),
'--language': that.book.config.get('language'),
'--book-producer': 'GitBook',
'--publisher': 'GitBook',
'--chapter': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter \')]',
'--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 \')]',
'--level3-toc': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter-3 \')]',
'--no-chapters-in-toc': true,
'--max-levels': '1',
'--breadth-first': true
};
if (that.opts.format == 'epub') {
options = _.extend(options, {
'--dont-split-on-page-breaks': true
});
}
if (that.opts.format != 'pdf') return Promise(options);
var pdfOptions = that.book.config.get('pdf');
options = _.extend(options, {
'--chapter-mark': String(pdfOptions.chapterMark),
'--page-breaks-before': String(pdfOptions.pageBreaksBefore),
'--margin-left': String(pdfOptions.margin.left),
'--margin-right': String(pdfOptions.margin.right),
'--margin-top': String(pdfOptions.margin.top),
'--margin-bottom': String(pdfOptions.margin.bottom),
'--pdf-default-font-size': String(pdfOptions.fontSize),
'--pdf-mono-font-size': String(pdfOptions.fontSize),
'--paper-size': String(pdfOptions.paperSize),
'--pdf-page-numbers': Boolean(pdfOptions.pageNumbers),
'--pdf-header-template': that.getPDFTemplate('header'),
'--pdf-footer-template': that.getPDFTemplate('footer'),
'--pdf-sans-family': String(pdfOptions.fontFamily)
});
return that.getPDFTemplate('header')
.then(function(tpl) {
options['--pdf-header-template'] = tpl;
return that.getPDFTemplate('footer');
})
.then(function(tpl) {
options['--pdf-footer-template'] = tpl;
return options;
});
};
// Don't write multi-lingual index for wbook
EbookOutput.prototype.outputMultilingualIndex = function() {
};
module.exports = EbookOutput;

View File

@ -1,152 +0,0 @@
var _ = require('lodash');
var util = require('util');
var path = require('path');
var Output = require('./base');
var fs = require('../utils/fs');
var pathUtil = require('../utils/path');
var Promise = require('../utils/promise');
/*
This output requires the native fs module to output
book as a directory (mapping assets and pages)
*/
module.exports = function folderOutput(Base) {
Base = Base || Output;
function FolderOutput() {
Base.apply(this, arguments);
this.opts.root = path.resolve(this.opts.root || this.book.resolve('_book'));
}
util.inherits(FolderOutput, Base);
// Copy an asset file (non-parsable), ex: images, etc
FolderOutput.prototype.onAsset = function(filename) {
return this.copyFile(
this.book.resolve(filename),
filename
);
};
// Prepare the generation by creating the output folder
FolderOutput.prototype.prepare = function() {
var that = this;
return Promise()
.then(function() {
return FolderOutput.super_.prototype.prepare.apply(that);
})
// Cleanup output folder
.then(function() {
that.log.debug.ln('removing previous output directory');
return fs.rmDir(that.root())
.fail(function() {
return Promise();
});
})
// Create output folder
.then(function() {
that.log.debug.ln('creating output directory');
return fs.mkdirp(that.root());
})
// Add output folder to ignored files
.then(function() {
that.ignore.addPattern([
path.relative(that.book.root, that.root())
]);
});
};
// Prepare output for a language book
FolderOutput.prototype.onLanguageBook = function(book) {
return new this.constructor(book, _.extend({}, this.opts, {
// Language output should be output in sub-directory of output
root: path.resolve(this.root(), book.language)
}), this);
};
// ----- Utility methods -----
// Return path to the root folder
FolderOutput.prototype.root = function() {
return this.opts.root;
};
// Resolve a file in the output directory
FolderOutput.prototype.resolve = function(filename) {
return pathUtil.resolveInRoot.apply(null, [this.root()].concat(_.toArray(arguments)));
};
// Copy a file to the output
FolderOutput.prototype.copyFile = function(from, to) {
var that = this;
return Promise()
.then(function() {
to = that.resolve(to);
var folder = path.dirname(to);
// Ensure folder exists
return fs.mkdirp(folder);
})
.then(function() {
return fs.copy(from, to);
});
};
// Write a file/buffer to the output folder
FolderOutput.prototype.writeFile = function(filename, buf) {
var that = this;
return Promise()
.then(function() {
filename = that.resolve(filename);
var folder = path.dirname(filename);
// Ensure folder exists
return fs.mkdirp(folder);
})
// Write the file
.then(function() {
return fs.writeFile(filename, buf);
});
};
// Return true if a file exists in the output folder
FolderOutput.prototype.hasFile = function(filename) {
var that = this;
return Promise()
.then(function() {
return fs.exists(that.resolve(filename));
});
};
// Create a new unique file
// Returns its filename
FolderOutput.prototype.createNewFile = function(base, filename) {
var that = this;
if (!filename) {
filename = path.basename(filename);
base = path.dirname(base);
}
return fs.uniqueFilename(this.resolve(base), filename)
.then(function(out) {
out = path.join(base, out);
return fs.ensure(that.resolve(out))
.thenResolve(out);
});
};
return FolderOutput;
};

View File

@ -1,47 +0,0 @@
var conrefsLoader = require('./conrefs');
var JSONOutput = conrefsLoader();
JSONOutput.prototype.name = 'json';
// Don't copy asset on JSON output
JSONOutput.prototype.onAsset = function(filename) {};
// Write a page (parsable file)
JSONOutput.prototype.onPage = function(page) {
var that = this;
// Parse the page
return page.toHTML(this)
// Write as json
.then(function() {
var json = page.getOutputContext(that);
// Delete some private properties
delete json.config;
// Specify JSON output version
json.version = '3';
return that.writeFile(
page.withExtension('.json'),
JSON.stringify(json, null, 4)
);
});
};
// At the end of generation, generate README.json for multilingual books
JSONOutput.prototype.finish = function() {
if (!this.book.isMultilingual()) return;
// Copy README.json from main book
var mainLanguage = this.book.langs.getDefault().id;
return this.copyFile(
this.resolve(mainLanguage, 'README.json'),
'README.json'
);
};
module.exports = JSONOutput;

View File

@ -1,233 +0,0 @@
var _ = require('lodash');
var path = require('path');
var util = require('util');
var nunjucks = require('nunjucks');
var I18n = require('i18n-t');
var Promise = require('../utils/promise');
var location = require('../utils/location');
var fs = require('../utils/fs');
var defaultFilters = require('../template/filters');
var FSLoader = require('../template/fs-loader');
var conrefsLoader = require('./conrefs');
var Output = require('./base');
// Directory for a theme with the templates
function templatesPath(dir) {
return path.join(dir, '_layouts');
}
function _WebsiteOutput() {
Output.apply(this, arguments);
// Nunjucks environment
this.env;
// Plugin instance for the main theme
this.theme;
// Plugin instance for the default theme
this.defaultTheme;
// Resources loaded from plugins
this.resources;
// i18n for themes
this.i18n = new I18n();
}
util.inherits(_WebsiteOutput, Output);
var WebsiteOutput = conrefsLoader(_WebsiteOutput);
// Name of the generator
// It's being used as a prefix for templates
WebsiteOutput.prototype.name = 'website';
// Load and setup the theme
WebsiteOutput.prototype.prepare = function() {
var that = this;
return Promise()
.then(function() {
return WebsiteOutput.super_.prototype.prepare.apply(that);
})
.then(function() {
// This list is ordered to give priority to templates in the book
var searchPaths = _.pluck(that.plugins.list(), 'root');
// The book itself can contains a "_layouts" folder
searchPaths.unshift(that.book.root);
// Load i18n
_.each(searchPaths.concat().reverse(), function(searchPath) {
var i18nRoot = path.resolve(searchPath, '_i18n');
if (!fs.existsSync(i18nRoot)) return;
that.i18n.load(i18nRoot);
});
that.env = new nunjucks.Environment(new FSLoader(_.map(searchPaths, templatesPath)));
// Add GitBook default filters
_.each(defaultFilters, function(fn, filter) {
that.env.addFilter(filter, fn);
});
// Translate using _i18n locales
that.env.addFilter('t', function(s) {
return that.i18n.t(that.book.config.get('language'), s);
});
// Transform an absolute path into a relative path
// using this.ctx.page.path
that.env.addFilter('resolveFile', function(href) {
return location.normalize(that.resolveForPage(this.ctx.file.path, href));
});
// Test if a file exists
that.env.addFilter('fileExists', function(href) {
return fs.existsSync(that.resolve(href));
});
// Transform a '.md' into a '.html' (README -> index)
that.env.addFilter('contentURL', function(s) {
return that.toURL(s);
});
// Relase path to an asset
that.env.addFilter('resolveAsset', function(href) {
href = path.join('gitbook', href);
// Resolve for current file
if (this.ctx.file) {
href = that.resolveForPage(this.ctx.file.path, '/' + href);
}
// Use assets from parent
if (that.book.isLanguageBook()) {
href = path.join('../', href);
}
return location.normalize(href);
});
})
// Copy assets from themes before copying files from book
.then(function() {
if (that.book.isLanguageBook()) return;
// Assets from the book are already copied
// Copy assets from plugins (start with default plugins)
return Promise.serie(that.plugins.list().reverse(), function(plugin) {
// Copy assets only if exists (don't fail otherwise)
var assetFolder = path.join(plugin.root, '_assets', that.name);
if (!fs.existsSync(assetFolder)) return;
that.log.debug.ln('copy assets from theme', assetFolder);
return fs.copyDir(
assetFolder,
that.resolve('gitbook'),
{
deleteFirst: false,
overwrite: true,
confirm: true
}
);
});
})
// Load resources for plugins
.then(function() {
return that.plugins.getResources(that.name)
.then(function(resources) {
that.resources = resources;
});
});
};
// Write a page (parsable file)
WebsiteOutput.prototype.onPage = function(page) {
var that = this;
// Parse the page
return page.toHTML(this)
// Render the page template with the same context as the json output
.then(function() {
return that.render('page', page.getOutputContext(that));
})
// Write the HTML file
.then(function(html) {
return that.writeFile(
that.outputPath(page.path),
html
);
});
};
// Finish generation, create ebook using ebook-convert
WebsiteOutput.prototype.finish = function() {
var that = this;
return Promise()
.then(function() {
return WebsiteOutput.super_.prototype.finish.apply(that);
})
// Copy assets from plugins
.then(function() {
if (that.book.isLanguageBook()) return;
return that.plugins.copyResources(that.name, that.resolve('gitbook'));
})
// Generate homepage to select languages
.then(function() {
if (!that.book.isMultilingual()) return;
return that.outputMultilingualIndex();
});
};
// ----- Utilities ----
// Write multi-languages index
WebsiteOutput.prototype.outputMultilingualIndex = function() {
var that = this;
return that.render('languages', that.getContext())
.then(function(html) {
return that.writeFile(
'index.html',
html
);
});
};
// Render a template using nunjucks
// Templates are stored in `_layouts` folders
WebsiteOutput.prototype.render = function(tpl, context) {
var filename = this.templateName(tpl);
context = _.extend(context, {
template: {
self: filename
},
plugins: {
resources: this.resources
},
options: this.opts
});
return Promise.nfcall(this.env.render.bind(this.env), filename, context);
};
// Return a complete name for a template
WebsiteOutput.prototype.templateName = function(name) {
return path.join(this.name, name+'.html');
};
module.exports = WebsiteOutput;

View File

@ -1,280 +0,0 @@
var _ = require('lodash');
var url = require('url');
var cheerio = require('cheerio');
var domSerializer = require('dom-serializer');
var slug = require('github-slugid');
var Promise = require('../utils/promise');
var location = require('../utils/location');
// Selector to ignore
var ANNOTATION_IGNORE = '.no-glossary,code,pre,a,script,h1,h2,h3,h4,h5,h6';
function HTMLPipeline(htmlString, opts) {
_.bindAll(this);
this.opts = _.defaults(opts || {}, {
// Called once the description has been found
onDescription: function(description) { },
// Calcul new href for a relative link
onRelativeLink: _.identity,
// Output an image
onImage: _.identity,
// Syntax highlighting
onCodeBlock: _.identity,
// Output a svg, if returns null the svg is kept inlined
onOutputSVG: _.constant(null),
// Words to annotate
annotations: [],
// When an annotation is applied
onAnnotation: function () { }
});
this.$ = cheerio.load(htmlString, {
// We should parse html without trying to normalize too much
xmlMode: false,
// SVG need some attributes to use uppercases
lowerCaseAttributeNames: false,
lowerCaseTags: false
});
}
// Transform a query of elements in the page
HTMLPipeline.prototype._transform = function(query, fn) {
var that = this;
var $elements = this.$(query);
return Promise.serie($elements, function(el) {
var $el = that.$(el);
return fn.call(that, $el);
});
};
// Normalize links
HTMLPipeline.prototype.transformLinks = function() {
return this._transform('a', function($a) {
var href = $a.attr('href');
if (!href) return;
if (location.isAnchor(href)) {
// Don't "change" anchor links
} else if (location.isRelative(href)) {
// Preserve anchor
var parsed = url.parse(href);
var filename = this.opts.onRelativeLink(parsed.pathname);
$a.attr('href', filename + (parsed.hash || ''));
} else {
// External links
$a.attr('target', '_blank');
}
});
};
// Normalize images
HTMLPipeline.prototype.transformImages = function() {
return this._transform('img', function($img) {
return Promise(this.opts.onImage($img.attr('src')))
.then(function(filename) {
$img.attr('src', filename);
});
});
};
// Normalize code blocks
HTMLPipeline.prototype.transformCodeBlocks = function() {
return this._transform('code', function($code) {
// Extract language
var lang = _.chain(
($code.attr('class') || '').split(' ')
)
.map(function(cl) {
// Markdown
if (cl.search('lang-') === 0) return cl.slice('lang-'.length);
// Asciidoc
if (cl.search('language-') === 0) return cl.slice('language-'.length);
return null;
})
.compact()
.first()
.value();
var source = $code.text();
return Promise(this.opts.onCodeBlock(source, lang))
.then(function(blk) {
if (blk.html === false) {
$code.text(blk.body);
} else {
$code.html(blk.body);
}
});
});
};
// Add ID to headings
HTMLPipeline.prototype.transformHeadings = function() {
var that = this;
this.$('h1,h2,h3,h4,h5,h6').each(function() {
var $h = that.$(this);
// Already has an ID?
if ($h.attr('id')) return;
$h.attr('id', slug($h.text()));
});
};
// Outline SVG from the HML
HTMLPipeline.prototype.transformSvgs = function() {
var that = this;
return this._transform('svg', function($svg) {
var content = [
'<?xml version="1.0" encoding="UTF-8"?>',
renderDOM(that.$, $svg)
].join('\n');
return Promise(that.opts.onOutputSVG(content))
.then(function(filename) {
if (!filename) return;
$svg.replaceWith(that.$('<img>').attr('src', filename));
});
});
};
// Annotate the content
HTMLPipeline.prototype.applyAnnotations = function() {
var that = this;
_.each(this.opts.annotations, function(annotation) {
var searchRegex = new RegExp( '\\b(' + pregQuote(annotation.name.toLowerCase()) + ')\\b' , 'gi' );
that.$('*').each(function() {
var $this = that.$(this);
if (
$this.is(ANNOTATION_IGNORE) ||
$this.parents(ANNOTATION_IGNORE).length > 0
) return;
replaceText(that.$, this, searchRegex, function(match) {
that.opts.onAnnotation(annotation);
return '<a href="' + that.opts.onRelativeLink(annotation.href) + '" '
+ 'class="glossary-term" title="'+_.escape(annotation.description)+'">'
+ match
+ '</a>';
});
});
});
};
// Extract page description from html
// This can totally be improved
HTMLPipeline.prototype.extractDescription = function() {
var $p = this.$('p').first();
var description = $p.text().trim().slice(0, 155);
this.opts.onDescription(description);
};
// Write content to the pipeline
HTMLPipeline.prototype.output = function() {
var that = this;
return Promise()
.then(this.extractDescription)
.then(this.transformImages)
.then(this.transformHeadings)
.then(this.transformCodeBlocks)
.then(this.transformSvgs)
.then(this.applyAnnotations)
// Transform of links should be applied after annotations
// because annotations are created as links
.then(this.transformLinks)
.then(function() {
return renderDOM(that.$);
});
};
// Render a cheerio DOM as html
function renderDOM($, dom, options) {
if (!dom && $._root && $._root.children) {
dom = $._root.children;
}
options = options|| dom.options || $._options;
return domSerializer(dom, options);
}
// Replace text in an element
function replaceText($, el, search, replace, text_only ) {
return $(el).each(function(){
var node = this.firstChild,
val,
new_val,
// Elements to be removed at the end.
remove = [];
// Only continue if firstChild exists.
if ( node ) {
// Loop over all childNodes.
while (node) {
// Only process text nodes.
if ( node.nodeType === 3 ) {
// The original node value.
val = node.nodeValue;
// The new value.
new_val = val.replace( search, replace );
// Only replace text if the new value is actually different!
if ( new_val !== val ) {
if ( !text_only && /</.test( new_val ) ) {
// The new value contains HTML, set it in a slower but far more
// robust way.
$(node).before( new_val );
// Don't remove the node yet, or the loop will lose its place.
remove.push( node );
} else {
// The new value contains no HTML, so it can be set in this
// very fast, simple way.
node.nodeValue = new_val;
}
}
}
node = node.nextSibling;
}
}
// Time to remove those elements!
if (remove.length) $(remove).remove();
});
}
function pregQuote( str ) {
return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, '\\$1');
}
module.exports = HTMLPipeline;

View File

@ -1,247 +0,0 @@
var _ = require('lodash');
var path = require('path');
var direction = require('direction');
var fm = require('front-matter');
var error = require('../utils/error');
var pathUtil = require('../utils/path');
var location = require('../utils/location');
var parsers = require('../parsers');
var gitbook = require('../gitbook');
var pluginCompatibility = require('../plugins/compatibility');
var HTMLPipeline = require('./html');
/*
A page represent a parsable file in the book (Markdown, Asciidoc, etc)
*/
function Page(book, filename) {
if (!(this instanceof Page)) return new Page(book, filename);
var extension;
_.bindAll(this);
this.book = book;
this.log = this.book.log;
// Map of attributes from YAML frontmatter
// Description is also extracted by default from content
this.attributes = {};
// Current content
this.content = '';
// Relative path to the page
this.path = location.normalize(filename);
// Absolute path to the page
this.rawPath = this.book.resolve(filename);
// Last modification date
this.mtime = 0;
// Can we parse it?
extension = path.extname(this.path);
this.parser = parsers.getByExt(extension);
if (!this.parser) throw error.ParsingError(new Error('Can\'t parse file "'+this.path+'"'));
this.type = this.parser.name;
}
// Return the filename of the page with another extension
// "README.md" -> "README.html"
Page.prototype.withExtension = function(ext) {
return pathUtil.setExtension(this.path, ext);
};
// Resolve a filename relative to this page
// It returns a path relative to the book root folder
Page.prototype.resolveLocal = function() {
var dir = path.dirname(this.path);
var file = path.join.apply(path, _.toArray(arguments));
return location.toAbsolute(file, dir, '');
};
// Resolve a filename relative to this page
// It returns an absolute path for the FS
Page.prototype.resolve = function() {
return this.book.resolve(this.resolveLocal.apply(this, arguments));
};
// Convert an absolute path (in the book) to a relative path from this page
Page.prototype.relative = function(name) {
// Convert /test.png -> test.png
name = location.toAbsolute(name, '', '');
return location.relative(
this.resolve('.') + '/',
this.book.resolve(name)
);
};
// Return a page result of a relative page from this page
Page.prototype.followPage = function(filename) {
var absPath = this.resolveLocal(filename);
return this.book.getPage(absPath);
};
// Update content of the page
Page.prototype.update = function(content) {
this.content = content;
};
// Read the page as a string
Page.prototype.read = function() {
var that = this;
return this.book.statFile(this.path)
.then(function(stat) {
that.mtime = stat.mtime;
return that.book.readFile(that.path);
})
.then(this.update);
};
// Return templating context for this page
// This is used both for themes and page parsing
Page.prototype.getContext = function() {
var article = this.book.summary.getArticle(this);
var next = article? article.next() : null;
var prev = article? article.prev() : null;
// Detect text direction in this page
var dir = this.book.config.get('direction');
if (!dir) {
dir = direction(this.content);
if (dir == 'neutral') dir = null;
}
return {
file: {
path: this.path,
mtime: this.mtime,
type: this.type
},
page: _.extend({}, this.attributes, {
title: article? article.title : null,
next: next? next.getContext() : null,
previous: prev? prev.getContext() : null,
level: article? article.level : null,
depth: article? article.depth() : 0,
content: this.content,
dir: dir
})
};
};
// Return complete context for templating (page + book + summary + ...)
Page.prototype.getOutputContext = function(output) {
return _.extend({}, this.getContext(), output.getContext());
};
// Parse the page and return its content
Page.prototype.toHTML = function(output) {
var that = this;
this.log.debug.ln('start parsing file', this.path);
// Call a hook in the output
// using an utility to "keep" compatibility with gitbook 2
function hook(name) {
return pluginCompatibility.pageHook(that, function(ctx) {
return output.plugins.hook(name, ctx);
})
.then(function(result) {
if(_.isString(result)) that.update(result);
});
}
return this.read()
// Parse yaml front matter
.then(function() {
var parsed = fm(that.content);
// Extract attributes
that.attributes = parsed.attributes;
// Keep only the body
that.update(parsed.body);
})
.then(function() {
return hook('page:before');
})
// Pre-process page with parser
.then(function() {
return that.parser.page.prepare(that.content)
.then(that.update);
})
// Render template
.then(function() {
return output.template.render(that.content, that.getOutputContext(output), {
path: that.path
})
.then(that.update);
})
// Render markup using the parser
.then(function() {
return that.parser.page(that.content)
.then(function(out) {
that.update(out.content);
});
})
// Post process templating
.then(function() {
return output.template.postProcess(that.content)
.then(that.update);
})
// Normalize HTML output
.then(function() {
var pipelineOpts = {
onRelativeLink: _.partial(output.onRelativeLink, that),
onImage: _.partial(output.onOutputImage, that),
onOutputSVG: _.partial(output.onOutputSVG, that),
// Use 'code' template block
onCodeBlock: function(source, lang) {
return output.template.applyBlock('code', {
body: source,
kwargs: {
language: lang
}
});
},
// Extract description from page's content if no frontmatter
onDescription: function(description) {
if (that.attributes.description) return;
that.attributes.description = description;
},
// Convert glossary entries to annotations
annotations: that.book.glossary.annotations()
};
var pipeline = new HTMLPipeline(that.content, pipelineOpts);
return pipeline.output()
.then(that.update);
})
.then(function() {
return hook('page');
})
// Return content itself
.then(function() {
return that.content;
});
};
module.exports = Page;

View File

@ -1,70 +0,0 @@
var _ = require('lodash');
var path = require('path');
var markdownParser = require('gitbook-markdown');
var asciidocParser = require('gitbook-asciidoc');
var Promise = require('./utils/promise');
// This list is ordered by priority of parsers to use
var PARSERS = [
createParser(markdownParser, {
name: 'markdown',
extensions: ['.md', '.markdown', '.mdown']
}),
createParser(asciidocParser, {
name: 'asciidoc',
extensions: ['.adoc', '.asciidoc']
})
];
// Prepare and compose a parser
function createParser(parser, base) {
var nparser = base;
nparser.glossary = Promise.wrapfn(parser.glossary);
nparser.glossary.toText = Promise.wrapfn(parser.glossary.toText);
nparser.summary = Promise.wrapfn(parser.summary);
nparser.summary.toText = Promise.wrapfn(parser.summary.toText);
nparser.langs = Promise.wrapfn(parser.langs);
nparser.langs.toText = Promise.wrapfn(parser.langs.toText);
nparser.readme = Promise.wrapfn(parser.readme);
nparser.page = Promise.wrapfn(parser.page);
nparser.page.prepare = Promise.wrapfn(parser.page.prepare || _.identity);
nparser.inline = Promise.wrapfn(parser.inline);
return nparser;
}
// Return a specific parser
function getParser(name) {
return _.find(PARSERS, {
name: name
});
}
// Return a specific parser according to an extension
function getParserByExt(ext) {
return _.find(PARSERS, function(input) {
return input.name == ext || _.contains(input.extensions, ext);
});
}
// Return parser for a file
function getParserForFile(filename) {
return getParser(path.extname(filename));
}
module.exports = {
all: PARSERS,
extensions: _.flatten(_.pluck(PARSERS, 'extensions')),
get: getParser,
getByExt: getParserByExt,
getForFile: getParserForFile
};

View File

@ -1,45 +0,0 @@
var _ = require('lodash');
var error = require('../utils/error');
/*
Return the context for a plugin.
It tries to keep compatibilities with GitBook v2
*/
function pluginCtx(plugin) {
var book = plugin.book;
var ctx = book;
return ctx;
}
// Call a function "fn" with a context of page similar to the one in GitBook v2
function pageHook(page, fn) {
var ctx = {
type: page.type,
content: page.content,
path: page.path,
rawPath: page.rawPath
};
// Deprecate sections
error.deprecateField(ctx, 'sections', [
{ content: ctx.content }
], '"sections" property is deprecated, use page.content instead');
return fn(ctx)
.then(function(result) {
if (!result) return undefined;
if (result.content) {
return result.content;
}
if (result.sections) {
return _.pluck(result.sections, 'content').join('\n');
}
});
}
module.exports = {
pluginCtx: pluginCtx,
pageHook: pageHook
};

View File

@ -1,188 +0,0 @@
var _ = require('lodash');
var path = require('path');
var Promise = require('../utils/promise');
var fs = require('../utils/fs');
var BookPlugin = require('./plugin');
var registry = require('./registry');
var pluginsConfig = require('../config/plugins');
/*
PluginsManager is an interface to work with multiple plugins at once:
- Extract assets from plugins
- Call hooks for all plugins, etc
*/
function PluginsManager(book) {
this.book = book;
this.log = this.book.log;
this.plugins = [];
_.bindAll(this);
}
// Returns the list of plugins
PluginsManager.prototype.list = function() {
return this.plugins;
};
// Return count of plugins loaded
PluginsManager.prototype.count = function() {
return _.size(this.plugins);
};
// Returns a plugin by its name
PluginsManager.prototype.get = function(name) {
return _.find(this.plugins, {
id: name
});
};
// Load a plugin (could be a BookPlugin or {name,path})
PluginsManager.prototype.load = function(plugin) {
var that = this;
if (_.isArray(plugin)) {
return Promise.serie(plugin, that.load);
}
return Promise()
// Initiate and load the plugin
.then(function() {
if (!(plugin instanceof BookPlugin)) {
plugin = new BookPlugin(that.book, plugin.name, plugin.path);
}
if (that.get(plugin.id)) {
throw new Error('Plugin "'+plugin.id+'" is already loaded');
}
if (plugin.isLoaded()) return plugin;
else return plugin.load()
.thenResolve(plugin);
})
// Setup the plugin
.then(this._setup);
};
// Load all plugins from the book's configuration
PluginsManager.prototype.loadAll = function() {
var that = this;
var pluginNames = _.pluck(this.book.config.get('plugins'), 'name');
return registry.list(this.book)
.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
// Register its filter, blocks, etc
PluginsManager.prototype._setup = function(plugin) {
this.plugins.push(plugin);
};
// Install all plugins for the book
PluginsManager.prototype.install = function() {
var that = this;
var plugins = _.filter(this.book.config.get('plugins'), function(plugin) {
return !pluginsConfig.isDefaultPlugin(plugin.name);
});
if (plugins.length == 0) {
this.log.info.ln('nothing to install!');
return Promise(0);
}
this.log.info.ln('installing', plugins.length, 'plugins');
return Promise.serie(plugins, function(plugin) {
return registry.install(that.book, plugin.name, plugin.version);
})
.thenResolve(plugins.length);
};
// Call a hook on all plugins to transform an input
PluginsManager.prototype.hook = function(name, input) {
return Promise.reduce(this.plugins, function(current, plugin) {
return plugin.hook(name, current);
}, input);
};
// Extract all resources for a namespace
PluginsManager.prototype.getResources = function(namespace) {
return Promise.reduce(this.plugins, function(out, plugin) {
return plugin.getResources(namespace)
.then(function(pluginResources) {
_.each(BookPlugin.RESOURCES, function(resourceType) {
out[resourceType] = (out[resourceType] || []).concat(pluginResources[resourceType] || []);
});
return out;
});
}, {});
};
// Copy all resources for a plugin
PluginsManager.prototype.copyResources = function(namespace, outputRoot) {
return Promise.serie(this.plugins, function(plugin) {
return plugin.getResources(namespace)
.then(function(resources) {
if (!resources.assets) return;
var input = path.resolve(plugin.root, resources.assets);
var output = path.resolve(outputRoot, plugin.npmId);
return fs.copyDir(input, output);
});
});
};
// Get all filters and blocks
PluginsManager.prototype.getFilters = function() {
return _.reduce(this.plugins, function(out, plugin) {
var filters = plugin.getFilters();
return _.extend(out, filters);
}, {});
};
PluginsManager.prototype.getBlocks = function() {
return _.reduce(this.plugins, function(out, plugin) {
var blocks = plugin.getBlocks();
return _.extend(out, blocks);
}, {});
};
module.exports = PluginsManager;

View File

@ -1,288 +0,0 @@
var _ = require('lodash');
var path = require('path');
var url = require('url');
var resolve = require('resolve');
var mergeDefaults = require('merge-defaults');
var jsonschema = require('jsonschema');
var jsonSchemaDefaults = require('json-schema-defaults');
var Promise = require('../utils/promise');
var error = require('../utils/error');
var gitbook = require('../gitbook');
var registry = require('./registry');
var compatibility = require('./compatibility');
var HOOKS = [
'init', 'finish', 'finish:before', 'config', 'page', 'page:before'
];
var RESOURCES = ['js', 'css'];
// Return true if an error is a "module not found"
// Wait on https://github.com/substack/node-resolve/pull/81 to be merged
function isModuleNotFound(err) {
return err.message.indexOf('Cannot find module') >= 0;
}
function BookPlugin(book, pluginId, pluginFolder) {
this.book = book;
this.log = this.book.log.prefix(pluginId);
this.id = pluginId;
this.npmId = registry.npmId(pluginId);
this.root = pluginFolder;
this.packageInfos = undefined;
this.content = undefined;
// Cache for resources
this._resources = {};
_.bindAll(this);
}
// Return true if plugin has been loaded correctly
BookPlugin.prototype.isLoaded = function() {
return Boolean(this.packageInfos && this.content);
};
// Bind a function to the plugin's context
BookPlugin.prototype.bind = function(fn) {
return fn.bind(compatibility.pluginCtx(this));
};
// Load this plugin from its root folder
BookPlugin.prototype.load = function(folder) {
var that = this;
if (this.isLoaded()) {
return Promise.reject(new Error('Plugin "' + this.id + '" is already loaded'));
}
// Try loading plugins from different location
var p = Promise()
.then(function() {
// Locate plugin and load pacjage.json
try {
var res = resolve.sync('./package.json', { basedir: that.root });
that.root = path.dirname(res);
that.packageInfos = require(res);
} catch (err) {
if (!isModuleNotFound(err)) throw err;
that.packageInfos = undefined;
that.content = undefined;
return;
}
// Load plugin JS content
try {
that.content = require(that.root);
} catch(err) {
// It's no big deal if the plugin doesn't have an "index.js"
// (For example: themes)
if (isModuleNotFound(err)) {
that.content = {};
} else {
throw new error.PluginError(err, {
plugin: that.id
});
}
}
})
.then(that.validate)
// Validate the configuration and update it
.then(function() {
var config = that.book.config.get(that.getConfigKey(), {});
return that.validateConfig(config);
})
.then(function(config) {
that.book.config.set(that.getConfigKey(), config);
});
this.log.info('loading plugin "' + this.id + '"... ');
return this.log.info.promise(p);
};
// Verify the definition of a plugin
// Also verify that the plugin accepts the current gitbook version
// This method throws erros if plugin is invalid
BookPlugin.prototype.validate = function() {
var isValid = (
this.isLoaded() &&
this.packageInfos &&
this.packageInfos.name &&
this.packageInfos.engines &&
this.packageInfos.engines.gitbook
);
if (!isValid) {
throw new Error('Error loading plugin "' + this.id + '" at "' + this.root + '"');
}
if (!gitbook.satisfies(this.packageInfos.engines.gitbook)) {
throw new Error('GitBook doesn\'t satisfy the requirements of this plugin: '+this.packageInfos.engines.gitbook);
}
};
// Normalize, validate configuration for this plugin using its schema
// Throw an error when shcema is not respected
BookPlugin.prototype.validateConfig = function(config) {
var that = this;
return Promise()
.then(function() {
var schema = that.packageInfos.gitbook || {};
if (!schema) return config;
// Normalize schema
schema.id = '/'+that.getConfigKey();
schema.type = 'object';
// Validate and throw if invalid
var v = new jsonschema.Validator();
var result = v.validate(config, schema, {
propertyName: that.getConfigKey()
});
// Throw error
if (result.errors.length > 0) {
throw new error.ConfigurationError(new Error(result.errors[0].stack));
}
// Insert default values
var defaults = jsonSchemaDefaults(schema);
return mergeDefaults(config, defaults);
});
};
// Return key for configuration
BookPlugin.prototype.getConfigKey = function() {
return 'pluginsConfig.'+this.id;
};
// Call a hook and returns its result
BookPlugin.prototype.hook = function(name, input) {
var that = this;
var hookFunc = this.content.hooks? this.content.hooks[name] : null;
input = input || {};
if (!hookFunc) return Promise(input);
this.book.log.debug.ln('call hook "' + name + '" for plugin "' + this.id + '"');
if (!_.contains(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 Promise()
.then(function() {
return that.bind(hookFunc)(input);
});
};
// Return resources without normalization
BookPlugin.prototype._getResources = function(base) {
var that = this;
return Promise()
.then(function() {
if (that._resources[base]) return that._resources[base];
var book = that.content[base];
// Compatibility with version 1.x.x
if (base == 'website') book = book || that.content.book;
// Nothing specified, fallback to default
if (!book) {
return Promise({});
}
// Dynamic function
if(typeof book === 'function') {
// Call giving it the context of our book
return that.bind(book)();
}
// Plain data object
return book;
})
.then(function(resources) {
that._resources[base] = resources;
return _.cloneDeep(resources);
});
};
// Normalize a specific resource
BookPlugin.prototype.normalizeResource = function(resource) {
// Parse the resource path
var parsed = url.parse(resource);
// This is a remote resource
// so we will simply link to using it's URL
if (parsed.protocol) {
return {
'url': resource
};
}
// This will be copied over from disk
// and shipped with the book's build
return { 'path': this.npmId+'/'+resource };
};
// Normalize resources and return them
BookPlugin.prototype.getResources = function(base) {
var that = this;
return this._getResources(base)
.then(function(resources) {
_.each(RESOURCES, function(resourceType) {
resources[resourceType] = _.map(resources[resourceType] || [], that.normalizeResource);
});
return resources;
});
};
// Normalize filters and return them
BookPlugin.prototype.getFilters = function() {
var that = this;
return _.mapValues(this.content.filters || {}, function(fn, filter) {
return function() {
var ctx = _.extend(compatibility.pluginCtx(that), this);
return fn.apply(ctx, arguments);
};
});
};
// Normalize blocks and return them
BookPlugin.prototype.getBlocks = function() {
var that = this;
return _.mapValues(this.content.blocks || {}, function(block, blockName) {
block = _.isFunction(block)? { process: block } : block;
var fn = block.process;
block.process = function() {
var ctx = _.extend(compatibility.pluginCtx(that), this);
return fn.apply(ctx, arguments);
};
return block;
});
};
module.exports = BookPlugin;
module.exports.RESOURCES = RESOURCES;

View File

@ -1,165 +0,0 @@
var npm = require('npm');
var npmi = require('npmi');
var path = require('path');
var semver = require('semver');
var _ = require('lodash');
var readInstalled = require('read-installed');
var Promise = require('../utils/promise');
var gitbook = require('../gitbook');
var PLUGIN_PREFIX = 'gitbook-plugin-';
// Return an absolute name for the plugin (the one on NPM)
function npmId(name) {
if (name.indexOf(PLUGIN_PREFIX) === 0) return name;
return [PLUGIN_PREFIX, name].join('');
}
// Return a plugin ID 9the one on GitBook
function pluginId(name) {
return name.replace(PLUGIN_PREFIX, '');
}
// Validate an NPM plugin ID
function validateId(name) {
return name && name.indexOf(PLUGIN_PREFIX) === 0;
}
// Initialize NPM for operations
var initNPM = _.memoize(function() {
return Promise.nfcall(npm.load, {
silent: true,
loglevel: 'silent'
});
});
// Link a plugin for use in a specific book
function linkPlugin(book, pluginPath) {
book.log('linking', pluginPath);
}
// Resolve the latest version for a plugin
function resolveVersion(plugin) {
var npnName = npmId(plugin);
return initNPM()
.then(function() {
return Promise.nfcall(npm.commands.view, [npnName+'@*', 'engines'], true);
})
.then(function(versions) {
return _.chain(versions)
.pairs()
.map(function(v) {
return {
version: v[0],
gitbook: (v[1].engines || {}).gitbook
};
})
.filter(function(v) {
return v.gitbook && gitbook.satisfies(v.gitbook);
})
.sort(function(v1, v2) {
return semver.lt(v1.version, v2.version)? 1 : -1;
})
.pluck('version')
.first()
.value();
});
}
// Install a plugin in a book
function installPlugin(book, plugin, version) {
book.log.info.ln('installing plugin', plugin);
var npnName = npmId(plugin);
return Promise()
.then(function() {
if (version) return version;
book.log.info.ln('No version specified, resolve plugin "' + plugin + '"');
return resolveVersion(plugin);
})
// Install the plugin with the resolved version
.then(function(version) {
if (!version) {
throw new Error('Found no satisfactory version for plugin "' + plugin + '"');
}
book.log.info.ln('install plugin "' + plugin +'" from npm ('+npnName+') with version', version);
return Promise.nfcall(npmi, {
'name': npnName,
'version': version,
'path': book.root,
'npmLoad': {
'loglevel': 'silent',
'loaded': true,
'prefix': book.root
}
});
})
.then(function() {
book.log.info.ok('plugin "' + plugin + '" installed with success');
});
}
// 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 = {
npmId: npmId,
pluginId: pluginId,
validateId: validateId,
resolve: resolveVersion,
link: linkPlugin,
install: installPlugin,
list: listPlugins,
listInstalled: listInstalled
};

View File

@ -1,36 +0,0 @@
var _ = require('lodash');
module.exports = {
// Return non-parsed html
// since blocks are by default non-parsable, a simple identity method works fine
html: _.identity,
// Highlight a code block
// This block can be replaced by plugins
code: function(blk) {
return {
html: false,
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

@ -1,15 +0,0 @@
var moment = require('moment');
module.exports = {
// Format a date
// ex: 'MMMM Do YYYY, h:mm:ss a
date: function(time, format) {
return moment(time).format(format);
},
// Relative Time
dateFromNow: function(time) {
return moment(time).fromNow();
}
};

View File

@ -1,80 +0,0 @@
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;

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