Compare commits

...

715 Commits

Author SHA1 Message Date
Samy Pessé 856b222a61 Bump version to 4.0.0-alpha.3 🚀 2017-01-13 19:11:15 +01:00
Samy Pessé fade3740c1 Fix publish script 2017-01-13 19:10:49 +01:00
Samy Pessé 803d6218a7 Remove bump script from bootstrapping 2017-01-13 19:03:48 +01:00
Nicolas Gaborit 7128db842a Test and fix conversion of empty parts (#1675)
* Test and fix conversion of empty parts

* Cover case of last part empty
2017-01-13 18:56:32 +01:00
Nicolas Gaborit 0342a11da0 Fix and test Book.getDefaultExt() (#1674) 2017-01-13 17:55:22 +01:00
Samy Pessé af80cbcd7c Add script to publish a new version 2017-01-13 16:18:28 +01:00
Samy Pessé a84d02ece0 Bump version to 4.0.0-alpha.2 🚀 2017-01-13 16:07:41 +01:00
Nicolas Gaborit 45b9fc2094 Adding more tests for (summary <-> document) transformations (#1665)
* Add tests for summaryFromDocument

* Add tests for summaryToDocument

* Add tests for untitled parts (HR)
2017-01-12 11:45:03 +01:00
Nicolas Gaborit a96ef569bd Clarify the use of {{ book.var }} (#1652)
Fixes #1425
2017-01-11 17:41:47 +01:00
Samy Pessé 4b3ac4e827 Add tests to changelog 2017-01-06 17:17:14 +01:00
Samy Pessé 7029c01806 Switch to markup-it for parsing (#1659)
* Start new parsers in gitbook itself

* Update markup-it

* Fix eslint errors

* Adapt basic parsing for summary

* Start tests for summaryFromDocument

* Continue

* Add parsing of glossary

* Add back languages parsing

* Adapt most tests for parsing

* Adapt all tests 🙌

* Adapt travis tests

* Bootstrap lerna before running tests

* Fix lowercase in require (linux)

* Fix command gitbook init

* Fix generation of ready by init command

* Fix generation of summary

* Fix watch after serve

* Add trademark to sidebar

* Add back favicon to default theme

* Open trademark in new tab
2017-01-06 17:15:30 +01:00
Samy Pessé 11893255d1 Fix images in readme 2016-12-22 11:36:15 +01:00
Samy Pessé 53f94b317e Improve the readme 2016-12-22 11:26:08 +01:00
Samy Pessé 4717b0a17f Add banner and move images to docs folder 2016-12-22 11:08:47 +01:00
Samy Pessé 43ce7badb7 Change instruction in readme to mention "yarn" 2016-12-22 11:04:41 +01:00
Samy Pessé 9e399da06a Update lerna@2.0.0-beta.31 2016-12-22 10:59:22 +01:00
Julia Kulla-Mader 026b33227d Removing extra period (#1579) 2016-12-22 10:21:47 +01:00
AJ e94634bda5 Updated book.js link (#1591)
The book.js link was pointing to a 404 page. This commit updates the link.
2016-12-22 10:20:47 +01:00
Samy Pessé 194ebc3da9 Merge pull request #1543 from GitbookIO/dream
React for rendering website with plugins
2016-12-22 10:18:38 +01:00
Samy Pessé 16af264360 Merge branch 'master' into dream 2016-12-22 10:17:28 +01:00
Julia Kulla-Mader 64ccb6b00b Google Analytics spelling error (#1580) 2016-12-22 09:58:11 +01:00
Soreine 8bf3241b63 Remove fixed-size font effects in issue template 2016-12-21 15:39:32 +01:00
Nicolas Gaborit 6a4f6eadd2 Improve GitHub issue template (#1648)
It is now short and simple, and redirects to the feedback repo if
needed.
2016-12-21 15:12:48 +01:00
Marlon Bernardes 532df30db7 Minor grammar fix (#1642) 2016-12-19 17:06:28 +01:00
Jonathan Andrew Wolter fd1f7a5609 correct typo s/serie/series/ (#1638) 2016-12-17 22:01:33 +01:00
Samy Pesse febdd8a8bd Prevent null as location in history 2016-12-04 21:18:45 +01:00
Samy Pesse b04724ae0b Debounce scroll watcher in body 2016-12-04 20:55:40 +01:00
Samy Pesse 5dbd8f5740 History is activated with a setup location 2016-12-04 20:43:55 +01:00
Samy Pesse f3af207e6e Update url when scrolling in default theme 2016-12-04 20:27:44 +01:00
Samy Pesse 7d05f8cb89 Update ied@2.3.6 and ad trailing newline in package.json 2016-12-04 19:46:54 +01:00
Soreine 9d3c397446 plugin: Add more categories 2016-12-02 17:52:49 +01:00
Soreine 0681a51c2f plugin: Improve messages for create CLI 2016-12-02 17:50:03 +01:00
Soreine 36e548fd3c core: Remove unused Button.href prop 2016-12-02 17:35:40 +01:00
Soreine ec149e6112 plugin: Display help by default 2016-12-02 17:35:33 +01:00
Soreine e1bcb4ed7d plugin: Add CONTRIBUTING.md 2016-12-02 17:34:54 +01:00
Soreine 4aae18617b plugin-search: Remove react dependency 2016-12-02 16:01:35 +01:00
Soreine 6d00944576 Fixed React version 2016-12-02 15:59:01 +01:00
Soreine be8b11dcb4 ReactInjection was moved 2016-12-02 15:58:25 +01:00
Johan Preynat 1780435c4f Use fixed size for sidebar and provide animation 2016-10-31 17:13:19 +01:00
Johan Preynat ebf7a5cf50 Export ReactCSSTransitionGroup from gitbook-core 2016-10-31 17:12:47 +01:00
Guillermo Lengemann 5f0a81d18b GNU/Linux Calibre installation instruction for ebook (#1533)
Adding details to generating ebook an pdfs in GNU/Linux.
2016-10-19 18:01:59 +02:00
Hur jung ju b564ef6580 Add 'emphasis' in 'SUMMARY.md' and trimming (#1566) 2016-10-19 17:56:03 +02:00
Soreine 1d877db5f0 search: Clear search with Esc or button 2016-10-19 01:06:44 +02:00
Soreine 92678bb93f Clearer doc for File 2016-10-19 00:34:57 +02:00
Soreine 4f2c729d60 core: Fix HotKeys default keymap 2016-10-19 00:34:19 +02:00
Soreine 59079bc18d plugin-lunr: Fix reading page search attributes 2016-10-18 16:40:49 +02:00
Samy Pesse 66c2a6c076 Add roles "body:wrapper", "toolbar:wrapper", "page:wrapper" 2016-10-17 23:56:41 +02:00
Samy Pesse 182bb9eb86 Remove react dependencies from "gitbook" 2016-10-17 23:40:02 +02:00
Samy Pesse 9ab738e478 Use same react instance between server and gitbook
Fix injection of DOM properties not working on the browser
2016-10-17 23:39:40 +02:00
Samy Pesse d0555e2bd0 Add background to dropdown 2016-10-17 23:38:28 +02:00
Samy Pesse cdf9842d68 Fix display of button in left toolbar 2016-10-17 23:02:17 +02:00
Samy Pesse a1ae5c2295 Normalize action types in plugins 2016-10-17 20:50:09 +02:00
Samy Pesse 373a486a14 Replace GitBook.Shapes by GitBook.PropTypes 2016-10-17 18:24:29 +02:00
Samy Pesse f262a7cd89 Add languages to client side state, and add "current" to json repr 2016-10-14 23:05:37 +02:00
Samy Pesse b63b0cc50d Normalize Panel in gitbook-core 2016-10-14 11:09:24 +02:00
Samy Pesse 5839d4d8a8 Improve "gitbook-plugin create" to prompt for title/categories 2016-10-13 23:54:27 +02:00
Samy Pesse d9c3cb418a Add component tooltip from gitbook-core 2016-10-13 23:31:05 +02:00
Samy Pesse 12c7ac1498 Add roles "page:header" and "page:footer" 2016-10-13 21:53:15 +02:00
Samy Pesse 2c3072fd8a Fix update of page attributes in hook "page" 2016-10-13 21:53:15 +02:00
Johan Preynat bb14f48c39 Add tests for multiple inline template blocks 2016-10-13 21:07:23 +02:00
Samy Pesse d56019dc18 Hook "page" should not be able to edit content but attributes 2016-10-13 20:55:41 +02:00
Samy Pesse 0a6216a093 Limit size of text in search results 2016-10-13 20:25:46 +02:00
Samy Pesse 4c174f916e Add highlighting for search terms in results 2016-10-13 19:33:04 +02:00
Samy Pesse 4db57fe5c5 Fix rendering of search 2016-10-13 19:19:54 +02:00
Samy Pesse 2a78bcebab Search plugin should provide roles "search:results" and "search:result" 2016-10-13 18:08:48 +02:00
Samy Pesse be14a8ea4d Fix search input 2016-10-13 17:57:09 +02:00
Samy Pesse 37920c758b Normalize style of dropdown 2016-10-13 17:57:09 +02:00
Soreine 52f9409e24 gitbook-plugin: Add eslint as devDependency 2016-10-13 17:31:33 +02:00
Samy Pesse 05646996c0 Rename "navigation" to "history" and add loading bar 2016-10-13 16:17:00 +02:00
Samy Pesse c439cf8513 Increase content font-size 2016-10-13 15:46:18 +02:00
Soreine 1a3e4e8337 Small tweaks to doc 2016-10-13 15:45:19 +02:00
Soreine 631cebfd2f search: Document search handlers API 2016-10-13 15:45:19 +02:00
Samy Pesse f1f2024489 Add support for align HTML attribute 2016-10-13 15:22:52 +02:00
Samy Pesse 227b329e78 Start style for dropdowns 2016-10-13 14:47:06 +02:00
Samy Pesse da8c78b3c8 Refactor Backdrop, Hotkeys and Dropdown 2016-10-13 13:35:59 +02:00
Samy Pesse c42c5897fe Refactor code of plugin sharing 2016-10-13 13:23:19 +02:00
Nicolas Gaborit 3aea3e5d88 Adapt plugin sharing (#1553)
* Reuse old package config

* Add plugin config shape

* Add ButtonGroup to core components

* List all sharing sites

* Displaying buttons from config

* First iteration of Dropdown component (need CSS)

* Using Dropdown for sharing button

* Create HotKeys component

* Move Backdrop to its own file

* Trying a cleaner API for Dropdown

* Add README.md

* livereload: Add missing gitbook-plugin dependency

* sharing: Now use Immutable state

* sharing: Adapt quickly to new Dropdown

* sharing: Fix sharing from dropdown
2016-10-13 12:56:46 +02:00
Samy Pesse 95b3b4ebb7 Revert fontsettings plugin 2016-10-13 11:10:53 +02:00
Samy Pesse ba56801352 Add base for new fontsettings plugin 2016-10-13 00:18:47 +02:00
Samy Pesse 48ee0e35b0 Start changelog 2016-10-12 21:27:55 +02:00
Samy Pesse 2fff291cad Readme should always be generated as "index.html" 2016-10-12 20:56:36 +02:00
Johan Preynat baea45d924 Enhance display of headings 2016-10-12 19:49:58 +02:00
Samy Pesse 52d0384605 Improve some CSS for default theme 2016-10-12 16:07:28 +02:00
Samy Pesse 961b9d7031 Fix error for code blocks without language 2016-10-12 15:47:29 +02:00
Samy Pesse 5b0622014a Adapt test for listDependencies 2016-10-12 14:18:46 +02:00
Samy Pesse 7253dbc476 Use ied instead of npm for installation of plugins 2016-10-12 14:09:48 +02:00
Samy Pesse b4dd3e1ddc Adapt test for plugins.findForBook 2016-10-12 12:17:34 +02:00
Samy Pesse 4b6fc0ae2f Adapt tests for website rendering and render languages index 2016-10-12 12:12:08 +02:00
Samy Pesse 5d4dc499a5 Remove references to plugin resources 2016-10-12 11:56:31 +02:00
Samy Pesse 0e7aece96d Use a single instance of Git for output 2016-10-12 11:13:31 +02:00
Samy Pesse b7c245d75a Add yarn.lock to .gitignore 2016-10-12 11:04:56 +02:00
Samy Pesse 5fed4e1ced Adapt findInstalled for unit tests 2016-10-12 11:04:09 +02:00
Samy Pesse c71da9955e Improve performance of plugins listing 2016-10-11 11:36:17 +02:00
Samy Pesse 3a2abb870b gitbook-plugin create should add eslint config 2016-10-11 10:19:48 +02:00
Samy Pesse e8947a2c11 Update nunjucks@2.5.2 2016-10-11 10:11:35 +02:00
Samy Pesse 885a161d42 Update react-safe-html@0.4.0 2016-10-11 09:25:07 +02:00
Samy Pesse fe0d4bf024 Add timing logs for browser rendering 2016-10-11 00:11:17 +02:00
Samy Pesse aa39b8e71b Don't log deprecated when no plugins is using deprecated APIs 2016-10-10 23:18:38 +02:00
Samy Pesse 3000dcefe1 Add compiled plugin to gitignore for livereload plugin 2016-10-10 19:07:21 +02:00
Samy Pesse f1405d3958 remove compiledp lugin from git 2016-10-10 19:06:38 +02:00
Samy Pesse caeadd431b Adapt plugin livereload for new APIs 2016-10-10 17:43:27 +02:00
Samy Pesse d5796ccf04 Import plugin "livereload" from https://github.com/GitbookIO/plugin-livereload 2016-10-10 16:34:39 +02:00
David Graf 71cce33836 moved insertion of livereload script tag to the correct place 2016-10-10 16:18:35 +02:00
David Graf 9eeb7304c7 s/host/hostname 2016-10-10 16:18:35 +02:00
David Graf 3b5bea0eb5 typo 2016-10-10 16:18:35 +02:00
David Graf 17d921cb11 use current domain instead of localhost 2016-10-10 16:18:34 +02:00
David Graf 10ab06a32c initial commit 2016-10-10 16:18:34 +02:00
Samy Pesse 1bc802e4c8 Fix outline effect on summary articles 2016-10-10 13:07:24 +02:00
Samy Pesse 9a9b5a2621 Show "copied" text when clicking the copy button 2016-10-10 12:56:23 +02:00
Samy Pesse 66231f8b7f Add plugin "copy-code" to default 2016-10-10 12:45:18 +02:00
Samy Pesse 7037ac55bd Use URIIndex.resolve in resolveLinks modifiers 2016-10-09 18:25:54 +02:00
Samy Pesse cf35fef3b3 Use map.getIn for plugin heading 2016-10-09 17:23:24 +02:00
Samy Pesse 16800cc70a Adapt plugin headings to use normal links 2016-10-09 17:00:33 +02:00
Samy Pesse 417013a230 Resolve assets and lunr index before loading 2016-10-09 16:52:27 +02:00
Johan Preynat 5a57a2c62a Merge remote-tracking branch 'origin/dream' into dream
# Conflicts:
#	packages/gitbook-plugin-headings/package.json
2016-10-09 00:58:30 +02:00
Johan Preynat 4cfffb26f2 gitbook-plugin-headings: Add CSS 2016-10-09 00:55:13 +02:00
Johan Preynat 9d4221936e gitbook-plugin-headings: Update plugin to display anchors 2016-10-09 00:55:04 +02:00
Johan Preynat 34baae1a5d gitbook-plugin-headings: Add base default options 2016-10-09 00:54:37 +02:00
Johan Preynat cd0ed86589 Add classnames to dependencies for gitbook-plugin-headings 2016-10-09 00:54:04 +02:00
Samy Pesse 2d429b731b Fix scroll for content in default theme 2016-10-08 21:12:20 +02:00
Samy Pesse 03b89194d7 Prepare for multiple role rendering for ebooks 2016-10-08 18:19:49 +02:00
Samy Pesse 68ec88c620 Add script to bump version 2016-10-08 14:32:35 +02:00
Samy Pesse 47a27fc12d Remove templating blocks post process 2016-10-07 11:53:02 +02:00
Samy Pesse 9cedcc6b6e Adapt tests for TemplateBlock 2016-10-07 11:46:57 +02:00
jdaviescoates d6de8f4173 Update examples.md with Loomio guide (#1550) 2016-10-07 11:40:06 +02:00
Samy Pesse 1d291ff2f0 Fix style of toolbar and add title to it 2016-10-06 23:04:20 +02:00
Samy Pesse 58ae96b7f6 Add "url" to summary articles and files in json representation 2016-10-06 20:08:47 +02:00
Samy Pesse 1354582c4e Generate an url index for generation 2016-10-06 16:29:32 +02:00
Samy Pesse 4119917555 Add class URIIndex to store table path to url 2016-10-06 11:21:08 +02:00
Samy Pesse 9d20a9afa5 Store config as immutable in state 2016-10-06 09:41:47 +02:00
Samy Pesse 45683185a2 Add reducer for file in gitbook state 2016-10-05 23:19:11 +02:00
Samy Pesse 6ce4949df3 Add "url" to file 2016-10-05 23:12:00 +02:00
Soreine 0614613b84 Starting gitbook-plugin-sharing 2016-10-05 21:41:43 +02:00
Johan Preynat 6faf6a53f0 Replace plugin fontsettings by creating plugin headings 🔨 2016-10-05 20:14:08 +02:00
Samy Pesse bced415a50 Fix include of immutable in theme-default 2016-10-05 16:19:56 +02:00
Samy Pesse 805886e4a9 We trigger current url history only once the page has been mounted 2016-10-05 14:22:01 +02:00
Samy Pesse 83c98cf4d8 Fix search not refreshed when adding an handler 2016-10-05 14:01:22 +02:00
Samy Pesse 86848e7acb Fix format of lunr results 2016-10-05 13:36:38 +02:00
Samy Pesse 9ce3646d6e Add base for loading lunr index 2016-10-05 02:28:12 +02:00
Samy Pesse 45752fc79c Complete highlight plugin 2016-10-05 01:47:16 +02:00
Samy Pesse a863b55c79 Start new highlight.js plugin 2016-10-04 18:53:58 +02:00
Samy Pesse 16ca48b2c0 Correctly update page when url changed 2016-10-04 18:32:31 +02:00
Samy Pesse 87ae2481e4 Add style for plugin hint 2016-10-04 15:32:59 +02:00
Samy Pesse ccb5dcf559 Fix template of plugin to not mention theme 2016-10-04 15:30:34 +02:00
Samy Pesse 84315a99f1 First handling of templating blocks 2016-10-03 23:34:27 +02:00
Samy Pesse 026cc8304d Don't compile tests file with babel 2016-10-03 18:41:25 +02:00
Samy Pesse cefbfdc7b8 Clear query in url when search is cleared 2016-10-03 16:01:11 +02:00
Samy Pesse 4049279269 Emit current location when listening 2016-10-03 15:48:28 +02:00
Samy Pesse b04befd67c Use replace of url for search to avoid filling history 2016-10-03 15:21:40 +02:00
Samy Pesse d82032298d Fix new api for activate/deactivate 2016-10-03 14:56:55 +02:00
Samy Pesse 34eb35699d Change "init to "activate" 2016-10-03 10:35:24 +02:00
Samy Pesse 3ae72bb47c Sync search with querystring 2016-10-03 00:59:26 +02:00
Samy Pesse cd2d5e5101 Pass actions to thunk acitons 2016-10-02 15:28:25 +02:00
Samy Pesse 5abac3a79a Fix size of button in toolbar 2016-10-02 13:33:16 +02:00
Samy Pesse d22d090b05 Add style for search input in default theme 2016-10-02 01:04:25 +02:00
Samy Pesse 24b657f790 Fix font and buttons in default theme 2016-10-02 00:53:38 +02:00
Samy Pesse 649a6c980c Include font-awesome in default theme 2016-10-02 00:37:36 +02:00
Samy Pesse 24624f0d0e Fix clear of search 2016-10-02 00:15:59 +02:00
Samy Pesse 9aaf561c41 Complete i18n for search plugin 2016-10-01 23:11:50 +02:00
Samy Pesse 8be4cbc887 Add action I18n.registerLocales and locales for default theme 2016-10-01 23:11:50 +02:00
Johan Preynat 32556e6724 Add fix for handling parsing page errors 2016-10-01 22:48:47 +02:00
Johan Preynat ad425b9772 Bump version to 3.2.2 2016-10-01 22:35:54 +02:00
Johan Preynat 5238fa352c Rollback to use gitbook-markdown@1.3.2 2016-10-01 22:30:43 +02:00
Johan Preynat 32183b6650 Merge branch 'fix-3.1-version' 2016-10-01 21:49:06 +02:00
Johan Preynat cb3d6a7f01 Correctly handle page parsing errors 2016-10-01 21:47:25 +02:00
Johan Preynat c978f0ab21 Add comment 2016-10-01 21:47:08 +02:00
Samy Pesse 8753897edc Fix js error because of changing API 2016-10-01 14:39:09 +02:00
Samy Pesse da83d9e91d Change api for registering components 2016-10-01 14:14:30 +02:00
Samy Pesse 2cf507a807 Base api to use i18n 2016-09-30 14:16:48 +02:00
Samy Pesse 1489129407 Improve Gitbook.connect to accept a mapActionsToProps 2016-09-29 23:53:18 +02:00
Samy Pesse b5c407cf7b Start plugin "hints" 2016-09-29 19:38:55 +02:00
Samy Pesse 9d8dffb6f0 Fix query action for search 2016-09-29 19:04:56 +02:00
Samy Pesse 6015defe11 Integrate search in default theme 2016-09-29 17:20:29 +02:00
Samy Pesse 7f28379154 Fix dist command 2016-09-29 16:03:16 +02:00
Samy Pesse 6f98ee1846 Add npmignore and gitignore to plugin template 2016-09-29 13:17:24 +02:00
Samy Pesse 42d53ec032 Add gitbook-plugin command to create a plugin 2016-09-29 13:15:41 +02:00
Samy Pesse 287c80b2e1 Start search plugin 2016-09-28 18:14:35 +02:00
Samy Pessé 33ff60cee7 Add changes for 3.2.1 2016-09-28 15:56:36 +02:00
Samy Pesse 26fc021b90 Fix lerna bootstrap 2016-09-28 14:54:01 +02:00
Samy Pesse 33d7e022e6 Bump version to 3.2.1 2016-09-28 13:21:24 +02:00
Samy Pesse 09782b964b Update theme-default@1.0.6 2016-09-28 13:10:47 +02:00
Johan Preynat 7938ee8ec6 Use nunjucks@2.5.2 2016-09-28 10:32:14 +02:00
Samy Pesse c314281da0 Add CONTRIBUTING.md 2016-09-28 10:03:38 +02:00
Peter B Smith 2e8e4f63d7 fixing a typo of the word "publishing" (#1536) 2016-09-28 09:49:29 +02:00
Samy Pesse bc5df83846 Start designing toolbar 2016-09-27 17:08:34 +02:00
Samy Pesse f1ef1902f3 Style page with gitbook-markdown-css 2016-09-27 16:39:58 +02:00
Samy Pesse 748c46172b Fix reducers mutating the state instead of creating a new one 2016-09-27 16:12:58 +02:00
Samy Pesse 51f4cd6dae Fix link and navigation click 2016-09-26 18:45:12 +02:00
Samy Pesse 64ce8bcf42 Fix gitbook-plugin permission 2016-09-26 17:48:02 +02:00
Samy Pesse ff42b58f77 Add basic for pjax navigation 2016-09-26 17:26:36 +02:00
Samy Pesse 7fa3e34f9f Mark article as active in summary 2016-09-25 19:05:35 +02:00
Samy Pesse e46cd78f80 Fix core reducer destroying other reducers 2016-09-25 17:44:08 +02:00
Samy Pesse fab88bb47e Add flex box components 2016-09-24 23:24:47 +02:00
Samy Pesse fa7cf3a65f Start documenting components 2016-09-22 18:28:37 +02:00
Samy Pesse d0885491b7 Provide immutable proptypes 2016-09-22 11:08:57 +02:00
Samy Pesse 45c54d0cd7 Base tree rendering for summary 2016-09-22 10:54:54 +02:00
Samy Pesse f49089a7d3 Add immutable state for readme/summary
Add shapes for these states
2016-09-22 10:37:06 +02:00
Samy Pesse 0b1888e184 First working version in the browser 2016-09-22 09:54:24 +02:00
Samy Pesse 2257e42299 Add js scripts to html 2016-09-21 19:17:32 +02:00
Samy Pesse 0e0c1a473b Fix head extension by plugins 2016-09-21 17:29:52 +02:00
Samy Pesse a18370551a Prepare showing page html 2016-09-21 15:54:58 +02:00
Samy Pesse a8aeab17d6 Fix render of UnsafeComponent 2016-09-21 01:35:59 +02:00
Samy Pesse 2adca5deb9 Fix composition with plugin reducers 2016-09-21 01:05:40 +02:00
Samy Pesse 3b3f43a35d Fix rendering of unsafe injected components 2016-09-20 19:41:25 +02:00
Samy Pesse ced0be930d Working loading of plugin 2016-09-20 19:19:01 +02:00
Samy Pesse 6d58a90613 Prepare loading of plugin 2016-09-20 15:12:53 +02:00
Samy Pessé 2796065886 Add base for gitbook-plugin builder 2016-09-19 11:33:23 +02:00
jdaviescoates b9fa6c3051 Update examples.md (#1522)
By adding the Loomio and Enspiral handbook examples
2016-09-08 20:38:59 +02:00
Samy Pesse 82c2c214eb Fix build of gitbook.core.min.js 2016-09-05 22:29:41 +02:00
Samy Pesse ee35da4be5 Add task to build gitbook.core.min.js 2016-09-05 21:07:31 +02:00
Samy Pessé 24c4f41945 gitbook-core should also be compiled 2016-09-05 16:17:43 +02:00
Samy Pessé fe298f2a3f Fix error during fake rendering 2016-09-05 14:41:21 +02:00
Samy Pessé ab3d94a55d Fix regression in TemplateBlock 2016-09-05 14:15:33 +02:00
Samy Pessé ff31514c66 Start bootstraping rendering with react in GitBook 2016-09-05 14:02:49 +02:00
Samy Pessé 3f1ec310ca Add compilation ES6 with babel for "gitbook" 2016-09-05 12:36:53 +02:00
Samy Pessé b796069ade Move gitbook source code to es6 (linted) 2016-09-05 12:32:25 +02:00
Samy Pessé 839904a704 Start architecturing new default theme 2016-09-05 12:14:37 +02:00
Samy Pessé 0e74c9bad5 Add action to register a component 2016-09-05 11:59:40 +02:00
Samy Pessé d7e415b0b3 Add components for injections 2016-09-05 11:51:52 +02:00
Samy Pessé b170b4d197 Start basis of gitbook-core 2016-09-05 11:04:36 +02:00
Samy Pessé a14ca3e268 Switch to lerna 2016-09-05 11:04:18 +02:00
Samy Pessé 9c071dade5 Merge pull request #1481 from mcveat/patch-1
Fixed typo
2016-08-04 00:19:11 +02:00
Piotr Adamski 56e44651fa Fixed typo 2016-08-03 22:08:12 +02:00
Samy Pessé 42fac98992 Fix command for getting gitbook version 2016-08-01 09:47:31 +02:00
Samy Pessé 4f5513add0 Create template for GitHub issues 2016-07-25 20:39:31 +02:00
Samy Pesse 90cbfc0596 Add OSX testing on Travis 2016-07-15 11:38:58 +02:00
Samy Pessé 38ab9fa05a Bump version to 3.2.0 2016-07-11 16:48:13 +02:00
Samy Pessé 12e20c5fcc Update theme-default@1.0.5 2016-07-11 16:47:16 +02:00
Samy Pessé 6b103f4532 Fix #1408: fix i18n in website generator and add tests for it 2016-07-11 16:27:22 +02:00
Soreine d4fa5c18a8 editPlugin and getPluginConfig 2016-07-11 15:18:35 +02:00
Soreine 2a116dfdb0 Fix reducedObject when default property is undefined 2016-07-11 15:18:35 +02:00
Samy Pessé 0349f54f3c Export file model 2016-07-07 16:41:05 +02:00
Samy Pessé 695bda312c Bump version to 3.2.0-pre.1 2016-07-05 17:15:48 +02:00
Samy Pessé 144bd4c97a Update gitbook-markdown@2.0.1 2016-07-05 11:07:51 +02:00
Samy Pessé e105983cf8 Add back property "output" to page/template context 2016-07-04 18:08:44 +02:00
Soreine ea3afbed75 Update gitbook-plugin-lunr@1.2.0 2016-07-01 17:52:15 +02:00
Soreine d563abe3df Reduce size of logo to give room to version number 2016-06-30 15:37:31 +02:00
Soreine 70fd4ded29 Update theme-official for doc 2016-06-30 12:15:50 +02:00
Samy Pessé 9ce9388dfc Bump version to 3.2.0-pre.0 2016-06-30 11:09:18 +02:00
Samy Pessé c575ad83ae Update gitbook-markdown@2.0.0 2016-06-30 10:51:46 +02:00
Samy Pessé 936fa1545b Add tests for locationUtils.flatten 2016-06-29 10:32:49 +02:00
Samy Pessé c9c2dde2b6 Add method "isFile" and "isReadme" in SummaryArticle 2016-06-29 10:31:06 +02:00
Samy Pessé d9a1d387c7 Merge pull request #1397 from mpfeil/master
add open and browser args to serve command
2016-06-23 11:40:05 +02:00
Matthias Pfeil bf29d187c8 disable open by default 2016-06-20 17:24:08 +02:00
Matthias Pfeil d86f9b2667 add open and browser args to serve command 2016-06-17 16:00:53 +02:00
Samy Pesse aca4313fc3 Enable long stack support when process.env.CI is true 2016-06-11 12:38:19 +02:00
Samy Pesse 4cb5bb06fb Bump version to 3.1.1 2016-06-10 22:19:09 +02:00
Samy Pesse 24988b75ea Fix extsAsciidoc.js 2016-06-10 22:14:04 +02:00
Samy Pesse 775e858d05 Expose markdown/asciidoc extensions to gitbook module 2016-06-10 22:13:31 +02:00
Samy Pesse 1e3d871c1e Merge branch 'fix/plugins_loading' 2016-06-10 22:08:16 +02:00
Samy Pesse 94a4310c1e Adapt Plugins.loadForBook to use correct order 2016-06-10 22:04:19 +02:00
Samy Pessé e8ee57be83 Start fixing order of plugins in dependencies 2016-06-10 18:34:24 +02:00
Samy Pessé 50a132cb2c Add method "createChildLevel" to SummaryPart 2016-06-10 18:11:22 +02:00
Samy Pessé 59be32a54f Add method .createChildLevel for SummaryArticle 2016-06-10 18:08:56 +02:00
Soreine 4ae7cdb5d1 Mention autocover plugin in docs 2016-06-10 18:03:40 +02:00
Samy Pessé 990a4efeb5 Update gitbook-markdown and gitbook-asciidoc 2016-06-10 17:53:10 +02:00
Samy Pessé 9c927dcf13 Fix #1374 and #1360: apply transform of conrefsLoader using a string, not a buffer 2016-06-10 17:45:30 +02:00
Samy Pessé 7a08b3f109 Fix #1377: fix listing of plugins when using gitbook as a library 2016-06-10 17:15:04 +02:00
Samy Pessé 94d4234cb8 Merge branch 'fix/plugins_order' 2016-06-10 16:44:13 +02:00
Samy Pessé fbc851bc4e Fix listAll to add default plugins after normal ones 2016-06-10 16:43:32 +02:00
Samy Pessé e92b661a2a Adapt sortPlugins to only sort an ordered map of plugins 2016-06-10 16:37:44 +02:00
Soreine b9fe60915f Add tests for insertPart 2016-06-10 15:40:51 +02:00
Samy Pessé 1bf7d092f0 Merge pull request #1363 from todvora/master
Fixed target attribute for external links
2016-06-10 00:28:50 +02:00
Samy Pesse 826d472511 Fix documentation about themes 2016-06-08 20:06:50 +02:00
Samy Pessé a6b1f4bd87 Compare list of plugins sorted to array of names in tests 2016-06-08 15:08:11 +02:00
Samy Pessé 004f91f02c sortPlugins should not be async 2016-06-08 15:02:34 +02:00
Samy Pessé e6c6ec4293 Improve testing for sortPlugins 2016-06-08 14:57:54 +02:00
Soreine a1d3acf065 Fix serve cli using new ConfigModifier.addPlugin 2016-06-08 10:42:27 +02:00
Soreine a732660f59 docs: cover remove extraneous line 2016-06-08 10:35:12 +02:00
Nicolas Gaborit e1bb0638f8 docs: cover, fix formatting 2016-06-08 10:16:54 +02:00
Soreine 788c0c7e7e Adds cover.jpg info 2016-06-08 10:12:42 +02:00
Tomas Dvorak 762fe16a17 fixed target attribute for external links 2016-06-07 18:34:36 +02:00
Samy Pessé 15123805d1 Fix linting error 2016-06-07 16:24:22 +02:00
Samy Pessé 23aecb4f2b Improve output of page to text with frontmatter 2016-06-07 16:23:12 +02:00
Samy Pessé f1d200b81b Rollback frontmatter parsing to js-yaml to be browserify compatible 2016-06-07 14:32:12 +02:00
Samy Pessé b566711ee1 Add method toText to Page 2016-06-07 12:51:35 +02:00
Samy Pessé 22da5aac11 Switch frontmatter parsing to gray-matter 2016-06-07 12:47:16 +02:00
Samy Pessé f7b95102c5 Expose Page model 2016-06-07 12:36:32 +02:00
Samy Pessé ca70c934d6 Split page parsing into parsePageFromString and parsePage
Add tests for parsePageFromString
2016-06-07 12:31:21 +02:00
Samy Pessé 1cc48fa314 Use Immutable instead of lodash for mergeDefaults 2016-06-07 12:06:29 +02:00
Samy Pessé b63b5574d3 Only merge objects in mergeDefaults 2016-06-07 11:40:54 +02:00
Samy Pessé 67106e8698 Improve plugins config modifier 2016-06-07 11:06:53 +02:00
Samy Pessé dc2ab51a88 Update i18n-t and juice 2016-06-07 10:54:34 +02:00
Samy Pessé cedfa186f2 Remove merge-defaults dependency 2016-06-07 10:48:04 +02:00
Samy Pessé 80f0d1bfed Update gitbook-markdown and gitbook-asciidoc to use lodash@4 2016-06-07 10:36:01 +02:00
Samy Pessé 7178ab1f82 Improve Config.toText to return only the required fields 2016-06-07 10:26:28 +02:00
Samy Pesse bf895b707e Improve addPlugin to not append default plugins to the list 2016-06-07 00:56:24 +02:00
Samy Pesse 996170a4ad ConfigModifier.removePlugin should toggle off if default 2016-06-07 00:48:47 +02:00
Samy Pesse aa66a4f928 Expose DEFAULT_PLUGINS as a list of PluginDependency 2016-06-06 22:04:13 +02:00
Samy Pesse 23010fa54a Adapt tests for config modifiers 2016-06-06 21:22:44 +02:00
Samy Pesse 0baebc7979 Change config modifier to work on a config object 2016-06-06 21:20:00 +02:00
Samy Pesse 257158321c Bump version to 3.1.0 2016-06-06 19:51:44 +02:00
Samy Pesse ddf0a41e29 Add method editPlugin to config modifiers 2016-06-06 19:42:44 +02:00
Samy Pesse 357e1e8e96 Add unit tests for postRender 2016-06-06 19:24:12 +02:00
Samy Pesse 7a89f9869a Update gitbook-markdown@1.3.0 and gitbook-asciidoc@1.2.0 2016-06-06 18:07:31 +02:00
Samy Pesse 30fbd22efe Add modifier togglePlugin 2016-06-06 17:27:47 +02:00
Samy Pesse 29df73ba72 Add method "toText" to config 2016-06-06 17:16:53 +02:00
Soreine f8676483c1 Default paths are relative to the current book 2016-06-06 16:55:00 +02:00
Johan Preynat c79a6f2fba Use theme-default@1.0.4 2016-06-06 15:46:44 +02:00
Samy Pessé c38e67ea61 Merge pull request #1344 from GitbookIO/fix/readme-in-summary
Search for README file in whole summary before inserting as first article
2016-06-06 12:18:06 +02:00
Johan Preynat 97a5f8805e Use plugin-fontsettings@2.0.0 2016-06-02 10:24:12 +02:00
Johan Preynat 421fef6ebf Search for README file in whole summary before inserting as first article 2016-05-30 15:07:39 +02:00
Johan Preynat 33406c3082 Merge pull request #1336 from GitbookIO/loading/themes
Load plugins before themes
2016-05-28 11:56:34 +02:00
Johan Preynat c3851889b0 Throw an error if generateMock() cannot create the temp directory 2016-05-27 17:15:23 +02:00
Johan Preynat 4a5de2da6b Use sortPlugins() in loadForBook() 2016-05-27 15:06:20 +02:00
Johan Preynat 1afb23cada Add test file for sortPlugins() 2016-05-27 15:06:08 +02:00
Johan Preynat a3b0f3f868 Export plugins sorting method to load plugins before themes 2016-05-27 15:05:55 +02:00
Johan Preynat 0aa32916d6 Add prefix for theme plugins as constant 2016-05-27 15:05:33 +02:00
Samy Pessé ebe845d46c Bump version to 3.0.3 2016-05-27 12:16:56 +02:00
Samy Pessé edee8d2fc5 Update gitbook-theme-default@1.0.3 2016-05-27 12:07:35 +02:00
Samy Pessé 5f3de0876f Add header 'X-Current-Location' to serve and fix handling of directories 2016-05-27 12:01:28 +02:00
Samy Pessé 2ceab04d01 Increase timeout for tests of plugin installation 2016-05-27 11:40:05 +02:00
Samy Pessé de37de44e8 Add tests for installation of all plugins 2016-05-27 11:18:12 +02:00
Samy Pessé ccb87d5a13 Add unit tests for installPlugin 2016-05-27 11:10:39 +02:00
Samy Pessé e2e0ca3f05 Add test for plugin version resolution 2016-05-27 10:54:56 +02:00
Samy Pessé b33a79704b Switch plugin installation and resolveVersion to use PluginDependency instead of Plugin 2016-05-27 09:38:23 +02:00
Samy Pessé 6def174b84 Merge pull request #1334 from ryanswanson/master
Fixed two issues for handling git URLs for plugins
2016-05-27 09:27:11 +02:00
Samy Pessé 346a18b444 Merge pull request #1335 from GitbookIO/fix/1333
Fix #1333: fix invalid URLs for glossary annotations
2016-05-26 22:30:06 +02:00
Samy Pesse b32685c698 Add test for custom glossary file 2016-05-26 22:01:40 +02:00
Samy Pesse 09b3618da8 Annotate glossary terms before resolving links 2016-05-26 21:38:27 +02:00
Samy Pesse 901328f247 Complete basic tests for glossary 2016-05-26 21:37:10 +02:00
Samy Pesse b47ebaa9d9 Use real glossary name when annotating 2016-05-26 21:10:06 +02:00
Ryan Swanson 465e8d6c0a Fixed two issues for handling git URLs for plugins that are published to git repositories rather than on npmjs.org.
Fixed issue in pluginDependency to dereference 'name' and 'version' using Immutable Map.get(...) accessors since config values are now wrapped by Immutable.fromJS(...) in config.js > setValue(...).  Added associated unit test.

Fixed issue in resolveVersion where a plugin may be using a git URL rather than a semver for the version portion of the plugin config definition.  In addition, refactored the resolveVersion function into a new module to allow for unit testing.  Added associated unit test.
2016-05-26 14:30:29 -04:00
Samy Pessé ecd86ec877 Method for parsing glossary when not listed in summary 2016-05-26 17:26:11 +02:00
Samy Pessé 5d7864154c Start test for glossary generation 2016-05-26 17:25:49 +02:00
Samy Pessé 7b915428f7 Fix #1316: fix regression for supporting uppercase filenames in structure 2016-05-26 11:07:13 +02:00
Samy Pesse 0bce3517fd Bump version to 3.0.2 2016-05-25 21:50:38 +02:00
Samy Pesse 089815a8fd Fix #1328: fix crash when anchor links 2016-05-25 21:49:58 +02:00
Samy Pesse 7e8f1a9632 Bump version to 3.0.1 2016-05-25 20:00:04 +02:00
Samy Pesse 8469648241 Update theme-default@1.0.2 2016-05-25 17:14:15 +02:00
Samy Pessé e7a44358a5 Update Changelog 2016-05-24 15:38:26 +02:00
Samy Pessé fb930d889f Fix anchor breaking links resolution 2016-05-24 15:32:30 +02:00
Samy Pessé 721a75309b Fix #1316: adapt regexp to handle dot in filenames 2016-05-23 13:00:01 +02:00
Samy Pessé b856bff91b Add failing test for #1316 2016-05-23 12:48:46 +02:00
Samy Pesse 0744f000f9 Fix doc for conrefs 2016-05-22 19:59:53 +02:00
Samy Pesse d1e226dba5 Bump version to 3.0.0 2016-05-20 22:18:37 +02:00
Samy Pesse 1923d28d71 Update dependencies 2016-05-20 22:17:44 +02:00
Samy Pessé 484a306adb Fix error with isInRoot when empty root 2016-05-20 21:41:02 +02:00
Samy Pessé 5bfc9f2f56 Fix PathUtils.isInRoot on windows 2016-05-20 21:37:22 +02:00
Samy Pesse 86a65e5287 Improve appveyor configuration 2016-05-20 19:23:51 +02:00
Samy Pesse 956d301dd7 Add node v6 to appveyor matrix 2016-05-20 18:22:44 +02:00
Samy Pesse 00d1a7b5a6 Update theme-default@1.0.1 2016-05-20 18:17:34 +02:00
Samy Pessé 2718c9c017 Fix test command for windows 2016-05-19 10:45:25 +02:00
Samy Pesse 3234c1e03f Fix #1310: enforce language config in language books 2016-05-19 00:15:09 +02:00
Aaron O'Mullan c8f8eece92 Merge pull request #1308 from arebee/patch-1
Fix english phrasing for Node version.
2016-05-19 00:10:08 +02:00
Richard Burte 7394efa9dc Fix english phrasing for Node version. 2016-05-18 09:54:04 -07:00
Samy Pesse 3e4241fc88 Implement renderInline method and missing output methods for API 2016-05-17 19:45:48 +02:00
Samy Pesse a5ae79604c Implement renderBlock method for plugin API 2016-05-17 19:30:28 +02:00
Soreine fc4bdd68a1 Can only use file names 2016-05-17 16:47:52 +02:00
Soreine f83975b022 Mention the structure configuration variable 2016-05-17 16:37:13 +02:00
Soreine 167d3957bf Expose lookup method for parsable files 2016-05-17 12:48:03 +02:00
Samy Pessé a9571fd67d Bump version to 3.0.0-pre.15 2016-05-12 16:53:14 +02:00
Samy Pessé ac87f48ba5 Update theme-default@1.0.0 2016-05-12 16:52:19 +02:00
Samy Pessé 05403f527d Fix bug that can happen when configuration file doesn't exist 2016-05-12 16:45:09 +02:00
Samy Pessé 53c72f0806 Add api method getPageByPath for plugins 2016-05-12 16:29:36 +02:00
Samy Pessé c8c2a61f96 Fix #1289: add option to filter files when listing using fs.listAllFiles 2016-05-12 16:10:11 +02:00
Samy Pessé 08d780d1f6 Fix #1300: parse page frontmatter and content when listing pages 2016-05-12 15:48:02 +02:00
Samy Pessé 2e74fafa36 Add template method "getPageByPath" 2016-05-12 15:12:08 +02:00
Samy Pessé 28ac9ab88a Fix #1299: pass fileExists as global function for template, accept null/undefined as param 2016-05-12 15:04:30 +02:00
Samy Pessé 26acab4dfb Fix generation of ebook for multilingual book (gitbook pdf) 2016-05-12 14:29:25 +02:00
Samy Pessé d4b562e001 Fix get of cover for multilingual books 2016-05-12 14:12:43 +02:00
Soreine 61956e0646 Revert "Extend Book.getLanguageBook"
This reverts commit 7892259fe6.
2016-05-12 14:08:29 +02:00
Soreine 7892259fe6 Extend Book.getLanguageBook 2016-05-12 13:35:29 +02:00
Samy Pessé b97d1c7796 Fix languages not being added to json output 2016-05-12 13:25:09 +02:00
Samy Pessé a091c5b331 Fix path resolution when generating page with conrefs 2016-05-12 12:19:00 +02:00
Samy Pessé 784b3379b5 Improve logging when generating multilingual book 2016-05-12 11:20:27 +02:00
Johan Preynat 62446e75d2 Update book's root folder definition in Changelog 2016-05-12 11:18:00 +02:00
Samy Pessé 8aa2f3075c Fix #1294: multilingual book should extend the book's config 2016-05-12 11:15:13 +02:00
Samy Pessé 839ee0385a Add method "getLanguageBook" to book model 2016-05-12 11:02:37 +02:00
Samy Pesse d6b60c6116 Adapt eslint configuration for mocha 2016-05-11 16:56:18 +02:00
Samy Pesse 9013f02850 Add test for transform option of ConrefLoader 2016-05-11 15:08:10 +02:00
Samy Pesse 961d43f085 Fix #1293: replace block shortcuts when including files 2016-05-11 14:45:54 +02:00
Samy Pessé 7bd49606e3 Merge pull request #1291 from GitbookIO/summary-modifiers
Summary modifiers
2016-05-11 13:57:21 +02:00
Samy Pesse d7c8635350 Increase mocha timeout 2016-05-11 13:57:00 +02:00
Soreine 19e9ff81d2 Tweak insertPart signature 2016-05-11 13:11:32 +02:00
Soreine e137bf632a Adds editArticleRef 2016-05-11 13:11:32 +02:00
Soreine 2fef898a1e Modifier moveArticleAfter 2016-05-11 13:11:32 +02:00
Soreine 0945807184 Greatly simplify moveArticle's implementation 2016-05-11 13:11:32 +02:00
Soreine d85663b153 Tests remove/insert articles and update insert signature 2016-05-11 13:11:31 +02:00
Soreine 481a7d790c Insert/remove part 2016-05-11 13:11:31 +02:00
Samy Pesse ef589a6b13 Switch tests to mocha while keeping jest structure 2016-05-11 13:02:24 +02:00
Samy Pesse d5c4af3377 Add immutable ignore instance for book 2016-05-11 13:02:24 +02:00
Soreine 0b4df4cdf2 Precise h2 headings for Glossary 2016-05-11 10:55:34 +02:00
Samy Pessé d6814dc48e Don't copy config file as asset 2016-05-10 15:23:59 +02:00
Samy Pessé ce50a1d50f Bump version to 3.0.0-pre.14 2016-05-10 14:37:22 +02:00
Samy Pessé a76e018d36 Update theme-default@1.0.0-pre.10 2016-05-10 14:37:05 +02:00
Samy Pessé b06f1fd677 Fix article.depth starting at 2 2016-05-10 14:36:03 +02:00
Samy Pessé 3d8b1880dc Fix page's title in pdf header/footer 2016-05-10 14:25:50 +02:00
Samy Pessé 1a13c8c9bb Add option "--no-live" for command serve 2016-05-10 13:14:32 +02:00
Samy Pessé 852fae3a09 Fix #1232: add option "--no-watch" to disable watcher and live reloading 2016-05-10 13:08:19 +02:00
Samy Pessé d342d31c8e Fix error when summary contains entry without ref 2016-05-10 12:12:19 +02:00
Samy Pesse 5b3f54fbc8 Fix "gitbook init" when article has no ref 2016-05-10 09:55:22 +02:00
Samy Pessé 308ece784c Merge pull request #1288 from todvora/patch-1
Fixed summary encoding
2016-05-10 08:55:40 +02:00
Tomas Dvorak 0758ddf04e Fixed summary encoding 2016-05-10 08:17:52 +02:00
Samy Pesse f24d9aef73 Add test for block replaced with anchors 2016-05-09 22:17:46 +02:00
Samy Pesse f8bb55f258 Adapt unit tests for blocks 2016-05-09 22:01:25 +02:00
Samy Pesse 76daebeb84 Fix unit test for templateBlock using a removed method "getParse" 2016-05-09 18:41:21 +02:00
Samy Pesse b0d366b7e8 Fix linting warning in models/templateBlock.js 2016-05-08 22:45:26 +02:00
Samy Pesse 3d0054dd64 Fix #1286: by default blocks should not be parsable 2016-05-08 22:33:41 +02:00
Samy Pessé 13bc89ff44 Merge pull request #1285 from GitbookIO/api/summary
[WIP] Api for plugins to access summary
2016-05-08 22:28:15 +02:00
Samy Pesse bb910f984e Improve comment for API summary.walk 2016-05-08 22:26:46 +02:00
Samy Pessé e576fdea14 Merge pull request #1278 from dylang/patch-1
bump bash-color to version that has a license
2016-05-08 18:44:53 +02:00
Samy Pesse c7864927ec Add method summary.getArticleByPath to summary api 2016-05-08 11:08:27 +02:00
Samy Pesse 54956de59f Start API for plugins to access the summary 2016-05-08 10:33:45 +02:00
Samy Pesse 2cbd30d1f2 Fix command "gitbook init" 2016-05-07 00:24:12 +02:00
Dylan Greene 55614f1675 bump bash-color to version that has a license
Version currently being used does not have a license making it difficult for companies to use.
2016-05-06 09:56:43 -04:00
Samy Pesse 4f7421993f Bump version to 3.0.0-pre.13 2016-05-06 10:59:52 +02:00
Samy Pesse 32ad7b530e Fix #1266: don't fail on "a" tags without "href" 2016-05-06 10:57:41 +02:00
Samy Pesse ee5d5010f6 Correctly read assets using input fs when copying 2016-05-06 10:54:13 +02:00
Samy Pesse cf7e5884cd Add method readAsStream on fs model 2016-05-06 10:53:55 +02:00
Samy Pesse 11a0a64d9e Add failing test for #1272 2016-05-06 10:36:55 +02:00
Samy Pesse b6051babac Adapt unit tests for new templating post process 2016-05-05 11:09:35 +02:00
Samy Pesse 34947b5e20 Add mode TemplateOutput to represent {content + blocks} from template 2016-05-05 11:00:05 +02:00
Samy Pesse c621380b66 Add tests for replaceShortcuts 2016-05-04 20:18:48 +02:00
Samy Pesse c4b54033ce Fix #1268: fix shortcuts of template block not being applied 2016-05-04 19:01:31 +02:00
Samy Pesse 4d85d6eb6e Fix #1267: don't fail when "node_modules" folder doesn't exist 2016-05-04 17:41:15 +02:00
Samy Pessé cc9274c9af Bump version to 3.0.0-pre.12 2016-05-04 13:50:52 +02:00
Johan Preynat bbdf7efc34 Fix location.relative(dir, file) trailing slash if file is a directory 2016-05-04 11:51:39 +02:00
Samy Pessé 38a986995a Merge pull request #1264 from GitbookIO/fix/plugin-resources
Fix Loading plugin resources
2016-05-04 10:33:29 +02:00
Johan Preynat ee8d35df7a Update Plugins.listResources() to use the prepared resources list 2016-05-04 10:30:11 +02:00
Johan Preynat be411cd5ff Update copyPluginAssets to use prepared resources 2016-05-04 10:27:21 +02:00
Johan Preynat 2104019171 List resources per plugin in website generator onInit() 2016-05-04 10:26:57 +02:00
Johan Preynat 4e564707df Add resources to website generator state 2016-05-04 10:26:06 +02:00
Johan Preynat da8611e0c3 Add global method resolve(fileName) 2016-05-04 10:25:37 +02:00
Samy Pessé 5d11641a5f Merge pull request #1260 from GitbookIO/move-article
Adds move and remove article modifier.
2016-05-04 09:47:50 +02:00
Samy Pessé 24d38e4fcf Fix #1263: Fix page generation for AsciiDoc 2016-05-04 09:30:09 +02:00
Samy Pessé efb65a8413 Remove filter for pdf/epub/mobi files when outputting 2016-05-03 17:18:13 +02:00
Soreine 3fc90554f5 Add tests, and fixes. 2016-05-03 16:02:58 +02:00
Soreine 4ebe28faae Update insert specification 2016-05-03 13:11:32 +02:00
Soreine 693171cb05 Adds move and remove article modifier. 2016-05-03 12:37:26 +02:00
Samy Pessé 42a2d090aa Bump version to 3.0.0-pre.11 2016-05-03 12:15:30 +02:00
Samy Pessé c84eaa83dd Merge pull request #1259 from GitbookIO/png-data-uri
Adding data-uri support for PNG Images in Books
2016-05-03 12:10:09 +02:00
Johan Preynat 74c3ff80cb lib/models/config.js: Fix wrong call to PluginDependency.listToArray() to PluginDependency.listFromArray() 2016-05-03 12:04:52 +02:00
Johan Preynat 5893d3ee64 lib/parse/parseConfig.js: Remove unused require 2016-05-03 12:04:04 +02:00
Johan Preynat 7f435c56a0 lib/output/modifiers/inlineAssets.js: Add inlinePng() modifier 2016-05-03 11:30:02 +02:00
Johan Preynat 59b7100cf6 lib/output/modifiers/__tests__/inlinePng.js: Add test file for inlinePng modifier 2016-05-03 11:29:40 +02:00
Johan Preynat 0923d1aa24 lib/output/modifiers/inlinePng.js: Add modifier to save inline PNG images 2016-05-03 11:29:17 +02:00
Johan Preynat ffe6c36f2f lib/output/modifiers/resolveImages.js: Don't resolve images when src is a data URI 2016-05-03 11:28:39 +02:00
Johan Preynat 8c4752c5d8 lib/utils/__tests__/location.js: Add location.isDataURI() test file 2016-05-03 11:27:36 +02:00
Johan Preynat d6202e522b lib/utils/images.js: Add convertInlinePNG() method 2016-05-03 11:27:05 +02:00
Johan Preynat 50a24c62c5 lib/utils/location.js: Add isDataURI() method 2016-05-03 11:26:38 +02:00
Soreine 6b17d08892 Adds default file naming utilities in Book model 2016-05-03 11:17:27 +02:00
Samy Pesse 79c91fd8c5 Log npm version being used when running gitbook install 2016-05-02 22:18:19 +02:00
Samy Pesse 95764258ab Update dependencies 2016-05-02 22:16:51 +02:00
Samy Pesse 41e6872557 Merge branch 'fix/plugins_deps' 2016-05-02 22:02:45 +02:00
Samy Pesse 30c96c37c2 Adapt Plugin.listAll to use a list of PluginDependency 2016-05-02 22:02:24 +02:00
Samy Pesse 20f1cf3dff Enable promise long stack trace when testing or debugging 2016-05-02 22:01:05 +02:00
Samy Pesse 8de0d6186e Fix mock fs for windows testing 2016-05-02 19:16:06 +02:00
Samy Pesse edd7d8aefe Update gitbook-asciidoc@1.1.4 2016-05-02 18:45:17 +02:00
Samy Pesse a8da4903f4 Update gitbook-asciidoc@1.1.3 to fix error on linux 2016-05-02 17:27:18 +02:00
Samy Pesse c8df5033f7 Update gitbook-asciidoc@1.1.2 2016-05-02 17:25:41 +02:00
Samy Pesse 412f418da3 Update gitbook-markdown@1.2.3 2016-05-02 17:15:54 +02:00
Johan Preynat 3782d21069 lib/output/ebook/onPage.js: Fix missing page's filepath at Modifiers.inlineAssets() call 2016-05-02 16:54:14 +02:00
Johan Preynat 82a29f4470 lib/models/languages.js: Use lang.ref 2016-05-02 16:37:47 +02:00
Johan Preynat 4a65326b24 Update jest@12.0.2 2016-05-02 16:37:24 +02:00
Johan Preynat d75d8729be lib/output/ebook/onFinish.js: Use lowercase summary.html filename from themes 2016-05-02 16:26:00 +02:00
Samy Pessé c87060454e Add model for plugin dependencies in the book.json 2016-05-02 16:24:20 +02:00
Samy Pessé bbba52c7ff Update gitbook-markdown and gitbook-asciidoc 2016-05-02 16:21:44 +02:00
Samy Pessé 33418bdb33 Document method in utils/command.js 2016-05-02 15:21:08 +02:00
Samy Pessé de47db2b66 Fix escaping of ebook-convert options 2016-05-02 15:09:10 +02:00
Johan Preynat fa6fcffbe1 Improve lib/plugins/__tests__/findInstalled.js 2016-05-02 15:06:02 +02:00
Samy Pessé 93d3f11891 Fix js error when using node < 4.x 2016-05-02 15:03:29 +02:00
Johan Preynat 59479f10fc Fix lib/plugins/__tests__/findInstalled.js 2016-05-02 14:46:54 +02:00
Samy Pessé 11e72253d4 Bump version to 3.0.0-pre.10 2016-05-02 14:40:54 +02:00
Samy Pessé 37fca9c88a Update theme-default@1.0.0-pre.9 2016-05-02 14:39:39 +02:00
Johan Preynat 6d67048e98 Use gitbook-plugin-search@2.2.1 2016-05-02 14:36:23 +02:00
Johan Preynat c516a5c1bd lib/fs/node.js: Use fresh-require@1.0.3 to read uncached configuration files 2016-05-02 14:34:34 +02:00
Johan Preynat 429431c13c lib/api/deprecate.js: Fix deprecateRenameMethod() 2016-05-02 14:05:11 +02:00
Samy Pessé b80dfd7b9a Add method contentLink as deprecated for plugin API 2016-05-02 13:50:51 +02:00
Samy Pessé 4c144b6ceb Fix Plugins.listAll to handle strings 2016-05-02 13:42:18 +02:00
Samy Pessé 718e32773a Add method removePlugin to ConfigModifier 2016-05-02 13:41:07 +02:00
Samy Pessé fb75d5d3ee Add this.gitbook.version 2016-05-02 13:41:07 +02:00
Samy Pessé 99bbcbebfc Fix modifiers for ebook format 2016-05-02 13:41:07 +02:00
Soreine 289e4b5dc1 Tweak Summary.toText doc 2016-05-02 13:38:51 +02:00
Johan Preynat a974dcdab6 lib/templating/postRender.js: Fix missing argument to engine.getBlock() 2016-05-02 13:32:27 +02:00
Samy Pessé 9a48620152 Document encodeGlobal api 2016-05-02 10:02:46 +02:00
Samy Pessé e444b3a402 Fix launch of livereload server 2016-05-02 09:59:44 +02:00
Samy Pessé fabf4f0c0a Enable livereload plugin when serving content 2016-05-02 09:56:32 +02:00
Samy Pessé d467888d6a Improve timing debug 2016-05-02 09:47:11 +02:00
Samy Pessé eb46311561 getArticleByPath is now a global function for template instead of a filter 2016-05-02 09:38:24 +02:00
Samy Pessé 63839bb0a7 Fix apply of async html modifiers 2016-05-02 09:29:49 +02:00
Samy Pessé 793fada0fb Add test for async template block 2016-05-02 09:19:57 +02:00
Samy Pessé 494ba78156 Complete api method template.applyBlock 2016-05-02 09:14:01 +02:00
Samy Pessé 9816b27ebf Update nunjucks@2.4.2 2016-05-02 09:06:59 +02:00
Samy Pessé 1c43b7891b Log resolved version when installing plugins 2016-05-02 09:02:36 +02:00
Samy Pessé 9d7739213e Remove should from git.js test 2016-05-02 08:57:18 +02:00
Samy Pessé 2ab5f6a305 Fix case of helper/resolveFileToUrl.js 2016-05-02 08:53:32 +02:00
Samy Pesse c73103969f Add signature for api template.applyBlock 2016-05-01 23:27:38 +02:00
Samy Pesse 6cbbdf5d5b Improve spacing when installing plugins 2016-05-01 22:58:25 +02:00
Samy Pesse 3a48435497 Cleanup folder before generating output 2016-05-01 19:35:50 +02:00
Samy Pesse 084925de8b Improve plugin loading to not fake module errors 2016-05-01 19:07:57 +02:00
Samy Pesse 5bac2fe695 Fix #1253: add instructions for generating ebook in docs 2016-05-01 16:15:19 +02:00
Samy Pesse 2c29fb241f Always log timing infos 2016-05-01 15:55:27 +02:00
Johan Preynat 77dc11bcc2 lib/cli/options.js: Return pure JS Array for CLI log options 2016-05-01 12:09:58 +02:00
Samy Pesse 5154622566 Add @soreine and @jpreynat to authors list 2016-04-30 23:50:47 +02:00
Samy Pesse c681cd286a Remove lodash dependency 2016-04-30 23:07:43 +02:00
Samy Pesse c1d53ec11f Switch parsers to a model 2016-04-30 22:06:16 +02:00
Samy Pesse 36b49c66c6 Merge branch 'fixes' 2016-04-30 20:15:08 +02:00
Samy Pesse 80b8e340da Fix add global env to templateEngine 2016-04-30 20:08:04 +02:00
Samy Pesse b51470db39 Add tests for conrefs loader 2016-04-30 20:06:43 +02:00
Samy Pesse a5f4596f8d Add tests for templateEngine 2016-04-30 19:00:35 +02:00
Johan Preynat 5f3c3760c0 lib/models/templateBlock.js: Fix convert blocks to pure JS array before passing to parser.parseUntilBlocks() 2016-04-30 17:05:30 +02:00
Johan Preynat 8b05885d57 lib/models/templateBlock.js: Use function for Ext() instead of var 2016-04-30 17:05:30 +02:00
Johan Preynat d9d7ab4ee5 lib/models/templateBlock.js: Add test file lib/models/__tests__/templateBlock.js 2016-04-30 17:05:30 +02:00
Samy Pesse 30bce5f9bb Add option "--timing" to mesure gitbook performances 2016-04-30 14:37:44 +02:00
Samy Pesse fbe0282738 Add tests for git utility 2016-04-30 12:30:56 +02:00
Samy Pesse 5bad08b9a0 Add this.generator as deprecated 2016-04-30 12:14:43 +02:00
Samy Pesse 310691bca2 Use areIdenticalPaths for Summary.getByPath 2016-04-29 23:43:01 +02:00
Samy Pesse 8f7967acd0 Log page causing error when a bug occurs during generation 2016-04-29 23:32:41 +02:00
Samy Pesse faf18476b1 Don't fail if page doesn't exist 2016-04-29 23:23:54 +02:00
Samy Pesse 9acccad23a Fix validation of configuration for plugins 2016-04-29 23:19:33 +02:00
Samy Pesse db1a0aedf8 Fix listing of plugins when contains "-" 2016-04-29 23:13:31 +02:00
Samy Pesse 01a26fda25 Fix linting error 2016-04-29 20:18:28 +02:00
Samy Pessé 0229272b0a Add api this.readFile and this.readFileAsString 2016-04-29 17:46:59 +02:00
Samy Pessé c817acff09 Add context for page templates 2016-04-29 17:40:35 +02:00
Samy Pessé a5eec81819 Fix context for page template 2016-04-29 17:34:33 +02:00
Samy Pessé 98f70879bd Complete ebook generation using ebook-convert 2016-04-29 16:50:50 +02:00
Samy Pessé ceb4627f4e Use API to create context for filters/blocks 2016-04-29 15:30:56 +02:00
Samy Pessé 8d9d4fe23f Merge remote-tracking branch 'origin/fixes' into fixes 2016-04-29 15:28:01 +02:00
Samy Pessé 730298110c Add ebook commands pdf/mobi/epub 2016-04-29 15:27:53 +02:00
Johan Preynat 8578f7d0d1 lib/api/encodePage.js: Fix deprecated page.progress assignment 2016-04-29 15:16:56 +02:00
Samy Pessé 07a5daefb0 Generate SUMMARY.html for ebook output 2016-04-29 15:09:11 +02:00
Samy Pessé 0ed524bce5 Correctly setup configuration for plugins in multilingual books 2016-04-29 15:08:54 +02:00
Johan Preynat 94199af7b2 Add location.areIdenticalPaths(p1,p2) to effectively compare two paths 2016-04-29 14:38:12 +02:00
Johan Preynat b8d7a00f58 Find installed gitbook-plugins in node_modules folder 2016-04-29 14:38:12 +02:00
Johan Preynat aca963befc Fix typo 2016-04-29 14:38:12 +02:00
Samy Pessé 3ea4e334bf It should generate index for languages 2016-04-29 13:04:54 +02:00
Samy Pessé 9955fa8c29 Use plugin assets from parent when language book 2016-04-29 12:46:31 +02:00
Samy Pessé fb37a6bcab Inherit plugins list from parent output when generating multilingual book 2016-04-29 11:31:54 +02:00
Samy Pessé 4f8bdbb518 Add tests for parsing a book with option "root" 2016-04-29 11:11:42 +02:00
Samy Pessé 73c190891d Fix parsing of multilingual books 2016-04-29 10:58:25 +02:00
Samy Pessé 93e701f471 Complete deprecated page.progress and this.navigation 2016-04-29 10:17:16 +02:00
Samy Pesse 68b9bf7e38 Encode navigation for compatibility with gitbook v2 2016-04-28 22:45:50 +02:00
Samy Pesse 826f90505c Add command "init" 2016-04-28 21:25:58 +02:00
Samy Pessé 7ae36f16c8 Add base for deprecated page.naviation 2016-04-28 15:45:25 +02:00
Samy Pessé 281f6d5535 Extend timeout for tests 2016-04-28 15:36:54 +02:00
Samy Pessé b4072a06c4 Add basic tests for website generator 2016-04-28 15:28:46 +02:00
Samy Pessé cb3ee423c9 Add basic tests for json generator 2016-04-28 15:20:25 +02:00
Samy Pessé 1360a744a7 Export more models 2016-04-28 15:02:24 +02:00
Samy Pessé f3058598dc Always generate a README.json as introduction of json output 2016-04-28 14:42:07 +02:00
Samy Pessé 94db0f6ec9 Fix typo in JSONUtils.encodeBook 2016-04-28 14:39:11 +02:00
Samy Pessé f08732095d Copy readme.json from main language for json output of multilingual 2016-04-28 14:37:19 +02:00
Samy Pessé f946650ddb Add "next" and "previous" to page JSON repr 2016-04-28 14:28:41 +02:00
Samy Pessé 14fd6bbdce Add tests for PathUtils 2016-04-28 14:16:15 +02:00
Samy Pessé 5059e8df34 Add modifiers to edit article and insert in summary 2016-04-28 14:12:21 +02:00
Samy Pessé 3d405a1ac5 Fix ref in summary when parsed without ref 2016-04-28 14:03:58 +02:00
Samy Pessé 78e00db018 Add tests for SummaryModifier.editPartTitle 2016-04-28 11:35:30 +02:00
Samy Pessé e62bc31e63 Add method setSummary to book 2016-04-28 11:17:33 +02:00
Samy Pessé 4763f1342c Add base modifiers for summary as SummaryModifier 2016-04-28 11:15:51 +02:00
Samy Pessé 9413e53b00 Add tests for parseIgnore 2016-04-28 10:37:52 +02:00
Samy Pessé 35d352bbeb Handle correctly ignored file in multilanguages book 2016-04-28 09:59:34 +02:00
Samy Pesse 8dd5e0c250 Add comments for todos 2016-04-27 22:42:09 +02:00
Samy Pesse 139e487998 Correctly resolve images starting with / 2016-04-27 22:36:59 +02:00
Samy Pesse 9bb0a3fbd3 Add tests for svg inliner 2016-04-27 22:18:33 +02:00
Samy Pessé 9a1a6f2625 Document cli/server.js 2016-04-27 18:02:49 +02:00
Samy Pessé e781d6d2b1 Add test for assets inliner 2016-04-27 17:58:35 +02:00
Samy Pessé 5b342b50ce Add language property to book for multilingual 2016-04-27 16:41:17 +02:00
Samy Pessé 869822d180 Use orderedMap for language books 2016-04-27 16:26:16 +02:00
Samy Pessé e50422bce5 Use code TemplateBlock to highlight code 2016-04-27 16:22:17 +02:00
Samy Pessé 999882e723 Add test for highlightCode html modifier 2016-04-27 15:42:55 +02:00
Samy Pessé d68dc82159 Merge remote-tracking branch 'origin/fixes' into fixes 2016-04-27 15:12:25 +02:00
Samy Pessé cc9fdd0032 Generate multilingual books 2016-04-27 15:12:18 +02:00
Soreine 503cc4a729 Check for stdout, for browser compatibility 2016-04-27 14:52:26 +02:00
Samy Pessé d585269228 Correctly ignore summary and templates from assets 2016-04-27 14:37:02 +02:00
Samy Pessé 50f535b910 Fix plugin's config validation 2016-04-27 14:31:45 +02:00
Samy Pessé 978bdd149e Add browser specific index.js 2016-04-27 14:18:23 +02:00
Samy Pessé a9d558e891 Add method ".toText" on glossary and summary 2016-04-27 14:11:45 +02:00
Samy Pessé 8291eb58df Complete api method output.toURL 2016-04-27 13:53:32 +02:00
Samy Pessé 7298dec353 Add plugins.resources for template context 2016-04-27 13:47:12 +02:00
Samy Pessé 7574b01863 Merge remote-tracking branch 'origin/fixes' into fixes 2016-04-27 13:13:38 +02:00
Samy Pessé 98e739a12b Add JS context for template 2016-04-27 13:13:27 +02:00
Soreine 262ce03e46 Exposes FS in public interface 2016-04-27 13:12:51 +02:00
Samy Pessé 1906691a35 Fix order of plugins assets copy 2016-04-27 12:48:40 +02:00
Samy Pessé 4ca23b84a4 Fix filters for theme templates 2016-04-27 12:08:00 +02:00
Samy Pessé b05eb8440b Don;t log deprecation warning for own logic 2016-04-27 10:38:54 +02:00
Samy Pessé f4de7748f5 Complete generator name in plugins API 2016-04-27 09:59:09 +02:00
Samy Pessé 93c26a839b Add this.book and this.options as deprecated 2016-04-27 09:42:40 +02:00
Samy Pesse 878e7441f2 Stop running test on node 0.12 for windows 2016-04-26 23:19:19 +02:00
Samy Pesse 27d387e3e1 Load i18n translations 2016-04-26 21:48:56 +02:00
Samy Pesse 7b7e4f60bc Correctly set log level 2016-04-26 20:49:20 +02:00
Samy Pesse 8bac111acf Add base for plugin JS api 2016-04-26 17:02:59 +02:00
Samy Pesse 7a46d3b497 Add command "serve" 2016-04-26 15:51:52 +02:00
Samy Pesse 357aa92e31 Add default filters to theme templating engine 2016-04-26 15:50:11 +02:00
Samy Pesse fa17f16716 Add base for commands
Improve plugins installation
2016-04-26 15:23:28 +02:00
Samy Pesse ce99c8c706 Add more tests for summary 2016-04-26 14:25:54 +02:00
Samy Pesse be828cf004 Add duration to final log for generation 2016-04-26 14:12:26 +02:00
Samy Pesse 1057bb15d3 Complete method getByPath of Summary 2016-04-26 13:03:53 +02:00
Samy Pesse ea5e8891a8 Add test for readme parsing 2016-04-26 11:17:47 +02:00
Samy Pesse 57d83f082a Add basic tests for summary parsing 2016-04-26 11:15:50 +02:00
Samy Pesse 8a3f80ba82 Add test for parsing glossary 2016-04-26 11:00:36 +02:00
Samy Pesse 374ebd6f7a Add mock fs and tests for it 2016-04-26 10:23:37 +02:00
Samy Pesse ab2ebefe2b Copy resources from plugins to output folder 2016-04-25 22:30:41 +02:00
Samy Pesse c34a9f6213 Copy assets from themes to output folder 2016-04-25 21:13:17 +02:00
Samy Pessé f5df41c75f Enable extension "do" 2016-04-25 15:44:20 +02:00
Samy Pessé 0c2dc06cb7 Fix resolution of theme's templates 2016-04-25 15:36:18 +02:00
Samy Pessé 4552ccd7d7 Add loader for themes 2016-04-25 14:44:49 +02:00
Samy Pessé 525bcd8295 Disable travis tests on 0.12 2016-04-25 14:37:49 +02:00
Samy Pessé ac2ad8d8b5 Add conrefs loader for templating 2016-04-25 14:29:51 +02:00
Samy Pessé 14ce495e9e Add more tests for LocationUtils 2016-04-25 14:15:28 +02:00
Samy Pessé 4aed2cf652 Add post processing for templates 2016-04-25 13:52:56 +02:00
Samy Pessé 244fb0ca28 Replace shortcuts in templating 2016-04-25 12:24:59 +02:00
Samy Pessé f6e123f1ed Add base rendering for template 2016-04-25 11:58:55 +02:00
Samy Pessé e1e4e7f011 Add tests for link resolution 2016-04-25 11:28:21 +02:00
Samy Pessé 814b6ff489 Complete json format 2016-04-25 11:05:06 +02:00
Samy Pessé 3aa17ab1f4 Convert markdown to html for pages 2016-04-25 10:58:10 +02:00
Samy Pessé 4646ed8f4a Fix plugins loading 2016-04-25 10:32:44 +02:00
Samy Pesse 3f5c8acb3c Add fiel for testing resolveLinks 2016-04-24 23:45:47 +02:00
Samy Pesse 93b257314a Add default filters and blocks 2016-04-24 19:12:03 +02:00
Samy Pesse 19a1db8cb4 Add base structure for templating 2016-04-24 18:52:37 +02:00
Samy Pesse c3275a4aa9 Parse multilingual books 2016-04-24 00:15:38 +02:00
Samy Pesse 927185d95c add test for modifier "annotateText" 2016-04-23 23:58:08 +02:00
Samy Pesse a3df6c6f0c Add assets inliner modifier for HTML 2016-04-23 21:10:53 +02:00
Samy Pesse e1fa977b5b Add json encoding utils 2016-04-23 17:10:16 +02:00
Samy Pesse ce95f316b9 Add method to be article by level 2016-04-23 11:22:20 +02:00
Samy Pessé a162af5a75 Add base for calling hook on page/global 2016-04-22 22:58:37 +02:00
Samy Pessé fb0ea4610d Add base utils for generation 2016-04-22 22:21:34 +02:00
Samy Pessé 4f86a9978b Add test for config 2016-04-22 11:56:34 +02:00
Samy Pessé cc78b880ce Add utils to install plugins 2016-04-22 11:33:25 +02:00
Samy Pessé 4336fdb241 Base 2016-04-22 11:00:21 +02:00
Samy Pessé 87db7cf1d4 Merge remote-tracking branch 'origin/master' 2016-04-20 17:26:49 +02:00
Samy Pessé 09ff802c7e When using structure.readme, fix link to readme to resolve to index.html 2016-04-20 17:26:13 +02:00
Johan Preynat 71b59b1950 Correctly detect promises from block.process in TemplateEngine.applyBlock() 2016-04-20 15:59:53 +02:00
Samy Pessé a3300ad9c6 Comment template code 2016-04-20 15:02:39 +02:00
Samy Pessé 8f40d4affa Fix reliability of template.self in macro import 2016-04-20 10:03:04 +02:00
Samy Pessé 560f3fcccb Update plugin-search@2.2.0 2016-04-20 09:42:11 +02:00
Samy Pesse ecaadf63c5 Correctly ignore structure file (only in root folder) 2016-04-20 00:06:45 +02:00
Samy Pesse d490a6e0b4 Correctly load plugins when using "root" option 2016-04-19 17:40:01 +02:00
Samy Pesse 53ced205b8 Ensure "template" is always set when rendering a template 2016-04-19 15:46:37 +02:00
Samy Pesse 61c4983949 Fix template context should be global in nunjucks for theming
Fix pdf_footer and pdf_header template not rendering
2016-04-19 15:33:35 +02:00
Samy Pesse fb862f466f Bump version to 3.0.0-pre.9 2016-04-19 14:18:01 +02:00
Samy Pesse 8d51300386 Bump version to 2.1.0 2016-04-19 14:16:51 +02:00
Samy Pesse c4b413563d Update theme-default@1.0.0-pre.8 2016-04-19 14:14:59 +02:00
Samy Pesse 8f852971f9 Cleanup book.js 2016-04-19 14:12:01 +02:00
Johan Preynat 3c54c89464 Use page.content if modified or fallback to page.sections in plugins hooks 2016-04-19 10:02:41 +02:00
Samy Pessé d07aa13822 Add tests for page hook 2016-04-18 17:14:55 +02:00
Samy Pessé c218f7d0e3 Correctly set template.self in all cases 2016-04-18 16:56:50 +02:00
Samy Pessé 72ad872e90 Fix js error when using output without root 2016-04-18 14:26:24 +02:00
Samy Pessé 854f244d22 Fix infinite loop on this.options 2016-04-18 14:17:51 +02:00
Samy Pessé 9acbb223db Add generator and output property as deprecated in config 2016-04-18 14:16:22 +02:00
Johan Preynat c59fc687db Ensure plugins compatibility with page.sections 2016-04-18 14:10:19 +02:00
Samy Pessé 0791b917f1 Add context for readme 2016-04-18 12:49:31 +02:00
Samy Pessé ba182ce8e4 Fix tests for copy of website theme 2016-04-15 11:33:38 +02:00
Samy Pessé 663291c515 Output context as global for themes 2016-04-15 11:33:24 +02:00
Samy Pessé 99253eecea Bump version to 3.0.0-pre.8 2016-04-14 15:02:49 +02:00
Samy Pessé 916a02cb36 Update plugins search@2.0.0 and theme-default@1.0.0-pre.7 2016-04-14 15:00:25 +02:00
Samy Pessé 114c4abcd1 Add method for theme: template.getJSContext 2016-04-14 14:51:42 +02:00
Samy Pessé e1e3d11932 Add lunr as default plugin 2016-04-14 14:51:22 +02:00
Samy Pessé df46217c78 Improve description extraction and adapt unit tests 2016-04-14 10:07:51 +02:00
Samy Pessé d18cc92396 Pass template to block's context 2016-04-14 09:51:56 +02:00
Samy Pessé 8f63b4b790 Remove unused variable 2016-04-13 17:32:01 +02:00
Samy Pessé ce7de93936 Improve block parsing to handle args/kwargs on subblocks 2016-04-13 16:22:53 +02:00
Johan Preynat ef2a24fe6f Add trailing dots to page.description when longer than 300 characters 2016-04-13 16:00:16 +02:00
Samy Pessé 038cd0155b Add filter "getArticleByPath" for theme templates 2016-04-13 11:48:29 +02:00
Samy Pessé fb430ac1ed Increase size of extracted description 2016-04-13 10:55:32 +02:00
Johan Preynat 53cfa3703b Update page context for plugins hooks 2016-04-13 09:33:26 +02:00
Johan Preynat 7ed9e8d145 Update plugin-fontsettings@1.0.3 / plugin-sharing@1.0.2 2016-04-12 17:05:32 +02:00
Samy Pessé 93e0b0cbea Watch _layouts when serving 2016-04-12 13:53:58 +02:00
Samy Pessé 0118bd3168 Cleanup code to list plugins 2016-04-11 12:29:53 +02:00
Samy Pessé 07d5245a5f Bump version to 3.0.0-pre.7 2016-04-11 09:59:58 +02:00
Samy Pessé 6b844213ec Update theme-default@1.0.0-pre.6 2016-04-11 09:59:18 +02:00
Samy Pessé dabdeb8a80 Fix #1195: fix external images and fix test for it 2016-04-11 09:56:13 +02:00
Samy Pessé ecd56315e6 Merge pull request #1220 from seanlane/patch-1
Fix minor spelling error.
2016-04-11 09:34:15 +02:00
Sean Lane 15b9e839e9 Fix minor spelling error. 2016-04-10 14:31:49 -06:00
Samy Pesse d7f7dcd47b Switch to ignore@3.1.1 2016-04-10 22:28:29 +02:00
Samy Pessé b1981e09b8 Fix generator deprecation warning being shown for each call 2016-04-10 18:20:51 +02:00
Samy Pessé c9edd0a795 Load plugins from book parent in multilingual book 2016-04-09 12:16:17 +02:00
Samy Pesse d83e0716bb Add test for main language in json output 2016-04-08 23:58:42 +02:00
Samy Pessé aed38afff3 Bump version to 3.0.0-pre.6 2016-04-08 16:35:35 +02:00
Samy Pessé 1df522e6ec Update node modules 2016-04-08 16:34:53 +02:00
Samy Pessé bd4ef2354e Merge pull request #1216 from GitbookIO/fix/ignore
Fix #1215: fix ignore rules not working
2016-04-08 15:48:33 +02:00
Samy Pessé 32a49a6969 Inherit ignore rules of output from book 2016-04-08 15:46:46 +02:00
Samy Pessé 21806687e6 Add base tests for fiailng ignore 2016-04-08 15:23:46 +02:00
Samy Pessé 7ade91a091 Disable ssh test when ci 2016-04-08 15:11:54 +02:00
Samy Pessé 4e41f96d89 Add "languages" property to all json files for multilingual book 2016-04-08 14:42:37 +02:00
Johan Preynat 325c32b4c9 Update gitbook-plugin-highlight@2.0.2 2016-04-06 10:50:03 +02:00
Samy Pessé 7d6606a8e5 Merge pull request #1202 from qjcg/patch-1
Proofreading
2016-04-06 09:19:37 +02:00
Samy Pessé c887d520eb Update github-slugid@1.0.1 2016-04-04 11:05:42 +02:00
John Gosset e6050fc558 Proofreading
A few proofreading changes with clarity in mind.
2016-03-31 13:23:15 -04:00
Samy Pessé cae4853334 Merge pull request #1194 from jashaj/master
Fix for incorrect splitting the plugin name/version
2016-03-24 21:50:26 +01:00
Jasha Joachimsthal 9ef2bebfe9 Misinterpreted second argument of String.prototype.split. Remainder of the array must be joined instead. 2016-03-24 21:21:23 +01:00
Samy Pessé 53294af810 Merge pull request #1192 from jashaj/master
Limit splitting plugin name for git with authentication
2016-03-24 16:54:29 +01:00
Jasha Joachimsthal 7d21c815a5 Limit splitting plugin name for git+ssh 2016-03-24 15:23:28 +01:00
Samy Pessé e07719a86c Fix template search paths to use plugins listed first 2016-03-22 10:59:25 +01:00
Soreine fa5bee6ca3 Import paragraph about Escaping nunchuck from old doc. 2016-03-21 15:07:22 +01:00
Samy Pessé 3a43ef2c3f Add doc for inline rendering 2016-03-18 11:55:24 +01:00
Samy Pessé 55deab03cd Add test for blocks markdown/asciidoc/markup 2016-03-18 11:26:31 +01:00
Samy Pessé edda84fefd Add block "markdown", "asciidoc" and "markup" 2016-03-18 11:20:57 +01:00
Samy Pessé 10b191f631 Use stabler block anchor in template output 2016-03-18 11:17:26 +01:00
Samy Pessé d5a575c5e0 Fix context for template block/filters 2016-03-18 11:14:04 +01:00
Samy Pessé 82d8c3e2d9 Add method renderInline and renderBlock to book 2016-03-18 11:10:58 +01:00
Samy Pessé bc0c8dbcf3 Update nunjucks@2.4.1 2016-03-18 11:07:21 +01:00
Samy Pessé de0245ff40 Update nunjucks@2.4.1 2016-03-18 11:03:22 +01:00
Samy Pessé 7f56ff0281 Fix doc for paragraph 2016-03-17 11:53:28 +01:00
Samy Pessé 2cfbf5411c Add question about text direction in the faq 2016-03-17 10:41:34 +01:00
Samy Pessé f70da1c82a Fix links in readme 2016-03-15 12:40:40 +01:00
Samy Pessé 0b4db49f57 Merge pull request #1181 from GitbookIO/feature/allplugins_tpl
All plugins can extend templates/theme
2016-03-15 12:37:52 +01:00
Samy Pessé fbffd54aa2 Load all plugins, even dependencies of plugins 2016-03-15 12:37:25 +01:00
Samy Pessé 4d19a33af2 Add method registry.list to list all plugins for a book 2016-03-15 11:43:37 +01:00
Samy Pessé 9d67a0e656 Add theme for extending theme from book 2016-03-11 11:21:47 +01:00
Samy Pessé 093005bfd9 Fix FSLoader for relative paths 2016-03-11 10:51:36 +01:00
Samy Pessé 514883f0bd Create new loader for nunjucks to avoid infinite loop 2016-03-11 10:43:37 +01:00
Samy Pessé 27681aad98 Update nunjucks@2.4.0 2016-03-11 10:20:26 +01:00
Samy Pessé 0d367da776 Use complete list of plugins for templates resolution / i18n / assets 2016-03-09 16:45:27 +01:00
Samy Pessé c499a8a13a Add back gitbook.generator property as deprecated 2016-03-09 15:50:38 +01:00
Samy Pessé 64a6de751d Change documentation's title 2016-03-09 15:22:04 +01:00
Samy Pessé d39f0d6ff0 Add "output" variables for templating 2016-03-09 15:18:57 +01:00
Samy Pessé bdbfbf7300 Merge doc for ebook and cover 2016-03-09 11:10:52 +01:00
Samy Pessé ccac3d2c3c Publish compleet reference of markdown syntax for gitbook 2016-03-08 17:26:36 +01:00
Samy Pessé f07278490f Add question about rtl to faq 2016-03-08 09:26:46 +01:00
Samy Pessé f1880fc53c Fix incorrect link in themes doc 2016-03-07 15:20:27 +01:00
Samy Pessé e8fe4a2dea Improve docs for plugins 2016-03-07 15:15:41 +01:00
Samy Pessé 1d79507665 Add book to templating context 2016-03-07 14:52:30 +01:00
Samy Pessé f05dc67887 Improve version suggestion 2016-03-07 14:06:58 +01:00
Samy Pessé ca5e57c3b7 Bump version to 3.0.0-pre.5 2016-03-07 12:26:52 +01:00
Samy Pessé 6b1fabdd92 Update theme-default@1.0.0-pre.5 2016-03-07 12:26:22 +01:00
642 changed files with 24889 additions and 8006 deletions
+3 -1
View File
@@ -1,2 +1,4 @@
docs/**/*
test/node_modules/**/*
_book/**/*
node_modules/**/*
test/**/*
+7 -15
View File
@@ -1,19 +1,11 @@
{
"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
"browser": true,
"mocha": true
},
"extends": "eslint:recommended"
}
"globals": {
"expect": true
},
"extends": "gitbook"
}
+30
View File
@@ -0,0 +1,30 @@
<!--
IMPORTANT NOTICE
Only post things related to the gitbook CLI
For GitBook.com, the Editor, or anything else
please go to
https://github.com/GitbookIO/feedback/issues
Please open separate issues for each bug report.
-->
### What is the current behavior?
<!--
- For bugs, provide the steps to reproduce
- Provide the GitBook version being used (`gitbook -V`)
- Provide system informations (OS and version)
- Provide your username and the URL of the book
-->
### What is the expected behavior?
<!--
Thank you for giving us your feedback!
-- The GitBook Team
-->
+5 -2
View File
@@ -22,14 +22,17 @@ build/Release
# Dependency directory
# Deployed apps should consider commenting this line out:
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
/node_modules
node_modules
# vim swapfile
*.swp
# GitBook with babel
/packages/gitbook-core/lib
/packages/gitbook-plugin/lib
# Output of documentation
_book
book.pdf
book.epub
book.mobi
+5 -1
View File
@@ -1,10 +1,14 @@
sudo: false
language: node_js
os:
- linux
- osx
node_js:
- "stable"
- "4.1"
- "0.12"
before_install:
- npm install svgexport -g
before_script:
- npm run bootstrap
after_success:
- npm run lint
+4 -3
View File
@@ -7,9 +7,10 @@ Names below are ordered by first contribution.
Author
------
- Samy Pessé <samypesse@gmail.com> (@SamyPesse)
- Aaron O'Mullan <aaron.omullan@gmail.com> (@AaronO)
- Samy Pessé <samy@gitbook.com> (@SamyPesse)
- Aaron O'Mullan <aaron@gitbook.com> (@AaronO)
- Johan Preynat <johan@gitbook.com> (@jpreynat)
- Nicolas Gaborit <nicolas@gitbook.com> (@soreine)
Contributors
------------
+65 -2
View File
@@ -2,7 +2,63 @@
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)
## 4.0.0
- **Breaking Changes:** Most plugins and themes should be incompatible with this version
- GitBook rendering is now done using React, which brings better APIs for plugins and a more interactive website.
- *Performances*: Installation of plugins is much faster thanks to `ied`
- *Performances*: Git conrefs are faster over large books
- *Website Feature:* new default plugin `copy-code` to copy code blocks content in one click
- *Website Feature:* new default plugin `heading-anchors` to have clickable headings
- *Website Feature:* `fontsettings` is now only controlling the font size, and can work on multiple themes
- *Plugin API*: hook `page` can no longer modify the content, only the page's attributes
- *Plugin API*: plugins can no longer export resources, instead use the `_assets` folder
- [ ] *Plugin API*: testing framework
- [ ] `gitbook init` creates `.gitignore` if not found
- [ ] Ignore files are no longer copied to the output
## 3.2.2
- Fix catching parsing errors leading to possible missing pages in generated book
- Rollback markdown parser to `kramed` until `markup-it` is stable enough
## 3.2.1
- Fix bug on Firefox when navigating to an url containing an hash
- Update nunjucks to fix JS error when page contains a lot of templating blocks
## 3.2.0
- Switch markdown parser from `kramed` to `markup-it`
- Fix support of `|` in tables
- Fix access to `output.name` in templating
- Fix i18n for website and PDF
- Fix minor scrolling issues on website
- Improve options of default search indexer (keywords, disabling per pages)
- Improve pertinence of search results
## 3.1.1
- Fix order of plugins during loading
- Fix error when using math and conrefs
- Fix target attribute for external links
- Fix serve command
## 3.1.0
- Always load themes after plugins
- README/Introduction doesn't require to be the first entry in the summary
- Highlight active entry in summary when scrolling a page with anchors
- Support empty parts in summary
## 3.0.3
- Fix redirection in `gitbook serve` when accessing a folder without trailing slash
- Fix links/annotations for glossary not being correctly resolved
- Fix regression for supporting uppercase filenames in structure files
- Fix `gitbook install` when using Git URLs for plugins
## 3.0.2
- Fix crash for anchor links
## 3.0.1
- Fix regression in link resolution when contain anchor
- Fix `structure.<name>` configuration not supporting filenames with dots
## 3.0.0
- 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
@@ -11,10 +67,17 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- `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
- Root folder for the book can be specified in the `"root"` property of the `book.json` file
- Multi-lingual books share assets folder
- YAML front matter is parsed and can extend page's properties
- Fix `uk` translation
- Fix heading ID including dashes
- Fix error in code highlighting for unknown languages
- Fix data-uri images being handled as external images
- Accept SSH url as plugin version
- Add templating blocks `markdown`, `asciidoc` and `markup`
- Better search experience
- Better default theme, more responsive and cleaner
## 2.6.7
- Fix bug with filenames including spaces
+51
View File
@@ -0,0 +1,51 @@
# Contributing
Want to contribute to GitBook? That would be awesome!
- [Send feedback](#send-feedback)
- [Reporting Bugs](#reporting-bugs)
- [Asking Questions](#asking-questions)
- [Submitting Pull Requests](#submitting-pull-requests)
- [Running Tests](#running-tests)
## Send feedback
We’ve done our best to test but your feedback is really important. If you encounter problems or have feedback about GitBook products (GitBook.com, GitBook Editor, or the Toolchain), please log an issue in the [GitbookIO/feedback](https://github.com/GitbookIO/feedback/issues) repository.
## Reporting Bugs
If you run into any weird behavior while using GitBook, feel free to open a new issue in this repository! To be most helpful, please include the steps to reproduce the bug as best you can, including the output of command `gitbook -V`.
## Asking Questions
Questions are very welcome :smile:! Previous questions that folks have asked are tagged with a [`question`](https://github.com/GitBookIO/gitbook/issues?q=is%3Aissue+is%3Aclosed+label%3Aquestion) label, so before opening a new issue double-check that someone hasn't asked it before. But if you don't see anything, or if you're not sure if it's the same, err on the side of asking!
We've also got a [GitBook Community Slack](https://slack.gitbook.com/) where you can ask questions and get answers from other people using GitBook.
## Submitting Pull Requests
All pull requests are super welcomed and greatly appreciated! Easy issues are marked with an [`easy-one`](https://github.com/GitBookIO/gitbook/issues?q=is%3Aopen+is%3Aissue+label%3Aeasy-one) label if you're looking for a simple place to get familiar with the code base.
Please include tests and docs with every pull request!
## Running Tests
To run the examples, you need to have the GitBook repository cloned to your computer. After that, you need to `cd` into the directory where you cloned it, and install the dependencies from `npm` using `yarn`.
```
$ yarn install
```
Then you'll need to bootstrap it:
```
$ npm run bootstrap
```
Which will also compile the source files. Then run the tests with:
```
$ npm test
```
+26 -18
View File
@@ -1,20 +1,28 @@
GitBook
=======
[![GitBook](https://raw.github.com/GitbookIO/gitbook/master/docs/images/banner.png)](https://www.gitbook.com)
[![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)
<p align="center">A <em>modern</em> customizable documentation format and toolchain</p>
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).
<p align="center"><a href="#features"><strong>Features</strong></a> · <a href="https://www.gitbook.com"><strong>Hosting</strong></a> · <a href="http://redux.js.org/"><strong>Example</strong></a> · <a href="https://www.gitbook.com/editor"><strong>Editor</strong></a> · <a href="https://toolchain.gitbook.com/"><strong>Documentation</strong></a> · <a href="./CONTRIBUTING.md"><strong>Contributing!</strong></a> · <a href="https://twitter.com/GitBookIO"><strong>Twitter</strong></a></p>
<br/>
![Preview](https://raw.github.com/GitbookIO/gitbook/master/docs/images/preview.png)
<p align="center">
<img src="https://badge.fury.io/js/gitbook.svg" />
<a href="https://travis-ci.org/GitbookIO/gitbook"><img src="https://travis-ci.org/GitbookIO/gitbook.png?branch=master" /></a>
<a href="https://ci.appveyor.com/project/GitBook/gitbook"><img src="https://ci.appveyor.com/api/projects/status/63nlflxcwmb2pue6?svg=true" /></a>
<a href="https://ci.appveyor.com/project/GitBook/gitbook"><img src="https://slack.gitbook.com/badge.svg" /></a>
</p>
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: [ReduxJS documentation](http://redux.js.org/).
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 [help.gitbook.com](http://help.gitbook.com/).
![Image](https://raw.github.com/GitbookIO/gitbook/master/preview.png)
Complete documentation is available at [toolchain.gitbook.com](http://toolchain.gitbook.com/).
## Getting started
@@ -32,14 +40,14 @@ We're always happy to help out with your books or any other questions you might
## Features
* [Output as a website or ebook (pdf, epub, mobi)](http://help.gitbook.com/format/output.html)
* [Multi-Languages](http://help.gitbook.com/format/languages.html)
* [Glossary](http://help.gitbook.com/format/glossary.html)
* [Cover](http://help.gitbook.com/format/cover.html)
* [AsciiDoc Support](http://help.gitbook.com/format/asciidoc.html)
* [Variables and Templating](http://help.gitbook.com/format/templating.html)
* [Content References](http://help.gitbook.com/format/conrefs.html)
* [Plugins](http://help.gitbook.com/format/plugins.html)
* 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
+11 -2
View File
@@ -5,13 +5,18 @@ init:
# Test against these versions of Node.js.
environment:
matrix:
- nodejs_version: "0.12"
- nodejs_version: "4.1"
- nodejs_version: "6"
- nodejs_version: "5"
- nodejs_version: "4"
matrix:
fast_finish: true
# 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
- set CI=true
# install svgexport
- npm install svgexport -g
# install modules
@@ -27,3 +32,7 @@ test_script:
# Don't actually build.
build: off
# Clone only last commit
shallow_clone: true
clone_depth: 1
+3 -7
View File
@@ -3,14 +3,10 @@ var pkg = require('./package.json');
module.exports = {
// Documentation for GitBook is stored under "docs"
root: './docs',
title: 'GitBook Documentation',
// Enforce use of GitBook v3
gitbook: pkg.version,
title: 'GitBook Toolchain Documentation',
// Use the "official" theme
plugins: ['theme-official', 'sitemap'],
theme: 'official',
plugins: ['sitemap'],
variables: {
version: pkg.version
@@ -18,7 +14,7 @@ module.exports = {
pluginsConfig: {
sitemap: {
hostname: 'https://docs.gitbook.com'
hostname: 'https://toolchain.gitbook.com'
}
}
};
+3 -4
View File
@@ -1,10 +1,10 @@
# GitBook Format Documentation
# GitBook Toolchain Documentation
This document aims to be a comprehensive guide to GitBook. It contains the full documentation for version **{{ book.version }}**. Help for GitBook.com specific questions can be found at [help.gitbook.com](https://help.gitbook.com).
### 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 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).
@@ -18,9 +18,8 @@ Check out the [GitBook Community Slack Channel](https://slack.gitbook.com), Stay
### FAQ
Some questions are frequently asked. If you have a problem you should [check this out](faq.md) first.
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.
+26 -4
View File
@@ -12,23 +12,45 @@
* [Configuration](config.md)
* [Glossary](lexicon.md)
* [Multi-Lingual](languages.md)
* [AsciiDoc](asciidoc.md)
* [Markdown](syntax/markdown.md)
* [Headings](syntax/markdown.md#headings)
* [Paragraphs](syntax/markdown.md#paragraphs)
* [Emphasis](syntax/markdown.md#emphasis)
* [Lists](syntax/markdown.md#lists)
* [Links](syntax/markdown.md#links)
* [Images](syntax/markdown.md#images)
* [Blockquotes](syntax/markdown.md#blockquotes)
* [Tables](syntax/markdown.md#tables)
* [Code](syntax/markdown.md#code)
* [HTML](syntax/markdown.md#html)
* [Footnotes](syntax/markdown.md#footnotes)
* [AsciiDoc](syntax/asciidoc.md)
* [eBook and PDF](ebook.md)
### Customization
* [Cover](cover.md)
* [Templating](templating/README.md)
* [Content References](templating/conrefs.md)
* [Variables](templating/variables.md)
* [Builtin](templating/builtin.md)
* [Plugins](plugins/README.md)
* [Create a plugin](plugins/create.md)
* [Hooks](plugins/hooks.md)
* [Blocks](plugins/blocks.md)
* [Filters](plugins/filters.md)
* [API & Context](plugins/api.md)
* [Test your plugin](plugins/testing.md)
* [Theming](themes/README.md)
### Plugin Development
* [Introduction](./api/README.md)
* [Node](./api/node.md)
* [Connect to the context](./api/connect.md)
* [Components](./api/components.md)
--
* [FAQ](faq.md)
* [Examples](examples.md)
* [Release notes](https://github.com/GitbookIO/gitbook/blob/master/CHANGES.md)
-26
View File
@@ -1,26 +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="{{ "faq.md"|resolveFile }}" class="btn btn-link pull-right hidden-xs">
F.A.Q
</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 %}
+24
View File
@@ -0,0 +1,24 @@
# Plugin Architecture
A GitBook plugin is a NPM package that follows a defined convention.
`gitbook-plugin` is a command line utility to help you create, test and release plugins.
### Bootstrap your first plugin
Install `gitbook-plugin` from NPM:
```
$ npm install gitbook-plugin -g
```
Then create your plugin:
```
$ gitbook-plugin create
```
You will be asked for a plugin name, and a few other things to complete the creation process.
### Publish your plugin
+86
View File
@@ -0,0 +1,86 @@
# Components
## Injection
Plugins can inject components by registering React components to some roles.
#### Register a components
During the initialization phase of your plugin, dispatch the `GitBook.registerComponent` action:
```js
dispatch(GitBook.registerComponent(MyCustomButton, { role: 'toolbar:buttons:left' }));
```
#### Roles
Custom roles can be use for interopability with other plugins, but GitBook and the default theme set a convention for common roles:
| Role | Description | Props |
| ---- | ----------- | ----- |
| `page:container` | DIV container for the page's content | `{ page: Page }` |
| `summary:container` | DIV container for the whole summary | `{ summary: Summary }` |
| `summary:parts` | DIV container for summary's parts | `{ parts: List<SummaryPart> }` |
| `summary:part` | DIV for a specific part | `{ part: SummaryPart }` |
| `summary:articles` | UL container for a part's articles | `{ articles: List<SummaryArticle> }` |
| `summary:article` | LI for a specific article | `{ article: SummaryArticle }` |
## Default Components
#### `GitBook.Head`
Extends the meta tags of the page. This is an alias for [react-helmet](https://github.com/nfl/react-helmet).
```js
<GitBook.Head
title="My page"
/>
```
#### `GitBook.ImportCSS`
Import a CSS file by resolving the path correctly according to the current page:
```js
<GitBook.ImportCSS href="myfile.css" />
```
#### `GitBook.ImportJS`
Import a JS file by resolving the path correctly according to the current page:
```js
<GitBook.ImportJS src="mylib.js" />
```
#### `GitBook.InjectedComponent`
Inject a component matching a specific role:
```js
<GitBook.InjectedComponent matching={{ role: 'mycustomrole' }} props={{ someProp: 1 }}>
<b>Inner content</b>
</GitBook.InjectedComponent>
```
#### `GitBook.InjectedComponentSet`
Same API as `InjectedComponentSet` but render the matching components in chain instead of composed:
```js
<GitBook.InjectedComponentSet matching={{ role: 'mytoolbar' }} />
```
**Warning:** Children are discarded.
#### `GitBook.FlexLayout` and `GitBook.FlexBox`
A simple wrapper that provides a Flexbox layout with the given direction and style. Any additional props you set on the Flexbox are rendered.
```js
<GitBook.FlexLayout column>
<GitBook.FlexBox>First column</GitBook.FlexBox>
<GitBook.FlexBox>Second column</GitBook.FlexBox>
</GitBook.FlexLayout>
```
+33
View File
@@ -0,0 +1,33 @@
# Connect to the context
`GitBook.connect(Component, [mapStateToProps], [mapActionsToProps])` connects a react component to the GitBook context.
It does not modify the component class passed to it.
Instead, it returns a new, connected component class, for you to use.
### `mapStateToProps(state, [ownProps]): stateProps`
If specified, the component will subscribe to GitBook store updates. Any time it updates, `mapStateToProps` will be called. Its result must be a plain object, and it will be merged into the component’s props.
If you omit it, the component will not be subscribed to the GitBook store. If `ownProps` is specified as a second argument, its value will be the props passed to your component, and `mapStateToProps` will be additionally re-invoked whenever the component receives new props (e.g. if props received from a parent component have shallowly changed, and you use the `ownProps` argument, `mapStateToProps` is re-evaluated).
For example to render the title of the current page:
```js
const GitBook = require('gitbook-core');
let PageTitle = React.createClass({
render() {
const { page } = this.props;
return <h1>{page.title}</h1>;
}
});
function mapStateToProps(state) {
return { page: state.page };
}
PageTitle = GitBook.connect(PageTitle, mapStateToProps);
```
### `mapActionsToProps(actions, [dispatch])`
+44
View File
@@ -0,0 +1,44 @@
# Internationalize your plugin
GitBook has built-in support for internationalization. Plugins can register new languages and provide the right messages for different languages.
### Register locale and messages
The first step is to register messages for a language:
```js
module.exports = GitBook.createPlugin({
init: (dispatch, getState, { I18n }) => {
dispatch(I18n.registerLocale('en-US', {
MY_PLUGIN_MESSAGE: 'Hello World'
}));
}
});
```
### Render a message in a component
`GitBook.connect` adds a `i18n` prop to access the internationalization:
```js
const GitBook = require('gitbook-core');
const { React } = GitBook;
const MyButton = React.createClass({
propTypes: {
i18n: GitBook.Shapes.i18n
},
render() {
const { i18n } = this.props;
return (
<GitBook.Button>
{i18.t('MY_PLUGIN_MESSAGE')}
</GitBook.Button>
);
}
});
module.exports = GitBook.connect(MyButton);
```
+23
View File
@@ -0,0 +1,23 @@
# Navigation
### Listen to url change
Listen for changes to the current location:
```js
const onLocationChanged = (location) => {
console.log(location.pathname);
console.log(location.query);
console.log(location.hash);
};
module.exports = GitBook.createPlugin({
init: (dispatch, getState, { Navigation }) => {
dispatch(Navigation.listen(onLocationChanged));
}
});
```
The `onLocationChanged` will be triggered for initial state.
### Changing the url
+97
View File
@@ -0,0 +1,97 @@
# Node APIs
GitBooks provides different Node 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` interface is the central point of GitBook, it centralize all access read methods.
```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.writeFile('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`.
+10 -3
View File
@@ -7,6 +7,7 @@ GitBook allows you to customize your book using a flexible configuration. These
| Variable | Description |
| -------- | ----------- |
| `root` | Path to the root folder containing all the book's files, except `book.json`|
| `structure` | To specify paths for Readme, Summary, Glossary etc. See [Structure paragraph](#structure). |
| `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. |
@@ -19,18 +20,24 @@ GitBook allows you to customize your book using a flexible configuration. These
Plugins and their configurations are specified in the `book.json`. See [the plugins section](plugins/README.md) for more details.
Since version 3.0.0, GitBook can use themes. See [the theming section](themes/README.md) for more details.
| Variable | Description |
| -------- | ----------- |
| `plugins` | List of plugins to load |
| `pluginsConfig` |Configuration for plugins |
### Theme
### Structure
Since version 3.0.0, GitBook can use themes. See [the theming section](themes/README.md) for more details.
In addition to the `root` variable, you can tell Gitbook the name of the files for Readme, Summary, Glossary, Languages (instead of using the default names such as `README.md`).
These files must be at the root of your book (or the root of every language book). Paths such as `dir/MY_README.md` are not accepted.
| Variable | Description |
| -------- | ----------- |
| `theme` | The theme to use for the book |
| `structure.readme` | Readme file name (defaults to `README.md`) |
| `structure.summary` | Summary file name (defaults to `SUMMARY.md`) |
| `structure.glossary` | Glossary file name (defaults to `GLOSSARY.md`) |
| `structure.languages` | Languages file name (defaults to `LANGS.md`) |
### PDF Options
-21
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
+55
View File
@@ -0,0 +1,55 @@
# Generating eBooks and PDFs
GitBook can generates a website, but can also output content as ebook (ePub, Mobi, PDF).
```
# Generate a PDF file
$ gitbook pdf ./ ./mybook.pdf
# Generate an ePub file
$ gitbook epub ./ ./mybook.epub
# Generate a Mobi file
$ gitbook mobi ./ ./mybook.mobi
```
### Installing ebook-convert
`ebook-convert` is required to generate ebooks (epub, mobi, pdf).
##### GNU/Linux
Install the [Calibre application](https://calibre-ebook.com/download).
```
$ sudo aptitude install calibre
```
In some GNU/Linux distributions node is installed as nodejs, you need to manually create a symlink:
```
$sudo ln -s /usr/bin/nodejs /usr/bin/node
```
##### 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. You can either provide one yourself, or generate one using the [autocover plugin](https://plugins.gitbook.com/plugin/autocover).
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.
A good cover should respect the following guidelines:
* Size of 1800x2360 pixels for `cover.jpg`, 200x262 for `cover_small.jpg`
* No border
* Clearly visible book title
* Any important text should be visible in the small version
+2 -1
View File
@@ -23,5 +23,6 @@ More than 50,000 books have been published on [GitBook.com](https://www.gitbook.
### Documentation
- [DuckDuckHack Documentation](http://docs.duckduckhack.com) by [DuckDuckGo](https://duckduckgo.com/about)
- [Loomio Handbook](http://loomio.coop/) and [guide to using Loomio](https://loomio.gitbooks.io/manual/content/en/index.html) both by [Loomio](https://www.loomio.org/)
- [Enspiral Handbook](http://handbook.enspiral.com/) by [Enspiral](http://enspiral.com/)
- This documentation
+23 -1
View File
@@ -1,5 +1,9 @@
# 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.
@@ -10,13 +14,31 @@ Any text editor should work! But we advise using the [GitBook Editor](https://ww
---
#### Does GitBook supports RTL/bi-directional text ?
The GitBook format supports right to left, and bi-directional writing. To enable it, you either need to specify a language (ex: `ar`), or force GitBook to use RTL in your `book.json`:
```json
{
"language": "ar",
"direction": "rtl"
}
```
With version 3.0 of GitBook, it's automatically detected according to the content.
_Note that, while the output book will indeed respect RTL, the Editor doesn't support RTL writing yet_.
#### Should I use an `.html` or `.md` extensions in my links?
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).
Yes, GitBooks can be created in [sub-directories](structure.md#subdirectory). GitBook.com and the CLI also looks by default in a series 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).
---
Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Before

Width:  |  Height:  |  Size: 227 KiB

After

Width:  |  Height:  |  Size: 227 KiB

+3 -3
View File
@@ -2,13 +2,13 @@
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 :
The `GLOSSARY.md` format is a list of `h2` headings, along with a description paragraph:
```markdown
# Term
## Term
Definition for this term
# Another term
## Another term
With it's definition, this can contain bold text
and all other kinds of inline markup ...
```
+26 -3
View File
@@ -23,7 +23,26 @@ Adding a nested list to a parent chapter will create subchapters.
Each chapter has a dedicated page (`part#/README.md`) and is split into subchapters.
##### Example with subchapters split into parts
##### Anchors
Chapters in 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
@@ -37,9 +56,13 @@ Each chapter has a dedicated page (`part#/README.md`) and is split into subchapt
* [We love feedback](part2/feedback_please.md)
* [Better tools for authors](part2/better_tools.md)
----
* [Last part without title](part3/title.md)
```
Here, parts are just groups of chapters and do not have dedicated pages, but will show in the navigation for example.
Parts are just groups of chapters and do not have dedicated pages, but according to the theme, it will show in the navigation.
### Pages
@@ -49,7 +72,7 @@ Most of the files for GitBook use the Markdown syntax by default. GitBook infers
##### Example of a chapter file
``` markdown
```markdown
# Title of the chapter
This is a great introduction.
+1 -1
View File
@@ -1,6 +1,6 @@
# 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.
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 Analytics, etc.
### How to find plugins?
+97
View File
@@ -0,0 +1,97 @@
# 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/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.writeFile('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`.
+62
View File
@@ -0,0 +1,62 @@
# 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 %}
```
+12 -1
View File
@@ -8,13 +8,24 @@ A GitBook plugin is a node package published on NPM that follow a defined conven
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!"
}
}
}
}
```
+57
View File
@@ -0,0 +1,57 @@
# 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;
}
}
};
```
+89
View File
@@ -0,0 +1,89 @@
# 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();
});
}
}
```
+2 -3
View File
@@ -2,7 +2,7 @@
### Testing your plugin locally
Testing your plugin on your book before plushing it is possible using [npm link](https://docs.npmjs.com/cli/link).
Testing your plugin on your book before publishing it is possible using [npm link](https://docs.npmjs.com/cli/link).
In the plugin's folder, run:
@@ -10,7 +10,7 @@ In the plugin's folder, run:
$ npm link
```
The nin your book's folder:
Then in your book's folder:
```
$ npm link gitbook-plugin-<plugin's name>
@@ -19,4 +19,3 @@ $ 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.
+1 -2
View File
@@ -14,7 +14,7 @@ It integrates well with the [GitBook Editor](https://www.gitbook.com/editor).
Installing GitBook is easy and straightforward. Your system just needs to meet these two requirements:
* NodeJS (v4.0.0 and above are adviced)
* NodeJS (v4.0.0 and above is recommended)
* Windows, Linux, Unix, or Mac OS X
##### Install with NPM
@@ -66,4 +66,3 @@ You can use the options `--log=debug` and `--debug` to get better error messages
```
$ gitbook build ./ --log=debug --debug
```
+4
View File
@@ -0,0 +1,4 @@
/* Reduce size of logo to allow long versions like 3.0.0-pre.1 */
.gb-page-header .container .logo h1 {
font-size: 24px ;
}
+3 -4
View File
@@ -12,7 +12,7 @@ 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.
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.
@@ -60,6 +60,5 @@ observer producing a sense of wonder.
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"). ```
but now stands for "PHP: Hypertext Preprocessor").
```
+223
View File
@@ -0,0 +1,223 @@
# Markdown
Most of the examples from this documentation are in Markdown. Markdown is default parser for GitBook, but one can also opt for the [AsciiDoc syntax](asciidoc.md).
Here’s 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\*.
```
+11 -1
View File
@@ -21,7 +21,7 @@ This looks up username from the context and displays it. Variable names can have
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.
GitBook provides a set of [predefined variables](variables.md) from the context.
### Filters
@@ -87,3 +87,13 @@ Current version is {{ 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 %}
```
+19
View File
@@ -0,0 +1,19 @@
# 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
+1 -1
View File
@@ -51,5 +51,5 @@ This is the default content
# License
{% import "./LICENSE" %}
{% include "./LICENSE" %}
```
+36 -2
View File
@@ -10,22 +10,37 @@ The following is a reference of the available data during book's parsing and the
| `gitbook` | GitBook specific information |
| `page` | Current page specific information |
| `file` | File associated with the current page specific information |
| `readme` | Information about the Readme |
| `glossary` | Information about the Glossary |
| `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 |
| `book.[value]` | All other values under `variables` in the `book.json` are accessible here |
For example, with this `book.json`:
```json
{
variables: {
hello: "everyone"
}
}
```
... the following text `{{ book.hello }}` will expand to `everyone`.
### GitBook Variables
| Variable | Description |
| -------- | ----------- |
| `gitbook.time` | The current time (when you run the `gitbook` command) . |
| `gitbook.time` | The current time (when you run the `gitbook` command). |
| `gitbook.version` | Version of GitBook used to generate the book |
### File Variables
@@ -62,3 +77,22 @@ The whole table of contents (`SUMMARY.md`) can be accessed:
| `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` |
### Readme Variables
| Variable | Description |
| -------- | ----------- |
| `readme.path` | Path to the Readme in the book |
### Glossary Variables
| Variable | Description |
| -------- | ----------- |
| `glossary.path` | Path to the Glossary in the book |
+19 -6
View File
@@ -1,25 +1,38 @@
# 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`.
Since version 3.0.0, GitBook can be easily themed. Books use the [theme-default](https://github.com/GitbookIO/theme-default) theme by default.
> **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.
A theme is a plugin containing templates and assets. Overriding any individual template is optional, since themes always extend 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) |
| `_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
Authors can extend the templates of a theme directly from their book's source (without creating an external theme). Templates will be resolved in the `_layouts` folder of the book first, then in the installed plugins/themes.
### Extend instead of Forking
When you want to make your theme changes available to multiple books, instead of forking the default theme, you can extend it using the [templating syntax](../templating/README.md):
```html
{% extends template.self %}
{% block body %}
{{ super() }}
... This will be added to the "body" block
{% endblock %}
```
Take a look at the [API](https://github.com/GitbookIO/theme-api) theme for a more complete example.
### Publish a theme
+4
View File
@@ -0,0 +1,4 @@
{
"lerna": "2.0.0-beta.31",
"version": "4.0.0-alpha.3"
}
-69
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;
-99
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;
-8
View File
@@ -1,8 +0,0 @@
module.exports = {
Readme: require('./readme'),
Summary: require('./summary'),
Glossary: require('./glossary'),
Langs: require('./langs')
};
-81
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;
-26
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;
-348
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;
-388
View File
@@ -1,388 +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.get(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)
);
});
};
// ----- 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;
-140
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
};
-199
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();
});
}
}
]
};
-96
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;
-42
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;
-132
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;
-67
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
};
-28
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
};
-106
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;
-66
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;
-33
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
};
-7
View File
@@ -1,7 +0,0 @@
var Book = require('./book');
var cli = require('./cli');
module.exports = {
Book: Book,
commands: cli.commands
};
-67
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;
-140
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;
};
-276
View File
@@ -1,276 +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 PluginsManager = require('../plugins');
var TemplateEngine = require('../template');
/*
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 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 a default context for templates
Output.prototype.getContext = function() {
return _.extend(
{},
this.book.getContext(),
this.book.langs.getContext(),
this.book.summary.getContext(),
this.book.glossary.getContext(),
this.book.config.getContext()
);
};
// 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;
-67
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;
};
-190
View File
@@ -1,190 +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';
// 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;
-152
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;
};
-47
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.getContext();
// 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;
-270
View File
@@ -1,270 +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 conrefsLoader = require('./conrefs');
var Output = require('./base');
// Tranform a theme ID into a plugin
function themeID(plugin) {
return 'theme-' + plugin;
}
// 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() {
var themeName = that.book.config.get('theme');
that.theme = that.plugins.get(themeID(themeName));
that.themeDefault = that.plugins.get(themeID('default'));
if (!that.theme) {
throw new Error('Theme "' + themeName + '" is not installed, add "' + themeID(themeName) + '" to your "book.json"');
}
if (that.themeDefault.root != that.theme.root) {
that.log.info.ln('build using theme "' + themeName + '"');
}
// This list is ordered to give priority to templates in the book
var searchPaths = _.chain([
// The book itself can contains a "_layouts" folder
that.book.root,
// Installed plugin (it can be identical to themeDefault.root)
that.theme.root,
// Is default theme still installed
that.themeDefault? that.themeDefault.root : null
])
.compact()
.uniq()
.value();
// Load i18n
_.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 nunjucks.FileSystemLoader(_.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;
return Promise.serie([
// Assets from the book are already copied
// The order is reversed from the template's one
// Is default theme still installed
that.themeDefault && that.themeDefault.root != that.theme.root?
that.themeDefault.root : null,
// Installed plugin (it can be identical to themeDefault.root)
that.theme.root
], function(folder) {
if (!folder) return;
// Copy assets only if exists (don't fail otherwise)
var assetFolder = path.join(folder, '_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, // Delete "to" before
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.getContext());
})
// 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: {
// Same template but in the default theme
default: this.themeDefault? path.resolve(templatesPath(this.themeDefault.root), filename) : null,
// Same template but in the theme
theme: path.resolve(templatesPath(this.theme.root), filename)
},
plugins: {
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;
-280
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;
-250
View File
@@ -1,250 +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.get(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 _.extend(
{
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
})
},
gitbook.getContext(),
this.book.getContext(),
this.book.langs.getContext(),
this.book.summary.getContext(),
this.book.glossary.getContext(),
this.book.config.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.getContext(), {
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;
-60
View File
@@ -1,60 +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);
return nparser;
}
// Return a specific parser according to an extension
function getParser(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,
getForFile: getParserForFile
};
-45
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
};
-155
View File
@@ -1,155 +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);
}
// 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, or a list of plugins
PluginsManager.prototype.load = function(name) {
var that = this;
if (_.isArray(name)) {
return Promise.serie(name, function(_name) {
return that.load(_name);
});
}
return Promise()
// Initiate and load the plugin
.then(function() {
var plugin;
if (!_.isString(name)) plugin = name;
else plugin = new BookPlugin(that.book, name);
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 plugins = _.pluck(this.book.config.get('plugins'), 'name');
this.log.info.ln('loading', plugins.length, 'plugins');
return this.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;
-299
View File
@@ -1,299 +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) {
this.book = book;
this.log = this.book.log.prefix(pluginId);
this.id = pluginId;
this.npmId = registry.npmId(pluginId);
this.root;
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
// An optional folder to search in can be passed
BookPlugin.prototype.load = function(folder) {
var that = this;
if (this.isLoaded()) {
return Promise.reject(new Error('Plugin "' + this.id + '" is already loaded'));
}
// Fodlers to search plugins in
var searchPaths = _.compact([
folder,
this.book.resolve('node_modules'),
__dirname
]);
// Try loading plugins from different location
var p = Promise.some(searchPaths, function(baseDir) {
// Locate plugin and load pacjage.json
try {
var res = resolve.sync(that.npmId + '/package.json', { basedir: baseDir });
that.root = path.dirname(res);
that.packageInfos = require(res);
} catch (err) {
if (!isModuleNotFound(err)) throw err;
that.packageInfos = undefined;
that.content = undefined;
return false;
}
// Load plugin JS content
try {
that.content = require(resolve.sync(that.npmId, { basedir: baseDir }));
} 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
});
}
}
return true;
})
.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.packageInfos &&
this.packageInfos.name &&
this.packageInfos.engines &&
this.packageInfos.engines.gitbook
);
if (!this.isLoaded()) {
throw new Error('Couldn\'t locate plugin "' + this.id + '", Run \'gitbook install\' to install plugins from registry.');
}
if (!isValid) {
throw new Error('Invalid plugin "' + this.id + '"');
}
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;
-115
View File
@@ -1,115 +0,0 @@
var npm = require('npm');
var npmi = require('npmi');
var semver = require('semver');
var _ = require('lodash');
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.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');
});
}
module.exports = {
npmId: npmId,
pluginId: pluginId,
validateId: validateId,
resolve: resolveVersion,
link: linkPlugin,
install: installPlugin
};
-16
View File
@@ -1,16 +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
};
}
};
-431
View File
@@ -1,431 +0,0 @@
var _ = require('lodash');
var path = require('path');
var nunjucks = require('nunjucks');
var escapeStringRegexp = require('escape-string-regexp');
var Promise = require('../utils/promise');
var error = require('../utils/error');
var parsers = require('../parsers');
var defaultBlocks = require('./blocks');
var defaultFilters = require('./filters');
var Loader = require('./loader');
// Return extension name for a specific block
function blockExtName(name) {
return 'Block'+name+'Extension';
}
// Normalize the result of block process function
function normBlockResult(blk) {
if (_.isString(blk)) blk = { body: blk };
return blk;
}
function TemplateEngine(output) {
this.output = output;
this.book = output.book;
this.log = this.book.log;
// Create file loader
this.loader = new Loader(this);
// Create nunjucks instance
this.env = new nunjucks.Environment(
this.loader,
{
// Escaping is done after by the asciidoc/markdown parser
autoescape: false,
// Syntax
tags: {
blockStart: '{%',
blockEnd: '%}',
variableStart: '{{',
variableEnd: '}}',
commentStart: '{###',
commentEnd: '###}'
}
}
);
// List of tags shortcuts
this.shortcuts = [];
// Map of blocks bodies (that requires post-processing)
this.blockBodies = {};
// Map of added blocks
this.blocks = {};
// Bind methods
_.bindAll(this);
// Add default blocks and filters
this.addBlocks(defaultBlocks);
this.addFilters(defaultFilters);
}
// Bind a function to a context
// Filters and blocks are binded to this context
TemplateEngine.prototype.bindContext = function(func) {
var ctx = {
ctx: this.ctx,
output: this.output,
generator: this.output.name
};
return _.bind(func, ctx);
};
// Interpolate a string content to replace shortcuts according to the filetype
TemplateEngine.prototype.interpolate = function(filepath, source) {
var parser = parsers.get(path.extname(filepath));
var type = parser? parser.name : null;
return this.applyShortcuts(type, source);
};
// Add a new custom filter
TemplateEngine.prototype.addFilter = function(filterName, func) {
try {
this.env.getFilter(filterName);
this.log.error.ln('conflict in filters, "'+filterName+'" is already set');
return false;
} catch(e) {
// Filter doesn't exist
}
this.log.debug.ln('add filter "'+filterName+'"');
this.env.addFilter(filterName, this.bindContext(function() {
var ctx = this;
var args = Array.prototype.slice.apply(arguments);
var callback = _.last(args);
Promise()
.then(function() {
return func.apply(ctx, args.slice(0, -1));
})
.nodeify(callback);
}), true);
return true;
};
// Add multiple filters at once
TemplateEngine.prototype.addFilters = function(filters) {
_.each(filters, function(filter, name) {
this.addFilter(name, filter);
}, this);
};
// Return true if a block is defined
TemplateEngine.prototype.hasBlock = function(name) {
return this.env.hasExtension(blockExtName(name));
};
// Remove/Disable a block
TemplateEngine.prototype.removeBlock = function(name) {
if (!this.hasBlock(name)) return;
// Remove nunjucks extension
this.env.removeExtension(blockExtName(name));
// Cleanup shortcuts
this.shortcuts = _.reject(this.shortcuts, {
block: name
});
};
// Add a block
// Using the extensions of nunjucks: https://mozilla.github.io/nunjucks/api.html#addextension
TemplateEngine.prototype.addBlock = function(name, block) {
var that = this, Ext, extName;
// Block can be a simple function
if (_.isFunction(block)) block = { process: block };
block = _.defaults(block || {}, {
shortcuts: [],
end: 'end'+name,
blocks: []
});
extName = blockExtName(name);
if (!block.process) {
throw new Error('Invalid block "' + name + '", it should have a "process" method');
}
if (this.hasBlock(name) && !defaultBlocks[name]) {
this.log.warn.ln('conflict in blocks, "'+name+'" is already defined');
}
// Cleanup previous block
this.removeBlock(name);
this.log.debug.ln('add block \''+name+'\'');
this.blocks[name] = block;
Ext = function () {
this.tags = [name];
this.parse = function(parser, nodes) {
var body = null;
var lastBlockName = null;
var lastBlockArgs = null;
var allBlocks = block.blocks.concat([block.end]);
var subbodies = {};
var tok = parser.nextToken();
var args = parser.parseSignature(null, true);
parser.advanceAfterBlockEnd(tok.value);
do {
// Read body
var currentBody = parser.parseUntilBlocks.apply(parser, allBlocks);
// Handle body with previous block name and args
if (lastBlockName) {
subbodies[lastBlockName] = subbodies[lastBlockName] || [];
subbodies[lastBlockName].push({
body: currentBody,
args: lastBlockArgs
});
} else {
body = currentBody;
}
// Read new block
lastBlockName = parser.peekToken().value;
// Parse signature and move to the end of the block
if (lastBlockName != block.end) {
lastBlockArgs = parser.parseSignature(null, true);
parser.advanceAfterBlockEnd(lastBlockName);
}
} while (lastBlockName != block.end);
parser.advanceAfterBlockEnd();
var bodies = [body];
_.each(block.blocks, function(blockName) {
subbodies[blockName] = subbodies[blockName] || [];
if (subbodies[blockName].length === 0) {
subbodies[blockName].push({
args: new nodes.NodeList(),
body: new nodes.NodeList()
});
}
bodies.push(subbodies[blockName][0].body);
});
return new nodes.CallExtensionAsync(this, 'run', args, bodies);
};
this.run = function(context) {
var args = Array.prototype.slice.call(arguments, 1);
var callback = args.pop();
// Extract blocks
var blocks = args
.concat([])
.slice(-block.blocks.length);
// Eliminate blocks from list
if (block.blocks.length > 0) args = args.slice(0, -block.blocks.length);
// Extract main body and kwargs
var body = args.pop();
var kwargs = _.isObject(_.last(args))? args.pop() : {};
// Extract blocks body
var _blocks = _.map(block.blocks, function(blockName, i){
return {
name: blockName,
body: blocks[i]()
};
});
Promise()
.then(function() {
return that.applyBlock(name, {
body: body(),
args: args,
kwargs: kwargs,
blocks: _blocks
}, context);
})
// Process the block returned
.then(that.processBlock)
.nodeify(callback);
};
};
// Add the Extension
this.env.addExtension(extName, new Ext());
// Add shortcuts if any
if (!_.isArray(block.shortcuts)) {
block.shortcuts = [block.shortcuts];
}
_.each(block.shortcuts, function(shortcut) {
this.log.debug.ln('add template shortcut from "'+shortcut.start+'" to block "'+name+'" for parsers ', shortcut.parsers);
this.shortcuts.push({
block: name,
parsers: shortcut.parsers,
start: shortcut.start,
end: shortcut.end,
tag: {
start: name,
end: block.end
}
});
}, this);
};
// Add multiple blocks at once
TemplateEngine.prototype.addBlocks = function(blocks) {
_.each(blocks, function(block, name) {
this.addBlock(name, block);
}, this);
};
// Apply a block to some content
// This method result depends on the type of block (async or sync)
TemplateEngine.prototype.applyBlock = function(name, blk, ctx) {
var func, block, r;
block = this.blocks[name];
if (!block) throw new Error('Block not found "'+name+'"');
if (_.isString(blk)) {
blk = {
body: blk
};
}
blk = _.defaults(blk, {
args: [],
kwargs: {},
blocks: []
});
// Bind and call block processor
func = this.bindContext(block.process);
r = func.call(ctx || {}, blk);
if (Promise.isPromise(r)) return r.then(normBlockResult);
else return normBlockResult(r);
};
// Process the result of block in a context
TemplateEngine.prototype.processBlock = function(blk) {
blk = _.defaults(blk, {
parse: false,
post: undefined
});
blk.id = _.uniqueId('blk');
var toAdd = (!blk.parse) || (blk.post !== undefined);
// Add to global map
if (toAdd) this.blockBodies[blk.id] = blk;
// Parsable block, just return it
if (blk.parse) {
return blk.body;
}
// Return it as a position marker
return '@%@'+blk.id+'@%@';
};
// Render a string (without post processing)
TemplateEngine.prototype.render = function(content, context, options) {
options = _.defaults(options || {}, {
path: null
});
var filename = options.path;
// Setup path and type
if (options.path) {
options.path = this.book.resolve(options.path);
}
// Replace shortcuts
content = this.applyShortcuts(options.type, content);
return Promise.nfcall(this.env.renderString.bind(this.env), content, context, options)
.fail(function(err) {
throw error.TemplateError(err, {
filename: filename || '<inline>'
});
});
};
// Render a string with post-processing
TemplateEngine.prototype.renderString = function(content, context, options) {
return this.render(content, context, options)
.then(this.postProcess);
};
// Apply a shortcut to a string
TemplateEngine.prototype.applyShortcut = function(content, shortcut) {
var regex = new RegExp(
escapeStringRegexp(shortcut.start) + '([\\s\\S]*?[^\\$])' + escapeStringRegexp(shortcut.end),
'g'
);
return content.replace(regex, function(all, match) {
return '{% '+shortcut.tag.start+' %}'+ match + '{% '+shortcut.tag.end+' %}';
});
};
// Replace position markers of blocks by body after processing
// This is done to avoid that markdown/asciidoc processer parse the block content
TemplateEngine.prototype.replaceBlocks = function(content) {
var that = this;
return content.replace(/\@\%\@([\s\S]+?)\@\%\@/g, function(match, key) {
var blk = that.blockBodies[key];
if (!blk) return match;
var body = blk.body;
return body;
});
};
// Apply all shortcuts to a template
TemplateEngine.prototype.applyShortcuts = function(type, content) {
return _.chain(this.shortcuts)
.filter(function(shortcut) {
return _.contains(shortcut.parsers, type);
})
.reduce(this.applyShortcut, content)
.value();
};
// Post process content
TemplateEngine.prototype.postProcess = function(content) {
var that = this;
return Promise(content)
.then(that.replaceBlocks)
.then(function(_content) {
return Promise.serie(that.blockBodies, function(blk, blkId) {
return Promise()
.then(function() {
if (!blk.post) return;
return blk.post();
})
.then(function() {
delete that.blockBodies[blkId];
});
})
.thenResolve(_content);
});
};
module.exports = TemplateEngine;
-42
View File
@@ -1,42 +0,0 @@
var nunjucks = require('nunjucks');
var location = require('../utils/location');
/*
Simple nunjucks loader which is passing the reponsability to the Output
*/
var Loader = nunjucks.Loader.extend({
async: true,
init: function(engine, opts) {
this.engine = engine;
this.output = engine.output;
},
getSource: function(sourceURL, callback) {
var that = this;
this.output.onGetTemplate(sourceURL)
.then(function(out) {
// We disable cache since content is modified (shortcuts, ...)
out.noCache = true;
// Transform template before runnign it
out.source = that.engine.interpolate(out.path, out.source);
return out;
})
.nodeify(callback);
},
resolve: function(from, to) {
return this.output.onResolveTemplate(from, to);
},
// Handle all files as relative, so that nunjucks pass responsability to 'resolve'
isRelative: function(filename) {
return location.isRelative(filename);
}
});
module.exports = Loader;
-80
View File
@@ -1,80 +0,0 @@
var _ = require('lodash');
var childProcess = require('child_process');
var spawn = require('spawn-cmd').spawn;
var Promise = require('./promise');
// Execute a command
function exec(command, options) {
var d = Promise.defer();
var child = childProcess.exec(command, options, function(err, stdout, stderr) {
if (!err) {
return d.resolve();
}
err.message = stdout.toString('utf8') + stderr.toString('utf8');
d.reject(err);
});
child.stdout.on('data', function (data) {
d.notify(data);
});
child.stderr.on('data', function (data) {
d.notify(data);
});
return d.promise;
}
// Spawn an executable
function spawnCmd(command, args, options) {
var d = Promise.defer();
var child = spawn(command, args, options);
child.on('error', function(error) {
return d.reject(error);
});
child.stdout.on('data', function (data) {
d.notify(data);
});
child.stderr.on('data', function (data) {
d.notify(data);
});
child.on('close', function(code) {
if (code === 0) {
d.resolve();
} else {
d.reject(new Error('Error with command "'+command+'"'));
}
});
return d.promise;
}
// Transform an option object to a command line string
function escapeShellArg(s) {
s = s.replace(/"/g, '\\"');
return '"' + s + '"';
}
function optionsToShellArgs(options) {
return _.chain(options)
.map(function(value, key) {
if (value === null || value === undefined || value === false) return null;
if (value === true) return key;
return key + '=' + escapeShellArg(value);
})
.compact()
.value()
.join(' ');
}
module.exports = {
exec: exec,
spawn: spawnCmd,
optionsToShellArgs: optionsToShellArgs
};
-118
View File
@@ -1,118 +0,0 @@
var fs = require('graceful-fs');
var mkdirp = require('mkdirp');
var destroy = require('destroy');
var rmdir = require('rmdir');
var tmp = require('tmp');
var request = require('request');
var path = require('path');
var cp = require('cp');
var cpr = require('cpr');
var Promise = require('./promise');
// Write a stream to a file
function writeStream(filename, st) {
var d = Promise.defer();
var wstream = fs.createWriteStream(filename);
var cleanup = function() {
destroy(wstream);
wstream.removeAllListeners();
};
wstream.on('finish', function () {
cleanup();
d.resolve();
});
wstream.on('error', function (err) {
cleanup();
d.reject(err);
});
st.on('error', function(err) {
cleanup();
d.reject(err);
});
st.pipe(wstream);
return d.promise;
}
// Return a promise resolved with a boolean
function fileExists(filename) {
var d = Promise.defer();
fs.exists(filename, function(exists) {
d.resolve(exists);
});
return d.promise;
}
// Generate temporary file
function genTmpFile(opts) {
return Promise.nfcall(tmp.file, opts)
.get(0);
}
// Generate temporary dir
function genTmpDir(opts) {
return Promise.nfcall(tmp.dir, opts)
.get(0);
}
// Download an image
function download(uri, dest) {
return writeStream(dest, request(uri));
}
// Find a filename available in a folder
function uniqueFilename(base, filename) {
var ext = path.extname(filename);
filename = path.resolve(base, filename);
filename = path.join(path.dirname(filename), path.basename(filename, ext));
var _filename = filename+ext;
var i = 0;
while (fs.existsSync(filename)) {
_filename = filename + '_' + i + ext;
i = i + 1;
}
return Promise(path.relative(base, _filename));
}
// Create all required folder to create a file
function ensureFile(filename) {
var base = path.dirname(filename);
return Promise.nfcall(mkdirp, base);
}
// Remove a folder
function rmDir(base) {
return Promise.nfcall(rmdir, base, {
fs: fs
});
}
module.exports = {
exists: fileExists,
existsSync: fs.existsSync,
mkdirp: Promise.nfbind(mkdirp),
readFile: Promise.nfbind(fs.readFile),
writeFile: Promise.nfbind(fs.writeFile),
stat: Promise.nfbind(fs.stat),
statSync: fs.statSync,
readdir: Promise.nfbind(fs.readdir),
writeStream: writeStream,
copy: Promise.nfbind(cp),
copyDir: Promise.nfbind(cpr),
tmpFile: genTmpFile,
tmpDir: genTmpDir,
download: download,
uniqueFilename: uniqueFilename,
ensure: ensureFile,
rmDir: rmDir
};
-129
View File
@@ -1,129 +0,0 @@
var _ = require('lodash');
var path = require('path');
var crc = require('crc');
var URI = require('urijs');
var pathUtil = require('./path');
var Promise = require('./promise');
var command = require('./command');
var fs = require('./fs');
var GIT_PREFIX = 'git+';
function Git() {
this.tmpDir;
this.cloned = {};
}
// Return an unique ID for a combinaison host/ref
Git.prototype.repoID = function(host, ref) {
return crc.crc32(host+'#'+(ref || '')).toString(16);
};
// Allocate a temporary folder for cloning repos in it
Git.prototype.allocateDir = function() {
var that = this;
if (this.tmpDir) return Promise();
return fs.tmpDir()
.then(function(dir) {
that.tmpDir = dir;
});
};
// Clone a git repository if non existant
Git.prototype.clone = function(host, ref) {
var that = this;
return this.allocateDir()
// Return or clone the git repo
.then(function() {
// Unique ID for repo/ref combinaison
var repoId = that.repoID(host, ref);
// Absolute path to the folder
var repoPath = path.join(that.tmpDir, repoId);
if (that.cloned[repoId]) return repoPath;
// Clone repo
return command.exec('git clone '+host+' '+repoPath)
// Checkout reference if specified
.then(function() {
that.cloned[repoId] = true;
if (!ref) return;
return command.exec('git checkout '+ref, { cwd: repoPath });
})
.thenResolve(repoPath);
});
};
// Get file from a git repo
Git.prototype.resolve = function(giturl) {
// Path to a file in a git repo?
if (!Git.isUrl(giturl)) {
if (this.resolveRoot(giturl)) return Promise(giturl);
return Promise(null);
}
if (_.isString(giturl)) giturl = Git.parseUrl(giturl);
if (!giturl) return Promise(null);
// Clone or get from cache
return this.clone(giturl.host, giturl.ref)
.then(function(repo) {
return path.resolve(repo, giturl.filepath);
});
};
// Return root of git repo from a filepath
Git.prototype.resolveRoot = function(filepath) {
var relativeToGit, repoId;
// No git repo cloned, or file is not in a git repository
if (!this.tmpDir || !pathUtil.isInRoot(this.tmpDir, filepath)) return null;
// Extract first directory (is the repo id)
relativeToGit = path.relative(this.tmpDir, filepath);
repoId = _.first(relativeToGit.split(path.sep));
if (!repoId) return;
// Return an absolute file
return path.resolve(this.tmpDir, repoId);
};
// Check if an url is a git dependency url
Git.isUrl = function(giturl) {
return (giturl.indexOf(GIT_PREFIX) === 0);
};
// Parse and extract infos
Git.parseUrl = function(giturl) {
var ref, uri, fileParts, filepath;
if (!Git.isUrl(giturl)) return null;
giturl = giturl.slice(GIT_PREFIX.length);
uri = new URI(giturl);
ref = uri.fragment() || null;
uri.fragment(null);
// Extract file inside the repo (after the .git)
fileParts = uri.path().split('.git');
filepath = fileParts.length > 1? fileParts.slice(1).join('.git') : '';
if (filepath[0] == '/') filepath = filepath.slice(1);
// Recreate pathname without the real filename
uri.path(_.first(fileParts)+'.git');
return {
host: uri.toString(),
ref: ref,
filepath: filepath
};
};
module.exports = Git;
-44
View File
@@ -1,44 +0,0 @@
var Promise = require('./promise');
var command = require('./command');
var fs = require('./fs');
var error = require('./error');
// Convert a svg file to a pmg
function convertSVGToPNG(source, dest, options) {
if (!fs.existsSync(source)) return Promise.reject(new error.FileNotFoundError({ filename: source }));
return command.spawn('svgexport', [source, dest])
.fail(function(err) {
if (err.code == 'ENOENT') {
err = error.RequireInstallError({
cmd: 'svgexport',
install: 'Install it using: "npm install svgexport -g"'
});
}
throw err;
})
.then(function() {
if (fs.existsSync(dest)) return;
throw new Error('Error converting '+source+' into '+dest);
});
}
// Convert a svg buffer to a png file
function convertSVGBufferToPNG(buf, dest) {
// Create a temporary SVG file to convert
return fs.tmpFile({
postfix: '.svg'
})
.then(function(tmpSvg) {
return fs.writeFile(tmpSvg, buf)
.then(function() {
return convertSVGToPNG(tmpSvg, dest);
});
});
}
module.exports = {
convertSVGToPNG: convertSVGToPNG,
convertSVGBufferToPNG: convertSVGBufferToPNG
};
-70
View File
@@ -1,70 +0,0 @@
var url = require('url');
var path = require('path');
// Is the url an external url
function isExternal(href) {
try {
return Boolean(url.parse(href).protocol);
} catch(err) {
return false;
}
}
// Inverse of isExternal
function isRelative(href) {
return !isExternal(href);
}
// Return true if the link is an achor
function isAnchor(href) {
try {
var parsed = url.parse(href);
return !!(!parsed.protocol && !parsed.path && parsed.hash);
} catch(err) {
return false;
}
}
// Normalize a path to be a link
function normalize(s) {
return path.normalize(s).replace(/\\/g, '/');
}
// Convert relative to absolute path
// dir: directory parent of the file currently in rendering process
// outdir: directory parent from the html output
function toAbsolute(_href, dir, outdir) {
if (isExternal(_href)) return _href;
outdir = outdir == undefined? dir : outdir;
_href = normalize(_href);
dir = normalize(dir);
outdir = normalize(outdir);
// Path "_href" inside the base folder
var hrefInRoot = path.normalize(path.join(dir, _href));
if (_href[0] == '/') hrefInRoot = path.normalize(_href.slice(1));
// Make it relative to output
_href = path.relative(outdir, hrefInRoot);
// Normalize windows paths
_href = normalize(_href);
return _href;
}
// Convert an absolute path to a relative path for a specific folder (dir)
// ('test/', 'hello.md') -> '../hello.md'
function relative(dir, file) {
return normalize(path.relative(dir, file));
}
module.exports = {
isExternal: isExternal,
isRelative: isRelative,
isAnchor: isAnchor,
normalize: normalize,
toAbsolute: toAbsolute,
relative: relative
};
-128
View File
@@ -1,128 +0,0 @@
var _ = require('lodash');
var util = require('util');
var color = require('bash-color');
var LEVELS = {
DEBUG: 0,
INFO: 1,
WARN: 2,
ERROR: 3,
DISABLED: 10
};
var COLORS = {
DEBUG: color.purple,
INFO: color.cyan,
WARN: color.yellow,
ERROR: color.red
};
function Logger(write, logLevel, prefix) {
if (!(this instanceof Logger)) return new Logger(write, logLevel);
this._write = write || function(msg) { process.stdout.write(msg); };
this.lastChar = '\n';
// Define log level
this.setLevel(logLevel);
_.bindAll(this);
// Create easy-to-use method like "logger.debug.ln('....')"
_.each(_.omit(LEVELS, 'DISABLED'), function(level, levelKey) {
levelKey = levelKey.toLowerCase();
this[levelKey] = _.partial(this.log, level);
this[levelKey].ln = _.partial(this.logLn, level);
this[levelKey].ok = _.partial(this.ok, level);
this[levelKey].fail = _.partial(this.fail, level);
this[levelKey].promise = _.partial(this.promise, level);
}, this);
}
// Create a new logger prefixed from this logger
Logger.prototype.prefix = function(prefix) {
return (new Logger(this._write, this.logLevel, prefix));
};
// Change minimum level
Logger.prototype.setLevel = function(logLevel) {
if (_.isString(logLevel)) logLevel = LEVELS[logLevel.toUpperCase()];
this.logLevel = logLevel;
};
// Print a simple string
Logger.prototype.write = function(msg) {
msg = msg.toString();
this.lastChar = _.last(msg);
return this._write(msg);
};
// Format a string using the first argument as a printf-like format.
Logger.prototype.format = function() {
return util.format.apply(util, arguments);
};
// Print a line
Logger.prototype.writeLn = function(msg) {
return this.write((msg || '')+'\n');
};
// Log/Print a message if level is allowed
Logger.prototype.log = function(level) {
if (level < this.logLevel) return;
var levelKey = _.findKey(LEVELS, function(v) { return v == level; });
var args = Array.prototype.slice.apply(arguments, [1]);
var msg = this.format.apply(this, args);
if (this.lastChar == '\n') {
msg = COLORS[levelKey](levelKey.toLowerCase()+':')+' '+msg;
}
return this.write(msg);
};
// Log/Print a line if level is allowed
Logger.prototype.logLn = function() {
if (this.lastChar != '\n') this.write('\n');
var args = Array.prototype.slice.apply(arguments);
args.push('\n');
return this.log.apply(this, args);
};
// Log a confirmation [OK]
Logger.prototype.ok = function(level) {
var args = Array.prototype.slice.apply(arguments, [1]);
var msg = this.format.apply(this, args);
if (arguments.length > 1) {
this.logLn(level, color.green('>> ') + msg.trim().replace(/\n/g, color.green('\n>> ')));
} else {
this.log(level, color.green('OK'), '\n');
}
};
// Log a "FAIL"
Logger.prototype.fail = function(level) {
return this.log(level, color.red('ERROR') + '\n');
};
// Log state of a promise
Logger.prototype.promise = function(level, p) {
var that = this;
return p.
then(function(st) {
that.ok(level);
return st;
}, function(err) {
that.fail(level);
throw err;
});
};
Logger.LEVELS = LEVELS;
Logger.COLORS = COLORS;
module.exports = Logger;
-62
View File
@@ -1,62 +0,0 @@
var Q = require('q');
var _ = require('lodash');
// Reduce an array to a promise
function reduce(arr, iter, base) {
return _.reduce(arr, function(prev, elem, i) {
return prev.then(function(val) {
return iter(val, elem, i);
});
}, Q(base));
}
// Transform an array
function serie(arr, iter, base) {
return reduce(arr, function(before, item, i) {
return Q(iter(item, i))
.then(function(r) {
before.push(r);
return before;
});
}, []);
}
// Iter over an array and return first result (not null)
function some(arr, iter) {
return _.reduce(arr, function(prev, elem, i) {
return prev.then(function(val) {
if (val) return val;
return iter(elem, i);
});
}, Q());
}
// Map an array using an async (promised) iterator
function map(arr, iter) {
return reduce(arr, function(prev, entry, i) {
return Q(iter(entry, i))
.then(function(out) {
prev.push(out);
return prev;
});
}, []);
}
// Wrap a fucntion in a promise
function wrap(func) {
return _.wrap(func, function(_func) {
var args = Array.prototype.slice.call(arguments, 1);
return Q()
.then(function() {
return _func.apply(null, args);
});
});
}
module.exports = Q;
module.exports.reduce = reduce;
module.exports.map = map;
module.exports.serie = serie;
module.exports.some = some;
module.exports.wrapfn = wrap;
+13 -79
View File
@@ -1,92 +1,26 @@
{
"name": "gitbook",
"version": "3.0.0-pre.4",
"homepage": "https://www.gitbook.com",
"description": "Library and cmd utility to generate GitBooks",
"main": "lib/index.js",
"dependencies": {
"bash-color": "0.0.3",
"cheerio": "0.20.0",
"chokidar": "1.4.3",
"cp": "0.2.0",
"cpr": "1.0.0",
"crc": "3.4.0",
"deprecated": "0.0.1",
"destroy": "1.0.4",
"direction": "0.1.5",
"dom-serializer": "0.1.0",
"error": "7.0.2",
"escape-string-regexp": "1.0.5",
"eslint": "^2.2.0",
"front-matter": "2.0.6",
"gitbook-asciidoc": "1.0.2",
"gitbook-markdown": "1.0.3",
"gitbook-plugin-fontsettings": "1.0.2",
"gitbook-plugin-highlight": "2.0.0",
"gitbook-plugin-livereload": "0.0.1",
"gitbook-plugin-search": "1.2.0",
"gitbook-plugin-sharing": "1.0.1",
"gitbook-plugin-theme-default": "1.0.0-pre.4",
"github-slugid": "1.0.0",
"graceful-fs": "4.1.3",
"i18n-t": "1.0.0",
"ignore": "3.0.3",
"json-schema-defaults": "0.1.1",
"jsonschema": "1.1.0",
"juice": "1.10.0",
"lodash": "3.10.1",
"merge-defaults": "0.2.1",
"mkdirp": "0.5.1",
"moment": "2.11.2",
"npm": "3.7.5",
"npmi": "1.0.1",
"nunjucks": "2.3.0",
"nunjucks-autoescape": "1.0.1",
"q": "1.4.1",
"request": "2.69.0",
"resolve": "0.6.3",
"rmdir": "1.2.0",
"semver": "5.1.0",
"send": "0.13.1",
"spawn-cmd": "0.0.2",
"tiny-lr": "0.2.1",
"tmp": "0.0.28",
"urijs": "1.17.1"
},
"private": true,
"devDependencies": {
"eslint": "2.2.0",
"mocha": "2.4.5",
"should": "8.2.2"
"eslint": "3.12.2",
"eslint-config-gitbook": "1.5.0",
"lerna": "2.0.0-beta.31"
},
"scripts": {
"test": "node_modules/.bin/mocha --reporter spec --bail --timeout 15000 ./test/all.js",
"lint": "eslint ."
"lint": "eslint .",
"test": "lerna run --concurrency 1 test",
"bootstrap": "lerna bootstrap",
"clean": "lerna clean",
"dist": "lerna run --concurrency 1 prepublish",
"publish-version": "./scripts/publish.sh"
},
"repository": {
"type": "git",
"url": "https://github.com/GitbookIO/gitbook.git"
"url": "git+https://github.com/GitbookIO/gitbook.git"
},
"bin": {
"gitbook": "./bin/gitbook.js"
},
"keywords": [
"git",
"book",
"gitbook"
],
"author": "FriendCode Inc. <contact@gitbook.com>",
"author": "GitBook Inc. <contact@gitbook.com>",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/GitbookIO/gitbook/issues"
},
"contributors": [
{
"name": "Aaron O'Mullan",
"email": "aaron@gitbook.com"
},
{
"name": "Samy Pessé",
"email": "samy@gitbook.com"
}
]
"homepage": "https://github.com/GitbookIO/gitbook#readme"
}
+3
View File
@@ -0,0 +1,3 @@
{
"presets": ["es2015", "react", "stage-2"]
}
+1
View File
@@ -0,0 +1 @@
gitbook.core.min.js
+3
View File
@@ -0,0 +1,3 @@
src
!lib
!dist
+51
View File
@@ -0,0 +1,51 @@
{
"name": "gitbook-core",
"version": "4.0.0-alpha.3",
"description": "Core for GitBook plugins API",
"main": "./lib/index.js",
"dependencies": {
"bluebird": "^3.4.6",
"classnames": "^2.2.5",
"entities": "^1.1.1",
"history": "^4.3.0",
"html-tags": "^1.1.1",
"immutable": "^3.8.1",
"mousetrap": "1.6.0",
"react": "15.4.1",
"react-addons-css-transition-group": "15.4.1",
"react-dom": "15.4.1",
"react-helmet": "^3.1.0",
"react-immutable-proptypes": "^2.1.0",
"react-intl": "^2.1.5",
"react-redux": "^4.4.5",
"react-safe-html": "0.4.0",
"redux": "^3.5.2",
"redux-thunk": "^2.1.0",
"reflexbox": "^2.2.2",
"whatwg-fetch": "^1.0.0"
},
"devDependencies": {
"babel-cli": "^6.14.0",
"babel-preset-es2015": "^6.14.0",
"babel-preset-react": "^6.11.1",
"babel-preset-stage-2": "^6.13.0",
"browserify": "^13.1.0",
"envify": "^3.4.1",
"uglify-js": "^2.7.3"
},
"scripts": {
"dist-lib": "rm -rf lib/ && babel -d lib/ src/",
"dist-standalone": "mkdir -p dist && browserify -r ./lib/index.js:gitbook-core -r react -r react-dom ./lib/index.js | uglifyjs -c > ./dist/gitbook.core.min.js",
"dist": "npm run dist-lib && npm run dist-standalone",
"prepublish": "npm run dist"
},
"repository": {
"type": "git",
"url": "https://github.com/GitbookIO/gitbook.git"
},
"author": "GitBook Inc. <contact@gitbook.com>",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/GitbookIO/gitbook/issues"
}
}
@@ -0,0 +1,16 @@
module.exports = {
// Components
REGISTER_COMPONENT: 'components/register',
UNREGISTER_COMPONENT: 'components/unregister',
// Navigation
HISTORY_ACTIVATE: 'history/activate',
HISTORY_DEACTIVATE: 'history/deactivate',
HISTORY_LISTEN: 'history/listen',
HISTORY_UPDATE: 'history/update',
PAGE_FETCH_START: 'history/fetch:start',
PAGE_FETCH_END: 'history/fetch:end',
PAGE_FETCH_ERROR: 'history/fetch:error',
// i18n
I18N_REGISTER_LOCALE: 'i18n/register:locale'
};
@@ -0,0 +1,37 @@
const ACTION_TYPES = require('./TYPES');
/**
* Find all components matching a descriptor
* @param {List<ComponentDescriptor>} state
* @param {String} matching.role
*/
function findMatchingComponents(state, matching) {
return state
.filter(({descriptor}) => {
if (matching.role && matching.role !== descriptor.role) {
return false;
}
return true;
})
.map(component => component.Component);
}
/**
* Register a new component
* @param {React.Class} Component
* @param {Descriptor} descriptor
* @return {Action}
*/
function registerComponent(Component, descriptor) {
return {
type: ACTION_TYPES.REGISTER_COMPONENT,
Component,
descriptor
};
}
module.exports = {
findMatchingComponents,
registerComponent
};
@@ -0,0 +1,188 @@
const ACTION_TYPES = require('./TYPES');
const getPayload = require('../lib/getPayload');
const Location = require('../models/Location');
const SUPPORTED = (
typeof window !== 'undefined' &&
window.history && window.history.pushState && window.history.replaceState &&
// pushState isn't reliable on iOS until 5.
!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/)
);
/**
* Initialize the history
*/
function activate() {
return (dispatch, getState) => {
dispatch({
type: ACTION_TYPES.HISTORY_ACTIVATE,
listener: (location) => {
location = Location.fromNative(location);
const prevLocation = getState().history.location;
// Fetch page if required
if (!prevLocation || location.pathname !== prevLocation.pathname) {
dispatch(fetchPage(location.pathname));
}
// Signal location to listener
dispatch(emit());
// Update the location
dispatch({
type: ACTION_TYPES.HISTORY_UPDATE,
location
});
}
});
// Trigger for existing listeners
dispatch(emit());
};
}
/**
* Emit current location
* @param {List|Array<Function>} to?
*/
function emit(to) {
return (dispatch, getState) => {
const { listeners, client } = getState().history;
if (!client) {
return;
}
const location = Location.fromNative(client.location);
to = to || listeners;
to.forEach(handler => {
handler(location, dispatch, getState);
});
};
}
/**
* Cleanup the history
*/
function deactivate() {
return { type: ACTION_TYPES.HISTORY_DEACTIVATE };
}
/**
* Push a new url into the history
* @param {String|Location} location
* @return {Action} action
*/
function push(location) {
return (dispatch, getState) => {
const { client } = getState().history;
location = Location.fromNative(location);
if (SUPPORTED) {
client.push(location.toNative());
} else {
redirect(location.toString());
}
};
}
/**
* Replace current state in history
* @param {String|Location} location
* @return {Action} action
*/
function replace(location) {
return (dispatch, getState) => {
const { client } = getState().history;
location = Location.fromNative(location);
if (SUPPORTED) {
client.replace(location.toNative());
} else {
redirect(location.toString());
}
};
}
/**
* Hard redirection
* @param {String} uri
* @return {Action} action
*/
function redirect(uri) {
return () => {
window.location.href = uri;
};
}
/**
* Listen to url change
* @param {Function} listener
* @return {Action} action
*/
function listen(listener) {
return (dispatch, getState) => {
dispatch({ type: ACTION_TYPES.HISTORY_LISTEN, listener });
// Trigger for existing listeners
dispatch(emit([ listener ]));
};
}
/**
* Fetch a new page and update the store accordingly
* @param {String} pathname
* @return {Action} action
*/
function fetchPage(pathname) {
return (dispatch, getState) => {
dispatch({ type: ACTION_TYPES.PAGE_FETCH_START });
window.fetch(pathname, {
credentials: 'include'
})
.then(
response => {
return response.text();
}
)
.then(
html => {
const payload = getPayload(html);
if (!payload) {
throw new Error('No payload found in page');
}
dispatch({ type: ACTION_TYPES.PAGE_FETCH_END, payload });
}
)
.catch(
error => {
// dispatch(redirect(pathname));
dispatch({ type: ACTION_TYPES.PAGE_FETCH_ERROR, error });
}
);
};
}
/**
* Fetch a new article
* @param {SummaryArticle} article
* @return {Action} action
*/
function fetchArticle(article) {
return fetchPage(article.path);
}
module.exports = {
activate,
deactivate,
listen,
push,
replace,
fetchPage,
fetchArticle
};
+33
View File
@@ -0,0 +1,33 @@
const ACTION_TYPES = require('./TYPES');
/**
* Register messages for a locale
* @param {String} locale
* @param {Map<String:String>} messages
* @return {Action}
*/
function registerLocale(locale, messages) {
return { type: ACTION_TYPES.I18N_REGISTER_LOCALE, locale, messages };
}
/**
* Register multiple locales
* @param {Map<String:Object>} locales
* @return {Action}
*/
function registerLocales(locales) {
return (dispatch) => {
for (const locale in locales) {
if (!locales.hasOwnProperty(locale)) {
continue;
}
dispatch(registerLocale(locale, locales[locale]));
}
};
}
module.exports = {
registerLocale,
registerLocales
};
@@ -0,0 +1,56 @@
const React = require('react');
const HotKeys = require('./HotKeys');
/**
* Backdrop for modals, dropdown, etc. that covers the whole screen
* and handles click and pressing escape.
*
* <Backdrop onClose={onCloseModal} />
*/
const Backdrop = React.createClass({
propTypes: {
// Callback when backdrop is closed
onClose: React.PropTypes.func.isRequired,
// Z-index for the backdrop
zIndex: React.PropTypes.number,
children: React.PropTypes.node
},
getDefaultProps() {
return {
zIndex: 200
};
},
onClick(event) {
const { onClose } = this.props;
event.preventDefault();
event.stopPropagation();
onClose();
},
render() {
const { zIndex, children, onClose } = this.props;
const style = {
zIndex,
position: 'fixed',
top: 0,
right: 0,
width: '100%',
height: '100%'
};
const keyMap = {
'escape': onClose
};
return (
<HotKeys keyMap={keyMap}>
<div style={style} onClick={this.onClick}>{children}</div>
</HotKeys>
);
}
});
module.exports = Backdrop;
@@ -0,0 +1,22 @@
const React = require('react');
const classNames = require('classnames');
const Button = React.createClass({
propTypes: {
active: React.PropTypes.bool,
className: React.PropTypes.string,
children: React.PropTypes.node,
onClick: React.PropTypes.func
},
render() {
const { children, active, onClick } = this.props;
const className = classNames('GitBook-Button', this.props.className, {
active
});
return <button className={className} onClick={onClick}>{children}</button>;
}
});
module.exports = Button;

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